diff options
-rw-r--r-- | build.xml | 122 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/util/CommandLineParser.scala | 145 | ||||
-rw-r--r-- | src/partest-alternative/README | 50 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/Actions.scala (renamed from src/partest/scala/tools/partest/Actions.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/Alarms.scala (renamed from src/partest/scala/tools/partest/Alarms.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/BuildContributors.scala (renamed from src/partest/scala/tools/partest/BuildContributors.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/Categories.scala (renamed from src/partest/scala/tools/partest/Categories.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/Compilable.scala (renamed from src/partest/scala/tools/partest/Compilable.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/Config.scala (renamed from src/partest/scala/tools/partest/Config.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/Dispatcher.scala (renamed from src/partest/scala/tools/partest/Dispatcher.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/Entities.scala (renamed from src/partest/scala/tools/partest/Entities.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/Housekeeping.scala (renamed from src/partest/scala/tools/partest/Housekeeping.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/Partest.scala (renamed from src/partest/scala/tools/partest/Partest.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/PartestSpec.scala (renamed from src/partest/scala/tools/partest/PartestSpec.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/Properties.scala (renamed from src/partest/scala/tools/partest/Properties.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/Results.scala (renamed from src/partest/scala/tools/partest/Results.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/Runner.scala (renamed from src/partest/scala/tools/partest/Runner.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/Statistics.scala (renamed from src/partest/scala/tools/partest/Statistics.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/Universe.scala (renamed from src/partest/scala/tools/partest/Universe.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/ant/JavaTask.scala (renamed from src/partest/scala/tools/partest/ant/JavaTask.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/antlib.xml | 3 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/category/AllCategories.scala (renamed from src/partest/scala/tools/partest/category/AllCategories.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/category/Analysis.scala (renamed from src/partest/scala/tools/partest/category/Analysis.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/category/Compiler.scala (renamed from src/partest/scala/tools/partest/category/Compiler.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/category/Runner.scala (renamed from src/partest/scala/tools/partest/category/Runner.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/io/ANSIWriter.scala (renamed from src/partest/scala/tools/partest/io/ANSIWriter.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/io/Diff.java (renamed from src/partest/scala/tools/partest/io/Diff.java) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/io/DiffPrint.java (renamed from src/partest/scala/tools/partest/io/DiffPrint.java) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/io/JUnitReport.scala (renamed from src/partest/scala/tools/partest/io/JUnitReport.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/io/Logging.scala (renamed from src/partest/scala/tools/partest/io/Logging.scala) | 0 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/nest/StreamAppender.scala | 94 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/package.scala | 45 | ||||
-rw-r--r-- | src/partest-alternative/scala/tools/partest/util/package.scala (renamed from src/partest/scala/tools/partest/util/package.scala) | 0 | ||||
-rw-r--r-- | src/partest/README | 76 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/PartestDefaults.scala | 30 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/PartestTask.scala | 287 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/antlib.xml | 3 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/AntRunner.scala | 30 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/CompileManager.scala | 197 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/ConsoleFileManager.scala | 190 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/ConsoleRunner.scala | 209 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/Diff.java | 874 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/DiffPrint.java | 607 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/DirectRunner.scala | 78 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/FileManager.scala | 110 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/NestRunner.scala | 16 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/NestUI.scala | 118 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/PathSettings.scala | 41 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/ReflectiveRunner.scala | 88 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/RunnerUtils.scala | 29 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/TestFile.scala | 49 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/Worker.scala | 1071 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/package.scala | 59 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/utils/PrintMgr.scala | 52 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/utils/Properties.scala | 18 | ||||
-rw-r--r-- | test/disabled/pos/spec-traits.scala (renamed from test/files/pos/spec-traits.scala) | 0 | ||||
-rw-r--r-- | test/disabled/pos/t1254/t1254.java (renamed from test/files/pos/t1254/t1254.java) | 0 | ||||
-rw-r--r-- | test/disabled/run/stream_length.check | 1 | ||||
-rw-r--r-- | test/disabled/run/stream_length.scala (renamed from test/files/run/stream_length.scala) | 0 | ||||
-rw-r--r-- | test/disabled/run/t2946/Parsers.scala (renamed from test/files/run/t2946/Parsers.scala) | 0 | ||||
-rw-r--r-- | test/disabled/run/t2946/ResponseCommon.scala (renamed from test/files/run/t2946/ResponseCommon.scala) | 0 | ||||
-rw-r--r-- | test/disabled/run/t2946/Test.scala (renamed from test/files/run/t2946/Test.scala) | 0 | ||||
-rw-r--r-- | test/disabled/scalacheck/redblack.scala (renamed from test/files/scalacheck/redblack.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-neg/function0.check (renamed from test/continuations/neg/function0.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-neg/function0.scala (renamed from test/continuations/neg/function0.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-neg/function2.check (renamed from test/continuations/neg/function2.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-neg/function2.scala (renamed from test/continuations/neg/function2.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-neg/function3.check (renamed from test/continuations/neg/function3.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-neg/function3.scala (renamed from test/continuations/neg/function3.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-neg/infer0.check (renamed from test/continuations/neg/infer0.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-neg/infer0.scala (renamed from test/continuations/neg/infer0.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-neg/infer2.check (renamed from test/continuations/neg/infer2.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-neg/infer2.scala (renamed from test/continuations/neg/infer2.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-neg/lazy.check (renamed from test/continuations/neg/lazy.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-neg/lazy.scala (renamed from test/continuations/neg/lazy.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-neg/t1929.check (renamed from test/continuations/neg/t1929.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-neg/t1929.scala (renamed from test/continuations/neg/t1929.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-neg/t2285.check (renamed from test/continuations/neg/t2285.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-neg/t2285.scala (renamed from test/continuations/neg/t2285.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-neg/t2949.check (renamed from test/continuations/neg/t2949.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-neg/t2949.scala (renamed from test/continuations/neg/t2949.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-neg/trycatch2.check (renamed from test/continuations/neg/trycatch2.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-neg/trycatch2.scala (renamed from test/continuations/neg/trycatch2.scala) | 0 | ||||
-rwxr-xr-x | test/files/continuations-run/basics.check (renamed from test/continuations/run/basics.check) | 0 | ||||
-rwxr-xr-x | test/files/continuations-run/basics.scala (renamed from test/continuations/run/basics.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/function1.check (renamed from test/continuations/run/function1.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/function1.scala (renamed from test/continuations/run/function1.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/function4.check (renamed from test/continuations/run/function4.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/function4.scala (renamed from test/continuations/run/function4.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/function5.check (renamed from test/continuations/run/function5.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/function5.scala (renamed from test/continuations/run/function5.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/function6.check (renamed from test/continuations/run/function6.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/function6.scala (renamed from test/continuations/run/function6.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/ifelse0.check (renamed from test/continuations/run/ifelse0.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/ifelse0.scala (renamed from test/continuations/run/ifelse0.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/ifelse1.check (renamed from test/continuations/run/ifelse1.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/ifelse1.scala (renamed from test/continuations/run/ifelse1.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/ifelse2.check (renamed from test/continuations/run/ifelse2.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/ifelse2.scala (renamed from test/continuations/run/ifelse2.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/ifelse3.check (renamed from test/continuations/run/ifelse3.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/ifelse3.scala (renamed from test/continuations/run/ifelse3.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/infer1.scala (renamed from test/continuations/run/infer1.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/match0.check (renamed from test/continuations/run/match0.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/match0.scala (renamed from test/continuations/run/match0.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/match1.check (renamed from test/continuations/run/match1.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/match1.scala (renamed from test/continuations/run/match1.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/match2.check (renamed from test/continuations/run/match2.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/match2.scala (renamed from test/continuations/run/match2.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/t1807.check (renamed from test/continuations/run/t1807.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/t1807.scala (renamed from test/continuations/run/t1807.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/t1808.scala (renamed from test/continuations/run/t1808.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/t1820.scala (renamed from test/continuations/run/t1820.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/t1821.check (renamed from test/continuations/run/t1821.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/t1821.scala (renamed from test/continuations/run/t1821.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/t2864.check (renamed from test/continuations/run/t2864.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/t2864.scala (renamed from test/continuations/run/t2864.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/t2934.check (renamed from test/continuations/run/t2934.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/t2934.scala (renamed from test/continuations/run/t2934.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/t3199.check (renamed from test/continuations/run/t3199.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/t3199.scala (renamed from test/continuations/run/t3199.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/t3199b.check (renamed from test/continuations/run/t3199b.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/t3199b.scala (renamed from test/continuations/run/t3199b.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/t3223.check (renamed from test/continuations/run/t3223.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/t3223.scala (renamed from test/continuations/run/t3223.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/t3225.check (renamed from test/continuations/run/t3225.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/t3225.scala (renamed from test/continuations/run/t3225.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/trycatch0.check (renamed from test/continuations/run/trycatch0.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/trycatch0.scala (renamed from test/continuations/run/trycatch0.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/trycatch1.check (renamed from test/continuations/run/trycatch1.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/trycatch1.scala (renamed from test/continuations/run/trycatch1.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/while0.check (renamed from test/continuations/run/while0.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/while0.scala (renamed from test/continuations/run/while0.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/while1.check (renamed from test/continuations/run/while1.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/while1.scala (renamed from test/continuations/run/while1.scala) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/while2.check (renamed from test/continuations/run/while2.check) | 0 | ||||
-rw-r--r-- | test/files/continuations-run/while2.scala (renamed from test/continuations/run/while2.scala) | 0 | ||||
-rw-r--r-- | test/files/jvm/genericNest.scala (renamed from test/files/jvm/genericNest/genericNest.scala) | 0 | ||||
-rw-r--r-- | test/files/jvm/methvsfield.java | 11 | ||||
-rw-r--r-- | test/files/jvm/methvsfield.scala | 4 | ||||
-rw-r--r-- | test/files/jvm/nest.java | 38 | ||||
-rw-r--r-- | test/files/jvm/nest.scala | 21 | ||||
-rw-r--r-- | test/files/jvm/outerEnum.scala (renamed from test/files/jvm/outerEnum/outerEnum.scala) | 0 | ||||
-rw-r--r-- | test/files/jvm/t1652.check | 2 | ||||
-rw-r--r-- | test/files/lib/annotations.jar.desired.sha1 (renamed from test/files/jvm/lib/annotations.jar.desired.sha1) | 0 | ||||
-rw-r--r-- | test/files/lib/enums.jar.desired.sha1 (renamed from test/files/jvm/outerEnum/enums.jar.desired.sha1) | 0 | ||||
-rw-r--r-- | test/files/lib/genericNest.jar.desired.sha1 (renamed from test/files/jvm/genericNest/genericNest.jar.desired.sha1) | 0 | ||||
-rw-r--r-- | test/files/lib/methvsfield.jar.desired.sha1 | 1 | ||||
-rw-r--r-- | test/files/lib/nest.jar.desired.sha1 (renamed from test/files/jvm/lib/nest.jar.desired.sha1) | 0 | ||||
-rw-r--r-- | test/files/lib/scalacheck.jar.desired.sha1 (renamed from lib/scalacheck.jar.desired.sha1) | 0 | ||||
-rw-r--r-- | test/files/neg/bug414.scala | 2 | ||||
-rw-r--r-- | test/files/neg/migration28.check | 3 | ||||
-rw-r--r-- | test/files/res/bug687.check | 5 | ||||
-rw-r--r-- | test/files/run/numbereq.scala | 2 | ||||
-rw-r--r-- | test/files/run/programmatic-main.check | 2 | ||||
-rwxr-xr-x | test/partest | 25 | ||||
-rwxr-xr-x | test/partest.bat | 4 | ||||
-rw-r--r-- | test/pending/jvm/t1464.check | 1 | ||||
-rw-r--r-- | test/pending/pos/t0644.scala | 11 | ||||
-rw-r--r-- | test/pending/run/bug2365/Test.scala | 35 | ||||
-rw-r--r-- | test/pending/run/bug2365/bug2365.javaopts | 1 | ||||
-rwxr-xr-x | test/pending/run/bug2365/run | 13 | ||||
-rw-r--r-- | test/pending/run/subarray.check | 2 |
162 files changed, 4712 insertions, 163 deletions
@@ -147,9 +147,7 @@ PROPERTIES <property name="lib.dir" value="${basedir}/lib"/> <property name="lib-ant.dir" value="${lib.dir}/ant"/> <property name="src.dir" value="${basedir}/src"/> - <property name="partest.rootdir" location="test" /> - <property name="partest.srcdir.default" value="files" /> - <property name="partest.options" value="" /> + <property name="partest.dir" value="${basedir}/test"/> <!-- Loads custom properties definitions --> <property file="${basedir}/build.properties"/> @@ -178,7 +176,7 @@ PROPERTIES <property name="dists.dir" value="${basedir}/dists"/> <property name="copyright.string" value="Copyright 2002-2010, LAMP/EPFL"/> - <property name="partest.version.number" value="0.9.3"/> + <property name="partest.version.number" value="0.9.2"/> <!-- These are NOT the flags used to run SuperSabbus, but the ones written into the script runners created with scala.tools.ant.ScalaTool --> @@ -186,11 +184,13 @@ PROPERTIES <!-- if ANT_OPTS is already set by the environment, it will be unaltered, but if it is unset it will take this default value. --> - <property name="env.ANT_OPTS" value="-Xms128M -Xmx1024M -Xss1M -XX:MaxPermSize=128M" /> + <property name="env.ANT_OPTS" value="-Xms512M -Xmx1536M -Xss1M -XX:MaxPermSize=128M" /> <!-- to find max heap usage: -Xaprof ; currently at 980M for locker.comp --> + <echo message="Using ANT_OPTS: ${env.ANT_OPTS}" /> - <property name="scalacfork.jvmargs" value="${env.ANT_OPTS}" /> - <echo message="Using scalacfork.jvmargs: ${scalacfork.jvmargs}" /> + <property + name="scalacfork.jvmargs" + value="${env.ANT_OPTS}"/> <!-- =========================================================================== INITIALISATION @@ -204,9 +204,7 @@ INITIALISATION <property name="scalac.args.quickonly" value=""/> <property name="scalac.args.all" value="${scalac.args} ${scalac.args.optimise}"/> - <echo message="Using scalac.args.all: ${scalac.args.all}"/> - <echo message="Using javac.args: ${javac.args}"/> - <property name="scalac.args.quick" value="${scalac.args.all} ${scalac.args.quickonly}"/> + <property name="scalac.args.quick" value="${scalac.args.all} ${scalac.args.quickonly}"/> <!-- Setting-up Ant contrib tasks --> <taskdef resource="net/sf/antcontrib/antlib.xml" classpath="${lib.dir}/ant/ant-contrib.jar"/> <!-- This is the start time for the distribution --> @@ -1498,54 +1496,68 @@ BOOTRAPING TEST AND TEST SUITE <exclude name="misc/scala-devel/plugins/*.jar"/> </same> </target> + + <!-- this target will run only those tests found in test/debug --> + <target name="test.debug"> + <antcall target="test.suite"> + <param name="partest.srcdir" value="debug" /> + </antcall> + </target> - <target name="test.suite" depends="pack.done"> - <partest classpathref="pack.classpath"> - <env key="PATH" path="${build-pack.dir}/bin:${env.PATH}" /> - <sysproperty key="partest.timeout" value="36000" /> - <sysproperty key="partest.test-warning" value="600" /> - <sysproperty key="partest.test-timeout" value="5400" /> - <sysproperty key="partest.srcdir" value="files" /> - <sysproperty key="partest.scalacopts" value="${scalac.args.all}" /> - <sysproperty key="partest.javacopts" value="${javac.args}" /> - <syspropertyset> - <propertyref prefix="partest"/> - </syspropertyset> + <target name="test.run" depends="pack.done"> + <partest showlog="yes" erroronfailed="yes" javacmd="${java.home}/bin/java" + timeout="1200000" + scalacopts="${scalac.args.optimise}"> + <compilationpath> + <path refid="pack.classpath"/> + <fileset dir="${partest.dir}/files/lib" includes="*.jar" /> + </compilationpath> + <runtests dir="${partest.dir}/files"> + <include name="run/**/*.scala"/> + <include name="jvm/**/*.scala"/> + </runtests> </partest> </target> - <target name="test.continuations.suite" depends="pack.done"> - <partest classpathref="pack.classpath"> - <env key="PATH" path="${build-pack.dir}/bin:${env.PATH}" /> - <sysproperty key="partest.srcdir" value="continuations" /> - <sysproperty key="partest.scalacopts" value="${scalac.args.optimise} -Xpluginsdir ${build-quick.dir}/misc/scala-devel/plugins -Xplugin-require:continuations -P:continuations:enable" /> - <sysproperty key="partest.runsets" value="neg run" /> - <syspropertyset> - <propertyref prefix="partest"/> - </syspropertyset> + <target name="test.suite" depends="pack.done"> + <property name="partest.srcdir" value="files" /> + <partest showlog="yes" erroronfailed="yes" javacmd="${java.home}/bin/java" + timeout="2400000" + srcdir="${partest.srcdir}" + scalacopts="${scalac.args.optimise}"> + <compilationpath> + <path refid="pack.classpath"/> + <pathelement location="${pack.dir}/lib/scala-swing.jar"/> + <fileset dir="${partest.dir}/files/lib" includes="*.jar" /> + </compilationpath> + <postests dir="${partest.dir}/${partest.srcdir}/pos" includes="*.scala"/> + <negtests dir="${partest.dir}/${partest.srcdir}/neg" includes="*.scala"/> + <runtests dir="${partest.dir}/${partest.srcdir}"> + <include name="run/**/*.scala"/> + </runtests> + <jvmtests dir="${partest.dir}/${partest.srcdir}/jvm" includes="*.scala"/> + <scalachecktests dir="${partest.dir}/${partest.srcdir}/scalacheck" includes="**/*.scala"/> + <residenttests dir="${partest.dir}/${partest.srcdir}/res" includes="*.res"/> + <buildmanagertests dir="${partest.dir}/${partest.srcdir}/buildmanager" includes="*"/> + <scalaptests dir="${partest.dir}/${partest.srcdir}/scalap" includes="**/*.scala"/> + <!-- <scripttests dir="${partest.dir}/${partest.srcdir}/script" includes="*.scala"/> --> </partest> </target> - - <target name="test.partest-opt" depends="pack.done"> - <antcall target="test.partest"> - <param name="scalac.args.optimise" value="-optimise"/> - </antcall> - </target> - <target name="test.partest" depends="pack.done"> - <partest classpathref="pack.classpath"> - <env key="PATH" path="${build-pack.dir}/bin:${env.PATH}" /> - <sysproperty key="partest.srcdir" value="partest-tests" /> - <sysproperty key="partest.scalacopts" value="${scalac.args.all}" /> - <sysproperty key="partest.javacopts" value="${javac.args}" /> - <sysproperty key="partest.verbose" value="true" /> - <sysproperty key="partest.trace" value="true" /> - <sysproperty key="partest.debug" value="true" /> - <sysproperty key="partest.test-timeout" value="25" /> - <sysproperty key="partest.scalacopts" value="${scalac.args.optimise}" /> - <syspropertyset> - <propertyref prefix="partest"/> - </syspropertyset> + <target name="test.continuations.suite" depends="pack.done"> + <property name="partest.srcdir" value="files" /> + <partest showlog="yes" erroronfailed="yes" javacmd="${java.home}/bin/java" + timeout="2400000" + srcdir="${partest.srcdir}" + scalacopts="${scalac.args.optimise} -Xpluginsdir ${build-quick.dir}/misc/scala-devel/plugins -Xplugin-require:continuations -P:continuations:enable"> + <compilationpath> + <path refid="pack.classpath"/> + <fileset dir="${partest.dir}/files/lib" includes="*.jar" /> + </compilationpath> + <negtests dir="${partest.dir}/${partest.srcdir}/continuations-neg" includes="*.scala"/> + <runtests dir="${partest.dir}/${partest.srcdir}"> + <include name="continuations-run/**/*.scala"/> + </runtests> </partest> </target> @@ -1767,7 +1779,7 @@ POSITIONS <target name="test.positions" depends="quick.comp"> <antcall target="test.positions.tests.sub" inheritRefs="true"> - <param name="test.tests.srcs" value="${partest.rootdir}/${partest.srcdir.default}/positions"/> + <param name="test.tests.srcs" value="${partest.dir}/files/positions"/> </antcall> <antcall target="test.positions.sub" inheritRefs="true"> <param name="test.srcs" value="${src.dir}/compiler"/> @@ -1791,13 +1803,13 @@ POSITIONS <param name="test.srcs" value="${src.dir}/scalap"/> </antcall> <antcall target="test.positions.tests.sub" inheritRefs="true"> - <param name="test.tests.srcs" value="${partest.rootdir}/${partest.srcdir.default}/pos"/> + <param name="test.tests.srcs" value="${partest.dir}/files/pos"/> </antcall> <antcall target="test.positions.tests.sub" inheritRefs="true"> - <param name="test.tests.srcs" value="${partest.rootdir}/${partest.srcdir.default}/run"/> + <param name="test.tests.srcs" value="${partest.dir}/files/run"/> </antcall> <antcall target="test.positions.tests.sub" inheritRefs="true"> - <param name="test.tests.srcs" value="${partest.rootdir}/${partest.srcdir.default}/neg"/> + <param name="test.tests.srcs" value="${partest.dir}/files/neg"/> </antcall> </target> diff --git a/src/compiler/scala/tools/nsc/util/CommandLineParser.scala b/src/compiler/scala/tools/nsc/util/CommandLineParser.scala new file mode 100644 index 0000000000..16d79d5776 --- /dev/null +++ b/src/compiler/scala/tools/nsc/util/CommandLineParser.scala @@ -0,0 +1,145 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.nsc +package util + +import scala.util.parsing.combinator._ +import scala.util.parsing.input.{ Reader } +import scala.util.parsing.input.CharArrayReader.EofCh +import scala.collection.mutable.ListBuffer + +/** A simple command line parser to replace the several different + * simple ones spread around trunk. + * + * XXX Note this has been completely obsolesced by scala.tools.cmd. + * I checked it back in as part of rolling partest back a month + * rather than go down the rabbit hole of unravelling dependencies. + */ + +trait ParserUtil extends Parsers { + class ParserPlus[+T](underlying: Parser[T]) { + def !~>[U](p: => Parser[U]): Parser[U] = (underlying ~! p) ^^ { case a~b => b } + def <~![U](p: => Parser[U]): Parser[T] = (underlying ~! p) ^^ { case a~b => a } + } + protected implicit def parser2parserPlus[T](p: Parser[T]): ParserPlus[T] = new ParserPlus(p) +} + +case class CommandLine( + args: List[String], + unaryArguments: List[String], + binaryArguments: List[String] +) { + def this(args: List[String]) = this(args, Nil, Nil) + def this(args: Array[String]) = this(args.toList, Nil, Nil) + def this(line: String) = this(CommandLineParser tokenize line, Nil, Nil) + + def withUnaryArgs(xs: List[String]) = copy(unaryArguments = xs) + def withBinaryArgs(xs: List[String]) = copy(binaryArguments = xs) + + def originalArgs = args + def assumeBinary = true + def enforceArity = true + def onlyKnownOptions = false + + val Terminator = "--" + val ValueForUnaryOption = "true" // so if --opt is given, x(--opt) = true + + def mapForUnary(opt: String) = Map(opt -> ValueForUnaryOption) + def errorFn(msg: String) = println(msg) + + /** argMap is option -> argument (or "" if it is a unary argument) + * residualArgs are what is left after removing the options and their args. + */ + lazy val (argMap, residualArgs) = { + val residualBuffer = new ListBuffer[String] + + def stripQuotes(s: String) = { + def isQuotedBy(c: Char) = s.length > 0 && s.head == c && s.last == c + if (List('"', '\'') exists isQuotedBy) s.tail.init else s + } + + def isValidOption(s: String) = !onlyKnownOptions || (unaryArguments contains s) || (binaryArguments contains s) + def isOption(s: String) = (s startsWith "-") && (isValidOption(s) || { unknownOption(s) ; false }) + def isUnary(s: String) = isOption(s) && (unaryArguments contains s) + def isBinary(s: String) = isOption(s) && !isUnary(s) && (assumeBinary || (binaryArguments contains s)) + + def unknownOption(opt: String) = + errorFn("Option '%s' not recognized.".format(opt)) + def missingArg(opt: String, what: String) = + errorFn("Option '%s' requires argument, found %s instead.".format(opt, what)) + + def loop(args: List[String]): Map[String, String] = { + def residual(xs: List[String]) = { residualBuffer ++= xs ; Map[String, String]() } + if (args.isEmpty) return Map() + val hd :: rest = args + if (rest.isEmpty) { + if (isBinary(hd) && enforceArity) + missingArg(hd, "EOF") + + if (isOption(hd)) mapForUnary(hd) else residual(args) + } + else + if (hd == Terminator) residual(rest) + else { + val hd1 :: hd2 :: rest = args + + if (hd2 == Terminator) mapForUnary(hd1) ++ residual(rest) + else if (isUnary(hd1)) mapForUnary(hd1) ++ loop(hd2 :: rest) + else if (isBinary(hd1)) { + // Disabling this check so + // --scalacopts "-verbose" works. We can't tell if it's quoted, + // the shell does us in. + // + // if (isOption(hd2) && enforceArity) + // missingArg(hd1, hd2) + + Map(hd1 -> hd2) ++ loop(rest) + } + else { residual(List(hd1)) ++ loop(hd2 :: rest) } + } + } + + (loop(args), residualBuffer map stripQuotes toList) + } + + def isSet(arg: String) = args contains arg + def get(arg: String) = argMap get arg + def getOrElse(arg: String, orElse: => String) = if (isSet(arg)) apply(arg) else orElse + def apply(arg: String) = argMap(arg) + + override def toString() = "CommandLine(\n%s)\n" format (args map (" " + _ + "\n") mkString) +} + +object CommandLineParser extends RegexParsers with ParserUtil { + override def skipWhitespace = false + + def elemExcept(xs: Elem*): Parser[Elem] = elem("elemExcept", x => x != EofCh && !(xs contains x)) + def elemOf(xs: Elem*): Parser[Elem] = elem("elemOf", xs contains _) + def escaped(ch: Char): Parser[String] = "\\" + ch + def mkQuoted(ch: Char): Parser[String] = ( + elem(ch) !~> rep(escaped(ch) | elemExcept(ch)) <~ ch ^^ (_.mkString) + | failure("Unmatched %s in input." format ch) + ) + + /** Apparently windows can't deal with the quotes sticking around. */ + lazy val squoted: Parser[String] = mkQuoted('\'') // ^^ (x => "'%s'" format x) + lazy val dquoted: Parser[String] = mkQuoted('"') // ^^ (x => "\"" + x + "\"") + lazy val token: Parser[String] = """\S+""".r + + lazy val argument: Parser[String] = squoted | dquoted | token + lazy val commandLine: Parser[List[String]] = phrase(repsep(argument, whiteSpace)) + + class ParseException(msg: String) extends RuntimeException(msg) + + def tokenize(line: String): List[String] = tokenize(line, x => throw new ParseException(x)) + def tokenize(line: String, errorFn: String => Unit): List[String] = { + parse(commandLine, line.trim) match { + case Success(args, _) => args + case NoSuccess(msg, rest) => errorFn(msg) ; Nil + } + } + def apply(line: String) = new CommandLine(tokenize(line)) +} diff --git a/src/partest-alternative/README b/src/partest-alternative/README new file mode 100644 index 0000000000..c7673fe2f8 --- /dev/null +++ b/src/partest-alternative/README @@ -0,0 +1,50 @@ +If you're looking for something to read, I suggest running ../test/partest +with no arguments, which at this moment prints this: + +Usage: partest [<options>] [<test> <test> ...] + <test>: a path to a test designator, typically a .scala file or a directory. + Examples: files/pos/test1.scala, files/res/bug785 + + Test categories: + --all run all tests (default, unless no options given) + --pos Compile files that are expected to build + --neg Compile files that are expected to fail + --run Test JVM backend + --jvm Test JVM backend + --res Run resident compiler scenarii + --buildmanager Run Build Manager scenarii + --scalacheck Run Scalacheck tests + --script Run script files + --shootout Run shootout tests + --scalap Run scalap tests + + Test "smart" categories: + --grep run all tests with a source file containing <expr> + --failed run all tests which failed on the last run + + Specifying paths and additional flags, ~ means repository root: + --rootdir path from ~ to partest (default: test) + --builddir path from ~ to test build (default: build/pack) + --srcdir path from --rootdir to sources (default: files) + --javaopts flags to java on all runs (overrides JAVA_OPTS) + --scalacopts flags to scalac on all tests (overrides SCALAC_OPTS) + --pack alias for --builddir build/pack + --quick alias for --builddir build/quick + + Options influencing output: + --trace show the individual steps taken by each test + --show-diff show diff between log and check file + --show-log show log on failures + --dry-run do not run tests, only show their traces. + --terse be less verbose (almost silent except for failures) + --verbose be more verbose (additive with --trace) + --debug maximum debugging output + --ansi print output in color + + Other options: + --timeout Timeout in seconds + --cleanup delete all stale files and dirs before run + --nocleanup do not delete any logfiles or object dirs + --stats collect and print statistics about the tests + --validate examine test filesystem for inconsistencies + --version print version diff --git a/src/partest/scala/tools/partest/Actions.scala b/src/partest-alternative/scala/tools/partest/Actions.scala index cb60152b71..cb60152b71 100644 --- a/src/partest/scala/tools/partest/Actions.scala +++ b/src/partest-alternative/scala/tools/partest/Actions.scala diff --git a/src/partest/scala/tools/partest/Alarms.scala b/src/partest-alternative/scala/tools/partest/Alarms.scala index f38d8d6268..f38d8d6268 100644 --- a/src/partest/scala/tools/partest/Alarms.scala +++ b/src/partest-alternative/scala/tools/partest/Alarms.scala diff --git a/src/partest/scala/tools/partest/BuildContributors.scala b/src/partest-alternative/scala/tools/partest/BuildContributors.scala index 64c7e07bc3..64c7e07bc3 100644 --- a/src/partest/scala/tools/partest/BuildContributors.scala +++ b/src/partest-alternative/scala/tools/partest/BuildContributors.scala diff --git a/src/partest/scala/tools/partest/Categories.scala b/src/partest-alternative/scala/tools/partest/Categories.scala index 172cca74b4..172cca74b4 100644 --- a/src/partest/scala/tools/partest/Categories.scala +++ b/src/partest-alternative/scala/tools/partest/Categories.scala diff --git a/src/partest/scala/tools/partest/Compilable.scala b/src/partest-alternative/scala/tools/partest/Compilable.scala index ddaa277842..ddaa277842 100644 --- a/src/partest/scala/tools/partest/Compilable.scala +++ b/src/partest-alternative/scala/tools/partest/Compilable.scala diff --git a/src/partest/scala/tools/partest/Config.scala b/src/partest-alternative/scala/tools/partest/Config.scala index 288a3034e9..288a3034e9 100644 --- a/src/partest/scala/tools/partest/Config.scala +++ b/src/partest-alternative/scala/tools/partest/Config.scala diff --git a/src/partest/scala/tools/partest/Dispatcher.scala b/src/partest-alternative/scala/tools/partest/Dispatcher.scala index 2a9d99ab60..2a9d99ab60 100644 --- a/src/partest/scala/tools/partest/Dispatcher.scala +++ b/src/partest-alternative/scala/tools/partest/Dispatcher.scala diff --git a/src/partest/scala/tools/partest/Entities.scala b/src/partest-alternative/scala/tools/partest/Entities.scala index bea505b594..bea505b594 100644 --- a/src/partest/scala/tools/partest/Entities.scala +++ b/src/partest-alternative/scala/tools/partest/Entities.scala diff --git a/src/partest/scala/tools/partest/Housekeeping.scala b/src/partest-alternative/scala/tools/partest/Housekeeping.scala index a624ca8adb..a624ca8adb 100644 --- a/src/partest/scala/tools/partest/Housekeeping.scala +++ b/src/partest-alternative/scala/tools/partest/Housekeeping.scala diff --git a/src/partest/scala/tools/partest/Partest.scala b/src/partest-alternative/scala/tools/partest/Partest.scala index b3fe9a98ef..b3fe9a98ef 100644 --- a/src/partest/scala/tools/partest/Partest.scala +++ b/src/partest-alternative/scala/tools/partest/Partest.scala diff --git a/src/partest/scala/tools/partest/PartestSpec.scala b/src/partest-alternative/scala/tools/partest/PartestSpec.scala index c25119b3af..c25119b3af 100644 --- a/src/partest/scala/tools/partest/PartestSpec.scala +++ b/src/partest-alternative/scala/tools/partest/PartestSpec.scala diff --git a/src/partest/scala/tools/partest/Properties.scala b/src/partest-alternative/scala/tools/partest/Properties.scala index 4eeb0359ec..4eeb0359ec 100644 --- a/src/partest/scala/tools/partest/Properties.scala +++ b/src/partest-alternative/scala/tools/partest/Properties.scala diff --git a/src/partest/scala/tools/partest/Results.scala b/src/partest-alternative/scala/tools/partest/Results.scala index 5d0e300136..5d0e300136 100644 --- a/src/partest/scala/tools/partest/Results.scala +++ b/src/partest-alternative/scala/tools/partest/Results.scala diff --git a/src/partest/scala/tools/partest/Runner.scala b/src/partest-alternative/scala/tools/partest/Runner.scala index 1a28e60896..1a28e60896 100644 --- a/src/partest/scala/tools/partest/Runner.scala +++ b/src/partest-alternative/scala/tools/partest/Runner.scala diff --git a/src/partest/scala/tools/partest/Statistics.scala b/src/partest-alternative/scala/tools/partest/Statistics.scala index 2ea3c6e8f0..2ea3c6e8f0 100644 --- a/src/partest/scala/tools/partest/Statistics.scala +++ b/src/partest-alternative/scala/tools/partest/Statistics.scala diff --git a/src/partest/scala/tools/partest/Universe.scala b/src/partest-alternative/scala/tools/partest/Universe.scala index 942fc1a8be..942fc1a8be 100644 --- a/src/partest/scala/tools/partest/Universe.scala +++ b/src/partest-alternative/scala/tools/partest/Universe.scala diff --git a/src/partest/scala/tools/partest/ant/JavaTask.scala b/src/partest-alternative/scala/tools/partest/ant/JavaTask.scala index 6740554dd8..6740554dd8 100644 --- a/src/partest/scala/tools/partest/ant/JavaTask.scala +++ b/src/partest-alternative/scala/tools/partest/ant/JavaTask.scala diff --git a/src/partest-alternative/scala/tools/partest/antlib.xml b/src/partest-alternative/scala/tools/partest/antlib.xml new file mode 100644 index 0000000000..af36f11368 --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/antlib.xml @@ -0,0 +1,3 @@ +<antlib> + <taskdef name="partest" classname="scala.tools.partest.ant.JavaTask"/> +</antlib> diff --git a/src/partest/scala/tools/partest/category/AllCategories.scala b/src/partest-alternative/scala/tools/partest/category/AllCategories.scala index 953f80324b..953f80324b 100644 --- a/src/partest/scala/tools/partest/category/AllCategories.scala +++ b/src/partest-alternative/scala/tools/partest/category/AllCategories.scala diff --git a/src/partest/scala/tools/partest/category/Analysis.scala b/src/partest-alternative/scala/tools/partest/category/Analysis.scala index 2c6c208ee5..2c6c208ee5 100644 --- a/src/partest/scala/tools/partest/category/Analysis.scala +++ b/src/partest-alternative/scala/tools/partest/category/Analysis.scala diff --git a/src/partest/scala/tools/partest/category/Compiler.scala b/src/partest-alternative/scala/tools/partest/category/Compiler.scala index 49775d5031..49775d5031 100644 --- a/src/partest/scala/tools/partest/category/Compiler.scala +++ b/src/partest-alternative/scala/tools/partest/category/Compiler.scala diff --git a/src/partest/scala/tools/partest/category/Runner.scala b/src/partest-alternative/scala/tools/partest/category/Runner.scala index 10bf5794a9..10bf5794a9 100644 --- a/src/partest/scala/tools/partest/category/Runner.scala +++ b/src/partest-alternative/scala/tools/partest/category/Runner.scala diff --git a/src/partest/scala/tools/partest/io/ANSIWriter.scala b/src/partest-alternative/scala/tools/partest/io/ANSIWriter.scala index 0ddcd97a5f..0ddcd97a5f 100644 --- a/src/partest/scala/tools/partest/io/ANSIWriter.scala +++ b/src/partest-alternative/scala/tools/partest/io/ANSIWriter.scala diff --git a/src/partest/scala/tools/partest/io/Diff.java b/src/partest-alternative/scala/tools/partest/io/Diff.java index c7a3d42f30..c7a3d42f30 100644 --- a/src/partest/scala/tools/partest/io/Diff.java +++ b/src/partest-alternative/scala/tools/partest/io/Diff.java diff --git a/src/partest/scala/tools/partest/io/DiffPrint.java b/src/partest-alternative/scala/tools/partest/io/DiffPrint.java index 2b2ad93ec7..2b2ad93ec7 100644 --- a/src/partest/scala/tools/partest/io/DiffPrint.java +++ b/src/partest-alternative/scala/tools/partest/io/DiffPrint.java diff --git a/src/partest/scala/tools/partest/io/JUnitReport.scala b/src/partest-alternative/scala/tools/partest/io/JUnitReport.scala index 63ae200020..63ae200020 100644 --- a/src/partest/scala/tools/partest/io/JUnitReport.scala +++ b/src/partest-alternative/scala/tools/partest/io/JUnitReport.scala diff --git a/src/partest/scala/tools/partest/io/Logging.scala b/src/partest-alternative/scala/tools/partest/io/Logging.scala index 52239ffb2c..52239ffb2c 100644 --- a/src/partest/scala/tools/partest/io/Logging.scala +++ b/src/partest-alternative/scala/tools/partest/io/Logging.scala diff --git a/src/partest-alternative/scala/tools/partest/nest/StreamAppender.scala b/src/partest-alternative/scala/tools/partest/nest/StreamAppender.scala new file mode 100644 index 0000000000..8cebcf1685 --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/nest/StreamAppender.scala @@ -0,0 +1,94 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +import java.io._ + +object StreamAppender { + def wrapIn(in: InputStream): BufferedReader = new BufferedReader(new InputStreamReader(in)) + def wrapIn(reader: Reader): BufferedReader = new BufferedReader(reader) + def wrapIn(str: String): BufferedReader = new BufferedReader(new StringReader(str)) + + def wrapOut(out: OutputStream): PrintWriter = new PrintWriter(new OutputStreamWriter(out), true) + def wrapOut(writer: Writer): PrintWriter = new PrintWriter(writer, true) + def wrapOut(): PrintWriter = wrapOut(new StringWriter) + + def apply(reader: BufferedReader, writer: Writer): StreamAppender = + new StreamAppender(reader, wrapOut(writer)) + + def apply(reader: Reader, writer: Writer): StreamAppender = + apply(wrapIn(reader), writer) + + def apply(in: InputStream, writer: Writer): StreamAppender = + apply(wrapIn(in), writer) + + def apply(str: String, writer: Writer): StreamAppender = + apply(wrapIn(str), writer) + + def apply(in: File, out: File): StreamAppender = + apply(new FileReader(in), new FileWriter(out)) + + def appendToString(in1: InputStream, in2: InputStream): String = { + val swriter1 = new StringWriter + val swriter2 = new StringWriter + val app1 = StreamAppender(wrapIn(in1), swriter1) + val app2 = StreamAppender(wrapIn(in2), swriter2) + + val async = new Thread(app2) + async.start() + app1.run() + async.join() + swriter1.toString + swriter2.toString + } +/* + private def inParallel(t1: Runnable, t2: Runnable, t3: Runnable) { + val thr1 = new Thread(t1) + val thr2 = new Thread(t2) + thr1.start() + thr2.start() + t3.run() + thr1.join() + thr2.join() + } +*/ + private def inParallel(t1: Runnable, t2: Runnable) { + val thr = new Thread(t2) + thr.start() + t1.run() + thr.join() + } + + def concat(in: InputStream, err: InputStream, out: OutputStream) = new Runnable { + override def run() { + val outWriter = wrapOut(out) + val inApp = StreamAppender(in, outWriter) + + val errStringWriter = new StringWriter + val errApp = StreamAppender(wrapIn(err), errStringWriter) + + inParallel(inApp, errApp) + + // append error string to out + StreamAppender(errStringWriter.toString, outWriter).run() + } + } +} + +class StreamAppender(reader: BufferedReader, writer: PrintWriter) extends Runnable { + override def run() = runAndMap(identity) + private def lines() = Iterator continually reader.readLine() takeWhile (_ != null) + def closeAll() = { + reader.close() + writer.close() + } + + def runAndMap(f: String => String) = + try lines() map f foreach (writer println _) + catch { case e: IOException => e.printStackTrace() } +} diff --git a/src/partest-alternative/scala/tools/partest/package.scala b/src/partest-alternative/scala/tools/partest/package.scala new file mode 100644 index 0000000000..f6d216e379 --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/package.scala @@ -0,0 +1,45 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + */ + +package scala.tools + +import nsc.io.{ File, Path, Process, Directory } +import java.nio.charset.CharacterCodingException + +package object partest { + /** The CharacterCodingExceptions are thrown at least on windows trying + * to read a file like script/utf-8.scala + */ + private[partest] def safeSlurp(f: File) = + try if (f.exists) f.slurp() else "" + catch { case _: CharacterCodingException => "" } + + private[partest] def safeLines(f: File) = safeSlurp(f) split """\r\n|\r|\n""" toList + private[partest] def safeArgs(f: File) = toArgs(safeSlurp(f)) + private[partest] def isJava(f: Path) = f.isFile && (f hasExtension "java") + private[partest] def isScala(f: Path) = f.isFile && (f hasExtension "scala") + private[partest] def isJavaOrScala(f: Path) = isJava(f) || isScala(f) + + private[partest] def toArgs(line: String) = cmd toArgs line + private[partest] def fromArgs(args: List[String]) = cmd fromArgs args + + /** Strings, argument lists, etc. */ + + private[partest] def fromAnyArgs(args: List[Any]) = args mkString " " // separate to avoid accidents + private[partest] def toStringTrunc(x: Any, max: Int = 240) = { + val s = x.toString + if (s.length < max) s + else (s take max) + " [...]" + } + private[partest] def setProp(k: String, v: String) = scala.util.Properties.setProp(k, v) + + /** Pretty self explanatory. */ + def printAndExit(msg: String): Unit = { + println(msg) + exit(1) + } + + /** Apply a function and return the passed value */ + def returning[T](x: T)(f: T => Unit): T = { f(x) ; x } +}
\ No newline at end of file diff --git a/src/partest/scala/tools/partest/util/package.scala b/src/partest-alternative/scala/tools/partest/util/package.scala index bc5470ba5d..bc5470ba5d 100644 --- a/src/partest/scala/tools/partest/util/package.scala +++ b/src/partest-alternative/scala/tools/partest/util/package.scala diff --git a/src/partest/README b/src/partest/README index c7673fe2f8..81876fc810 100644 --- a/src/partest/README +++ b/src/partest/README @@ -1,50 +1,32 @@ -If you're looking for something to read, I suggest running ../test/partest -with no arguments, which at this moment prints this: +How partest choses the compiler / library: -Usage: partest [<options>] [<test> <test> ...] - <test>: a path to a test designator, typically a .scala file or a directory. - Examples: files/pos/test1.scala, files/res/bug785 + * ''-Dpartest.build=build/four-pack'' -> will search for libraries in + ''lib'' directory of given path + * ''--pack'' -> will set ''partest.build=build/pack'', and run all tests. + add ''--[kind]'' to run a selected set of tests. + * auto detection: + - partest.build property -> ''bin'' / ''lib'' directories + - distribution (''dists/latest'') + - supersabbus pack (''build/pack'') + - sabbus quick (''build/quick'') + - installed dist (test files in ''misc/scala-test/files'') - Test categories: - --all run all tests (default, unless no options given) - --pos Compile files that are expected to build - --neg Compile files that are expected to fail - --run Test JVM backend - --jvm Test JVM backend - --res Run resident compiler scenarii - --buildmanager Run Build Manager scenarii - --scalacheck Run Scalacheck tests - --script Run script files - --shootout Run shootout tests - --scalap Run scalap tests +How partest choses test files: the test files must be accessible from +the directory on which partest is run. So the test files must be either +at: + * ./test/files + * ./files (cwd is "test") + * ./misc/scala-test/files (installed scala distribution) - Test "smart" categories: - --grep run all tests with a source file containing <expr> - --failed run all tests which failed on the last run - - Specifying paths and additional flags, ~ means repository root: - --rootdir path from ~ to partest (default: test) - --builddir path from ~ to test build (default: build/pack) - --srcdir path from --rootdir to sources (default: files) - --javaopts flags to java on all runs (overrides JAVA_OPTS) - --scalacopts flags to scalac on all tests (overrides SCALAC_OPTS) - --pack alias for --builddir build/pack - --quick alias for --builddir build/quick - - Options influencing output: - --trace show the individual steps taken by each test - --show-diff show diff between log and check file - --show-log show log on failures - --dry-run do not run tests, only show their traces. - --terse be less verbose (almost silent except for failures) - --verbose be more verbose (additive with --trace) - --debug maximum debugging output - --ansi print output in color - - Other options: - --timeout Timeout in seconds - --cleanup delete all stale files and dirs before run - --nocleanup do not delete any logfiles or object dirs - --stats collect and print statistics about the tests - --validate examine test filesystem for inconsistencies - --version print version +Other arguments: + * --pos next files test a compilation success + * --neg next files test a compilation failure + * --run next files test the interpreter and all backends + * --jvm next files test the JVM backend + * --res next files test the resident compiler + * --buildmanager next files test the build manager + * --shootout next files are shootout tests + * --script next files test the script runner + * ''-Dpartest.scalac_opts=...'' -> add compiler options + * ''--verbose'' -> print verbose messages + * ''-Dpartest.debug=true'' -> print debug messages diff --git a/src/partest/scala/tools/partest/PartestDefaults.scala b/src/partest/scala/tools/partest/PartestDefaults.scala new file mode 100644 index 0000000000..139c54dedd --- /dev/null +++ b/src/partest/scala/tools/partest/PartestDefaults.scala @@ -0,0 +1,30 @@ +package scala.tools +package partest + +import nsc.io.{ File, Path, Process, Directory } +import util.{ PathResolver } +import nsc.Properties.{ propOrElse, propOrNone, propOrEmpty } + +object PartestDefaults { + import nsc.Properties._ + private def wrapAccessControl[T](body: => Option[T]): Option[T] = + try body catch { case _: java.security.AccessControlException => None } + + def testRootName = propOrNone("partest.root") + def srcDirName = propOrElse("partest.srcdir", "files") + def testRootDir = testRootName map (x => Directory(x)) + + def classPath = PathResolver.Environment.javaUserClassPath // XXX + + def javaCmd = propOrElse("partest.javacmd", "java") + def javacCmd = propOrElse("partest.javac_cmd", "javac") + def javaOpts = propOrElse("partest.java_opts", "") + def scalacOpts = propOrElse("partest.scalac_opts", "-deprecation") + + def testBuild = propOrNone("partest.build") + def errorCount = propOrElse("partest.errors", "0").toInt + def numActors = propOrElse("partest.actors", "8").toInt + def poolSize = wrapAccessControl(propOrNone("actors.corePoolSize")) + + def timeout = "1200000" +} diff --git a/src/partest/scala/tools/partest/PartestTask.scala b/src/partest/scala/tools/partest/PartestTask.scala new file mode 100644 index 0000000000..230a6f73ec --- /dev/null +++ b/src/partest/scala/tools/partest/PartestTask.scala @@ -0,0 +1,287 @@ +/* __ *\ +** ________ ___ / / ___ Scala Parallel Testing ** +** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.tools +package partest + +import scala.actors.Actor._ +import scala.util.Properties.setProp +import scala.tools.nsc.io.{ Directory, Path => SPath } +import nsc.Settings +import nsc.util.ClassPath +import util.PathResolver +import scala.tools.ant.sabbus.CompilationPathProperty + +import java.io.File +import java.net.URLClassLoader +import java.lang.reflect.Method + +import org.apache.tools.ant.Task +import org.apache.tools.ant.types.{Path, Reference, FileSet} + +class PartestTask extends Task with CompilationPathProperty { + + def addConfiguredPosTests(input: FileSet) { + posFiles = Some(input) + } + + def addConfiguredNegTests(input: FileSet) { + negFiles = Some(input) + } + + def addConfiguredRunTests(input: FileSet) { + runFiles = Some(input) + } + + def addConfiguredJvmTests(input: FileSet) { + jvmFiles = Some(input) + } + + def addConfiguredResidentTests(input: FileSet) { + residentFiles = Some(input) + } + + def addConfiguredBuildManagerTests(input: FileSet) { + buildManagerFiles = Some(input) + } + + def addConfiguredScalacheckTests(input: FileSet) { + scalacheckFiles = Some(input) + } + + def addConfiguredScriptTests(input: FileSet) { + scriptFiles = Some(input) + } + + def addConfiguredShootoutTests(input: FileSet) { + shootoutFiles = Some(input) + } + + def addConfiguredScalapTests(input: FileSet) { + scalapFiles = Some(input) + } + + def setSrcDir(input: String) { + srcDir = Some(input) + } + + def setClasspath(input: Path) { + if (classpath.isEmpty) + classpath = Some(input) + else + classpath.get.append(input) + } + + def createClasspath(): Path = { + if (classpath.isEmpty) classpath = Some(new Path(getProject())) + classpath.get.createPath() + } + + def setClasspathref(input: Reference) { + createClasspath().setRefid(input) + } + + def setShowLog(input: Boolean) { + showLog = input + } + + def setShowDiff(input: Boolean) { + showDiff = input + } + + def setErrorOnFailed(input: Boolean) { + errorOnFailed = input + } + + def setJavaCmd(input: File) { + javacmd = Some(input) + } + + def setJavacCmd(input: File) { + javaccmd = Some(input) + } + + def setScalacOpts(opts: String) { + scalacOpts = Some(opts) + } + + def setTimeout(delay: String) { + timeout = Some(delay) + } + + def setDebug(input: Boolean) { + debug = input + } + + def setJUnitReportDir(input: File) { + jUnitReportDir = Some(input) + } + + private var classpath: Option[Path] = None + private var srcDir: Option[String] = None + private var javacmd: Option[File] = None + private var javaccmd: Option[File] = None + private var showDiff: Boolean = false + private var showLog: Boolean = false + private var runFailed: Boolean = false + private var posFiles: Option[FileSet] = None + private var negFiles: Option[FileSet] = None + private var runFiles: Option[FileSet] = None + private var jvmFiles: Option[FileSet] = None + private var residentFiles: Option[FileSet] = None + private var buildManagerFiles: Option[FileSet] = None + private var scalacheckFiles: Option[FileSet] = None + private var scriptFiles: Option[FileSet] = None + private var shootoutFiles: Option[FileSet] = None + private var scalapFiles: Option[FileSet] = None + private var errorOnFailed: Boolean = false + private var scalacOpts: Option[String] = None + private var timeout: Option[String] = None + private var jUnitReportDir: Option[File] = None + private var debug = false + + def fileSetToDir(fs: FileSet) = Directory(fs getDir getProject) + def fileSetToArray(fs: FileSet): Array[SPath] = { + val root = fileSetToDir(fs) + (fs getDirectoryScanner getProject).getIncludedFiles map (root / _) + } + + private def getFiles(fileSet: Option[FileSet]): Array[File] = fileSet match { + case None => Array() + case Some(fs) => fileSetToArray(fs) filterNot (_ hasExtension "log") map (_.jfile) + } + + private def getFilesAndDirs(fileSet: Option[FileSet]): Array[File] = fileSet match { + case None => Array() + case Some(fs) => + def shouldExclude(name: String) = (name endsWith ".obj") || (name startsWith ".") + + val fileTests = getFiles(Some(fs)) filterNot (x => shouldExclude(x.getName)) + val dirTests: Iterator[SPath] = fileSetToDir(fs).dirs filterNot (x => shouldExclude(x.name)) + val dirResult = dirTests.toList.toArray map (_.jfile) + + dirResult ++ fileTests + } + + private def getPosFiles = getFilesAndDirs(posFiles) + private def getNegFiles = getFilesAndDirs(negFiles) + private def getRunFiles = getFiles(runFiles) + private def getJvmFiles = getFilesAndDirs(jvmFiles) + private def getResidentFiles = getFiles(residentFiles) + private def getBuildManagerFiles = getFilesAndDirs(buildManagerFiles) + private def getScalacheckFiles = getFiles(scalacheckFiles) + private def getScriptFiles = getFiles(scriptFiles) + private def getShootoutFiles = getFiles(shootoutFiles) + private def getScalapFiles = getFiles(scalapFiles) + + override def execute() { + if (isPartestDebug) + setProp("partest.debug", "true") + + srcDir foreach (x => setProp("partest.srcdir", x)) + + val classpath = this.compilationPath getOrElse error("Mandatory attribute 'compilationPath' is not set.") + + val scalaLibrary = { + (classpath.list map { fs => new File(fs) }) find { f => + f.getName match { + case "scala-library.jar" => true + case "library" if (f.getParentFile.getName == "classes") => true + case _ => false + } + } + } getOrElse error("Provided classpath does not contain a Scala library.") + + val antRunner = new scala.tools.partest.nest.AntRunner + val antFileManager = antRunner.fileManager + + antFileManager.showDiff = showDiff + antFileManager.showLog = showLog + antFileManager.failed = runFailed + antFileManager.CLASSPATH = ClassPath.join(classpath.list: _*) + antFileManager.LATEST_LIB = scalaLibrary.getAbsolutePath + + javacmd foreach (x => antFileManager.JAVACMD = x.getAbsolutePath) + javaccmd foreach (x => antFileManager.JAVAC_CMD = x.getAbsolutePath) + scalacOpts foreach (antFileManager.SCALAC_OPTS = _) + timeout foreach (antFileManager.timeout = _) + + type TFSet = (Array[File], String, String) + val testFileSets = List( + (getPosFiles, "pos", "Compiling files that are expected to build"), + (getNegFiles, "neg", "Compiling files that are expected to fail"), + (getRunFiles, "run", "Compiling and running files"), + (getJvmFiles, "jvm", "Compiling and running files"), + (getResidentFiles, "res", "Running resident compiler scenarii"), + (getBuildManagerFiles, "buildmanager", "Running Build Manager scenarii"), + (getScalacheckFiles, "scalacheck", "Running scalacheck tests"), + (getScriptFiles, "script", "Running script files"), + (getShootoutFiles, "shootout", "Running shootout tests"), + (getScalapFiles, "scalap", "Running scalap tests") + ) + + def runSet(set: TFSet): (Int, Int, Iterable[String]) = { + val (files, name, msg) = set + if (files.isEmpty) (0, 0, List()) + else { + log(msg) + val results: Iterable[(String, Int)] = antRunner.reflectiveRunTestsForFiles(files, name) + val (succs, fails) = resultsToStatistics(results) + + val failed: Iterable[String] = results collect { + case (path, 1) => path + " [FAILED]" + case (path, 2) => path + " [TIMOUT]" + } + + // create JUnit Report xml files if directory was specified + jUnitReportDir foreach { d => + d.mkdir + + val report = testReport(name, results, succs, fails) + scala.xml.XML.save(d.getAbsolutePath+"/"+name+".xml", report) + } + + (succs, fails, failed) + } + } + + val _results = testFileSets map runSet + val allSuccesses = _results map (_._1) sum + val allFailures = _results map (_._2) sum + val allFailedPaths = _results flatMap (_._3) + + def f = if (errorOnFailed && allFailures > 0) error(_) else log(_: String) + def s = if (allFailures > 1) "s" else "" + val msg = + if (allFailures > 0) + "Test suite finished with %d case%s failing:\n".format(allFailures, s)+ + allFailedPaths.mkString("\n") + else if (allSuccesses == 0) "There were no tests to run." + else "Test suite finished with no failures." + + f(msg) + } + def oneResult(res: (String, Int)) = + <testcase name={res._1}>{ + res._2 match { + case 0 => scala.xml.NodeSeq.Empty + case 1 => <failure message="Test failed"/> + case 2 => <failure message="Test timed out"/> + } + }</testcase> + + def testReport(kind: String, results: Iterable[(String, Int)], succs: Int, fails: Int) = + <testsuite name={kind} tests={(succs + fails).toString} failures={fails.toString}> + <properties/> + { + results.map(oneResult(_)) + } + </testsuite> +} diff --git a/src/partest/scala/tools/partest/antlib.xml b/src/partest/scala/tools/partest/antlib.xml index af36f11368..b3b98e853f 100644 --- a/src/partest/scala/tools/partest/antlib.xml +++ b/src/partest/scala/tools/partest/antlib.xml @@ -1,3 +1,4 @@ <antlib> - <taskdef name="partest" classname="scala.tools.partest.ant.JavaTask"/> + <taskdef name="partest" + classname="scala.tools.partest.PartestTask"/> </antlib> diff --git a/src/partest/scala/tools/partest/nest/AntRunner.scala b/src/partest/scala/tools/partest/nest/AntRunner.scala new file mode 100644 index 0000000000..cb819720fc --- /dev/null +++ b/src/partest/scala/tools/partest/nest/AntRunner.scala @@ -0,0 +1,30 @@ +/* __ *\ +** ________ ___ / / ___ Scala Parallel Testing ** +** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.tools.partest +package nest + +import java.io.File +import scala.tools.nsc.io.{ Directory } + +class AntRunner extends DirectRunner { + + val fileManager = new FileManager { + var JAVACMD: String = "java" + var JAVAC_CMD: String = "javac" + var CLASSPATH: String = _ + var LATEST_LIB: String = _ + val testRootPath: String = "test" + val testRootDir: Directory = Directory(testRootPath) + } + + def reflectiveRunTestsForFiles(kindFiles: Array[File], kind: String) = + runTestsForFiles(kindFiles.toList, kind) +} diff --git a/src/partest/scala/tools/partest/nest/CompileManager.scala b/src/partest/scala/tools/partest/nest/CompileManager.scala new file mode 100644 index 0000000000..22568ad2d0 --- /dev/null +++ b/src/partest/scala/tools/partest/nest/CompileManager.scala @@ -0,0 +1,197 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +import scala.tools.nsc.{ Global, Settings, CompilerCommand, FatalError, io } +import scala.tools.nsc.reporters.{ Reporter, ConsoleReporter } +import scala.tools.nsc.util.ClassPath +import scala.tools.util.PathResolver +import io.Path + +import java.io.{ File, BufferedReader, PrintWriter, FileReader, Writer, FileWriter, StringWriter } +import File.pathSeparator + +class ExtConsoleReporter(override val settings: Settings, reader: BufferedReader, var writer: PrintWriter) +extends ConsoleReporter(settings, reader, writer) { + def this(settings: Settings) = this(settings, Console.in, new PrintWriter(new FileWriter("/dev/null"))) +} + +abstract class SimpleCompiler { + def compile(out: Option[File], files: List[File], kind: String, log: File): Boolean +} + +class TestSettings(fileMan: FileManager) extends Settings(_ => ()) { } + +class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler { + def newGlobal(settings: Settings, reporter: Reporter): Global = + new Global(settings, reporter) + + def newGlobal(settings: Settings, logWriter: FileWriter): Global = { + val rep = newReporter(settings, logWriter) + rep.shortname = true + newGlobal(settings, rep) + } + + def newSettings(out: Option[String]) = { + val settings = new TestSettings(fileManager) + settings.usejavacp.value = true + settings.deprecation.value = true + settings.nowarnings.value = false + settings.encoding.value = "ISO-8859-1" // XXX why? + + val classpathElements = settings.classpath.value :: fileManager.LATEST_LIB :: out.toList + settings.classpath.value = ClassPath.join(classpathElements: _*) + out foreach (settings.outdir.value = _) + + settings + } + + def newReporter(sett: Settings, writer: Writer = new StringWriter) = + new ExtConsoleReporter(sett, Console.in, new PrintWriter(writer)) + + private def updatePluginPath(options: String): String = { + val dir = fileManager.testRootDir + def absolutize(path: String) = Path(path) match { + case x if x.isAbsolute => x.path + case x => (fileManager.testRootDir / x).toAbsolute.path + } + + val (opt1, opt2) = (options split "\\s").toList partition (_ startsWith "-Xplugin:") + val plugins = opt1 map (_ stripPrefix "-Xplugin:") flatMap (_ split pathSeparator) map absolutize + val pluginOption = if (opt1.isEmpty) Nil else List("-Xplugin:" + (plugins mkString pathSeparator)) + + (opt2 ::: pluginOption) mkString " " + } + + def compile(out: Option[File], files: List[File], kind: String, log: File): Boolean = { + val testSettings = newSettings(out map (_.getAbsolutePath)) + val logWriter = new FileWriter(log) + + // check whether there is a ".flags" file + val flagsFileName = "%s.flags" format (basename(log.getName) dropRight 4) // 4 is "-run" or similar + val argString = (io.File(log).parent / flagsFileName) ifFile (x => updatePluginPath(x.slurp())) getOrElse "" + val allOpts = fileManager.SCALAC_OPTS+" "+argString + val args = (allOpts split "\\s").toList + + NestUI.verbose("scalac options: "+allOpts) + + val command = new CompilerCommand(args, testSettings) + val global = newGlobal(command.settings, logWriter) + val testRep: ExtConsoleReporter = global.reporter.asInstanceOf[ExtConsoleReporter] + + val testFileFn: (File, FileManager) => TestFile = kind match { + case "pos" => PosTestFile.apply + case "neg" => NegTestFile.apply + case "run" => RunTestFile.apply + case "jvm" => JvmTestFile.apply + case "shootout" => ShootoutTestFile.apply + case "scalap" => ScalapTestFile.apply + case "scalacheck" => ScalaCheckTestFile.apply + } + val test: TestFile = testFileFn(files.head, fileManager) + test.defineSettings(command.settings, out.isEmpty) + val toCompile = files map (_.getPath) + + try { + NestUI.verbose("compiling "+toCompile) + try new global.Run compile toCompile + catch { + case FatalError(msg) => + testRep.error(null, "fatal error: " + msg) + } + + testRep.printSummary + testRep.writer.flush + testRep.writer.close + } + catch { + case e => + e.printStackTrace() + return false + } + finally logWriter.close() + + !testRep.hasErrors + } +} + +// class ReflectiveCompiler(val fileManager: ConsoleFileManager) extends SimpleCompiler { +// import fileManager.{latestCompFile, latestPartestFile} +// +// val sepUrls = Array(latestCompFile.toURI.toURL, latestPartestFile.toURI.toURL) +// //NestUI.verbose("constructing URLClassLoader from URLs "+latestCompFile+" and "+latestPartestFile) +// +// val sepLoader = new java.net.URLClassLoader(sepUrls, null) +// +// val sepCompilerClass = +// sepLoader.loadClass("scala.tools.partest.nest.DirectCompiler") +// val sepCompiler = sepCompilerClass.newInstance() +// +// // needed for reflective invocation +// val fileClass = Class.forName("java.io.File") +// val stringClass = Class.forName("java.lang.String") +// val sepCompileMethod = +// sepCompilerClass.getMethod("compile", fileClass, stringClass) +// val sepCompileMethod2 = +// sepCompilerClass.getMethod("compile", fileClass, stringClass, fileClass) +// +// /* This method throws java.lang.reflect.InvocationTargetException +// * if the compiler crashes. +// * This exception is handled in the shouldCompile and shouldFailCompile +// * methods of class CompileManager. +// */ +// def compile(out: Option[File], files: List[File], kind: String, log: File): Boolean = { +// val res = sepCompileMethod2.invoke(sepCompiler, out, files, kind, log).asInstanceOf[java.lang.Boolean] +// res.booleanValue() +// } +// } + +class CompileManager(val fileManager: FileManager) { + var compiler: SimpleCompiler = new /*ReflectiveCompiler*/ DirectCompiler(fileManager) + + var numSeparateCompilers = 1 + def createSeparateCompiler() = { + numSeparateCompilers += 1 + compiler = new /*ReflectiveCompiler*/ DirectCompiler(fileManager) + } + + /* This method returns true iff compilation succeeds. + */ + def shouldCompile(files: List[File], kind: String, log: File): Boolean = { + createSeparateCompiler() + compiler.compile(None, files, kind, log) + } + + /* This method returns true iff compilation succeeds. + */ + def shouldCompile(out: File, files: List[File], kind: String, log: File): Boolean = { + createSeparateCompiler() + compiler.compile(Some(out), files, kind, log) + } + + /* This method returns true iff compilation fails + * _and_ the compiler does _not_ crash or loop. + * + * If the compiler crashes, this method returns false. + */ + def shouldFailCompile(files: List[File], kind: String, log: File): Boolean = { + createSeparateCompiler() + !compiler.compile(None, files, kind, log) + } + + /* This method returns true iff compilation fails + * _and_ the compiler does _not_ crash or loop. + * + * If the compiler crashes, this method returns false. + */ + def shouldFailCompile(out: File, files: List[File], kind: String, log: File): Boolean = { + createSeparateCompiler() + !compiler.compile(Some(out), files, kind, log) + } +} diff --git a/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala b/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala new file mode 100644 index 0000000000..58d16a3f45 --- /dev/null +++ b/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala @@ -0,0 +1,190 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +import java.io.{ File, FilenameFilter, IOException, StringWriter } +import java.net.URI +import scala.util.Properties.{ propOrElse, scalaCmd, scalacCmd } +import scala.tools.util.PathResolver +import scala.tools.nsc.{ Settings } +import scala.tools.nsc.{ io, util } +import util.{ ClassPath } +import io.{ Path, Directory } +import File.pathSeparator +import ClassPath.{ join } +import PathResolver.{ Environment, Defaults } +import RunnerUtils._ + + +class ConsoleFileManager extends FileManager { + var testBuild: Option[String] = PartestDefaults.testBuild + def testBuildFile = testBuild map (testParent / _) + + var testClasses: Option[String] = None + + def this(buildPath: String, rawClasses: Boolean) = { + this() + if (rawClasses) + testClasses = Some(buildPath) + else + testBuild = Some(buildPath) + // re-run because initialization of default + // constructor must be updated + findLatest() + } + + def this(buildPath: String) = { + this(buildPath, false) + } + + def this(buildPath: String, rawClasses: Boolean, moreOpts: String) = { + this(buildPath, rawClasses) + SCALAC_OPTS = SCALAC_OPTS+" "+moreOpts + } + + lazy val srcDir = PathSettings.srcDir + lazy val testRootDir = PathSettings.testRoot + lazy val testRootPath = testRootDir.toAbsolute.path + def testParent = testRootDir.parent + + var CLASSPATH = PartestDefaults.classPath + var JAVACMD = PartestDefaults.javaCmd + var JAVAC_CMD = PartestDefaults.javacCmd + + + NestUI.verbose("CLASSPATH: "+CLASSPATH) + + if (!srcDir.isDirectory) { + NestUI.failure("Source directory \"" + srcDir.path + "\" not found") + exit(1) + } + + CLASSPATH = { + val libs = (srcDir / Directory("lib")).files filter (_ hasExtension "jar") map (_.normalize.path) + + // add all jars in libs + (CLASSPATH :: libs.toList) mkString pathSeparator + } + + def findLatest() { + NestUI.verbose("test parent: "+testParent) + + def prefixFileWith(parent: File, relPath: String) = (io.File(parent) / relPath).normalize + def prefixFile(relPath: String) = (testParent / relPath).normalize + + if (!testClasses.isEmpty) { + testClassesDir = Path(testClasses.get).normalize.toDirectory + NestUI.verbose("Running with classes in "+testClassesDir) + + latestFile = testClassesDir.parent / "bin" + latestLibFile = testClassesDir / "library" + latestCompFile = testClassesDir / "compiler" + latestPartestFile = testClassesDir / "partest" + latestFjbgFile = testParent / "lib" / "fjbg.jar" + } + else if (testBuild.isDefined) { + val dir = Path(testBuild.get) + NestUI.verbose("Running on "+dir) + latestFile = dir / "bin" + latestLibFile = dir / "lib/scala-library.jar" + latestCompFile = dir / "lib/scala-compiler.jar" + latestPartestFile = dir / "lib/scala-partest.jar" + } + else { + def setupQuick() { + NestUI.verbose("Running build/quick") + latestFile = prefixFile("build/quick/bin") + latestLibFile = prefixFile("build/quick/classes/library") + latestCompFile = prefixFile("build/quick/classes/compiler") + latestPartestFile = prefixFile("build/quick/classes/partest") + } + + def setupInst() { + NestUI.verbose("Running dist (installed)") + val p = testParent.getParentFile + latestFile = prefixFileWith(p, "bin") + latestLibFile = prefixFileWith(p, "lib/scala-library.jar") + latestCompFile = prefixFileWith(p, "lib/scala-compiler.jar") + latestPartestFile = prefixFileWith(p, "lib/scala-partest.jar") + } + + def setupDist() { + NestUI.verbose("Running dists/latest") + latestFile = prefixFile("dists/latest/bin") + latestLibFile = prefixFile("dists/latest/lib/scala-library.jar") + latestCompFile = prefixFile("dists/latest/lib/scala-compiler.jar") + latestPartestFile = prefixFile("dists/latest/lib/scala-partest.jar") + } + + def setupPack() { + NestUI.verbose("Running build/pack") + latestFile = prefixFile("build/pack/bin") + latestLibFile = prefixFile("build/pack/lib/scala-library.jar") + latestCompFile = prefixFile("build/pack/lib/scala-compiler.jar") + latestPartestFile = prefixFile("build/pack/lib/scala-partest.jar") + } + + val dists = testParent / "dists" + val build = testParent / "build" + // in case of an installed dist, testRootDir is one level deeper + val bin = testParent.parent / "bin" + + def mostRecentOf(base: String, names: String*) = + names map (x => prefixFile(base + "/" + x).lastModified) reduceLeft (_ max _) + + // detect most recent build + val quickTime = mostRecentOf("build/quick/classes", "compiler/compiler.properties", "library/library.properties") + val packTime = mostRecentOf("build/pack/lib", "scala-compiler.jar", "scala-library.jar") + val distTime = mostRecentOf("dists/latest/lib", "scala-compiler.jar", "scala-library.jar") + val instTime = mostRecentOf("lib", "scala-compiler.jar", "scala-library.jar") + + val pairs = Map( + (quickTime, () => setupQuick()), + (packTime, () => setupPack()), + (distTime, () => setupDist()), + (instTime, () => setupInst()) + ) + + // run setup based on most recent time + pairs(pairs.keys max)() + + latestFjbgFile = prefixFile("lib/fjbg.jar") + } + + LATEST_LIB = latestLibFile.getAbsolutePath + } + + var LATEST_LIB: String = "" + + var latestFile: File = _ + var latestLibFile: File = _ + var latestCompFile: File = _ + var latestPartestFile: File = _ + var latestFjbgFile: File = _ + var testClassesDir: Directory = _ + // initialize above fields + findLatest() + + var testFiles: List[io.Path] = Nil + + def getFiles(kind: String, cond: Path => Boolean): List[File] = { + def ignoreDir(p: Path) = List("svn", "obj") exists (p hasExtension _) + + val dir = Directory(srcDir / kind) + + if (dir.isDirectory) NestUI.verbose("look in %s for tests" format dir) + else NestUI.failure("Directory '%s' not found" format dir) + + val files = + if (testFiles.nonEmpty) testFiles filter (_.parent isSame dir) + else dir.list filterNot ignoreDir filter cond toList + + ( if (failed) files filter (x => logFileExists(x, kind)) else files ) map (_.jfile) + } +} diff --git a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala new file mode 100644 index 0000000000..eae79f23af --- /dev/null +++ b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala @@ -0,0 +1,209 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +import java.io.{File, PrintStream, FileOutputStream, BufferedReader, + InputStreamReader, StringWriter, PrintWriter} +import utils.Properties._ +import RunnerUtils._ +import scala.tools.nsc.Properties.{ versionMsg, setProp } +import scala.tools.nsc.util.CommandLineParser +import scala.tools.nsc.io +import scala.tools.nsc.interpreter.returning +import io.{ Path, Process } + +class ConsoleRunner extends DirectRunner { + import PathSettings.{ srcDir, testRoot } + + case class TestSet(kind: String, filter: Path => Boolean, msg: String) + + val testSets = { + val pathFilter: Path => Boolean = _ hasExtension "scala" + + List( + TestSet("pos", pathFilter, "Testing compiler (on files whose compilation should succeed)"), + TestSet("neg", pathFilter, "Testing compiler (on files whose compilation should fail)"), + TestSet("run", pathFilter, "Testing JVM backend"), + TestSet("jvm", pathFilter, "Testing JVM backend"), + TestSet("res", x => x.isFile && (x hasExtension "res"), "Testing resident compiler"), + TestSet("buildmanager", _.isDirectory, "Testing Build Manager"), + TestSet("shootout", pathFilter, "Testing shootout tests"), + TestSet("script", pathFilter, "Testing script tests"), + TestSet("scalacheck", pathFilter, "Testing ScalaCheck tests"), + TestSet("scalap", _.isDirectory, "Run scalap decompiler tests") + ) + } + + var fileManager: ConsoleFileManager = _ + + private var testFiles: List[File] = List() + private val errors = PartestDefaults.errorCount + private val testSetKinds = testSets map (_.kind) + private val testSetArgs = testSets map ("--" + _.kind) + private val testSetArgMap = testSetArgs zip testSets toMap + + def denotesTestSet(arg: String) = testSetArgs contains arg + def denotesTestFile(arg: String) = (arg endsWith ".scala") || (arg endsWith ".res") + def denotesTestDir(arg: String) = Path(arg).isDirectory + def denotesTestPath(arg: String) = denotesTestDir(arg) || denotesTestFile(arg) + + private def printVersion { NestUI outline (versionMsg + "\n") } + + private val unaryArgs = List( + "--pack", "--all", "--verbose", "--show-diff", "--show-log", + "--failed", "--version", "--ansi", "--debug" + ) ::: testSetArgs + + private val binaryArgs = List( + "--grep", "--srcpath", "--buildpath", "--classpath" + ) + + def main(argstr: String) { + val parsed = CommandLineParser(argstr) withUnaryArgs unaryArgs withBinaryArgs binaryArgs + val args = parsed.residualArgs + + /** Early return on no args, version, or invalid args */ + if (argstr == "") return NestUI.usage() + if (parsed isSet "--version") return printVersion + if (args exists (x => !denotesTestPath(x))) { + val invalid = (args filterNot denotesTestPath).head + NestUI.failure("Invalid argument '%s'\n" format invalid) + return NestUI.usage() + } + + parsed get "--srcpath" foreach (x => setProp("partest.srcdir", x)) + + fileManager = + if (parsed isSet "--buildpath") new ConsoleFileManager(parsed("--buildpath")) + else if (parsed isSet "--classpath") new ConsoleFileManager(parsed("--classpath"), true) + else if (parsed isSet "--pack") new ConsoleFileManager("build/pack") + else new ConsoleFileManager // auto detection, see ConsoleFileManager.findLatest + + def argNarrowsTests(x: String) = denotesTestSet(x) || denotesTestFile(x) || denotesTestDir(x) + + NestUI._verbose = parsed isSet "--verbose" + fileManager.showDiff = parsed isSet "--show-diff" + fileManager.showLog = parsed isSet "--show-log" + fileManager.failed = parsed isSet "--failed" + + if (parsed isSet "--ansi") NestUI initialize NestUI.MANY + if (parsed isSet "--timeout") fileManager.timeout = parsed("--timeout") + if (parsed isSet "--debug") setProp("partest.debug", "true") + + def addTestFile(file: File) = { + if (!file.exists) + NestUI.failure("Test file '%s' not found, skipping.\n" format file) + else { + NestUI.verbose("adding test file " + file) + testFiles +:= file + } + } + + // If --grep is given we suck in every file it matches. + parsed get "--grep" foreach { expr => + val allFiles = srcDir.deepList() filter (_ hasExtension "scala") map (_.toFile) toList + val files = allFiles filter (_.slurp() contains expr) + + if (files.isEmpty) NestUI.failure("--grep string '%s' matched no files." format expr) + else NestUI.verbose("--grep string '%s' matched %d file(s)".format(expr, files.size)) + + files foreach (x => addTestFile(x.jfile)) + } + args foreach (x => addTestFile(new File(x))) + + // If no file arguments were given, we assume --all + val enabledTestSets: List[TestSet] = { + val enabledArgs = testSetArgs filter parsed.isSet + + if (args.isEmpty && !(parsed isSet "--grep") && (enabledArgs.isEmpty || (parsed isSet "--all"))) testSets + else enabledArgs map testSetArgMap + } + + val dir = + if (fileManager.testClasses.isDefined) fileManager.testClassesDir + else fileManager.testBuildFile getOrElse { + fileManager.latestCompFile.getParentFile.getParentFile.getCanonicalFile + } + + val vmBin = javaHome + File.separator + "bin" + val vmName = "%s (build %s, %s)".format(javaVmName, javaVmVersion, javaVmInfo) + val vmOpts = fileManager.JAVA_OPTS + + NestUI.verbose("enabled test sets: " + (enabledTestSets map (_.kind) mkString " ")) + + List( + "Scala compiler classes in: " + dir, + "Scala version is: " + versionMsg, + "Scalac options are: " + fileManager.SCALAC_OPTS, + "Java binaries in: " + vmBin, + "Java runtime is: " + vmName, + "Java options are: " + vmOpts, + "Source directory is: " + srcDir, + "" + ) foreach (x => NestUI outline (x + "\n")) + + val start = System.currentTimeMillis + val (successes, failures) = testCheckAll(enabledTestSets) + val end = System.currentTimeMillis + + val total = successes + failures + + val elapsedSecs = (end - start)/1000 + val elapsedMins = elapsedSecs/60 + val elapsedHrs = elapsedMins/60 + val dispMins = elapsedMins - elapsedHrs * 60 + val dispSecs = elapsedSecs - elapsedMins * 60 + + val dispElapsed = { + def form(num: Long) = if (num < 10) "0"+num else ""+num + form(elapsedHrs)+":"+form(dispMins)+":"+form(dispSecs) + } + + println + if (failures == 0) + NestUI.success("All of "+total+" tests were successful (elapsed time: "+dispElapsed+")\n") + else + NestUI.failure(failures+" of "+total+" tests failed (elapsed time: "+dispElapsed+")\n") + + System exit ( if (failures == errors) 0 else 1 ) + } + + def runTests(testSet: TestSet): (Int, Int) = { + val TestSet(kind, filter, msg) = testSet + + fileManager.getFiles(kind, filter) match { + case Nil => NestUI.verbose("test dir empty\n") ; (0, 0) + case files => + NestUI.verbose("test files: "+files) + NestUI.outline("\n"+msg+"\n") + resultsToStatistics(runTestsForFiles(files, kind)) + } + } + + /** + * @return (success count, failure count) + */ + def testCheckAll(enabledSets: List[TestSet]): (Int, Int) = { + def kindOf(f: File) = (srcDir relativize Path(f).normalize).segments.head + + val (valid, invalid) = testFiles partition (x => testSetKinds contains kindOf(x)) + invalid foreach (x => NestUI.failure("Invalid test file '%s', skipping.\n" format x)) + + val runTestsFileLists = + for ((kind, files) <- valid groupBy kindOf toList) yield { + NestUI.outline("\nTesting individual files\n") + resultsToStatistics(runTestsForFiles(files, kind)) + } + + NestUI.verbose("Run sets: "+enabledSets) + val results = runTestsFileLists ::: (enabledSets map runTests) + + (results map (_._1) sum, results map (_._2) sum) + } +} diff --git a/src/partest/scala/tools/partest/nest/Diff.java b/src/partest/scala/tools/partest/nest/Diff.java new file mode 100644 index 0000000000..abd09d0293 --- /dev/null +++ b/src/partest/scala/tools/partest/nest/Diff.java @@ -0,0 +1,874 @@ +// $Id$ + +package scala.tools.partest.nest; + +import java.util.Hashtable; + +/** A class to compare IndexedSeqs of objects. The result of comparison + is a list of <code>change</code> objects which form an + edit script. The objects compared are traditionally lines + of text from two files. Comparison options such as "ignore + whitespace" are implemented by modifying the <code>equals</code> + and <code>hashcode</code> methods for the objects compared. +<p> + The basic algorithm is described in: </br> + "An O(ND) Difference Algorithm and its Variations", Eugene Myers, + Algorithmica Vol. 1 No. 2, 1986, p 251. +<p> + This class outputs different results from GNU diff 1.15 on some + inputs. Our results are actually better (smaller change list, smaller + total size of changes), but it would be nice to know why. Perhaps + there is a memory overwrite bug in GNU diff 1.15. + + @author Stuart D. Gathman, translated from GNU diff 1.15 + Copyright (C) 2000 Business Management Systems, Inc. +<p> + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. +<p> + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +<p> + You should have received a copy of the <a href=COPYING.txt> + GNU General Public License</a> + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + */ + +public class Diff { + + /** Prepare to find differences between two arrays. Each element of + the arrays is translated to an "equivalence number" based on + the result of <code>equals</code>. The original Object arrays + are no longer needed for computing the differences. They will + be needed again later to print the results of the comparison as + an edit script, if desired. + */ + public Diff(Object[] a,Object[] b) { + Hashtable h = new Hashtable(a.length + b.length); + filevec[0] = new file_data(a,h); + filevec[1] = new file_data(b,h); + } + + /** 1 more than the maximum equivalence value used for this or its + sibling file. */ + private int equiv_max = 1; + + /** When set to true, the comparison uses a heuristic to speed it up. + With this heuristic, for files with a constant small density + of changes, the algorithm is linear in the file size. */ + public boolean heuristic = false; + + /** When set to true, the algorithm returns a guarranteed minimal + set of changes. This makes things slower, sometimes much slower. */ + public boolean no_discards = false; + + private int[] xvec, yvec; /* IndexedSeqs being compared. */ + private int[] fdiag; /* IndexedSeq, indexed by diagonal, containing + the X coordinate of the point furthest + along the given diagonal in the forward + search of the edit matrix. */ + private int[] bdiag; /* IndexedSeq, indexed by diagonal, containing + the X coordinate of the point furthest + along the given diagonal in the backward + search of the edit matrix. */ + private int fdiagoff, bdiagoff; + private final file_data[] filevec = new file_data[2]; + private int cost; + + /** Find the midpoint of the shortest edit script for a specified + portion of the two files. + + We scan from the beginnings of the files, and simultaneously from the ends, + doing a breadth-first search through the space of edit-sequence. + When the two searches meet, we have found the midpoint of the shortest + edit sequence. + + The value returned is the number of the diagonal on which the midpoint lies. + The diagonal number equals the number of inserted lines minus the number + of deleted lines (counting only lines before the midpoint). + The edit cost is stored into COST; this is the total number of + lines inserted or deleted (counting only lines before the midpoint). + + This function assumes that the first lines of the specified portions + of the two files do not match, and likewise that the last lines do not + match. The caller must trim matching lines from the beginning and end + of the portions it is going to specify. + + Note that if we return the "wrong" diagonal value, or if + the value of bdiag at that diagonal is "wrong", + the worst this can do is cause suboptimal diff output. + It cannot cause incorrect diff output. */ + + private int diag (int xoff, int xlim, int yoff, int ylim) { + final int[] fd = fdiag; // Give the compiler a chance. + final int[] bd = bdiag; // Additional help for the compiler. + final int[] xv = xvec; // Still more help for the compiler. + final int[] yv = yvec; // And more and more . . . + final int dmin = xoff - ylim; // Minimum valid diagonal. + final int dmax = xlim - yoff; // Maximum valid diagonal. + final int fmid = xoff - yoff; // Center diagonal of top-down search. + final int bmid = xlim - ylim; // Center diagonal of bottom-up search. + int fmin = fmid, fmax = fmid; // Limits of top-down search. + int bmin = bmid, bmax = bmid; // Limits of bottom-up search. + /* True if southeast corner is on an odd + diagonal with respect to the northwest. */ + final boolean odd = (fmid - bmid & 1) != 0; + + fd[fdiagoff + fmid] = xoff; + bd[bdiagoff + bmid] = xlim; + + for (int c = 1;; ++c) + { + int d; /* Active diagonal. */ + boolean big_snake = false; + + /* Extend the top-down search by an edit step in each diagonal. */ + if (fmin > dmin) + fd[fdiagoff + --fmin - 1] = -1; + else + ++fmin; + if (fmax < dmax) + fd[fdiagoff + ++fmax + 1] = -1; + else + --fmax; + for (d = fmax; d >= fmin; d -= 2) + { + int x, y, oldx, tlo = fd[fdiagoff + d - 1], thi = fd[fdiagoff + d + 1]; + + if (tlo >= thi) + x = tlo + 1; + else + x = thi; + oldx = x; + y = x - d; + while (x < xlim && y < ylim && xv[x] == yv[y]) { + ++x; ++y; + } + if (x - oldx > 20) + big_snake = true; + fd[fdiagoff + d] = x; + if (odd && bmin <= d && d <= bmax && bd[bdiagoff + d] <= fd[fdiagoff + d]) + { + cost = 2 * c - 1; + return d; + } + } + + /* Similar extend the bottom-up search. */ + if (bmin > dmin) + bd[bdiagoff + --bmin - 1] = Integer.MAX_VALUE; + else + ++bmin; + if (bmax < dmax) + bd[bdiagoff + ++bmax + 1] = Integer.MAX_VALUE; + else + --bmax; + for (d = bmax; d >= bmin; d -= 2) + { + int x, y, oldx, tlo = bd[bdiagoff + d - 1], thi = bd[bdiagoff + d + 1]; + + if (tlo < thi) + x = tlo; + else + x = thi - 1; + oldx = x; + y = x - d; + while (x > xoff && y > yoff && xv[x - 1] == yv[y - 1]) { + --x; --y; + } + if (oldx - x > 20) + big_snake = true; + bd[bdiagoff + d] = x; + if (!odd && fmin <= d && d <= fmax && bd[bdiagoff + d] <= fd[fdiagoff + d]) + { + cost = 2 * c; + return d; + } + } + + /* Heuristic: check occasionally for a diagonal that has made + lots of progress compared with the edit distance. + If we have any such, find the one that has made the most + progress and return it as if it had succeeded. + + With this heuristic, for files with a constant small density + of changes, the algorithm is linear in the file size. */ + + if (c > 200 && big_snake && heuristic) + { + int best = 0; + int bestpos = -1; + + for (d = fmax; d >= fmin; d -= 2) + { + int dd = d - fmid; + if ((fd[fdiagoff + d] - xoff)*2 - dd > 12 * (c + (dd > 0 ? dd : -dd))) + { + if (fd[fdiagoff + d] * 2 - dd > best + && fd[fdiagoff + d] - xoff > 20 + && fd[fdiagoff + d] - d - yoff > 20) + { + int k; + int x = fd[fdiagoff + d]; + + /* We have a good enough best diagonal; + now insist that it end with a significant snake. */ + for (k = 1; k <= 20; k++) + if (xvec[x - k] != yvec[x - d - k]) + break; + + if (k == 21) + { + best = fd[fdiagoff + d] * 2 - dd; + bestpos = d; + } + } + } + } + if (best > 0) + { + cost = 2 * c - 1; + return bestpos; + } + + best = 0; + for (d = bmax; d >= bmin; d -= 2) + { + int dd = d - bmid; + if ((xlim - bd[bdiagoff + d])*2 + dd > 12 * (c + (dd > 0 ? dd : -dd))) + { + if ((xlim - bd[bdiagoff + d]) * 2 + dd > best + && xlim - bd[bdiagoff + d] > 20 + && ylim - (bd[bdiagoff + d] - d) > 20) + { + /* We have a good enough best diagonal; + now insist that it end with a significant snake. */ + int k; + int x = bd[bdiagoff + d]; + + for (k = 0; k < 20; k++) + if (xvec[x + k] != yvec[x - d + k]) + break; + if (k == 20) + { + best = (xlim - bd[bdiagoff + d]) * 2 + dd; + bestpos = d; + } + } + } + } + if (best > 0) + { + cost = 2 * c - 1; + return bestpos; + } + } + } + } + + /** Compare in detail contiguous subsequences of the two files + which are known, as a whole, to match each other. + + The results are recorded in the IndexedSeqs filevec[N].changed_flag, by + storing a 1 in the element for each line that is an insertion or deletion. + + The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1. + + Note that XLIM, YLIM are exclusive bounds. + All line numbers are origin-0 and discarded lines are not counted. */ + + private void compareseq (int xoff, int xlim, int yoff, int ylim) { + /* Slide down the bottom initial diagonal. */ + while (xoff < xlim && yoff < ylim && xvec[xoff] == yvec[yoff]) { + ++xoff; ++yoff; + } + /* Slide up the top initial diagonal. */ + while (xlim > xoff && ylim > yoff && xvec[xlim - 1] == yvec[ylim - 1]) { + --xlim; --ylim; + } + + /* Handle simple cases. */ + if (xoff == xlim) + while (yoff < ylim) + filevec[1].changed_flag[1+filevec[1].realindexes[yoff++]] = true; + else if (yoff == ylim) + while (xoff < xlim) + filevec[0].changed_flag[1+filevec[0].realindexes[xoff++]] = true; + else + { + /* Find a point of correspondence in the middle of the files. */ + + int d = diag (xoff, xlim, yoff, ylim); + int c = cost; + int f = fdiag[fdiagoff + d]; + int b = bdiag[bdiagoff + d]; + + if (c == 1) + { + /* This should be impossible, because it implies that + one of the two subsequences is empty, + and that case was handled above without calling `diag'. + Let's verify that this is true. */ + throw new IllegalArgumentException("Empty subsequence"); + } + else + { + /* Use that point to split this problem into two subproblems. */ + compareseq (xoff, b, yoff, b - d); + /* This used to use f instead of b, + but that is incorrect! + It is not necessarily the case that diagonal d + has a snake from b to f. */ + compareseq (b, xlim, b - d, ylim); + } + } + } + + /** Discard lines from one file that have no matches in the other file. + */ + + private void discard_confusing_lines() { + filevec[0].discard_confusing_lines(filevec[1]); + filevec[1].discard_confusing_lines(filevec[0]); + } + + private boolean inhibit = false; + + /** Adjust inserts/deletes of blank lines to join changes + as much as possible. + */ + + private void shift_boundaries() { + if (inhibit) + return; + filevec[0].shift_boundaries(filevec[1]); + filevec[1].shift_boundaries(filevec[0]); + } + + public interface ScriptBuilder { + /** Scan the tables of which lines are inserted and deleted, + producing an edit script. + @param changed0 true for lines in first file which do not match 2nd + @param len0 number of lines in first file + @param changed1 true for lines in 2nd file which do not match 1st + @param len1 number of lines in 2nd file + @return a linked list of changes - or null + */ + public change build_script( + boolean[] changed0,int len0, + boolean[] changed1,int len1 + ); + } + + /** Scan the tables of which lines are inserted and deleted, + producing an edit script in reverse order. */ + + static class ReverseScript implements ScriptBuilder { + public change build_script( + final boolean[] changed0,int len0, + final boolean[] changed1,int len1) + { + change script = null; + int i0 = 0, i1 = 0; + while (i0 < len0 || i1 < len1) { + if (changed0[1+i0] || changed1[1+i1]) { + int line0 = i0, line1 = i1; + + /* Find # lines changed here in each file. */ + while (changed0[1+i0]) ++i0; + while (changed1[1+i1]) ++i1; + + /* Record this change. */ + script = new change(line0, line1, i0 - line0, i1 - line1, script); + } + + /* We have reached lines in the two files that match each other. */ + i0++; i1++; + } + + return script; + } + } + + static class ForwardScript implements ScriptBuilder { + /** Scan the tables of which lines are inserted and deleted, + producing an edit script in forward order. */ + public change build_script( + final boolean[] changed0,int len0, + final boolean[] changed1,int len1) + { + change script = null; + int i0 = len0, i1 = len1; + + while (i0 >= 0 || i1 >= 0) + { + if (changed0[i0] || changed1[i1]) + { + int line0 = i0, line1 = i1; + + /* Find # lines changed here in each file. */ + while (changed0[i0]) --i0; + while (changed1[i1]) --i1; + + /* Record this change. */ + script = new change(i0, i1, line0 - i0, line1 - i1, script); + } + + /* We have reached lines in the two files that match each other. */ + i0--; i1--; + } + + return script; + } + } + + /** Standard ScriptBuilders. */ + public final static ScriptBuilder + forwardScript = new ForwardScript(), + reverseScript = new ReverseScript(); + + /* Report the differences of two files. DEPTH is the current directory + depth. */ + public final change diff_2(final boolean reverse) { + return diff(reverse ? reverseScript : forwardScript); + } + + /** Get the results of comparison as an edit script. The script + is described by a list of changes. The standard ScriptBuilder + implementations provide for forward and reverse edit scripts. + Alternate implementations could, for instance, list common elements + instead of differences. + @param bld an object to build the script from change flags + @return the head of a list of changes + */ + public change diff(final ScriptBuilder bld) { + + /* Some lines are obviously insertions or deletions + because they don't match anything. Detect them now, + and avoid even thinking about them in the main comparison algorithm. */ + + discard_confusing_lines (); + + /* Now do the main comparison algorithm, considering just the + undiscarded lines. */ + + xvec = filevec[0].undiscarded; + yvec = filevec[1].undiscarded; + + int diags = + filevec[0].nondiscarded_lines + filevec[1].nondiscarded_lines + 3; + fdiag = new int[diags]; + fdiagoff = filevec[1].nondiscarded_lines + 1; + bdiag = new int[diags]; + bdiagoff = filevec[1].nondiscarded_lines + 1; + + compareseq (0, filevec[0].nondiscarded_lines, + 0, filevec[1].nondiscarded_lines); + fdiag = null; + bdiag = null; + + /* Modify the results slightly to make them prettier + in cases where that can validly be done. */ + + shift_boundaries (); + + /* Get the results of comparison in the form of a chain + of `struct change's -- an edit script. */ + return bld.build_script( + filevec[0].changed_flag, + filevec[0].buffered_lines, + filevec[1].changed_flag, + filevec[1].buffered_lines + ); + + } + + /** The result of comparison is an "edit script": a chain of change objects. + Each change represents one place where some lines are deleted + and some are inserted. + + LINE0 and LINE1 are the first affected lines in the two files (origin 0). + DELETED is the number of lines deleted here from file 0. + INSERTED is the number of lines inserted here in file 1. + + If DELETED is 0 then LINE0 is the number of the line before + which the insertion was done; vice versa for INSERTED and LINE1. */ + + public static class change { + /** Previous or next edit command. */ + public change link; + /** # lines of file 1 changed here. */ + public final int inserted; + /** # lines of file 0 changed here. */ + public final int deleted; + /** Line number of 1st deleted line. */ + public final int line0; + /** Line number of 1st inserted line. */ + public final int line1; + + /** Cons an additional entry onto the front of an edit script OLD. + LINE0 and LINE1 are the first affected lines in the two files (origin 0). + DELETED is the number of lines deleted here from file 0. + INSERTED is the number of lines inserted here in file 1. + + If DELETED is 0 then LINE0 is the number of the line before + which the insertion was done; vice versa for INSERTED and LINE1. */ + public change(int line0, int line1, int deleted, int inserted, change old) { + this.line0 = line0; + this.line1 = line1; + this.inserted = inserted; + this.deleted = deleted; + this.link = old; + //System.err.println(line0+","+line1+","+inserted+","+deleted); + } + } + + /** Data on one input file being compared. + */ + + class file_data { + + /** Allocate changed array for the results of comparison. */ + void clear() { + /* Allocate a flag for each line of each file, saying whether that line + is an insertion or deletion. + Allocate an extra element, always zero, at each end of each IndexedSeq. + */ + changed_flag = new boolean[buffered_lines + 2]; + } + + /** Return equiv_count[I] as the number of lines in this file + that fall in equivalence class I. + @return the array of equivalence class counts. + */ + int[] equivCount() { + int[] equiv_count = new int[equiv_max]; + for (int i = 0; i < buffered_lines; ++i) + ++equiv_count[equivs[i]]; + return equiv_count; + } + + /** Discard lines that have no matches in another file. + + A line which is discarded will not be considered by the actual + comparison algorithm; it will be as if that line were not in the file. + The file's `realindexes' table maps virtual line numbers + (which don't count the discarded lines) into real line numbers; + this is how the actual comparison algorithm produces results + that are comprehensible when the discarded lines are counted. +<p> + When we discard a line, we also mark it as a deletion or insertion + so that it will be printed in the output. + @param f the other file + */ + void discard_confusing_lines(file_data f) { + clear(); + /* Set up table of which lines are going to be discarded. */ + final byte[] discarded = discardable(f.equivCount()); + + /* Don't really discard the provisional lines except when they occur + in a run of discardables, with nonprovisionals at the beginning + and end. */ + filterDiscards(discarded); + + /* Actually discard the lines. */ + discard(discarded); + } + + /** Mark to be discarded each line that matches no line of another file. + If a line matches many lines, mark it as provisionally discardable. + @see equivCount() + @param counts The count of each equivalence number for the other file. + @return 0=nondiscardable, 1=discardable or 2=provisionally discardable + for each line + */ + + private byte[] discardable(final int[] counts) { + final int end = buffered_lines; + final byte[] discards = new byte[end]; + final int[] equivs = this.equivs; + int many = 5; + int tem = end / 64; + + /* Multiply MANY by approximate square root of number of lines. + That is the threshold for provisionally discardable lines. */ + while ((tem = tem >> 2) > 0) + many *= 2; + + for (int i = 0; i < end; i++) + { + int nmatch; + if (equivs[i] == 0) + continue; + nmatch = counts[equivs[i]]; + if (nmatch == 0) + discards[i] = 1; + else if (nmatch > many) + discards[i] = 2; + } + return discards; + } + + /** Don't really discard the provisional lines except when they occur + in a run of discardables, with nonprovisionals at the beginning + and end. */ + + private void filterDiscards(final byte[] discards) { + final int end = buffered_lines; + + for (int i = 0; i < end; i++) + { + /* Cancel provisional discards not in middle of run of discards. */ + if (discards[i] == 2) + discards[i] = 0; + else if (discards[i] != 0) + { + /* We have found a nonprovisional discard. */ + int j; + int length; + int provisional = 0; + + /* Find end of this run of discardable lines. + Count how many are provisionally discardable. */ + for (j = i; j < end; j++) + { + if (discards[j] == 0) + break; + if (discards[j] == 2) + ++provisional; + } + + /* Cancel provisional discards at end, and shrink the run. */ + while (j > i && discards[j - 1] == 2) { + discards[--j] = 0; --provisional; + } + + /* Now we have the length of a run of discardable lines + whose first and last are not provisional. */ + length = j - i; + + /* If 1/4 of the lines in the run are provisional, + cancel discarding of all provisional lines in the run. */ + if (provisional * 4 > length) + { + while (j > i) + if (discards[--j] == 2) + discards[j] = 0; + } + else + { + int consec; + int minimum = 1; + int tem = length / 4; + + /* MINIMUM is approximate square root of LENGTH/4. + A subrun of two or more provisionals can stand + when LENGTH is at least 16. + A subrun of 4 or more can stand when LENGTH >= 64. */ + while ((tem = tem >> 2) > 0) + minimum *= 2; + minimum++; + + /* Cancel any subrun of MINIMUM or more provisionals + within the larger run. */ + for (j = 0, consec = 0; j < length; j++) + if (discards[i + j] != 2) + consec = 0; + else if (minimum == ++consec) + /* Back up to start of subrun, to cancel it all. */ + j -= consec; + else if (minimum < consec) + discards[i + j] = 0; + + /* Scan from beginning of run + until we find 3 or more nonprovisionals in a row + or until the first nonprovisional at least 8 lines in. + Until that point, cancel any provisionals. */ + for (j = 0, consec = 0; j < length; j++) + { + if (j >= 8 && discards[i + j] == 1) + break; + if (discards[i + j] == 2) { + consec = 0; discards[i + j] = 0; + } + else if (discards[i + j] == 0) + consec = 0; + else + consec++; + if (consec == 3) + break; + } + + /* I advances to the last line of the run. */ + i += length - 1; + + /* Same thing, from end. */ + for (j = 0, consec = 0; j < length; j++) + { + if (j >= 8 && discards[i - j] == 1) + break; + if (discards[i - j] == 2) { + consec = 0; discards[i - j] = 0; + } + else if (discards[i - j] == 0) + consec = 0; + else + consec++; + if (consec == 3) + break; + } + } + } + } + } + + /** Actually discard the lines. + @param discards flags lines to be discarded + */ + private void discard(final byte[] discards) { + final int end = buffered_lines; + int j = 0; + for (int i = 0; i < end; ++i) + if (no_discards || discards[i] == 0) + { + undiscarded[j] = equivs[i]; + realindexes[j++] = i; + } + else + changed_flag[1+i] = true; + nondiscarded_lines = j; + } + + file_data(Object[] data,Hashtable h) { + buffered_lines = data.length; + + equivs = new int[buffered_lines]; + undiscarded = new int[buffered_lines]; + realindexes = new int[buffered_lines]; + + for (int i = 0; i < data.length; ++i) { + Integer ir = (Integer)h.get(data[i]); + if (ir == null) + h.put(data[i],new Integer(equivs[i] = equiv_max++)); + else + equivs[i] = ir.intValue(); + } + } + + /** Adjust inserts/deletes of blank lines to join changes + as much as possible. + + We do something when a run of changed lines include a blank + line at one end and have an excluded blank line at the other. + We are free to choose which blank line is included. + `compareseq' always chooses the one at the beginning, + but usually it is cleaner to consider the following blank line + to be the "change". The only exception is if the preceding blank line + would join this change to other changes. + @param f the file being compared against + */ + + void shift_boundaries(file_data f) { + final boolean[] changed = changed_flag; + final boolean[] other_changed = f.changed_flag; + int i = 0; + int j = 0; + int i_end = buffered_lines; + int preceding = -1; + int other_preceding = -1; + + for (;;) + { + int start, end, other_start; + + /* Scan forwards to find beginning of another run of changes. + Also keep track of the corresponding point in the other file. */ + + while (i < i_end && !changed[1+i]) + { + while (other_changed[1+j++]) + /* Non-corresponding lines in the other file + will count as the preceding batch of changes. */ + other_preceding = j; + i++; + } + + if (i == i_end) + break; + + start = i; + other_start = j; + + for (;;) + { + /* Now find the end of this run of changes. */ + + while (i < i_end && changed[1+i]) i++; + end = i; + + /* If the first changed line matches the following unchanged one, + and this run does not follow right after a previous run, + and there are no lines deleted from the other file here, + then classify the first changed line as unchanged + and the following line as changed in its place. */ + + /* You might ask, how could this run follow right after another? + Only because the previous run was shifted here. */ + + if (end != i_end + && equivs[start] == equivs[end] + && !other_changed[1+j] + && end != i_end + && !((preceding >= 0 && start == preceding) + || (other_preceding >= 0 + && other_start == other_preceding))) + { + changed[1+end++] = true; + changed[1+start++] = false; + ++i; + /* Since one line-that-matches is now before this run + instead of after, we must advance in the other file + to keep in synch. */ + ++j; + } + else + break; + } + + preceding = i; + other_preceding = j; + } + } + + /** Number of elements (lines) in this file. */ + final int buffered_lines; + + /** IndexedSeq, indexed by line number, containing an equivalence code for + each line. It is this IndexedSeq that is actually compared with that + of another file to generate differences. */ + private final int[] equivs; + + /** IndexedSeq, like the previous one except that + the elements for discarded lines have been squeezed out. */ + final int[] undiscarded; + + /** IndexedSeq mapping virtual line numbers (not counting discarded lines) + to real ones (counting those lines). Both are origin-0. */ + final int[] realindexes; + + /** Total number of nondiscarded lines. */ + int nondiscarded_lines; + + /** Array, indexed by real origin-1 line number, + containing true for a line that is an insertion or a deletion. + The results of comparison are stored here. */ + boolean[] changed_flag; + + } +} diff --git a/src/partest/scala/tools/partest/nest/DiffPrint.java b/src/partest/scala/tools/partest/nest/DiffPrint.java new file mode 100644 index 0000000000..494bc06e4a --- /dev/null +++ b/src/partest/scala/tools/partest/nest/DiffPrint.java @@ -0,0 +1,607 @@ +// $Id$ + +package scala.tools.partest.nest; + +import java.io.*; +import java.util.Vector; +import java.util.Date; +//import com.objectspace.jgl.predicates.UnaryPredicate; + +interface UnaryPredicate { + boolean execute(Object obj); +} + +/** A simple framework for printing change lists produced by <code>Diff</code>. + @see bmsi.util.Diff + @author Stuart D. Gathman + Copyright (C) 2000 Business Management Systems, Inc. +<p> + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. +<p> + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +<p> + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +public class DiffPrint { + /** A Base class for printing edit scripts produced by Diff. + This class divides the change list into "hunks", and calls + <code>print_hunk</code> for each hunk. Various utility methods + are provided as well. + */ + public static abstract class Base { + protected Base(Object[] a,Object[] b, Writer w) { + outfile = new PrintWriter(w); + file0 = a; + file1 = b; + } + /** Set to ignore certain kinds of lines when printing + an edit script. For example, ignoring blank lines or comments. + */ + protected UnaryPredicate ignore = null; + + /** Set to the lines of the files being compared. + */ + protected Object[] file0, file1; + + /** Divide SCRIPT into pieces by calling HUNKFUN and + print each piece with PRINTFUN. + Both functions take one arg, an edit script. + + PRINTFUN takes a subscript which belongs together (with a null + link at the end) and prints it. */ + public void print_script(Diff.change script) { + Diff.change next = script; + + while (next != null) + { + Diff.change t, end; + + /* Find a set of changes that belong together. */ + t = next; + end = hunkfun(next); + + /* Disconnect them from the rest of the changes, + making them a hunk, and remember the rest for next iteration. */ + next = end.link; + end.link = null; + //if (DEBUG) + // debug_script(t); + + /* Print this hunk. */ + print_hunk(t); + + /* Reconnect the script so it will all be freed properly. */ + end.link = next; + } + outfile.flush(); + } + + /** Called with the tail of the script + and returns the last link that belongs together with the start + of the tail. */ + + protected Diff.change hunkfun(Diff.change hunk) { + return hunk; + } + + protected int first0, last0, first1, last1, deletes, inserts; + protected PrintWriter outfile; + + /** Look at a hunk of edit script and report the range of lines in each file + that it applies to. HUNK is the start of the hunk, which is a chain + of `struct change'. The first and last line numbers of file 0 are stored + in *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1. + Note that these are internal line numbers that count from 0. + + If no lines from file 0 are deleted, then FIRST0 is LAST0+1. + + Also set *DELETES nonzero if any lines of file 0 are deleted + and set *INSERTS nonzero if any lines of file 1 are inserted. + If only ignorable lines are inserted or deleted, both are + set to 0. */ + + protected void analyze_hunk(Diff.change hunk) { + int f0, l0 = 0, f1, l1 = 0, show_from = 0, show_to = 0; + int i; + Diff.change next; + boolean nontrivial = (ignore == null); + + show_from = show_to = 0; + + f0 = hunk.line0; + f1 = hunk.line1; + + for (next = hunk; next != null; next = next.link) + { + l0 = next.line0 + next.deleted - 1; + l1 = next.line1 + next.inserted - 1; + show_from += next.deleted; + show_to += next.inserted; + for (i = next.line0; i <= l0 && ! nontrivial; i++) + if (!ignore.execute(file0[i])) + nontrivial = true; + for (i = next.line1; i <= l1 && ! nontrivial; i++) + if (!ignore.execute(file1[i])) + nontrivial = true; + } + + first0 = f0; + last0 = l0; + first1 = f1; + last1 = l1; + + /* If all inserted or deleted lines are ignorable, + tell the caller to ignore this hunk. */ + + if (!nontrivial) + show_from = show_to = 0; + + deletes = show_from; + inserts = show_to; + } + + /** Print the script header which identifies the files compared. */ + protected void print_header(String filea, String fileb) { } + + protected abstract void print_hunk(Diff.change hunk); + + protected void print_1_line(String pre,Object linbuf) { + outfile.println(pre + linbuf.toString()); + } + + /** Print a pair of line numbers with SEPCHAR, translated for file FILE. + If the two numbers are identical, print just one number. + + Args A and B are internal line numbers. + We print the translated (real) line numbers. */ + + protected void print_number_range (char sepchar, int a, int b) { + /* Note: we can have B < A in the case of a range of no lines. + In this case, we should print the line number before the range, + which is B. */ + if (++b > ++a) + outfile.print("" + a + sepchar + b); + else + outfile.print(b); + } + + public static char change_letter(int inserts, int deletes) { + if (inserts == 0) + return 'd'; + else if (deletes == 0) + return 'a'; + else + return 'c'; + } + } + + /** Print a change list in the standard diff format. + */ + public static class NormalPrint extends Base { + + public NormalPrint(Object[] a,Object[] b, Writer w) { + super(a,b,w); + } + + /** Print a hunk of a normal diff. + This is a contiguous portion of a complete edit script, + describing changes in consecutive lines. */ + + protected void print_hunk (Diff.change hunk) { + + /* Determine range of line numbers involved in each file. */ + analyze_hunk(hunk); + if (deletes == 0 && inserts == 0) + return; + + /* Print out the line number header for this hunk */ + print_number_range (',', first0, last0); + outfile.print(change_letter(inserts, deletes)); + print_number_range (',', first1, last1); + outfile.println(); + + /* Print the lines that the first file has. */ + if (deletes != 0) + for (int i = first0; i <= last0; i++) + print_1_line ("< ", file0[i]); + + if (inserts != 0 && deletes != 0) + outfile.println("---"); + + /* Print the lines that the second file has. */ + if (inserts != 0) + for (int i = first1; i <= last1; i++) + print_1_line ("> ", file1[i]); + } + } + + /** Prints an edit script in a format suitable for input to <code>ed</code>. + The edit script must be generated with the reverse option to + be useful as actual <code>ed</code> input. + */ + public static class EdPrint extends Base { + + public EdPrint(Object[] a,Object[] b, Writer w) { + super(a,b,w); + } + + /** Print a hunk of an ed diff */ + protected void print_hunk(Diff.change hunk) { + + /* Determine range of line numbers involved in each file. */ + analyze_hunk (hunk); + if (deletes == 0 && inserts == 0) + return; + + /* Print out the line number header for this hunk */ + print_number_range (',', first0, last0); + outfile.println(change_letter(inserts, deletes)); + + /* Print new/changed lines from second file, if needed */ + if (inserts != 0) + { + boolean inserting = true; + for (int i = first1; i <= last1; i++) + { + /* Resume the insert, if we stopped. */ + if (! inserting) + outfile.println(i - first1 + first0 + "a"); + inserting = true; + + /* If the file's line is just a dot, it would confuse `ed'. + So output it with a double dot, and set the flag LEADING_DOT + so that we will output another ed-command later + to change the double dot into a single dot. */ + + if (".".equals(file1[i])) + { + outfile.println(".."); + outfile.println("."); + /* Now change that double dot to the desired single dot. */ + outfile.println(i - first1 + first0 + 1 + "s/^\\.\\././"); + inserting = false; + } + else + /* Line is not `.', so output it unmodified. */ + print_1_line ("", file1[i]); + } + + /* End insert mode, if we are still in it. */ + if (inserting) + outfile.println("."); + } + } + } + + /** Prints an edit script in context diff format. This and its + 'unified' variation is used for source code patches. + */ + public static class ContextPrint extends Base { + + protected int context = 3; + + public ContextPrint(Object[] a,Object[] b, Writer w) { + super(a,b,w); + } + + protected void print_context_label (String mark, File inf, String label) { + if (label != null) + outfile.println(mark + ' ' + label); + else if (inf.lastModified() > 0) + // FIXME: use DateFormat to get precise format needed. + outfile.println( + mark + ' ' + inf.getPath() + '\t' + new Date(inf.lastModified()) + ); + else + /* Don't pretend that standard input is ancient. */ + outfile.println(mark + ' ' + inf.getPath()); + } + + public void print_header(String filea,String fileb) { + print_context_label ("***", new File(filea), filea); + print_context_label ("---", new File(fileb), fileb); + } + + /** If function_regexp defined, search for start of function. */ + private String find_function(Object[] lines, int start) { + return null; + } + + protected void print_function(Object[] file,int start) { + String function = find_function (file0, first0); + if (function != null) { + outfile.print(" "); + outfile.print( + (function.length() < 40) ? function : function.substring(0,40) + ); + } + } + + protected void print_hunk(Diff.change hunk) { + + /* Determine range of line numbers involved in each file. */ + + analyze_hunk (hunk); + + if (deletes == 0 && inserts == 0) + return; + + /* Include a context's width before and after. */ + + first0 = Math.max(first0 - context, 0); + first1 = Math.max(first1 - context, 0); + last0 = Math.min(last0 + context, file0.length - 1); + last1 = Math.min(last1 + context, file1.length - 1); + + + outfile.print("***************"); + + /* If we looked for and found a function this is part of, + include its name in the header of the diff section. */ + print_function (file0, first0); + + outfile.println(); + outfile.print("*** "); + print_number_range (',', first0, last0); + outfile.println(" ****"); + + if (deletes != 0) { + Diff.change next = hunk; + + for (int i = first0; i <= last0; i++) { + /* Skip past changes that apply (in file 0) + only to lines before line I. */ + + while (next != null && next.line0 + next.deleted <= i) + next = next.link; + + /* Compute the marking for line I. */ + + String prefix = " "; + if (next != null && next.line0 <= i) + /* The change NEXT covers this line. + If lines were inserted here in file 1, this is "changed". + Otherwise it is "deleted". */ + prefix = (next.inserted > 0) ? "!" : "-"; + + print_1_line (prefix, file0[i]); + } + } + + outfile.print("--- "); + print_number_range (',', first1, last1); + outfile.println(" ----"); + + if (inserts != 0) { + Diff.change next = hunk; + + for (int i = first1; i <= last1; i++) { + /* Skip past changes that apply (in file 1) + only to lines before line I. */ + + while (next != null && next.line1 + next.inserted <= i) + next = next.link; + + /* Compute the marking for line I. */ + + String prefix = " "; + if (next != null && next.line1 <= i) + /* The change NEXT covers this line. + If lines were deleted here in file 0, this is "changed". + Otherwise it is "inserted". */ + prefix = (next.deleted > 0) ? "!" : "+"; + + print_1_line (prefix, file1[i]); + } + } + } + } + + /** Prints an edit script in context diff format. This and its + 'unified' variation is used for source code patches. + */ + public static class UnifiedPrint extends ContextPrint { + + public UnifiedPrint(Object[] a,Object[] b, Writer w) { + super(a,b,w); + } + + public void print_header(String filea,String fileb) { + print_context_label ("---", new File(filea), filea); + print_context_label ("+++", new File(fileb), fileb); + } + + private void print_number_range (int a, int b) { + //translate_range (file, a, b, &trans_a, &trans_b); + + /* Note: we can have B < A in the case of a range of no lines. + In this case, we should print the line number before the range, + which is B. */ + if (b < a) + outfile.print(b + ",0"); + else + super.print_number_range(',',a,b); + } + + protected void print_hunk(Diff.change hunk) { + /* Determine range of line numbers involved in each file. */ + analyze_hunk (hunk); + + if (deletes == 0 && inserts == 0) + return; + + /* Include a context's width before and after. */ + + first0 = Math.max(first0 - context, 0); + first1 = Math.max(first1 - context, 0); + last0 = Math.min(last0 + context, file0.length - 1); + last1 = Math.min(last1 + context, file1.length - 1); + + + + outfile.print("@@ -"); + print_number_range (first0, last0); + outfile.print(" +"); + print_number_range (first1, last1); + outfile.print(" @@"); + + /* If we looked for and found a function this is part of, + include its name in the header of the diff section. */ + print_function(file0,first0); + + outfile.println(); + + Diff.change next = hunk; + int i = first0; + int j = first1; + + while (i <= last0 || j <= last1) { + + /* If the line isn't a difference, output the context from file 0. */ + + if (next == null || i < next.line0) { + outfile.print(' '); + print_1_line ("", file0[i++]); + j++; + } + else { + /* For each difference, first output the deleted part. */ + + int k = next.deleted; + while (k-- > 0) { + outfile.print('-'); + print_1_line ("", file0[i++]); + } + + /* Then output the inserted part. */ + + k = next.inserted; + while (k-- > 0) { + outfile.print('+'); + print_1_line ("", file1[j++]); + } + + /* We're done with this hunk, so on to the next! */ + + next = next.link; + } + } + } + } + + + /** Read a text file into an array of String. This provides basic diff + functionality. A more advanced diff utility will use specialized + objects to represent the text lines, with options to, for example, + convert sequences of whitespace to a single space for comparison + purposes. + */ + static String[] slurp(String file) throws IOException { + BufferedReader rdr = new BufferedReader(new FileReader(file)); + Vector s = new Vector(); + for (;;) { + String line = rdr.readLine(); + if (line == null) break; + s.addElement(line); + } + String[] a = new String[s.size()]; + s.copyInto(a); + return a; + } + + public static void main(String[] argv) throws IOException { + String filea = argv[argv.length - 2]; + String fileb = argv[argv.length - 1]; + String[] a = slurp(filea); + String[] b = slurp(fileb); + Diff d = new Diff(a,b); + char style = 'n'; + for (int i = 0; i < argv.length - 2; ++i) { + String f = argv[i]; + if (f.startsWith("-")) { + for (int j = 1; j < f.length(); ++j) { + switch (f.charAt(j)) { + case 'e': // Ed style + style = 'e'; break; + case 'c': // Context diff + style = 'c'; break; + case 'u': + style = 'u'; break; + } + } + } + } + boolean reverse = style == 'e'; + Diff.change script = d.diff_2(reverse); + if (script == null) + System.err.println("No differences"); + else { + Base p; + Writer w = new OutputStreamWriter(System.out); + switch (style) { + case 'e': + p = new EdPrint(a,b,w); break; + case 'c': + p = new ContextPrint(a,b,w); break; + case 'u': + p = new UnifiedPrint(a,b,w); break; + default: + p = new NormalPrint(a,b,w); + } + p.print_header(filea,fileb); + p.print_script(script); + } + } + + public static void doDiff(String[] argv, Writer w) throws IOException { + String filea = argv[argv.length - 2]; + String fileb = argv[argv.length - 1]; + String[] a = slurp(filea); + String[] b = slurp(fileb); + Diff d = new Diff(a,b); + char style = 'n'; + for (int i = 0; i < argv.length - 2; ++i) { + String f = argv[i]; + if (f.startsWith("-")) { + for (int j = 1; j < f.length(); ++j) { + switch (f.charAt(j)) { + case 'e': // Ed style + style = 'e'; break; + case 'c': // Context diff + style = 'c'; break; + case 'u': + style = 'u'; break; + } + } + } + } + boolean reverse = style == 'e'; + Diff.change script = d.diff_2(reverse); + if (script == null) + w.write("No differences\n"); + else { + Base p; + switch (style) { + case 'e': + p = new EdPrint(a,b,w); break; + case 'c': + p = new ContextPrint(a,b,w); break; + case 'u': + p = new UnifiedPrint(a,b,w); break; + default: + p = new NormalPrint(a,b,w); + } + p.print_header(filea,fileb); + p.print_script(script); + } + } + +} diff --git a/src/partest/scala/tools/partest/nest/DirectRunner.scala b/src/partest/scala/tools/partest/nest/DirectRunner.scala new file mode 100644 index 0000000000..f774320f4e --- /dev/null +++ b/src/partest/scala/tools/partest/nest/DirectRunner.scala @@ -0,0 +1,78 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +import java.io.{File, PrintStream, FileOutputStream, BufferedReader, + InputStreamReader, StringWriter, PrintWriter} +import java.util.StringTokenizer +import scala.util.Properties.{ setProp } +import scala.tools.nsc.io.Directory + +import scala.actors.Actor._ +import scala.actors.TIMEOUT + +trait DirectRunner { + + def fileManager: FileManager + + import PartestDefaults.numActors + + if (isPartestDebug) + scala.actors.Debug.level = 3 + + if (PartestDefaults.poolSize.isEmpty) { + scala.actors.Debug.info("actors.corePoolSize not defined") + setProp("actors.corePoolSize", "16") + } + + def runTestsForFiles(kindFiles: List[File], kind: String): scala.collection.immutable.Map[String, Int] = { + val len = kindFiles.length + val (testsEach, lastFrag) = (len/numActors, len%numActors) + val last = numActors-1 + val workers = for (i <- List.range(0, numActors)) yield { + val toTest = kindFiles.slice(i*testsEach, (i+1)*testsEach) + val worker = new Worker(fileManager) + worker.start() + if (i == last) + worker ! RunTests(kind, (kindFiles splitAt (last*testsEach))._2) + else + worker ! RunTests(kind, toTest) + worker + } + + var logsToDelete: List[File] = List() + var outdirsToDelete: List[File] = List() + var results = new scala.collection.immutable.HashMap[String, Int] + workers foreach { w => + receiveWithin(3600 * 1000) { + case Results(res, logs, outdirs) => + logsToDelete :::= logs filter (_.toDelete) + outdirsToDelete :::= outdirs + results ++= res + case TIMEOUT => + // add at least one failure + NestUI.verbose("worker timed out; adding failed test") + results += ("worker timed out; adding failed test" -> 2) + } + } + + if (isPartestDebug) + fileManager.showTestTimings() + + if (!isPartestDebug) { + for (x <- logsToDelete ::: outdirsToDelete) { + NestUI.verbose("deleting "+x) + Directory(x).deleteRecursively() + } + } + + results + } + +} diff --git a/src/partest/scala/tools/partest/nest/FileManager.scala b/src/partest/scala/tools/partest/nest/FileManager.scala new file mode 100644 index 0000000000..bdbb34b3c4 --- /dev/null +++ b/src/partest/scala/tools/partest/nest/FileManager.scala @@ -0,0 +1,110 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +import java.io.{File, FilenameFilter, IOException, StringWriter, + FileInputStream, FileOutputStream, BufferedReader, + FileReader, PrintWriter, FileWriter} +import java.net.URI +import scala.tools.nsc.io.{ Path, Directory } +import scala.collection.mutable.HashMap + +trait FileManager { + /** + * Compares two files using a Java implementation of the GNU diff + * available at http://www.bmsi.com/java/#diff. + * + * @param f1 the first file to be compared + * @param f2 the second file to be compared + * @return the text difference between the compared files + */ + def compareFiles(f1: File, f2: File): String = { + val diffWriter = new StringWriter + val args = Array(f1.getCanonicalPath(), f2.getCanonicalPath()) + + DiffPrint.doDiff(args, diffWriter) + val res = diffWriter.toString + if (res startsWith "No") "" else res + } + + def testRootDir: Directory + def testRootPath: String + + var JAVACMD: String + var JAVAC_CMD: String + + var CLASSPATH: String + var LATEST_LIB: String + + var showDiff = false + var showLog = false + var failed = false + + var SCALAC_OPTS = PartestDefaults.scalacOpts + var JAVA_OPTS = PartestDefaults.javaOpts + var timeout = PartestDefaults.timeout + + /** Only when --debug is given. */ + lazy val testTimings = new HashMap[String, Long] + def recordTestTiming(name: String, milliseconds: Long) = + synchronized { testTimings(name) = milliseconds } + def showTestTimings() { + testTimings.toList sortBy (-_._2) foreach { case (k, v) => println("%s: %s".format(k, v)) } + } + + def getLogFile(dir: File, fileBase: String, kind: String): LogFile = + new LogFile(dir, fileBase + "-" + kind + ".log") + + def getLogFile(file: File, kind: String): LogFile = { + val dir = file.getParentFile + val fileBase = basename(file.getName) + getLogFile(dir, fileBase, kind) + } + + def logFileExists(file: File, kind: String) = + getLogFile(file, kind).canRead + + def overwriteFileWith(dest: File, file: File) = + dest.isFile && copyFile(file, dest) + + + def copyFile(from: File, dest: File): Boolean = { + def copyFile0(from: File, to: File): Boolean = + try { + val appender = StreamAppender(from, to) + appender.run() + appender.closeAll() + true + } catch { + case _: IOException => false + } + + if (from.isDirectory) { + assert(dest.isDirectory, "cannot copy directory to file") + val subDir:Directory = Path(dest) / Directory(from.getName) + subDir.createDirectory() + from.listFiles.toList.forall(copyFile(_, subDir)) + } else + copyFile0(from, if (dest.isDirectory) new File(dest, from.getName) else dest) + } + + def mapFile(file: File, suffix: String, dir: File, replace: String => String) { + val tmpFile = File.createTempFile("tmp", suffix, dir) // prefix required by API + + val appender = StreamAppender(file, tmpFile) + appender.runAndMap(replace) + appender.closeAll() + + val appender2 = StreamAppender(tmpFile, file) + appender2.run() + appender2.closeAll() + + tmpFile.delete() + } +} diff --git a/src/partest/scala/tools/partest/nest/NestRunner.scala b/src/partest/scala/tools/partest/nest/NestRunner.scala new file mode 100644 index 0000000000..158521875e --- /dev/null +++ b/src/partest/scala/tools/partest/nest/NestRunner.scala @@ -0,0 +1,16 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +object NestRunner { + def main(args: Array[String]) { + val argstr = args.mkString(" ") + (new ReflectiveRunner).main(argstr) + } +} diff --git a/src/partest/scala/tools/partest/nest/NestUI.scala b/src/partest/scala/tools/partest/nest/NestUI.scala new file mode 100644 index 0000000000..efff4e8375 --- /dev/null +++ b/src/partest/scala/tools/partest/nest/NestUI.scala @@ -0,0 +1,118 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +import java.io.PrintWriter + +object NestUI { + + val NONE = 0 + val SOME = 1 + val MANY = 2 + + private var _outline = "" + private var _success = "" + private var _failure = "" + private var _warning = "" + private var _default = "" + + def initialize(number: Int) = number match { + case MANY => + _outline = Console.BOLD + Console.BLACK + _success = Console.BOLD + Console.GREEN + _failure = Console.BOLD + Console.RED + _warning = Console.BOLD + Console.YELLOW + _default = Console.RESET + case SOME => + _outline = Console.BOLD + Console.BLACK + _success = Console.RESET + _failure = Console.BOLD + Console.BLACK + _warning = Console.BOLD + Console.BLACK + _default = Console.RESET + case _ => + } + + def outline(msg: String) = print(_outline + msg + _default) + def outline(msg: String, wr: PrintWriter) = synchronized { + wr.print(_outline + msg + _default) + } + + def success(msg: String) = print(_success + msg + _default) + def success(msg: String, wr: PrintWriter) = synchronized { + wr.print(_success + msg + _default) + } + + def failure(msg: String) = print(_failure + msg + _default) + def failure(msg: String, wr: PrintWriter) = synchronized { + wr.print(_failure + msg + _default) + } + + def warning(msg: String) = print(_warning + msg + _default) + def warning(msg: String, wr: PrintWriter) = synchronized { + wr.print(_warning + msg + _default) + } + + def normal(msg: String) = print(_default + msg) + def normal(msg: String, wr: PrintWriter) = synchronized { + wr.print(_default + msg) + } + + def usage() { + println("Usage: NestRunner [<options>] [<testfile> ..] [<resfile>]") + println(" <testfile>: list of files ending in '.scala'") + println(" <resfile>: a file not ending in '.scala'") + println(" <options>:") + println + println(" Test categories:") + println(" --all run all tests") + println(" --pos run compilation tests (success)") + println(" --neg run compilation tests (failure)") + println(" --run run interpreter and backend tests") + println(" --jvm run JVM backend tests") + println(" --res run resident compiler tests") + println(" --buildmanager run Build Manager tests") + println(" --scalacheck run ScalaCheck tests") + println(" --script run script runner tests") + println(" --shootout run shootout tests") + println(" --grep <expr> run all tests whose source file contains <expr>") + println + println(" Other options:") + println(" --pack pick compiler/library in build/pack, and run all tests") + println(" --show-log show log") + println(" --show-diff show diff between log and check file") + println(" --failed run only those tests that failed during the last run") + println(" --verbose show progress information") + println(" --buildpath set (relative) path to build jars") + println(" ex.: --buildpath build/pack") + println(" --classpath set (absolute) path to build classes") + println(" --srcpath set (relative) path to test source files") + println(" ex.: --srcpath pending") + println(" --debug enable debugging output") + println + println(utils.Properties.versionString) + println("maintained by Philipp Haller (EPFL)") + exit(1) + } + + var _verbose = false + var _debug = false + + def verbose(msg: String) { + if (_verbose) { + outline("debug: ") + println(msg) + } + } + def debug(msg: String) { + if (isPartestDebug) { + outline("debug: ") + println(msg) + } + } +} diff --git a/src/partest/scala/tools/partest/nest/PathSettings.scala b/src/partest/scala/tools/partest/nest/PathSettings.scala new file mode 100644 index 0000000000..41bba5782e --- /dev/null +++ b/src/partest/scala/tools/partest/nest/PathSettings.scala @@ -0,0 +1,41 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + */ + +package scala.tools.partest +package nest + +import scala.tools.nsc.Properties.{ setProp, propOrEmpty, propOrNone, propOrElse } +import scala.tools.nsc.util.ClassPath +import scala.tools.nsc.io +import io.{ Path, File, Directory } +import RunnerUtils._ +import java.net.URLClassLoader + +object PathSettings { + import PartestDefaults.{ testRootDir, srcDirName } + + private def cwd = Directory.Current getOrElse error("user.dir property not set") + private def isPartestDir(d: Directory) = (d.name == "test") && (d / srcDirName isDirectory) + + // Directory <root>/test + lazy val testRoot: Directory = testRootDir getOrElse { + val candidates: List[Directory] = (cwd :: cwd.parents) flatMap (d => List(d, Directory(d / "test"))) + + candidates find isPartestDir getOrElse error("Directory 'test' not found.") + } + + // Directory <root>/test/files + lazy val srcDir = Directory(testRoot / srcDirName normalize) + + // Directory <root>/test/files/lib + lazy val srcLibDir = Directory(srcDir / "lib") + + lazy val scalaCheck = srcLibDir.files find (_.name startsWith "scalacheck") getOrElse { + error("No scalacheck jar found in '%s'" format srcLibDir) + } +} + +class PathSettings() { + // def classpathAsURLs: List[URL] +} diff --git a/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala b/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala new file mode 100644 index 0000000000..b3f199a3d6 --- /dev/null +++ b/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala @@ -0,0 +1,88 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +import scala.tools.nsc.Properties.{ setProp, propOrEmpty } +import scala.tools.nsc.util.ClassPath +import scala.tools.nsc.io +import io.Path +import RunnerUtils._ +import java.net.URLClassLoader + +/* This class is used to load an instance of DirectRunner using + * a custom class loader. + * The purpose is to "auto-detect" a good classpath for the + * rest of the classes (Worker, CompileManager etc.), so that + * the main NestRunner can be started merely by putting its + * class on the classpath (ideally). + */ +class ReflectiveRunner { + // TODO: we might also use fileManager.CLASSPATH + // to use the same classes as used by `scala` that + // was used to start the runner. + val sepRunnerClassName = "scala.tools.partest.nest.ConsoleRunner" + + def main(args: String) { + val argList = (args.split("\\s")).toList + + if (isPartestDebug) + showAllJVMInfo + + // find out which build to test + val buildPath = searchPath("--buildpath", argList) + val classPath = searchPath("--classpath", argList) + val fileManager = + if (!buildPath.isEmpty) + new ConsoleFileManager(buildPath.get) + else if (!classPath.isEmpty) + new ConsoleFileManager(classPath.get, true) + else if (argList contains "--pack") + new ConsoleFileManager("build/pack") + else // auto detection + new ConsoleFileManager + + import fileManager. + { latestCompFile, latestLibFile, latestPartestFile, latestFjbgFile } + val files = + Array(latestCompFile, latestLibFile, latestPartestFile, latestFjbgFile) map (x => io.File(x)) + + val sepUrls = files map (_.toURL) + val sepLoader = new URLClassLoader(sepUrls, null) + + if (isPartestDebug) + println("Loading classes from:\n" + sepUrls.mkString("\n")) + + val paths = classPath match { + case Some(cp) => Nil + case _ => files.toList map (_.path) + } + val newClasspath = ClassPath.join(paths: _*) + + setProp("java.class.path", newClasspath) + setProp("scala.home", "") + + if (isPartestDebug) + for (prop <- List("java.class.path", "sun.boot.class.path", "java.ext.dirs")) + println(prop + ": " + propOrEmpty(prop)) + + try { + val sepRunnerClass = sepLoader loadClass sepRunnerClassName + val sepRunner = sepRunnerClass.newInstance() + val sepMainMethod = sepRunnerClass.getMethod("main", Array(classOf[String]): _*) + val cargs: Array[AnyRef] = Array(args) + sepMainMethod.invoke(sepRunner, cargs: _*) + } + catch { + case cnfe: ClassNotFoundException => + cnfe.printStackTrace() + NestUI.failure(sepRunnerClassName +" could not be loaded from:\n") + sepUrls foreach (x => NestUI.failure(x + "\n")) + } + } +} diff --git a/src/partest/scala/tools/partest/nest/RunnerUtils.scala b/src/partest/scala/tools/partest/nest/RunnerUtils.scala new file mode 100644 index 0000000000..24445bb545 --- /dev/null +++ b/src/partest/scala/tools/partest/nest/RunnerUtils.scala @@ -0,0 +1,29 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +object RunnerUtils { + def splitArgs(str: String) = str split "\\s" filterNot (_ == "") toList + + def searchPath(option: String, as: List[String]): Option[String] = as match { + case `option` :: r :: _ => Some(r) + case _ :: rest => searchPath(option, rest) + case Nil => None + } + + def searchAndRemovePath(option: String, as: List[String]) = (as indexOf option) match { + case -1 => (None, as) + case idx => (Some(as(idx + 1)), (as take idx) ::: (as drop (idx + 2))) + } + + def searchAndRemoveOption(option: String, as: List[String]) = (as indexOf option) match { + case -1 => (false, as) + case idx => (true, (as take idx) ::: (as drop (idx + 1))) + } +} diff --git a/src/partest/scala/tools/partest/nest/TestFile.scala b/src/partest/scala/tools/partest/nest/TestFile.scala new file mode 100644 index 0000000000..741556fdd5 --- /dev/null +++ b/src/partest/scala/tools/partest/nest/TestFile.scala @@ -0,0 +1,49 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +import java.io.{ File => JFile } +import scala.tools.nsc.Settings +import scala.tools.nsc.io._ + +abstract class TestFile(kind: String) { + def file: JFile + def fileManager: FileManager + + val dir = file.toAbsolute.parent + val fileBase = file.stripExtension + lazy val objectDir = dir / "%s-%s.obj".format(fileBase, kind) createDirectory true + val flags: Option[String] = dir / "%s.flags".format(fileBase) ifFile { _.slurp().trim } + + def setOutDirTo = objectDir + + def defineSettings(settings: Settings, setOutDir: Boolean) = { + settings.classpath append dir.path + if (setOutDir) + settings.outdir.value = setOutDirTo.path + + flags foreach (settings processArgumentString _) + settings.classpath append fileManager.CLASSPATH + } + + override def toString(): String = "%s %s".format(kind, file) +} + +case class PosTestFile(file: JFile, fileManager: FileManager) extends TestFile("pos") +case class NegTestFile(file: JFile, fileManager: FileManager) extends TestFile("neg") +case class RunTestFile(file: JFile, fileManager: FileManager) extends TestFile("run") +case class BuildManagerTestFile(file: JFile, fileManager: FileManager) extends TestFile("bm") +case class ScalaCheckTestFile(file: JFile, fileManager: FileManager) extends TestFile("scalacheck") +case class JvmTestFile(file: JFile, fileManager: FileManager) extends TestFile("jvm") +case class ShootoutTestFile(file: JFile, fileManager: FileManager) extends TestFile("shootout") { + override def setOutDirTo = file.parent +} +case class ScalapTestFile(file: JFile, fileManager: FileManager) extends TestFile("scalap") { + override def setOutDirTo = file.parent +} diff --git a/src/partest/scala/tools/partest/nest/Worker.scala b/src/partest/scala/tools/partest/nest/Worker.scala new file mode 100644 index 0000000000..2f81dfd0f7 --- /dev/null +++ b/src/partest/scala/tools/partest/nest/Worker.scala @@ -0,0 +1,1071 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +import java.io._ +import java.net.{ URLClassLoader, URL } +import java.util.{ Timer, TimerTask } + +import scala.util.Properties.{ isWin } +import scala.tools.nsc.{ ObjectRunner, Settings, CompilerCommand, Global } +import scala.tools.nsc.io.{ AbstractFile, PlainFile, Path, Directory, File => SFile } +import scala.tools.nsc.reporters.ConsoleReporter +import scala.tools.nsc.util.{ ClassPath, FakePos } +import ClassPath.{ join, split } + +import scala.actors.{ Actor, Exit, TIMEOUT } +import scala.actors.Actor._ +import scala.tools.scalap.scalax.rules.scalasig.{ByteCode, ClassFileParser, ScalaSigAttributeParsers} + +import scala.collection.immutable.{ HashMap, Map => ImmMap } +import scala.collection.Map + +import scala.tools.nsc.interactive.{BuildManager, RefinedBuildManager} + +case class RunTests(kind: String, files: List[File]) +case class Results(results: ImmMap[String, Int], logs: List[LogFile], outdirs: List[File]) + +case class LogContext(file: LogFile, writers: Option[(StringWriter, PrintWriter)]) + +abstract class TestResult { + def file: File +} +case class Result(override val file: File, context: LogContext) extends TestResult +case class Timeout(override val file: File) extends TestResult + +class LogFile(parent: File, child: String) extends File(parent, child) { + var toDelete = false +} + +class Worker(val fileManager: FileManager) extends Actor { + import fileManager._ + + var reporter: ConsoleReporter = _ + val timer = new Timer + + def error(msg: String): Unit = reporter.error( + FakePos("scalac"), + msg + "\n scalac -help gives more information" + ) + + def act() { + react { + case RunTests(kind, files) => + // NestUI.verbose("received "+files.length+" to test") + val master = sender + runTests(kind, files) { results => + master ! Results(results, createdLogFiles, createdOutputDirs) + } + } + } + + def printInfoStart(file: File, printer: PrintWriter) { + NestUI.outline("testing: ", printer) + val filesdir = file.getAbsoluteFile.getParentFile.getParentFile + val testdir = filesdir.getParentFile + val totalWidth = 56 + val name = { + // 1. try with [...]/files/run/test.scala + val testPathLen = testdir.getAbsolutePath.length + val name = file.getAbsolutePath.substring(testPathLen) + if (name.length <= totalWidth) + name + // 2. try with [...]/run/test.scala + else { + val filesPathLen = filesdir.getAbsolutePath.length + file.getAbsolutePath.substring(filesPathLen) + } + } + NestUI.normal("[...]%s%s".format(name, " " * (totalWidth - name.length)), printer) + } + + def printInfoEnd(success: Boolean, printer: PrintWriter) { + NestUI.normal("[", printer) + if (success) NestUI.success(" OK ", printer) + else NestUI.failure("FAILED", printer) + NestUI.normal("]\n", printer) + } + + def printInfoTimeout(printer: PrintWriter) { + NestUI.normal("[", printer) + NestUI.failure("TIMOUT", printer) + NestUI.normal("]\n", printer) + } + + var log = "" + var createdLogFiles: List[LogFile] = Nil + var createdOutputDirs: List[File] = Nil + + def createLogFile(file: File, kind: String): LogFile = { + val logFile = fileManager.getLogFile(file, kind) + createdLogFiles ::= logFile + logFile + } + + def createOutputDir(dir: File, fileBase: String, kind: String): File = { + val outDir = Path(dir) / Directory("%s-%s.obj".format(fileBase, kind)) + outDir.createDirectory() + createdOutputDirs ::= outDir.jfile + outDir.jfile + } + + /* Note: not yet used/tested. */ + // def execTestObjectRunner(file: File, outDir: File, logFile: File) { + // val consFM = new ConsoleFileManager + // + // val classpath: List[URL] = { + // import consFM.{ latestCompFile, latestLibFile, latestPartestFile } + // val units = ( + // List(outDir, latestCompFile, latestLibFile, latestPartestFile) ::: + // ((CLASSPATH split File.pathSeparatorChar).toList map (x => new File(x))) + // ) + // units map (_.toURI.toURL) + // } + // + // NestUI.verbose("ObjectRunner classpath: "+classpath) + // + // try { + // // configure input/output files + // val logOut = new FileOutputStream(logFile) + // val logWriter = new PrintStream(logOut) + // + // // grab global lock + // fileManager.synchronized { + // withOutputRedirected(logWriter) { + // System.setProperty("java.library.path", logFile.getParentFile.getCanonicalFile.getAbsolutePath) + // System.setProperty("partest.output", outDir.getCanonicalFile.getAbsolutePath) + // System.setProperty("partest.lib", LATEST_LIB) + // System.setProperty("partest.cwd", outDir.getParent) + // ObjectRunner.run(classpath, "Test", List("jvm")) + // } + // } + // + // /*val out = new FileOutputStream(logFile, true) + // Console.withOut(new PrintStream(out)) { + // ObjectRunner.run(classpath, "Test", List("jvm")) + // } + // out.flush + // out.close*/ + // } catch { + // case e: Exception => + // NestUI.verbose(e+" ("+file.getPath+")") + // e.printStackTrace() + // } + // } + + def javac(outDir: File, files: List[File], output: File): Boolean = { + // compile using command-line javac compiler + val javacCmd = if ((fileManager.JAVAC_CMD.indexOf("${env.JAVA_HOME}") != -1) || + fileManager.JAVAC_CMD.equals("/bin/javac") || + fileManager.JAVAC_CMD.equals("\\bin\\javac")) + "javac" + else + fileManager.JAVAC_CMD + + val cmd = javacCmd+ + " -d "+outDir.getAbsolutePath+ + " -classpath "+ join(outDir.toString, CLASSPATH) + + " "+files.mkString(" ") + + val (success, msg) = try { + val exitCode = runCommand(cmd, output) + NestUI.verbose("javac returned exit code: "+exitCode) + if (exitCode != 0) + (false, "Running \"javac\" failed with exit code: "+exitCode+"\n"+cmd+"\n") + else + (true, "") + } catch { + case e: Exception => + val swriter = new StringWriter + e.printStackTrace(new PrintWriter(swriter)) + (false, "Running \"javac\" failed:\n"+cmd+"\n"+swriter.toString+"\n") + } + if (!success) { + val writer = new PrintWriter(new FileWriter(output, true), true) + writer.print(msg) + writer.close() + } + success + } + + /** Runs <code>command</code> redirecting standard out and + * error out to <code>output</code> file. + */ + def runCommand(command: String, output: File): Int = { + NestUI.verbose("running command:\n"+command) + val proc = Runtime.getRuntime.exec(command) + val in = proc.getInputStream + val err = proc.getErrorStream + val writer = new PrintWriter(new FileWriter(output), true) + val inApp = StreamAppender(in, writer) + val errApp = StreamAppender(err, writer) + val async = new Thread(errApp) + async.start() + inApp.run() + async.join() + writer.close() + + try proc.exitValue() + catch { case _: IllegalThreadStateException => 0 } + } + + def execTest(outDir: File, logFile: File, fileBase: String) { + // check whether there is a ".javaopts" file + val argsFile = new File(logFile.getParentFile, fileBase+".javaopts") + val argString = if (argsFile.exists) { + NestUI.verbose("Found javaopts file: "+argsFile) + val fileReader = new FileReader(argsFile) + val reader = new BufferedReader(fileReader) + val options = reader.readLine() + reader.close() + NestUI.verbose("Found javaopts file '%s', using options: '%s'".format(argsFile, options)) + options + } else "" + + def quote(path: String) = "\""+path+"\"" + + // Note! As this currently functions, JAVA_OPTS must precede argString + // because when an option is repeated to java only the last one wins. + // That means until now all the .javaopts files were being ignored because + // they all attempt to change options which are also defined in + // partest.java_opts, leading to debug output like: + // + // debug: Found javaopts file 'files/shootout/message.scala-2.javaopts', using options: '-Xss32k' + // debug: java -Xss32k -Xss2m -Xms256M -Xmx1024M -classpath [...] + val propertyOptions = List( + "-Djava.library.path="+logFile.getParentFile.getAbsolutePath, + "-Dpartest.output="+outDir.getAbsolutePath, + "-Dpartest.lib="+LATEST_LIB, + "-Dpartest.cwd="+outDir.getParent, + "-Djavacmd="+JAVACMD, + "-Duser.language=en -Duser.country=US" + ) ::: ( + if (isPartestDebug) List("-Dpartest.debug=true") else Nil + ) + + val cmd = ( + List( + JAVACMD, + JAVA_OPTS, + argString, + "-classpath " + join(outDir.toString, CLASSPATH) + ) ::: propertyOptions ::: List( + "scala.tools.nsc.MainGenericRunner", + "-usejavacp", + "Test", + "jvm" + ) + ) mkString " " + + runCommand(cmd, logFile) + + if (fileManager.showLog) { + // produce log as string in `log` + val reader = new BufferedReader(new FileReader(logFile)) + val swriter = new StringWriter + val pwriter = new PrintWriter(swriter, true) + val appender = new StreamAppender(reader, pwriter) + appender.run() + log = swriter.toString + } + } + + def getCheckFile(dir: File, fileBase: String, kind: String) = { + def chkFile(s: String) = Directory(dir) / "%s%s.check".format(fileBase, s) + val checkFile = if (chkFile("").isFile) chkFile("") else chkFile("-" + kind) + + if (checkFile.canRead) Some(checkFile) else None + } + + def existsCheckFile(dir: File, fileBase: String, kind: String) = + getCheckFile(dir, fileBase, kind).isDefined + + def compareOutput(dir: File, fileBase: String, kind: String, logFile: File): String = + // if check file exists, compare with log file + getCheckFile(dir, fileBase, kind) match { + case Some(f) => fileManager.compareFiles(logFile, f.jfile) + case _ => file2String(logFile) + } + + def file2String(logFile: File) = SFile(logFile).slurp() + def isJava(f: File) = SFile(f) hasExtension "java" + def isScala(f: File) = SFile(f) hasExtension "scala" + def isJavaOrScala(f: File) = isJava(f) || isScala(f) + + /** Runs a list of tests. + * + * @param kind The test kind (pos, neg, run, etc.) + * @param files The list of test files + */ + def runTests(kind: String, files: List[File])(topcont: ImmMap[String, Int] => Unit) { + val compileMgr = new CompileManager(fileManager) + var errors = 0 + var succeeded = true + var diff = "" + var log = "" + + def fail(what: Any) { + NestUI.verbose("scalac: compilation of "+what+" failed\n") + succeeded = false + } + def diffCheck(latestDiff: String) = { + diff = latestDiff + if (latestDiff != "") { + NestUI.verbose("output differs from log file\n") + succeeded = false + } + } + + /** 1. Creates log file and output directory. + * 2. Runs <code>script</code> function, providing log file and + * output directory as arguments. + */ + def runInContext(file: File, kind: String, script: (File, File) => Unit): LogContext = { + // when option "--failed" is provided + // execute test only if log file is present + // (which means it failed before) + val logFile = createLogFile(file, kind) + if (!fileManager.failed || logFile.canRead) { + val swr = new StringWriter + val wr = new PrintWriter(swr) + succeeded = true + diff = "" + log = "" + printInfoStart(file, wr) + + val fileBase: String = basename(file.getName) + NestUI.verbose(this+" running test "+fileBase) + val dir = file.getParentFile + val outDir = createOutputDir(dir, fileBase, kind) + NestUI.verbose("output directory: "+outDir) + + // run test-specific code + try { + if (isPartestDebug) { + val t1 = System.currentTimeMillis + script(logFile, outDir) + val t2 = System.currentTimeMillis + fileManager.recordTestTiming(file.getPath, t2 - t1) + } + else { + script(logFile, outDir) + } + } catch { + case e: Exception => + val writer = new PrintWriter(new FileWriter(logFile), true) + e.printStackTrace(writer) + writer.close() + succeeded = false + } + + LogContext(logFile, Some((swr, wr))) + } else + LogContext(logFile, None) + } + + def compileFilesIn(dir: File, kind: String, logFile: File, outDir: File) { + val testFiles = dir.listFiles.toList filter isJavaOrScala + + def isInGroup(f: File, num: Int) = SFile(f).stripExtension endsWith ("_" + num) + val groups = (0 to 9).toList map (num => testFiles filter (f => isInGroup(f, num))) + val noGroupSuffix = testFiles -- groups.flatten + + def compileGroup(g: List[File]) { + val (scalaFiles, javaFiles) = g partition isScala + + if (scalaFiles.nonEmpty) { + if (!compileMgr.shouldCompile(outDir, javaFiles ::: scalaFiles, kind, logFile)) + fail(g) + } + + if (succeeded && javaFiles.nonEmpty) { + succeeded = javac(outDir, javaFiles, logFile) + if (succeeded && scalaFiles.nonEmpty && !compileMgr.shouldCompile(outDir, scalaFiles, kind, logFile)) + fail(scalaFiles) + } + } + + if (noGroupSuffix.nonEmpty) + compileGroup(noGroupSuffix) + + groups foreach (grp => if (succeeded) compileGroup(grp)) + } + + def failCompileFilesIn(dir: File, kind: String, logFile: File, outDir: File) { + val testFiles = dir.listFiles.toList + val sourceFiles = testFiles filter isJavaOrScala + + if (sourceFiles.nonEmpty) { + if (!compileMgr.shouldFailCompile(outDir, sourceFiles, kind, logFile)) + fail(testFiles filter isScala) + } + } + + def runTestCommon(file: File, kind: String, expectFailure: Boolean)(onSuccess: (File, File) => Unit): LogContext = + runInContext(file, kind, (logFile: File, outDir: File) => { + + if (file.isDirectory) { + val f = if (expectFailure) failCompileFilesIn _ else compileFilesIn _ + f(file, kind, logFile, outDir) + } + else { + val f: (List[File], String, File) => Boolean = + if (expectFailure) compileMgr.shouldFailCompile _ + else compileMgr.shouldCompile _ + + if (!f(List(file), kind, logFile)) + fail(file) + } + + if (succeeded) // run test + onSuccess(logFile, outDir) + }) + + def runJvmTest(file: File, kind: String): LogContext = + runTestCommon(file, kind, expectFailure = false)((logFile, outDir) => { + val fileBase = basename(file.getName) + val dir = file.getParentFile + + //TODO: detect whether we have to use Runtime.exec + // val useRuntime = true + // + // if (useRuntime) + // execTest(outDir, logFile, fileBase) + // else + // execTestObjectRunner(file, outDir, logFile) + // // NestUI.verbose(this+" finished running "+fileBase) + execTest(outDir, logFile, fileBase) + + diffCheck(compareOutput(dir, fileBase, kind, logFile)) + }) + + def processSingleFile(file: File): LogContext = kind match { + case "scalacheck" => + runTestCommon(file, kind, expectFailure = false)((logFile, outDir) => { + val consFM = new ConsoleFileManager + import consFM.{ latestCompFile, latestLibFile, latestPartestFile } + + NestUI.verbose("compilation of "+file+" succeeded\n") + + val scalacheckURL = PathSettings.scalaCheck.toURL + val outURL = outDir.getCanonicalFile.toURI.toURL + val classpath: List[URL] = + List(outURL, scalacheckURL, latestCompFile.toURI.toURL, latestLibFile.toURI.toURL, latestPartestFile.toURI.toURL).distinct + + NestUI.debug("scalacheck urls") + classpath foreach (x => NestUI.debug(x.toString)) + + val logWriter = new PrintStream(new FileOutputStream(logFile)) + + withOutputRedirected(logWriter) { + ObjectRunner.run(classpath, "Test", Nil) + } + + NestUI.verbose(SFile(logFile).slurp()) + // obviously this must be improved upon + succeeded = SFile(logFile).lines() forall (_ contains " OK") + }) + + case "pos" => + runTestCommon(file, kind, expectFailure = false)((_, _) => ()) + + case "neg" => + runTestCommon(file, kind, expectFailure = true)((logFile, outDir) => { + // compare log file to check file + val fileBase = basename(file.getName) + val dir = file.getParentFile + + diffCheck( + // diff is contents of logFile + if (!existsCheckFile(dir, fileBase, kind)) file2String(logFile) + else compareOutput(dir, fileBase, kind, logFile) + ) + }) + + case "run" | "jvm" => + runJvmTest(file, kind) + + case "buildmanager" => + val logFile = createLogFile(file, kind) + if (!fileManager.failed || logFile.canRead) { + val swr = new StringWriter + val wr = new PrintWriter(swr) + succeeded = true; diff = "" + printInfoStart(file, wr) + val (outDir, testFile, changesDir, fileBase) = + + if (!file.isDirectory) { + succeeded = false + (null, null, null, null) + } else { + val fileBase: String = basename(file.getName) + NestUI.verbose(this+" running test "+fileBase) + val outDir = createOutputDir(file, fileBase, kind) + if (!outDir.exists) outDir.mkdir() + val testFile = new File(file, fileBase + ".test") + val changesDir = new File(file, fileBase + ".changes") + if (changesDir.isFile || !testFile.isFile) { + // if changes exists then it has to be a dir + if (!testFile.isFile) NestUI.verbose("invalid build manager test file") + if (changesDir.isFile) NestUI.verbose("invalid build manager changes directory") + succeeded = false + (null, null, null, null) + } else { + copyTestFiles(file, outDir) + NestUI.verbose("outDir: "+outDir) + NestUI.verbose("logFile: "+logFile) + (outDir, testFile, changesDir, fileBase) + } + } + + if (succeeded) { + // Pre-conditions satisfied + + try { + val sourcepath = outDir.getAbsolutePath+File.separator + + // configure input/output files + val logWriter = new PrintStream(new FileOutputStream(logFile)) + val testReader = new BufferedReader(new FileReader(testFile)) + val logConsoleWriter = new PrintWriter(logWriter) + + // create proper settings for the compiler + val settings = new Settings(error) + settings.outdir.value = outDir.getCanonicalFile.getAbsolutePath + settings.sourcepath.value = sourcepath + settings.classpath.value = fileManager.CLASSPATH + settings.Ybuildmanagerdebug.value = true + + // simulate Build Manager loop + val prompt = "builder > " + reporter = new ConsoleReporter(settings, scala.Console.in, logConsoleWriter) + val bM: BuildManager = + new RefinedBuildManager(settings) { + override protected def newCompiler(settings: Settings) = + new BuilderGlobal(settings, reporter) + } + + val testCompile = (line: String) => { + NestUI.verbose("compiling " + line) + val args = (line split ' ').toList + val command = new CompilerCommand(args, settings) + bM.update(filesToSet(settings.sourcepath.value, command.files), Set.empty) + !reporter.hasErrors + } + + val updateFiles = (line: String) => { + NestUI.verbose("updating " + line) + val res = + ((line split ' ').toList).forall(u => { + (u split "=>").toList match { + case origFileName::(newFileName::Nil) => + val newFile = new File(changesDir, newFileName) + if (newFile.isFile) { + val v = overwriteFileWith(new File(outDir, origFileName), newFile) + if (!v) + NestUI.verbose("'update' operation on " + u + " failed") + v + } else { + NestUI.verbose("File " + newFile + " is invalid") + false + } + case a => + NestUI.verbose("Other =: " + a) + false + } + }) + if (!res) + NestUI.verbose("updating failed") + else + NestUI.verbose("updating succeeded") + res + } + + def loop() { + val command = testReader.readLine() + if ((command ne null) && command.length() > 0) { + val commandResult = command match { + case s if (s.startsWith(">>update ")) => + updateFiles(s.stripPrefix(">>update ")) + case s if (s.startsWith(">>compile ")) => + val files = s.stripPrefix(">>compile ") + logWriter.println(prompt + files) + testCompile(files) // In the end, it can finish with an error + case _ => + NestUI.verbose("wrong command in test file: " + command) + false + } + + if (commandResult) loop() + + } else { + NestUI.verbose("finished") + succeeded = true + } + } + + withOutputRedirected(logWriter) { + loop() + testReader.close() + } + + fileManager.mapFile(logFile, "tmp", file, _.replace(sourcepath, "")) + + diffCheck(compareOutput(file, fileBase, kind, logFile)) + } + LogContext(logFile, Some((swr, wr))) + } else + LogContext(logFile, None) + } else + LogContext(logFile, None) + + case "res" => { + // when option "--failed" is provided + // execute test only if log file is present + // (which means it failed before) + + //val (logFileOut, logFileErr) = createLogFiles(file, kind) + val logFile = createLogFile(file, kind) + if (!fileManager.failed || logFile.canRead) { + val swr = new StringWriter + val wr = new PrintWriter(swr) + succeeded = true; diff = ""; log = "" + printInfoStart(file, wr) + + val fileBase: String = basename(file.getName) + NestUI.verbose(this+" running test "+fileBase) + val dir = file.getParentFile + val outDir = createOutputDir(dir, fileBase, kind) + if (!outDir.exists) outDir.mkdir() + val resFile = new File(dir, fileBase + ".res") + NestUI.verbose("outDir: "+outDir) + NestUI.verbose("logFile: "+logFile) + //NestUI.verbose("logFileErr: "+logFileErr) + NestUI.verbose("resFile: "+resFile) + + // run compiler in resident mode + // $SCALAC -d "$os_dstbase".obj -Xresident -sourcepath . "$@" + + try { + + val sourcedir = logFile.getParentFile.getCanonicalFile + val sourcepath = sourcedir.getAbsolutePath+File.separator + NestUI.verbose("sourcepath: "+sourcepath) + + val argString = + "-d "+outDir.getCanonicalFile.getAbsolutePath+ + " -Xresident"+ + " -sourcepath "+sourcepath + val argList = argString split ' ' toList + + // configure input/output files + val logOut = new FileOutputStream(logFile) + val logWriter = new PrintStream(logOut) + val resReader = new BufferedReader(new FileReader(resFile)) + val logConsoleWriter = new PrintWriter(new OutputStreamWriter(logOut)) + + // create compiler + val settings = new Settings(error) + settings.sourcepath.value = sourcepath + settings.classpath.value = fileManager.CLASSPATH + reporter = new ConsoleReporter(settings, scala.Console.in, logConsoleWriter) + val command = new CompilerCommand(argList, settings) + object compiler extends Global(command.settings, reporter) + + // simulate resident compiler loop + val prompt = "\nnsc> " + + val resCompile = (line: String) => { + NestUI.verbose("compiling "+line) + val cmdArgs = (line split ' ').toList map (fs => new File(dir, fs).getAbsolutePath) + NestUI.verbose("cmdArgs: "+cmdArgs) + val sett = new Settings(error) + sett.sourcepath.value = sourcepath + val command = new CompilerCommand(cmdArgs, sett) + (new compiler.Run) compile command.files + } + + def loop(action: (String) => Unit) { + logWriter.print(prompt) + val line = resReader.readLine() + if ((line ne null) && line.length() > 0) { +/* + val parent = self + self.trapExit = true + val child = link { + action(line) + } + + receiveWithin(fileManager.timeout.toLong) { + case TIMEOUT => + NestUI.verbose("action timed out") + false + case Exit(from, reason) if from == child => reason match { + case 'normal => // do nothing + case t: Throwable => + NestUI.verbose("while invoking compiler:") + NestUI.verbose("caught "+t) + t.printStackTrace + if (t.getCause != null) + t.getCause.printStackTrace + false + } + } +*/ + action(line) + loop(action) + } + } + + withOutputRedirected(logWriter) { + loop(resCompile) + resReader.close() + } + + def replaceSlashes(s: String): String = { + val path = dir.getAbsolutePath+File.separator + // find `path` in `line` + val index = s.indexOf(path) + val line = + if (index != -1) + s.substring(0, index) + s.substring(index + path.length, s.length) + else s + line.replace('\\', '/') + } + + fileManager.mapFile(logFile, "tmp", dir, replaceSlashes) + diffCheck(compareOutput(dir, fileBase, kind, logFile)) + + } catch { + case e: Exception => + e.printStackTrace() + succeeded = false + } + + LogContext(logFile, Some((swr, wr))) + } else + LogContext(logFile, None) + } + + case "shootout" => { + // when option "--failed" is provided + // execute test only if log file is present + // (which means it failed before) + val logFile = createLogFile(file, kind) + if (!fileManager.failed || logFile.canRead) { + val swr = new StringWriter + val wr = new PrintWriter(swr) + succeeded = true; diff = ""; log = "" + printInfoStart(file, wr) + + val fileBase: String = basename(file.getName) + NestUI.verbose(this+" running test "+fileBase) + val dir = file.getParentFile + val outDir = createOutputDir(dir, fileBase, kind) + if (!outDir.exists) outDir.mkdir() + + // 2. define file {outDir}/test.scala that contains code to compile/run + val testFile = new File(outDir, "test.scala") + NestUI.verbose("outDir: "+outDir) + NestUI.verbose("logFile: "+logFile) + NestUI.verbose("testFile: "+testFile) + + // 3. cat {test}.scala.runner {test}.scala > testFile + val runnerFile = new File(dir, fileBase+".scala.runner") + val bodyFile = new File(dir, fileBase+".scala") + val appender = StreamAppender.concat(new FileInputStream(runnerFile), + new FileInputStream(bodyFile), + new FileOutputStream(testFile)) + appender.run() + + try { // *catch-all* + // 4. compile testFile + if (!compileMgr.shouldCompile(List(testFile), kind, logFile)) { + NestUI.verbose("compilation of "+file+" failed\n") + succeeded = false + } else { + NestUI.verbose("compilation of "+testFile+"succeeded") + // -------- run test -------- + + //TODO: detect whether we have to use Runtime.exec + // val useRuntime = true + // + // if (useRuntime) + // execTest(outDir, logFile, fileBase) + // else + // execTestObjectRunner(file, outDir, logFile) + + execTest(outDir, logFile, fileBase) + + NestUI.verbose(this+" finished running "+fileBase) + } // successful compile + } catch { // *catch-all* + case e: Exception => + NestUI.verbose("caught "+e) + succeeded = false + } + + diffCheck(compareOutput(dir, fileBase, kind, logFile)) + + LogContext(logFile, Some((swr, wr))) + } else + LogContext(logFile, None) + } + + case "scalap" => { + + runInContext(file, kind, (logFile: File, outDir: File) => { + val sourceDir = file.getParentFile + val sourceDirName = sourceDir.getName + + // 1. Find file with result text + val results = sourceDir.listFiles(new FilenameFilter { + def accept(dir: File, name: String) = name == "result.test" + }) + + if (results.length != 1) { + NestUI.verbose("Result file not found in directory " + sourceDirName + " \n") + } else { + val resFile = results(0) + // 2. Compile source file + if (!compileMgr.shouldCompile(outDir, List(file), kind, logFile)) { + NestUI.verbose("compilerMgr failed to compile %s to %s".format(file, outDir)) + succeeded = false + } else { + + // 3. Decompile file and compare results + val isPackageObject = sourceDir.getName.startsWith("package") + val className = sourceDirName.capitalize + (if (!isPackageObject) "" else ".package") + val url = outDir.toURI.toURL + val loader = new URLClassLoader(Array(url), getClass.getClassLoader) + val clazz = loader.loadClass(className) + + val byteCode = ByteCode.forClass(clazz) + val result = scala.tools.scalap.Main.decompileScala(byteCode.bytes, isPackageObject) + + try { + val fstream = new FileWriter(logFile); + val out = new BufferedWriter(fstream); + out.write(result) + out.close(); + } catch { + case e: IOException => NestUI.verbose(e.getMessage()); succeeded = false + } + + diffCheck(fileManager.compareFiles(logFile, resFile)) + } + } + }) + } + + case "script" => { + // when option "--failed" is provided + // execute test only if log file is present + // (which means it failed before) + val logFile = createLogFile(file, kind) + if (!fileManager.failed || logFile.canRead) { + val swr = new StringWriter + val wr = new PrintWriter(swr) + succeeded = true; diff = ""; log = "" + printInfoStart(file, wr) + + val fileBase: String = basename(file.getName) + NestUI.verbose(this+" running test "+fileBase) + + // check whether there is an args file + val argsFile = new File(file.getParentFile, fileBase+".args") + NestUI.verbose("argsFile: "+argsFile) + val argString = if (argsFile.exists) { + val swriter = new StringWriter + val app = StreamAppender(new BufferedReader(new FileReader(argsFile)), + swriter) + app.run() + " "+swriter.toString + } else "" + + try { + val cmdString = + if (isWin) { + val batchFile = new File(file.getParentFile, fileBase+".bat") + NestUI.verbose("batchFile: "+batchFile) + batchFile.getAbsolutePath + } + else file.getAbsolutePath + val proc = Runtime.getRuntime.exec(cmdString+argString) + val in = proc.getInputStream + val err = proc.getErrorStream + val writer = new PrintWriter(new FileWriter(logFile), true) + val inApp = new StreamAppender(new BufferedReader(new InputStreamReader(in)), + writer) + val errApp = new StreamAppender(new BufferedReader(new InputStreamReader(err)), + writer) + val async = new Thread(errApp) + async.start() + inApp.run() + async.join() + + writer.close() + + diffCheck(compareOutput(file.getParentFile, fileBase, kind, logFile)) + } catch { // *catch-all* + case e: Exception => + NestUI.verbose("caught "+e) + succeeded = false + } + + LogContext(logFile, Some((swr, wr))) + } else + LogContext(logFile, None) + } + } + + def reportAll(results: ImmMap[String, Int], cont: ImmMap[String, Int] => Unit) { + // NestUI.verbose("finished testing "+kind+" with "+errors+" errors") + // NestUI.verbose("created "+compileMgr.numSeparateCompilers+" separate compilers") + timer.cancel() + cont(results) + } + + def reportResult(state: Int, logFile: Option[LogFile], writers: Option[(StringWriter, PrintWriter)]) { + val good = (state == 0) + if (!good) { + errors += 1 + NestUI.verbose("incremented errors: "+errors) + } + + try { + // delete log file only if test was successful + if (good && !logFile.isEmpty && !isPartestDebug) + logFile.get.toDelete = true + + writers match { + case Some((swr, wr)) => + if (state == 2) + printInfoTimeout(wr) + else + printInfoEnd(good, wr) + wr.flush() + swr.flush() + NestUI.normal(swr.toString) + if (state == 1 && fileManager.showDiff && diff != "") + NestUI.normal(diff) + if (state == 1 && fileManager.showLog) + showLog(logFile.get) + case None => + } + } catch { + case npe: NullPointerException => + } + } + + val numFiles = files.size + if (numFiles == 0) + reportAll(ImmMap(), topcont) + + // maps canonical file names to the test result (0: OK, 1: FAILED, 2: TIMOUT) + var status = new HashMap[String, Int] + + var fileCnt = 1 + Actor.loopWhile(fileCnt <= numFiles) { + val parent = self + + actor { + val testFile = files(fileCnt-1) + + val ontimeout = new TimerTask { + def run() = parent ! Timeout(testFile) + } + timer.schedule(ontimeout, fileManager.timeout.toLong) + + val context = try { + processSingleFile(testFile) + } catch { + case t: Throwable => + NestUI.verbose("while invoking compiler ("+files+"):") + NestUI.verbose("caught "+t) + t.printStackTrace + if (t.getCause != null) + t.getCause.printStackTrace + LogContext(null, None) + } + parent ! Result(testFile, context) + } + + react { + case res: TestResult => + val path = res.file.getCanonicalPath + status.get(path) match { + case Some(stat) => // ignore message + case None => + res match { + case Timeout(_) => + status = status + (path -> 2) + val swr = new StringWriter + val wr = new PrintWriter(swr) + printInfoStart(res.file, wr) + succeeded = false + reportResult(2, None, Some((swr, wr))) + case Result(_, logs) => + status = status + (path -> (if (succeeded) 0 else 1)) + reportResult( + if (succeeded) 0 else 1, + if (logs != null) Some(logs.file) else None, + if (logs != null) logs.writers else None) + } + if (fileCnt == numFiles) + reportAll(status, topcont) + fileCnt += 1 + } + } + } + } + + private def withOutputRedirected(out: PrintStream)(func: => Unit) { + val oldStdOut = System.out + val oldStdErr = System.err + + try { + System.setOut(out) + System.setErr(out) + func + out.flush() + out.close() + } finally { + System.setOut(oldStdOut) + System.setErr(oldStdErr) + } + } + + private def filesToSet(pre: String, fs: List[String]): Set[AbstractFile] = + fs flatMap (s => Option(AbstractFile getFile (pre + s))) toSet + + private def copyTestFiles(testDir: File, destDir: File) { + val invalidExts = List("changes", "svn", "obj") + testDir.listFiles.toList filter ( + f => (isJavaOrScala(f) && f.isFile) || + (f.isDirectory && !(invalidExts.contains(SFile(f).extension)))) foreach + { f => fileManager.copyFile(f, destDir) } + } + + def showLog(logFile: File) { + try { + val logReader = new BufferedReader(new FileReader(logFile)) + val strWriter = new StringWriter + val logWriter = new PrintWriter(strWriter, true) + val logAppender = new StreamAppender(logReader, logWriter) + logAppender.run() + logReader.close() + val log = strWriter.toString + NestUI.normal(log) + } catch { + case fnfe: java.io.FileNotFoundException => + NestUI.failure("Couldn't open log file \""+logFile+"\".") + } + } +} diff --git a/src/partest/scala/tools/partest/package.scala b/src/partest/scala/tools/partest/package.scala index f6d216e379..e9eda6fb75 100644 --- a/src/partest/scala/tools/partest/package.scala +++ b/src/partest/scala/tools/partest/package.scala @@ -4,42 +4,37 @@ package scala.tools -import nsc.io.{ File, Path, Process, Directory } -import java.nio.charset.CharacterCodingException +import java.io.{ File => JFile } +import nsc.io.{ Path, Process, Directory } +import util.{ PathResolver } +import nsc.Properties.{ propOrElse, propOrNone, propOrEmpty } package object partest { - /** The CharacterCodingExceptions are thrown at least on windows trying - * to read a file like script/utf-8.scala - */ - private[partest] def safeSlurp(f: File) = - try if (f.exists) f.slurp() else "" - catch { case _: CharacterCodingException => "" } - - private[partest] def safeLines(f: File) = safeSlurp(f) split """\r\n|\r|\n""" toList - private[partest] def safeArgs(f: File) = toArgs(safeSlurp(f)) - private[partest] def isJava(f: Path) = f.isFile && (f hasExtension "java") - private[partest] def isScala(f: Path) = f.isFile && (f hasExtension "scala") - private[partest] def isJavaOrScala(f: Path) = isJava(f) || isScala(f) - - private[partest] def toArgs(line: String) = cmd toArgs line - private[partest] def fromArgs(args: List[String]) = cmd fromArgs args - - /** Strings, argument lists, etc. */ - - private[partest] def fromAnyArgs(args: List[Any]) = args mkString " " // separate to avoid accidents - private[partest] def toStringTrunc(x: Any, max: Int = 240) = { - val s = x.toString - if (s.length < max) s - else (s take max) + " [...]" + import nest.NestUI + + implicit private[partest] def temporaryPath2File(x: Path): JFile = x.jfile + implicit private[partest] def temporaryFile2Path(x: JFile): Path = Path(x) + + def basename(name: String): String = Path(name).stripExtension + def resultsToStatistics(results: Iterable[(_, Int)]): (Int, Int) = { + val (files, failures) = results map (_._2 == 0) partition (_ == true) + (files.size, failures.size) + } + + def vmArgString = { + val str = Process.javaVmArguments mkString " " + "Java VM started with arguments: '%s'" format str + } + + def allPropertiesString = { + import collection.JavaConversions._ + System.getProperties.toList.sorted map { case (k, v) => "%s -> %s\n".format(k, v) } mkString } - private[partest] def setProp(k: String, v: String) = scala.util.Properties.setProp(k, v) - /** Pretty self explanatory. */ - def printAndExit(msg: String): Unit = { - println(msg) - exit(1) + def showAllJVMInfo { + NestUI.verbose(vmArgString) + NestUI.verbose(allPropertiesString) } - /** Apply a function and return the passed value */ - def returning[T](x: T)(f: T => Unit): T = { f(x) ; x } + def isPartestDebug = propOrEmpty("partest.debug") == "true" }
\ No newline at end of file diff --git a/src/partest/scala/tools/partest/utils/PrintMgr.scala b/src/partest/scala/tools/partest/utils/PrintMgr.scala new file mode 100644 index 0000000000..10533130f1 --- /dev/null +++ b/src/partest/scala/tools/partest/utils/PrintMgr.scala @@ -0,0 +1,52 @@ +/* __ *\ +** ________ ___ / / ___ Scala Parallel Testing ** +** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.tools.partest +package utils + +/** + * @author Thomas Hofer + */ +object PrintMgr { + + val NONE = 0 + val SOME = 1 + val MANY = 2 + + var outline = "" + var success = "" + var failure = "" + var warning = "" + var default = "" + + def initialization(number: Int) = number match { + case MANY => + outline = Console.BOLD + Console.BLACK + success = Console.BOLD + Console.GREEN + failure = Console.BOLD + Console.RED + warning = Console.BOLD + Console.YELLOW + default = Console.RESET + case SOME => + outline = Console.BOLD + Console.BLACK + success = Console.RESET + failure = Console.BOLD + Console.BLACK + warning = Console.BOLD + Console.BLACK + default = Console.RESET + case _ => + } + + def printOutline(msg: String) = print(outline + msg + default) + + def printSuccess(msg: String) = print(success + msg + default) + + def printFailure(msg: String) = print(failure + msg + default) + + def printWarning(msg: String) = print(warning + msg + default) +} diff --git a/src/partest/scala/tools/partest/utils/Properties.scala b/src/partest/scala/tools/partest/utils/Properties.scala new file mode 100644 index 0000000000..237ddea14e --- /dev/null +++ b/src/partest/scala/tools/partest/utils/Properties.scala @@ -0,0 +1,18 @@ +/* __ *\ +** ________ ___ / / ___ Scala Parallel Testing ** +** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.tools.partest +package utils + +/** Loads partest.properties from the jar. */ +object Properties extends scala.util.PropertiesTrait { + protected def propCategory = "partest" + protected def pickJarBasedOn = classOf[Application] +} diff --git a/test/files/pos/spec-traits.scala b/test/disabled/pos/spec-traits.scala index 9e339a14ad..9e339a14ad 100644 --- a/test/files/pos/spec-traits.scala +++ b/test/disabled/pos/spec-traits.scala diff --git a/test/files/pos/t1254/t1254.java b/test/disabled/pos/t1254/t1254.java index 25b733cf28..25b733cf28 100644 --- a/test/files/pos/t1254/t1254.java +++ b/test/disabled/pos/t1254/t1254.java diff --git a/test/disabled/run/stream_length.check b/test/disabled/run/stream_length.check new file mode 100644 index 0000000000..9906de773c --- /dev/null +++ b/test/disabled/run/stream_length.check @@ -0,0 +1 @@ +Length: 970299 diff --git a/test/files/run/stream_length.scala b/test/disabled/run/stream_length.scala index 68e9cad5ac..68e9cad5ac 100644 --- a/test/files/run/stream_length.scala +++ b/test/disabled/run/stream_length.scala diff --git a/test/files/run/t2946/Parsers.scala b/test/disabled/run/t2946/Parsers.scala index c0961034c4..c0961034c4 100644 --- a/test/files/run/t2946/Parsers.scala +++ b/test/disabled/run/t2946/Parsers.scala diff --git a/test/files/run/t2946/ResponseCommon.scala b/test/disabled/run/t2946/ResponseCommon.scala index fa9d8acccb..fa9d8acccb 100644 --- a/test/files/run/t2946/ResponseCommon.scala +++ b/test/disabled/run/t2946/ResponseCommon.scala diff --git a/test/files/run/t2946/Test.scala b/test/disabled/run/t2946/Test.scala index e9d9896a0e..e9d9896a0e 100644 --- a/test/files/run/t2946/Test.scala +++ b/test/disabled/run/t2946/Test.scala diff --git a/test/files/scalacheck/redblack.scala b/test/disabled/scalacheck/redblack.scala index 0334c1218d..0334c1218d 100644 --- a/test/files/scalacheck/redblack.scala +++ b/test/disabled/scalacheck/redblack.scala diff --git a/test/continuations/neg/function0.check b/test/files/continuations-neg/function0.check index 0a66763a0f..0a66763a0f 100644 --- a/test/continuations/neg/function0.check +++ b/test/files/continuations-neg/function0.check diff --git a/test/continuations/neg/function0.scala b/test/files/continuations-neg/function0.scala index 4112ee3835..4112ee3835 100644 --- a/test/continuations/neg/function0.scala +++ b/test/files/continuations-neg/function0.scala diff --git a/test/continuations/neg/function2.check b/test/files/continuations-neg/function2.check index 4833057652..4833057652 100644 --- a/test/continuations/neg/function2.check +++ b/test/files/continuations-neg/function2.check diff --git a/test/continuations/neg/function2.scala b/test/files/continuations-neg/function2.scala index ae0fda509d..ae0fda509d 100644 --- a/test/continuations/neg/function2.scala +++ b/test/files/continuations-neg/function2.scala diff --git a/test/continuations/neg/function3.check b/test/files/continuations-neg/function3.check index 4705ad9ed9..4705ad9ed9 100644 --- a/test/continuations/neg/function3.check +++ b/test/files/continuations-neg/function3.check diff --git a/test/continuations/neg/function3.scala b/test/files/continuations-neg/function3.scala index 0c3f1667e5..0c3f1667e5 100644 --- a/test/continuations/neg/function3.scala +++ b/test/files/continuations-neg/function3.scala diff --git a/test/continuations/neg/infer0.check b/test/files/continuations-neg/infer0.check index 1dd072ef09..1dd072ef09 100644 --- a/test/continuations/neg/infer0.check +++ b/test/files/continuations-neg/infer0.check diff --git a/test/continuations/neg/infer0.scala b/test/files/continuations-neg/infer0.scala index 894d5228b1..894d5228b1 100644 --- a/test/continuations/neg/infer0.scala +++ b/test/files/continuations-neg/infer0.scala diff --git a/test/continuations/neg/infer2.check b/test/files/continuations-neg/infer2.check index 59eb670bc3..59eb670bc3 100644 --- a/test/continuations/neg/infer2.check +++ b/test/files/continuations-neg/infer2.check diff --git a/test/continuations/neg/infer2.scala b/test/files/continuations-neg/infer2.scala index a890ac1fc4..a890ac1fc4 100644 --- a/test/continuations/neg/infer2.scala +++ b/test/files/continuations-neg/infer2.scala diff --git a/test/continuations/neg/lazy.check b/test/files/continuations-neg/lazy.check index bfa44c59a4..bfa44c59a4 100644 --- a/test/continuations/neg/lazy.check +++ b/test/files/continuations-neg/lazy.check diff --git a/test/continuations/neg/lazy.scala b/test/files/continuations-neg/lazy.scala index dffc57ffa0..dffc57ffa0 100644 --- a/test/continuations/neg/lazy.scala +++ b/test/files/continuations-neg/lazy.scala diff --git a/test/continuations/neg/t1929.check b/test/files/continuations-neg/t1929.check index f42c3a1e15..f42c3a1e15 100644 --- a/test/continuations/neg/t1929.check +++ b/test/files/continuations-neg/t1929.check diff --git a/test/continuations/neg/t1929.scala b/test/files/continuations-neg/t1929.scala index 02eda9170d..02eda9170d 100644 --- a/test/continuations/neg/t1929.scala +++ b/test/files/continuations-neg/t1929.scala diff --git a/test/continuations/neg/t2285.check b/test/files/continuations-neg/t2285.check index d5dff6a4f2..d5dff6a4f2 100644 --- a/test/continuations/neg/t2285.check +++ b/test/files/continuations-neg/t2285.check diff --git a/test/continuations/neg/t2285.scala b/test/files/continuations-neg/t2285.scala index f3c7f4c89c..f3c7f4c89c 100644 --- a/test/continuations/neg/t2285.scala +++ b/test/files/continuations-neg/t2285.scala diff --git a/test/continuations/neg/t2949.check b/test/files/continuations-neg/t2949.check index dd9768807c..dd9768807c 100644 --- a/test/continuations/neg/t2949.check +++ b/test/files/continuations-neg/t2949.check diff --git a/test/continuations/neg/t2949.scala b/test/files/continuations-neg/t2949.scala index ce27c7c0e8..ce27c7c0e8 100644 --- a/test/continuations/neg/t2949.scala +++ b/test/files/continuations-neg/t2949.scala diff --git a/test/continuations/neg/trycatch2.check b/test/files/continuations-neg/trycatch2.check index 5ff2838bad..5ff2838bad 100644 --- a/test/continuations/neg/trycatch2.check +++ b/test/files/continuations-neg/trycatch2.check diff --git a/test/continuations/neg/trycatch2.scala b/test/files/continuations-neg/trycatch2.scala index 761cee52ac..761cee52ac 100644 --- a/test/continuations/neg/trycatch2.scala +++ b/test/files/continuations-neg/trycatch2.scala diff --git a/test/continuations/run/basics.check b/test/files/continuations-run/basics.check index 54c059fdcb..54c059fdcb 100755 --- a/test/continuations/run/basics.check +++ b/test/files/continuations-run/basics.check diff --git a/test/continuations/run/basics.scala b/test/files/continuations-run/basics.scala index 9df209b11c..9df209b11c 100755 --- a/test/continuations/run/basics.scala +++ b/test/files/continuations-run/basics.scala diff --git a/test/continuations/run/function1.check b/test/files/continuations-run/function1.check index 7f8f011eb7..7f8f011eb7 100644 --- a/test/continuations/run/function1.check +++ b/test/files/continuations-run/function1.check diff --git a/test/continuations/run/function1.scala b/test/files/continuations-run/function1.scala index 3b39722e3a..3b39722e3a 100644 --- a/test/continuations/run/function1.scala +++ b/test/files/continuations-run/function1.scala diff --git a/test/continuations/run/function4.check b/test/files/continuations-run/function4.check index c7930257df..c7930257df 100644 --- a/test/continuations/run/function4.check +++ b/test/files/continuations-run/function4.check diff --git a/test/continuations/run/function4.scala b/test/files/continuations-run/function4.scala index b73eedb02c..b73eedb02c 100644 --- a/test/continuations/run/function4.scala +++ b/test/files/continuations-run/function4.scala diff --git a/test/continuations/run/function5.check b/test/files/continuations-run/function5.check index c7930257df..c7930257df 100644 --- a/test/continuations/run/function5.check +++ b/test/files/continuations-run/function5.check diff --git a/test/continuations/run/function5.scala b/test/files/continuations-run/function5.scala index a689ccf243..a689ccf243 100644 --- a/test/continuations/run/function5.scala +++ b/test/files/continuations-run/function5.scala diff --git a/test/continuations/run/function6.check b/test/files/continuations-run/function6.check index c7930257df..c7930257df 100644 --- a/test/continuations/run/function6.check +++ b/test/files/continuations-run/function6.check diff --git a/test/continuations/run/function6.scala b/test/files/continuations-run/function6.scala index 1a2792370a..1a2792370a 100644 --- a/test/continuations/run/function6.scala +++ b/test/files/continuations-run/function6.scala diff --git a/test/continuations/run/ifelse0.check b/test/files/continuations-run/ifelse0.check index f8bc79860d..f8bc79860d 100644 --- a/test/continuations/run/ifelse0.check +++ b/test/files/continuations-run/ifelse0.check diff --git a/test/continuations/run/ifelse0.scala b/test/files/continuations-run/ifelse0.scala index e34b86ee84..e34b86ee84 100644 --- a/test/continuations/run/ifelse0.scala +++ b/test/files/continuations-run/ifelse0.scala diff --git a/test/continuations/run/ifelse1.check b/test/files/continuations-run/ifelse1.check index 86a3fbc0c1..86a3fbc0c1 100644 --- a/test/continuations/run/ifelse1.check +++ b/test/files/continuations-run/ifelse1.check diff --git a/test/continuations/run/ifelse1.scala b/test/files/continuations-run/ifelse1.scala index 2ccc1ed730..2ccc1ed730 100644 --- a/test/continuations/run/ifelse1.scala +++ b/test/files/continuations-run/ifelse1.scala diff --git a/test/continuations/run/ifelse2.check b/test/files/continuations-run/ifelse2.check index f97a95b08d..f97a95b08d 100644 --- a/test/continuations/run/ifelse2.check +++ b/test/files/continuations-run/ifelse2.check diff --git a/test/continuations/run/ifelse2.scala b/test/files/continuations-run/ifelse2.scala index 536e350190..536e350190 100644 --- a/test/continuations/run/ifelse2.scala +++ b/test/files/continuations-run/ifelse2.scala diff --git a/test/continuations/run/ifelse3.check b/test/files/continuations-run/ifelse3.check index 95b562c8e6..95b562c8e6 100644 --- a/test/continuations/run/ifelse3.check +++ b/test/files/continuations-run/ifelse3.check diff --git a/test/continuations/run/ifelse3.scala b/test/files/continuations-run/ifelse3.scala index 5dbd079d1c..5dbd079d1c 100644 --- a/test/continuations/run/ifelse3.scala +++ b/test/files/continuations-run/ifelse3.scala diff --git a/test/continuations/run/infer1.scala b/test/files/continuations-run/infer1.scala index a6c6c07215..a6c6c07215 100644 --- a/test/continuations/run/infer1.scala +++ b/test/files/continuations-run/infer1.scala diff --git a/test/continuations/run/match0.check b/test/files/continuations-run/match0.check index f8bc79860d..f8bc79860d 100644 --- a/test/continuations/run/match0.check +++ b/test/files/continuations-run/match0.check diff --git a/test/continuations/run/match0.scala b/test/files/continuations-run/match0.scala index bd36238d7f..bd36238d7f 100644 --- a/test/continuations/run/match0.scala +++ b/test/files/continuations-run/match0.scala diff --git a/test/continuations/run/match1.check b/test/files/continuations-run/match1.check index 73053d3f4f..73053d3f4f 100644 --- a/test/continuations/run/match1.check +++ b/test/files/continuations-run/match1.check diff --git a/test/continuations/run/match1.scala b/test/files/continuations-run/match1.scala index ea4e219666..ea4e219666 100644 --- a/test/continuations/run/match1.scala +++ b/test/files/continuations-run/match1.scala diff --git a/test/continuations/run/match2.check b/test/files/continuations-run/match2.check index cbf91349cc..cbf91349cc 100644 --- a/test/continuations/run/match2.check +++ b/test/files/continuations-run/match2.check diff --git a/test/continuations/run/match2.scala b/test/files/continuations-run/match2.scala index 8d4f04870f..8d4f04870f 100644 --- a/test/continuations/run/match2.scala +++ b/test/files/continuations-run/match2.scala diff --git a/test/continuations/run/t1807.check b/test/files/continuations-run/t1807.check index 56a6051ca2..56a6051ca2 100644 --- a/test/continuations/run/t1807.check +++ b/test/files/continuations-run/t1807.check diff --git a/test/continuations/run/t1807.scala b/test/files/continuations-run/t1807.scala index 278b3a9936..278b3a9936 100644 --- a/test/continuations/run/t1807.scala +++ b/test/files/continuations-run/t1807.scala diff --git a/test/continuations/run/t1808.scala b/test/files/continuations-run/t1808.scala index 125c7c1cdf..125c7c1cdf 100644 --- a/test/continuations/run/t1808.scala +++ b/test/files/continuations-run/t1808.scala diff --git a/test/continuations/run/t1820.scala b/test/files/continuations-run/t1820.scala index 893ddab6d1..893ddab6d1 100644 --- a/test/continuations/run/t1820.scala +++ b/test/files/continuations-run/t1820.scala diff --git a/test/continuations/run/t1821.check b/test/files/continuations-run/t1821.check index f7b76115db..f7b76115db 100644 --- a/test/continuations/run/t1821.check +++ b/test/files/continuations-run/t1821.check diff --git a/test/continuations/run/t1821.scala b/test/files/continuations-run/t1821.scala index 0d5fb553be..0d5fb553be 100644 --- a/test/continuations/run/t1821.scala +++ b/test/files/continuations-run/t1821.scala diff --git a/test/continuations/run/t2864.check b/test/files/continuations-run/t2864.check index d411bb7c1a..d411bb7c1a 100644 --- a/test/continuations/run/t2864.check +++ b/test/files/continuations-run/t2864.check diff --git a/test/continuations/run/t2864.scala b/test/files/continuations-run/t2864.scala index 7a2579e45c..7a2579e45c 100644 --- a/test/continuations/run/t2864.scala +++ b/test/files/continuations-run/t2864.scala diff --git a/test/continuations/run/t2934.check b/test/files/continuations-run/t2934.check index a92586538e..a92586538e 100644 --- a/test/continuations/run/t2934.check +++ b/test/files/continuations-run/t2934.check diff --git a/test/continuations/run/t2934.scala b/test/files/continuations-run/t2934.scala index a1b8ca9e04..a1b8ca9e04 100644 --- a/test/continuations/run/t2934.scala +++ b/test/files/continuations-run/t2934.scala diff --git a/test/continuations/run/t3199.check b/test/files/continuations-run/t3199.check index a065247b8c..a065247b8c 100644 --- a/test/continuations/run/t3199.check +++ b/test/files/continuations-run/t3199.check diff --git a/test/continuations/run/t3199.scala b/test/files/continuations-run/t3199.scala index 3fd2f1959a..3fd2f1959a 100644 --- a/test/continuations/run/t3199.scala +++ b/test/files/continuations-run/t3199.scala diff --git a/test/continuations/run/t3199b.check b/test/files/continuations-run/t3199b.check index b5d8bb58d9..b5d8bb58d9 100644 --- a/test/continuations/run/t3199b.check +++ b/test/files/continuations-run/t3199b.check diff --git a/test/continuations/run/t3199b.scala b/test/files/continuations-run/t3199b.scala index 950c584153..950c584153 100644 --- a/test/continuations/run/t3199b.scala +++ b/test/files/continuations-run/t3199b.scala diff --git a/test/continuations/run/t3223.check b/test/files/continuations-run/t3223.check index ec635144f6..ec635144f6 100644 --- a/test/continuations/run/t3223.check +++ b/test/files/continuations-run/t3223.check diff --git a/test/continuations/run/t3223.scala b/test/files/continuations-run/t3223.scala index 4e510178e6..4e510178e6 100644 --- a/test/continuations/run/t3223.scala +++ b/test/files/continuations-run/t3223.scala diff --git a/test/continuations/run/t3225.check b/test/files/continuations-run/t3225.check index df1a8a9ce4..df1a8a9ce4 100644 --- a/test/continuations/run/t3225.check +++ b/test/files/continuations-run/t3225.check diff --git a/test/continuations/run/t3225.scala b/test/files/continuations-run/t3225.scala index ecfde279cf..ecfde279cf 100644 --- a/test/continuations/run/t3225.scala +++ b/test/files/continuations-run/t3225.scala diff --git a/test/continuations/run/trycatch0.check b/test/files/continuations-run/trycatch0.check index 36806909d0..36806909d0 100644 --- a/test/continuations/run/trycatch0.check +++ b/test/files/continuations-run/trycatch0.check diff --git a/test/continuations/run/trycatch0.scala b/test/files/continuations-run/trycatch0.scala index 74a078b5ef..74a078b5ef 100644 --- a/test/continuations/run/trycatch0.scala +++ b/test/files/continuations-run/trycatch0.scala diff --git a/test/continuations/run/trycatch1.check b/test/files/continuations-run/trycatch1.check index a028d2b1e1..a028d2b1e1 100644 --- a/test/continuations/run/trycatch1.check +++ b/test/files/continuations-run/trycatch1.check diff --git a/test/continuations/run/trycatch1.scala b/test/files/continuations-run/trycatch1.scala index ade13794e3..ade13794e3 100644 --- a/test/continuations/run/trycatch1.scala +++ b/test/files/continuations-run/trycatch1.scala diff --git a/test/continuations/run/while0.check b/test/files/continuations-run/while0.check index d58c55a31d..d58c55a31d 100644 --- a/test/continuations/run/while0.check +++ b/test/files/continuations-run/while0.check diff --git a/test/continuations/run/while0.scala b/test/files/continuations-run/while0.scala index 9735f9d2c3..9735f9d2c3 100644 --- a/test/continuations/run/while0.scala +++ b/test/files/continuations-run/while0.scala diff --git a/test/continuations/run/while1.check b/test/files/continuations-run/while1.check index 3d5f0b9a46..3d5f0b9a46 100644 --- a/test/continuations/run/while1.check +++ b/test/files/continuations-run/while1.check diff --git a/test/continuations/run/while1.scala b/test/files/continuations-run/while1.scala index fb5dc0079a..fb5dc0079a 100644 --- a/test/continuations/run/while1.scala +++ b/test/files/continuations-run/while1.scala diff --git a/test/continuations/run/while2.check b/test/files/continuations-run/while2.check index 9fe515181b..9fe515181b 100644 --- a/test/continuations/run/while2.check +++ b/test/files/continuations-run/while2.check diff --git a/test/continuations/run/while2.scala b/test/files/continuations-run/while2.scala index f36288929e..f36288929e 100644 --- a/test/continuations/run/while2.scala +++ b/test/files/continuations-run/while2.scala diff --git a/test/files/jvm/genericNest/genericNest.scala b/test/files/jvm/genericNest.scala index c1b0210117..c1b0210117 100644 --- a/test/files/jvm/genericNest/genericNest.scala +++ b/test/files/jvm/genericNest.scala diff --git a/test/files/jvm/methvsfield.java b/test/files/jvm/methvsfield.java new file mode 100644 index 0000000000..dadc98669a --- /dev/null +++ b/test/files/jvm/methvsfield.java @@ -0,0 +1,11 @@ +// This should be compiled with javac and saved +// in ../lib/methvsfield.jar . +class MethVsField +{ + int three = 3; + + int three() + { + return 3; + } +} diff --git a/test/files/jvm/methvsfield.scala b/test/files/jvm/methvsfield.scala new file mode 100644 index 0000000000..e9795ec6a8 --- /dev/null +++ b/test/files/jvm/methvsfield.scala @@ -0,0 +1,4 @@ +// bug #1062 +object Test extends Application { + println((new MethVsField).three) +} diff --git a/test/files/jvm/nest.java b/test/files/jvm/nest.java new file mode 100644 index 0000000000..3f6f0bebbd --- /dev/null +++ b/test/files/jvm/nest.java @@ -0,0 +1,38 @@ +package nestpkg; + + +/** This file is needed for test 'nest.scala'. It should + * be compiled with javac and packaged into lib/nest.jar + */ +public class nest { + public static class best { + public static class rest { + public static rest test = new rest(); + public static int x = 10; + public int inc(int i) { + return i + 1; + } + } + } + + + String name = "Outer name"; + + public class Inn { + int x; + + public Inn(int x) { + this.x = x; + } + + public void doSomething() { + System.out.println("Inn " + name + " x: " + x); + } + } + + protected class ProtInn { + public void doSomething() { + System.out.println("ProtInn " + name); + } + } +} diff --git a/test/files/jvm/nest.scala b/test/files/jvm/nest.scala new file mode 100644 index 0000000000..3ab62484fa --- /dev/null +++ b/test/files/jvm/nest.scala @@ -0,0 +1,21 @@ +//############################################################################ +// Test Scala interaction with Java nested classes and static members. +//############################################################################ + +/** found in nest.jar, compiled from nest.java */ +import nestpkg._; + +object Test extends Application { + val x = nest.best.rest.test + Console.println(x.inc(1)) + + val o = new nest.best; + val r = new nest.best.rest; + Console.println(nest.best.rest.test.inc(2)) + Console.println(nest.best.rest.x) + + print("Instantiating public inner class: ") + val outer = new nest + val inn = new outer.Inn(42) + inn.doSomething +} diff --git a/test/files/jvm/outerEnum/outerEnum.scala b/test/files/jvm/outerEnum.scala index 18794b7dbe..18794b7dbe 100644 --- a/test/files/jvm/outerEnum/outerEnum.scala +++ b/test/files/jvm/outerEnum.scala diff --git a/test/files/jvm/t1652.check b/test/files/jvm/t1652.check new file mode 100644 index 0000000000..dfa480ce6e --- /dev/null +++ b/test/files/jvm/t1652.check @@ -0,0 +1,2 @@ +OK1 +OK2 diff --git a/test/files/jvm/lib/annotations.jar.desired.sha1 b/test/files/lib/annotations.jar.desired.sha1 index 2b4292d796..2b4292d796 100644 --- a/test/files/jvm/lib/annotations.jar.desired.sha1 +++ b/test/files/lib/annotations.jar.desired.sha1 diff --git a/test/files/jvm/outerEnum/enums.jar.desired.sha1 b/test/files/lib/enums.jar.desired.sha1 index 46cd8e92cf..46cd8e92cf 100644 --- a/test/files/jvm/outerEnum/enums.jar.desired.sha1 +++ b/test/files/lib/enums.jar.desired.sha1 diff --git a/test/files/jvm/genericNest/genericNest.jar.desired.sha1 b/test/files/lib/genericNest.jar.desired.sha1 index e9321262f2..e9321262f2 100644 --- a/test/files/jvm/genericNest/genericNest.jar.desired.sha1 +++ b/test/files/lib/genericNest.jar.desired.sha1 diff --git a/test/files/lib/methvsfield.jar.desired.sha1 b/test/files/lib/methvsfield.jar.desired.sha1 new file mode 100644 index 0000000000..8c01532b88 --- /dev/null +++ b/test/files/lib/methvsfield.jar.desired.sha1 @@ -0,0 +1 @@ +be8454d5e7751b063ade201c225dcedefd252775 ?methvsfield.jar diff --git a/test/files/jvm/lib/nest.jar.desired.sha1 b/test/files/lib/nest.jar.desired.sha1 index 674ca79a5b..674ca79a5b 100644 --- a/test/files/jvm/lib/nest.jar.desired.sha1 +++ b/test/files/lib/nest.jar.desired.sha1 diff --git a/lib/scalacheck.jar.desired.sha1 b/test/files/lib/scalacheck.jar.desired.sha1 index ed9c46c3db..ed9c46c3db 100644 --- a/lib/scalacheck.jar.desired.sha1 +++ b/test/files/lib/scalacheck.jar.desired.sha1 diff --git a/test/files/neg/bug414.scala b/test/files/neg/bug414.scala index 7983fe88b9..2bc83eedcb 100644 --- a/test/files/neg/bug414.scala +++ b/test/files/neg/bug414.scala @@ -1,4 +1,4 @@ -case class Empty[a] extends IntMap[a]; +case class Empty[a]() extends IntMap[a]; case class Node[a](left: IntMap[a], keyVal: Pair[Int, a], right: IntMap[a]) extends IntMap[a]; abstract class IntMap[a] { def lookup(key: Int): a = this match { diff --git a/test/files/neg/migration28.check b/test/files/neg/migration28.check index 9e042a0f0b..4c8c58e0fd 100644 --- a/test/files/neg/migration28.check +++ b/test/files/neg/migration28.check @@ -1,5 +1,4 @@ -migration28.scala:5: error: method ++= in class Stack has changed semantics: -Stack ++= now pushes arguments on the stack from left to right. +migration28.scala:5: error: method ++= in class Stack is deprecated: use pushAll s ++= List(1,2,3) ^ migration28.scala:7: error: method foreach in class Stack has changed semantics: diff --git a/test/files/res/bug687.check b/test/files/res/bug687.check index 353101c38b..ee9520d1ea 100644 --- a/test/files/res/bug687.check +++ b/test/files/res/bug687.check @@ -1,10 +1,11 @@ + nsc> -nsc> -bug687/QueryB.scala:3: error: name clash between defined and inherited member: +nsc> bug687/QueryB.scala:3: error: name clash between defined and inherited member: method equals:(o: java.lang.Object)Boolean and method equals:(x$1: Any)Boolean in class Any have same type after erasure: (o: java.lang.Object)Boolean override def equals(o : Object) = false; ^ + nsc> nsc> diff --git a/test/files/run/numbereq.scala b/test/files/run/numbereq.scala index 52f32cc52a..b07c83dc3e 100644 --- a/test/files/run/numbereq.scala +++ b/test/files/run/numbereq.scala @@ -33,7 +33,7 @@ object Test { val sets = setneg1 ++ setneg2 ++ List(zero) ++ setpos1 ++ setpos2 for (set <- sets ; x <- set ; y <- set) { - println("'%s' == '%s' (%s == %s) (%s == %s)".format(x, y, x.hashCode, y.hashCode, x.##, y.##)) + // println("'%s' == '%s' (%s == %s) (%s == %s)".format(x, y, x.hashCode, y.hashCode, x.##, y.##)) assert(x == y, "%s/%s != %s/%s".format(x, x.getClass, y, y.getClass)) assert(x.## == y.##, "%s != %s".format(x.getClass, y.getClass)) } diff --git a/test/files/run/programmatic-main.check b/test/files/run/programmatic-main.check index 3429195265..e6c83a6f48 100644 --- a/test/files/run/programmatic-main.check +++ b/test/files/run/programmatic-main.check @@ -5,9 +5,7 @@ typer superaccessors pickler refchecks -selectiveanf liftcode -selectivecps uncurry tailcalls specialize diff --git a/test/partest b/test/partest index 87c7961689..1e7da8bd4a 100755 --- a/test/partest +++ b/test/partest @@ -75,23 +75,12 @@ if $cygwin; then fi # Reminder: substitution ${JAVA_OPTS:=-Xmx256M -Xms16M} DO NOT work on Solaris -[ -n "$JAVA_OPTS" ] || JAVA_OPTS="-Xms64M -Xmx1024M -Xss768K -XX:MaxPermSize=96M" -[ -n "$SCALAC_OPTS" ] || SCALAC_OPTS="" +[ -n "$JAVA_OPTS" ] || JAVA_OPTS="-Xmx512M -Xms16M" +[ -n "$SCALAC_OPTS" ] || SCALAC_OPTS="-deprecation" -# export SCALAC_OPTS -# export JAVA_OPTS -export JAVACMD - -${JAVACMD:=java} $JAVA_OPTS \ - -cp "$EXT_CLASSPATH" \ - -Dscala.home="${SCALA_HOME}" \ - scala.tools.partest.Runner "$@" +partestDebugStr="" +if [ ! -z "${PARTEST_DEBUG}" ] ; then + partestDebugStr="-Dpartest.debug=${PARTEST_DEBUG}" +fi -# ${JAVACMD:=java} $JAVA_OPTS \ -# -cp "$EXT_CLASSPATH" \ -# -Dscala.home="${SCALA_HOME}" \ -# -Dpartest.scalac_opts="${SCALAC_OPTS}" \ -# -Dpartest.javacmd="${JAVACMD}" \ -# -Dpartest.java_opts="${JAVA_OPTS}" \ -# -Dpartest.javac_cmd="${JAVA_HOME}/bin/javac" \ -# scala.tools.partest.Runner "$@" +${JAVACMD:=java} $JAVA_OPTS -cp "$EXT_CLASSPATH" ${partestDebugStr} -Dscala.home="${SCALA_HOME}" -Dpartest.javacmd="${JAVACMD}" -Dpartest.java_opts="${JAVA_OPTS}" -Dpartest.scalac_opts="${SCALAC_OPTS}" -Dpartest.javac_cmd="${JAVA_HOME}/bin/javac" scala.tools.partest.nest.NestRunner "$@" diff --git a/test/partest.bat b/test/partest.bat index 0b5618a164..39fe830082 100755 --- a/test/partest.bat +++ b/test/partest.bat @@ -55,8 +55,8 @@ if "%_EXTENSION_CLASSPATH%"=="" ( set _PROPS=-Dscala.home="%_SCALA_HOME%" -Dpartest.javacmd="%_JAVACMD%" -Dpartest.java_options="%_JAVA_OPTS%" -Dpartest.scalac_options="%_SCALAC_OPTS%" -Dpartest.javac_cmd="%JAVA_HOME%\bin\javac"
-rem echo %_JAVACMD% %_JAVA_OPTS% %_PROPS% -cp "%_EXTENSION_CLASSPATH%" scala.tools.partest.Runner %_ARGS%
-%_JAVACMD% %_JAVA_OPTS% %_PROPS% -cp "%_EXTENSION_CLASSPATH%" scala.tools.partest.Runner %_ARGS%
+rem echo %_JAVACMD% %_JAVA_OPTS% %_PROPS% -cp "%_EXTENSION_CLASSPATH%" scala.tools.partest.nest.NestRunner %_ARGS%
+%_JAVACMD% %_JAVA_OPTS% %_PROPS% -cp "%_EXTENSION_CLASSPATH%" scala.tools.partest.nest.NestRunner %_ARGS%
goto end
rem ##########################################################################
diff --git a/test/pending/jvm/t1464.check b/test/pending/jvm/t1464.check new file mode 100644 index 0000000000..c508d5366f --- /dev/null +++ b/test/pending/jvm/t1464.check @@ -0,0 +1 @@ +false diff --git a/test/pending/pos/t0644.scala b/test/pending/pos/t0644.scala new file mode 100644 index 0000000000..5ad12c3632 --- /dev/null +++ b/test/pending/pos/t0644.scala @@ -0,0 +1,11 @@ +class A { + def appply(): Int = 0 + def update(n: Int) {} +} + +class B extends A { + this() + this()=1 + super() + super()=1 +} diff --git a/test/pending/run/bug2365/Test.scala b/test/pending/run/bug2365/Test.scala new file mode 100644 index 0000000000..92b58f4a25 --- /dev/null +++ b/test/pending/run/bug2365/Test.scala @@ -0,0 +1,35 @@ +import scala.tools.nsc.io._ +import java.net.URL + +object A { def apply(d: { def apply(): Int}) = d.apply() } +object A2 { def apply(d: { def apply(): Int}) = d.apply() } +object A3 { def apply(d: { def apply(): Int}) = d.apply() } +object A4 { def apply(d: { def apply(): Int}) = d.apply() } + +class B extends Function0[Int] { + def apply() = 3 +} + +object Test +{ + type StructF0 = { def apply(): Int } + def main(args: Array[String]) { + for(i <- 0 until 150) + println(i + " " + test(A.apply) + " " + test(A2.apply) + " " + test(A3.apply) + " " + test(A3.apply)) + } + + def test(withF0: StructF0 => Int): Int = { + // Some large jar + val ivyJar = File("/local/lib/java/ivy.jar").toURL + // load a class in a separate loader that will be passed to A + val loader = new java.net.URLClassLoader(Array(File(".").toURL, ivyJar)) + // load a real class to fill perm gen space + Class.forName("org.apache.ivy.Ivy", true, loader).newInstance + // create a class from another class loader with an apply: Int method + val b = Class.forName("B", true, loader).newInstance + + // pass instance to a, which will call apply using structural type reflection. + // This should hold on to the class for B, which means bLoader will not get collected + withF0(b.asInstanceOf[StructF0]) + } +} diff --git a/test/pending/run/bug2365/bug2365.javaopts b/test/pending/run/bug2365/bug2365.javaopts new file mode 100644 index 0000000000..357e033c1c --- /dev/null +++ b/test/pending/run/bug2365/bug2365.javaopts @@ -0,0 +1 @@ +-XX:MaxPermSize=25M diff --git a/test/pending/run/bug2365/run b/test/pending/run/bug2365/run new file mode 100755 index 0000000000..f3c44ad086 --- /dev/null +++ b/test/pending/run/bug2365/run @@ -0,0 +1,13 @@ +#!/bin/sh +# +# This script should fail with any build of scala where #2365 +# is not fixed, and otherwise succeed. Failure means running out +# of PermGen space. + +CP=.:/local/lib/java/ivy.jar +# SCALAC=/scala/inst/28/bin/scalac +SCALAC=scalac +RUN_OPTS="-XX:MaxPermSize=25M -verbose:gc" + +$SCALAC -cp $CP *.scala +JAVA_OPTS="${RUN_OPTS}" scala -cp $CP Test diff --git a/test/pending/run/subarray.check b/test/pending/run/subarray.check new file mode 100644 index 0000000000..814f4a4229 --- /dev/null +++ b/test/pending/run/subarray.check @@ -0,0 +1,2 @@ +one +two |