From c55b106f503d5e712e69823cfeb1cab2460221eb Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 6 May 2010 16:37:13 +0000 Subject: Rolled partest back to r21328. changes necessary to plug it back in while preserving everything which has happened since then in tests and such, but we should be the lookout for overreversion. Review by phaller (but as a formality, I don't think it requires direct review.) --- build.xml | 122 ++- lib/scalacheck.jar.desired.sha1 | 1 - .../scala/tools/nsc/util/CommandLineParser.scala | 145 +++ src/partest-alternative/README | 50 + .../scala/tools/partest/Actions.scala | 231 +++++ .../scala/tools/partest/Alarms.scala | 86 ++ .../scala/tools/partest/BuildContributors.scala | 102 ++ .../scala/tools/partest/Categories.scala | 70 ++ .../scala/tools/partest/Compilable.scala | 106 ++ .../scala/tools/partest/Config.scala | 129 +++ .../scala/tools/partest/Dispatcher.scala | 162 +++ .../scala/tools/partest/Entities.scala | 74 ++ .../scala/tools/partest/Housekeeping.scala | 187 ++++ .../scala/tools/partest/Partest.scala | 81 ++ .../scala/tools/partest/PartestSpec.scala | 104 ++ .../scala/tools/partest/Properties.scala | 18 + .../scala/tools/partest/Results.scala | 121 +++ .../scala/tools/partest/Runner.scala | 36 + .../scala/tools/partest/Statistics.scala | 46 + .../scala/tools/partest/Universe.scala | 96 ++ .../scala/tools/partest/ant/JavaTask.scala | 57 ++ .../scala/tools/partest/antlib.xml | 3 + .../tools/partest/category/AllCategories.scala | 20 + .../scala/tools/partest/category/Analysis.scala | 64 ++ .../scala/tools/partest/category/Compiler.scala | 140 +++ .../scala/tools/partest/category/Runner.scala | 108 ++ .../scala/tools/partest/io/ANSIWriter.scala | 58 ++ .../scala/tools/partest/io/Diff.java | 874 ++++++++++++++++ .../scala/tools/partest/io/DiffPrint.java | 607 +++++++++++ .../scala/tools/partest/io/JUnitReport.scala | 38 + .../scala/tools/partest/io/Logging.scala | 137 +++ .../scala/tools/partest/nest/StreamAppender.scala | 94 ++ .../scala/tools/partest/package.scala | 45 + .../scala/tools/partest/util/package.scala | 61 ++ src/partest/README | 76 +- src/partest/scala/tools/partest/Actions.scala | 231 ----- src/partest/scala/tools/partest/Alarms.scala | 86 -- .../scala/tools/partest/BuildContributors.scala | 102 -- src/partest/scala/tools/partest/Categories.scala | 70 -- src/partest/scala/tools/partest/Compilable.scala | 106 -- src/partest/scala/tools/partest/Config.scala | 129 --- src/partest/scala/tools/partest/Dispatcher.scala | 162 --- src/partest/scala/tools/partest/Entities.scala | 74 -- src/partest/scala/tools/partest/Housekeeping.scala | 187 ---- src/partest/scala/tools/partest/Partest.scala | 81 -- .../scala/tools/partest/PartestDefaults.scala | 30 + src/partest/scala/tools/partest/PartestSpec.scala | 104 -- src/partest/scala/tools/partest/PartestTask.scala | 287 ++++++ src/partest/scala/tools/partest/Properties.scala | 18 - src/partest/scala/tools/partest/Results.scala | 121 --- src/partest/scala/tools/partest/Runner.scala | 36 - src/partest/scala/tools/partest/Statistics.scala | 46 - src/partest/scala/tools/partest/Universe.scala | 96 -- src/partest/scala/tools/partest/ant/JavaTask.scala | 57 -- src/partest/scala/tools/partest/antlib.xml | 3 +- .../tools/partest/category/AllCategories.scala | 20 - .../scala/tools/partest/category/Analysis.scala | 64 -- .../scala/tools/partest/category/Compiler.scala | 140 --- .../scala/tools/partest/category/Runner.scala | 108 -- .../scala/tools/partest/io/ANSIWriter.scala | 58 -- src/partest/scala/tools/partest/io/Diff.java | 874 ---------------- src/partest/scala/tools/partest/io/DiffPrint.java | 607 ----------- .../scala/tools/partest/io/JUnitReport.scala | 38 - src/partest/scala/tools/partest/io/Logging.scala | 137 --- .../scala/tools/partest/nest/AntRunner.scala | 30 + .../scala/tools/partest/nest/CompileManager.scala | 197 ++++ .../tools/partest/nest/ConsoleFileManager.scala | 190 ++++ .../scala/tools/partest/nest/ConsoleRunner.scala | 209 ++++ src/partest/scala/tools/partest/nest/Diff.java | 874 ++++++++++++++++ .../scala/tools/partest/nest/DiffPrint.java | 607 +++++++++++ .../scala/tools/partest/nest/DirectRunner.scala | 78 ++ .../scala/tools/partest/nest/FileManager.scala | 110 ++ .../scala/tools/partest/nest/NestRunner.scala | 16 + src/partest/scala/tools/partest/nest/NestUI.scala | 118 +++ .../scala/tools/partest/nest/PathSettings.scala | 41 + .../tools/partest/nest/ReflectiveRunner.scala | 88 ++ .../scala/tools/partest/nest/RunnerUtils.scala | 29 + .../scala/tools/partest/nest/TestFile.scala | 49 + src/partest/scala/tools/partest/nest/Worker.scala | 1071 ++++++++++++++++++++ src/partest/scala/tools/partest/package.scala | 59 +- src/partest/scala/tools/partest/util/package.scala | 61 -- .../scala/tools/partest/utils/PrintMgr.scala | 52 + .../scala/tools/partest/utils/Properties.scala | 18 + test/continuations/neg/function0.check | 6 - test/continuations/neg/function0.scala | 16 - test/continuations/neg/function2.check | 6 - test/continuations/neg/function2.scala | 16 - test/continuations/neg/function3.check | 6 - test/continuations/neg/function3.scala | 15 - test/continuations/neg/infer0.check | 4 - test/continuations/neg/infer0.scala | 14 - test/continuations/neg/infer2.check | 4 - test/continuations/neg/infer2.scala | 19 - test/continuations/neg/lazy.check | 6 - test/continuations/neg/lazy.scala | 16 - test/continuations/neg/t1929.check | 6 - test/continuations/neg/t1929.scala | 17 - test/continuations/neg/t2285.check | 6 - test/continuations/neg/t2285.scala | 11 - test/continuations/neg/t2949.check | 6 - test/continuations/neg/t2949.scala | 15 - test/continuations/neg/trycatch2.check | 7 - test/continuations/neg/trycatch2.scala | 33 - test/continuations/run/basics.check | 2 - test/continuations/run/basics.scala | 23 - test/continuations/run/function1.check | 1 - test/continuations/run/function1.scala | 16 - test/continuations/run/function4.check | 1 - test/continuations/run/function4.scala | 15 - test/continuations/run/function5.check | 1 - test/continuations/run/function5.scala | 15 - test/continuations/run/function6.check | 1 - test/continuations/run/function6.scala | 16 - test/continuations/run/ifelse0.check | 2 - test/continuations/run/ifelse0.scala | 18 - test/continuations/run/ifelse1.check | 4 - test/continuations/run/ifelse1.scala | 25 - test/continuations/run/ifelse2.check | 4 - test/continuations/run/ifelse2.scala | 16 - test/continuations/run/ifelse3.check | 2 - test/continuations/run/ifelse3.scala | 21 - test/continuations/run/infer1.scala | 33 - test/continuations/run/match0.check | 2 - test/continuations/run/match0.scala | 18 - test/continuations/run/match1.check | 2 - test/continuations/run/match1.scala | 18 - test/continuations/run/match2.check | 2 - test/continuations/run/match2.scala | 26 - test/continuations/run/t1807.check | 1 - test/continuations/run/t1807.scala | 14 - test/continuations/run/t1808.scala | 10 - test/continuations/run/t1820.scala | 14 - test/continuations/run/t1821.check | 4 - test/continuations/run/t1821.scala | 20 - test/continuations/run/t2864.check | 1 - test/continuations/run/t2864.scala | 30 - test/continuations/run/t2934.check | 1 - test/continuations/run/t2934.scala | 10 - test/continuations/run/t3199.check | 1 - test/continuations/run/t3199.scala | 20 - test/continuations/run/t3199b.check | 1 - test/continuations/run/t3199b.scala | 11 - test/continuations/run/t3223.check | 1 - test/continuations/run/t3223.scala | 19 - test/continuations/run/t3225.check | 12 - test/continuations/run/t3225.scala | 56 - test/continuations/run/trycatch0.check | 2 - test/continuations/run/trycatch0.scala | 25 - test/continuations/run/trycatch1.check | 4 - test/continuations/run/trycatch1.scala | 48 - test/continuations/run/while0.check | 1 - test/continuations/run/while0.scala | 22 - test/continuations/run/while1.check | 11 - test/continuations/run/while1.scala | 22 - test/continuations/run/while2.check | 19 - test/continuations/run/while2.scala | 23 - test/disabled/pos/spec-traits.scala | 83 ++ test/disabled/pos/t1254/t1254.java | 28 + test/disabled/run/stream_length.check | 1 + test/disabled/run/stream_length.scala | 15 + test/disabled/run/t2946/Parsers.scala | 4 + test/disabled/run/t2946/ResponseCommon.scala | 14 + test/disabled/run/t2946/Test.scala | 7 + test/disabled/scalacheck/redblack.scala | 157 +++ test/files/continuations-neg/function0.check | 6 + test/files/continuations-neg/function0.scala | 16 + test/files/continuations-neg/function2.check | 6 + test/files/continuations-neg/function2.scala | 16 + test/files/continuations-neg/function3.check | 6 + test/files/continuations-neg/function3.scala | 15 + test/files/continuations-neg/infer0.check | 4 + test/files/continuations-neg/infer0.scala | 14 + test/files/continuations-neg/infer2.check | 4 + test/files/continuations-neg/infer2.scala | 19 + test/files/continuations-neg/lazy.check | 6 + test/files/continuations-neg/lazy.scala | 16 + test/files/continuations-neg/t1929.check | 6 + test/files/continuations-neg/t1929.scala | 17 + test/files/continuations-neg/t2285.check | 6 + test/files/continuations-neg/t2285.scala | 11 + test/files/continuations-neg/t2949.check | 6 + test/files/continuations-neg/t2949.scala | 15 + test/files/continuations-neg/trycatch2.check | 7 + test/files/continuations-neg/trycatch2.scala | 33 + test/files/continuations-run/basics.check | 2 + test/files/continuations-run/basics.scala | 23 + test/files/continuations-run/function1.check | 1 + test/files/continuations-run/function1.scala | 16 + test/files/continuations-run/function4.check | 1 + test/files/continuations-run/function4.scala | 15 + test/files/continuations-run/function5.check | 1 + test/files/continuations-run/function5.scala | 15 + test/files/continuations-run/function6.check | 1 + test/files/continuations-run/function6.scala | 16 + test/files/continuations-run/ifelse0.check | 2 + test/files/continuations-run/ifelse0.scala | 18 + test/files/continuations-run/ifelse1.check | 4 + test/files/continuations-run/ifelse1.scala | 25 + test/files/continuations-run/ifelse2.check | 4 + test/files/continuations-run/ifelse2.scala | 16 + test/files/continuations-run/ifelse3.check | 2 + test/files/continuations-run/ifelse3.scala | 21 + test/files/continuations-run/infer1.scala | 33 + test/files/continuations-run/match0.check | 2 + test/files/continuations-run/match0.scala | 18 + test/files/continuations-run/match1.check | 2 + test/files/continuations-run/match1.scala | 18 + test/files/continuations-run/match2.check | 2 + test/files/continuations-run/match2.scala | 26 + test/files/continuations-run/t1807.check | 1 + test/files/continuations-run/t1807.scala | 14 + test/files/continuations-run/t1808.scala | 10 + test/files/continuations-run/t1820.scala | 14 + test/files/continuations-run/t1821.check | 4 + test/files/continuations-run/t1821.scala | 20 + test/files/continuations-run/t2864.check | 1 + test/files/continuations-run/t2864.scala | 30 + test/files/continuations-run/t2934.check | 1 + test/files/continuations-run/t2934.scala | 10 + test/files/continuations-run/t3199.check | 1 + test/files/continuations-run/t3199.scala | 20 + test/files/continuations-run/t3199b.check | 1 + test/files/continuations-run/t3199b.scala | 11 + test/files/continuations-run/t3223.check | 1 + test/files/continuations-run/t3223.scala | 19 + test/files/continuations-run/t3225.check | 12 + test/files/continuations-run/t3225.scala | 56 + test/files/continuations-run/trycatch0.check | 2 + test/files/continuations-run/trycatch0.scala | 25 + test/files/continuations-run/trycatch1.check | 4 + test/files/continuations-run/trycatch1.scala | 48 + test/files/continuations-run/while0.check | 1 + test/files/continuations-run/while0.scala | 22 + test/files/continuations-run/while1.check | 11 + test/files/continuations-run/while1.scala | 22 + test/files/continuations-run/while2.check | 19 + test/files/continuations-run/while2.scala | 23 + test/files/jvm/genericNest.scala | 13 + .../jvm/genericNest/genericNest.jar.desired.sha1 | 1 - test/files/jvm/genericNest/genericNest.scala | 13 - test/files/jvm/lib/annotations.jar.desired.sha1 | 1 - test/files/jvm/lib/nest.jar.desired.sha1 | 1 - test/files/jvm/methvsfield.java | 11 + test/files/jvm/methvsfield.scala | 4 + test/files/jvm/nest.java | 38 + test/files/jvm/nest.scala | 21 + test/files/jvm/outerEnum.scala | 9 + test/files/jvm/outerEnum/enums.jar.desired.sha1 | 1 - test/files/jvm/outerEnum/outerEnum.scala | 9 - test/files/jvm/t1652.check | 2 + test/files/lib/annotations.jar.desired.sha1 | 1 + test/files/lib/enums.jar.desired.sha1 | 1 + test/files/lib/genericNest.jar.desired.sha1 | 1 + test/files/lib/methvsfield.jar.desired.sha1 | 1 + test/files/lib/nest.jar.desired.sha1 | 1 + test/files/lib/scalacheck.jar.desired.sha1 | 1 + test/files/neg/bug414.scala | 2 +- test/files/neg/migration28.check | 3 +- test/files/pos/spec-traits.scala | 83 -- test/files/pos/t1254/t1254.java | 28 - test/files/res/bug687.check | 5 +- test/files/run/numbereq.scala | 2 +- test/files/run/programmatic-main.check | 2 - test/files/run/stream_length.scala | 15 - test/files/run/t2946/Parsers.scala | 4 - test/files/run/t2946/ResponseCommon.scala | 14 - test/files/run/t2946/Test.scala | 7 - test/files/scalacheck/redblack.scala | 157 --- test/partest | 25 +- test/partest.bat | 4 +- test/pending/jvm/t1464.check | 1 + test/pending/pos/t0644.scala | 11 + test/pending/run/bug2365/Test.scala | 35 + test/pending/run/bug2365/bug2365.javaopts | 1 + test/pending/run/bug2365/run | 13 + test/pending/run/subarray.check | 2 + 276 files changed, 9776 insertions(+), 5227 deletions(-) delete mode 100644 lib/scalacheck.jar.desired.sha1 create mode 100644 src/compiler/scala/tools/nsc/util/CommandLineParser.scala create mode 100644 src/partest-alternative/README create mode 100644 src/partest-alternative/scala/tools/partest/Actions.scala create mode 100644 src/partest-alternative/scala/tools/partest/Alarms.scala create mode 100644 src/partest-alternative/scala/tools/partest/BuildContributors.scala create mode 100644 src/partest-alternative/scala/tools/partest/Categories.scala create mode 100644 src/partest-alternative/scala/tools/partest/Compilable.scala create mode 100644 src/partest-alternative/scala/tools/partest/Config.scala create mode 100644 src/partest-alternative/scala/tools/partest/Dispatcher.scala create mode 100644 src/partest-alternative/scala/tools/partest/Entities.scala create mode 100644 src/partest-alternative/scala/tools/partest/Housekeeping.scala create mode 100644 src/partest-alternative/scala/tools/partest/Partest.scala create mode 100644 src/partest-alternative/scala/tools/partest/PartestSpec.scala create mode 100644 src/partest-alternative/scala/tools/partest/Properties.scala create mode 100644 src/partest-alternative/scala/tools/partest/Results.scala create mode 100644 src/partest-alternative/scala/tools/partest/Runner.scala create mode 100644 src/partest-alternative/scala/tools/partest/Statistics.scala create mode 100644 src/partest-alternative/scala/tools/partest/Universe.scala create mode 100644 src/partest-alternative/scala/tools/partest/ant/JavaTask.scala create mode 100644 src/partest-alternative/scala/tools/partest/antlib.xml create mode 100644 src/partest-alternative/scala/tools/partest/category/AllCategories.scala create mode 100644 src/partest-alternative/scala/tools/partest/category/Analysis.scala create mode 100644 src/partest-alternative/scala/tools/partest/category/Compiler.scala create mode 100644 src/partest-alternative/scala/tools/partest/category/Runner.scala create mode 100644 src/partest-alternative/scala/tools/partest/io/ANSIWriter.scala create mode 100644 src/partest-alternative/scala/tools/partest/io/Diff.java create mode 100644 src/partest-alternative/scala/tools/partest/io/DiffPrint.java create mode 100644 src/partest-alternative/scala/tools/partest/io/JUnitReport.scala create mode 100644 src/partest-alternative/scala/tools/partest/io/Logging.scala create mode 100644 src/partest-alternative/scala/tools/partest/nest/StreamAppender.scala create mode 100644 src/partest-alternative/scala/tools/partest/package.scala create mode 100644 src/partest-alternative/scala/tools/partest/util/package.scala delete mode 100644 src/partest/scala/tools/partest/Actions.scala delete mode 100644 src/partest/scala/tools/partest/Alarms.scala delete mode 100644 src/partest/scala/tools/partest/BuildContributors.scala delete mode 100644 src/partest/scala/tools/partest/Categories.scala delete mode 100644 src/partest/scala/tools/partest/Compilable.scala delete mode 100644 src/partest/scala/tools/partest/Config.scala delete mode 100644 src/partest/scala/tools/partest/Dispatcher.scala delete mode 100644 src/partest/scala/tools/partest/Entities.scala delete mode 100644 src/partest/scala/tools/partest/Housekeeping.scala delete mode 100644 src/partest/scala/tools/partest/Partest.scala create mode 100644 src/partest/scala/tools/partest/PartestDefaults.scala delete mode 100644 src/partest/scala/tools/partest/PartestSpec.scala create mode 100644 src/partest/scala/tools/partest/PartestTask.scala delete mode 100644 src/partest/scala/tools/partest/Properties.scala delete mode 100644 src/partest/scala/tools/partest/Results.scala delete mode 100644 src/partest/scala/tools/partest/Runner.scala delete mode 100644 src/partest/scala/tools/partest/Statistics.scala delete mode 100644 src/partest/scala/tools/partest/Universe.scala delete mode 100644 src/partest/scala/tools/partest/ant/JavaTask.scala delete mode 100644 src/partest/scala/tools/partest/category/AllCategories.scala delete mode 100644 src/partest/scala/tools/partest/category/Analysis.scala delete mode 100644 src/partest/scala/tools/partest/category/Compiler.scala delete mode 100644 src/partest/scala/tools/partest/category/Runner.scala delete mode 100644 src/partest/scala/tools/partest/io/ANSIWriter.scala delete mode 100644 src/partest/scala/tools/partest/io/Diff.java delete mode 100644 src/partest/scala/tools/partest/io/DiffPrint.java delete mode 100644 src/partest/scala/tools/partest/io/JUnitReport.scala delete mode 100644 src/partest/scala/tools/partest/io/Logging.scala create mode 100644 src/partest/scala/tools/partest/nest/AntRunner.scala create mode 100644 src/partest/scala/tools/partest/nest/CompileManager.scala create mode 100644 src/partest/scala/tools/partest/nest/ConsoleFileManager.scala create mode 100644 src/partest/scala/tools/partest/nest/ConsoleRunner.scala create mode 100644 src/partest/scala/tools/partest/nest/Diff.java create mode 100644 src/partest/scala/tools/partest/nest/DiffPrint.java create mode 100644 src/partest/scala/tools/partest/nest/DirectRunner.scala create mode 100644 src/partest/scala/tools/partest/nest/FileManager.scala create mode 100644 src/partest/scala/tools/partest/nest/NestRunner.scala create mode 100644 src/partest/scala/tools/partest/nest/NestUI.scala create mode 100644 src/partest/scala/tools/partest/nest/PathSettings.scala create mode 100644 src/partest/scala/tools/partest/nest/ReflectiveRunner.scala create mode 100644 src/partest/scala/tools/partest/nest/RunnerUtils.scala create mode 100644 src/partest/scala/tools/partest/nest/TestFile.scala create mode 100644 src/partest/scala/tools/partest/nest/Worker.scala delete mode 100644 src/partest/scala/tools/partest/util/package.scala create mode 100644 src/partest/scala/tools/partest/utils/PrintMgr.scala create mode 100644 src/partest/scala/tools/partest/utils/Properties.scala delete mode 100644 test/continuations/neg/function0.check delete mode 100644 test/continuations/neg/function0.scala delete mode 100644 test/continuations/neg/function2.check delete mode 100644 test/continuations/neg/function2.scala delete mode 100644 test/continuations/neg/function3.check delete mode 100644 test/continuations/neg/function3.scala delete mode 100644 test/continuations/neg/infer0.check delete mode 100644 test/continuations/neg/infer0.scala delete mode 100644 test/continuations/neg/infer2.check delete mode 100644 test/continuations/neg/infer2.scala delete mode 100644 test/continuations/neg/lazy.check delete mode 100644 test/continuations/neg/lazy.scala delete mode 100644 test/continuations/neg/t1929.check delete mode 100644 test/continuations/neg/t1929.scala delete mode 100644 test/continuations/neg/t2285.check delete mode 100644 test/continuations/neg/t2285.scala delete mode 100644 test/continuations/neg/t2949.check delete mode 100644 test/continuations/neg/t2949.scala delete mode 100644 test/continuations/neg/trycatch2.check delete mode 100644 test/continuations/neg/trycatch2.scala delete mode 100755 test/continuations/run/basics.check delete mode 100755 test/continuations/run/basics.scala delete mode 100644 test/continuations/run/function1.check delete mode 100644 test/continuations/run/function1.scala delete mode 100644 test/continuations/run/function4.check delete mode 100644 test/continuations/run/function4.scala delete mode 100644 test/continuations/run/function5.check delete mode 100644 test/continuations/run/function5.scala delete mode 100644 test/continuations/run/function6.check delete mode 100644 test/continuations/run/function6.scala delete mode 100644 test/continuations/run/ifelse0.check delete mode 100644 test/continuations/run/ifelse0.scala delete mode 100644 test/continuations/run/ifelse1.check delete mode 100644 test/continuations/run/ifelse1.scala delete mode 100644 test/continuations/run/ifelse2.check delete mode 100644 test/continuations/run/ifelse2.scala delete mode 100644 test/continuations/run/ifelse3.check delete mode 100644 test/continuations/run/ifelse3.scala delete mode 100644 test/continuations/run/infer1.scala delete mode 100644 test/continuations/run/match0.check delete mode 100644 test/continuations/run/match0.scala delete mode 100644 test/continuations/run/match1.check delete mode 100644 test/continuations/run/match1.scala delete mode 100644 test/continuations/run/match2.check delete mode 100644 test/continuations/run/match2.scala delete mode 100644 test/continuations/run/t1807.check delete mode 100644 test/continuations/run/t1807.scala delete mode 100644 test/continuations/run/t1808.scala delete mode 100644 test/continuations/run/t1820.scala delete mode 100644 test/continuations/run/t1821.check delete mode 100644 test/continuations/run/t1821.scala delete mode 100644 test/continuations/run/t2864.check delete mode 100644 test/continuations/run/t2864.scala delete mode 100644 test/continuations/run/t2934.check delete mode 100644 test/continuations/run/t2934.scala delete mode 100644 test/continuations/run/t3199.check delete mode 100644 test/continuations/run/t3199.scala delete mode 100644 test/continuations/run/t3199b.check delete mode 100644 test/continuations/run/t3199b.scala delete mode 100644 test/continuations/run/t3223.check delete mode 100644 test/continuations/run/t3223.scala delete mode 100644 test/continuations/run/t3225.check delete mode 100644 test/continuations/run/t3225.scala delete mode 100644 test/continuations/run/trycatch0.check delete mode 100644 test/continuations/run/trycatch0.scala delete mode 100644 test/continuations/run/trycatch1.check delete mode 100644 test/continuations/run/trycatch1.scala delete mode 100644 test/continuations/run/while0.check delete mode 100644 test/continuations/run/while0.scala delete mode 100644 test/continuations/run/while1.check delete mode 100644 test/continuations/run/while1.scala delete mode 100644 test/continuations/run/while2.check delete mode 100644 test/continuations/run/while2.scala create mode 100644 test/disabled/pos/spec-traits.scala create mode 100644 test/disabled/pos/t1254/t1254.java create mode 100644 test/disabled/run/stream_length.check create mode 100644 test/disabled/run/stream_length.scala create mode 100644 test/disabled/run/t2946/Parsers.scala create mode 100644 test/disabled/run/t2946/ResponseCommon.scala create mode 100644 test/disabled/run/t2946/Test.scala create mode 100644 test/disabled/scalacheck/redblack.scala create mode 100644 test/files/continuations-neg/function0.check create mode 100644 test/files/continuations-neg/function0.scala create mode 100644 test/files/continuations-neg/function2.check create mode 100644 test/files/continuations-neg/function2.scala create mode 100644 test/files/continuations-neg/function3.check create mode 100644 test/files/continuations-neg/function3.scala create mode 100644 test/files/continuations-neg/infer0.check create mode 100644 test/files/continuations-neg/infer0.scala create mode 100644 test/files/continuations-neg/infer2.check create mode 100644 test/files/continuations-neg/infer2.scala create mode 100644 test/files/continuations-neg/lazy.check create mode 100644 test/files/continuations-neg/lazy.scala create mode 100644 test/files/continuations-neg/t1929.check create mode 100644 test/files/continuations-neg/t1929.scala create mode 100644 test/files/continuations-neg/t2285.check create mode 100644 test/files/continuations-neg/t2285.scala create mode 100644 test/files/continuations-neg/t2949.check create mode 100644 test/files/continuations-neg/t2949.scala create mode 100644 test/files/continuations-neg/trycatch2.check create mode 100644 test/files/continuations-neg/trycatch2.scala create mode 100755 test/files/continuations-run/basics.check create mode 100755 test/files/continuations-run/basics.scala create mode 100644 test/files/continuations-run/function1.check create mode 100644 test/files/continuations-run/function1.scala create mode 100644 test/files/continuations-run/function4.check create mode 100644 test/files/continuations-run/function4.scala create mode 100644 test/files/continuations-run/function5.check create mode 100644 test/files/continuations-run/function5.scala create mode 100644 test/files/continuations-run/function6.check create mode 100644 test/files/continuations-run/function6.scala create mode 100644 test/files/continuations-run/ifelse0.check create mode 100644 test/files/continuations-run/ifelse0.scala create mode 100644 test/files/continuations-run/ifelse1.check create mode 100644 test/files/continuations-run/ifelse1.scala create mode 100644 test/files/continuations-run/ifelse2.check create mode 100644 test/files/continuations-run/ifelse2.scala create mode 100644 test/files/continuations-run/ifelse3.check create mode 100644 test/files/continuations-run/ifelse3.scala create mode 100644 test/files/continuations-run/infer1.scala create mode 100644 test/files/continuations-run/match0.check create mode 100644 test/files/continuations-run/match0.scala create mode 100644 test/files/continuations-run/match1.check create mode 100644 test/files/continuations-run/match1.scala create mode 100644 test/files/continuations-run/match2.check create mode 100644 test/files/continuations-run/match2.scala create mode 100644 test/files/continuations-run/t1807.check create mode 100644 test/files/continuations-run/t1807.scala create mode 100644 test/files/continuations-run/t1808.scala create mode 100644 test/files/continuations-run/t1820.scala create mode 100644 test/files/continuations-run/t1821.check create mode 100644 test/files/continuations-run/t1821.scala create mode 100644 test/files/continuations-run/t2864.check create mode 100644 test/files/continuations-run/t2864.scala create mode 100644 test/files/continuations-run/t2934.check create mode 100644 test/files/continuations-run/t2934.scala create mode 100644 test/files/continuations-run/t3199.check create mode 100644 test/files/continuations-run/t3199.scala create mode 100644 test/files/continuations-run/t3199b.check create mode 100644 test/files/continuations-run/t3199b.scala create mode 100644 test/files/continuations-run/t3223.check create mode 100644 test/files/continuations-run/t3223.scala create mode 100644 test/files/continuations-run/t3225.check create mode 100644 test/files/continuations-run/t3225.scala create mode 100644 test/files/continuations-run/trycatch0.check create mode 100644 test/files/continuations-run/trycatch0.scala create mode 100644 test/files/continuations-run/trycatch1.check create mode 100644 test/files/continuations-run/trycatch1.scala create mode 100644 test/files/continuations-run/while0.check create mode 100644 test/files/continuations-run/while0.scala create mode 100644 test/files/continuations-run/while1.check create mode 100644 test/files/continuations-run/while1.scala create mode 100644 test/files/continuations-run/while2.check create mode 100644 test/files/continuations-run/while2.scala create mode 100644 test/files/jvm/genericNest.scala delete mode 100644 test/files/jvm/genericNest/genericNest.jar.desired.sha1 delete mode 100644 test/files/jvm/genericNest/genericNest.scala delete mode 100644 test/files/jvm/lib/annotations.jar.desired.sha1 delete mode 100644 test/files/jvm/lib/nest.jar.desired.sha1 create mode 100644 test/files/jvm/methvsfield.java create mode 100644 test/files/jvm/methvsfield.scala create mode 100644 test/files/jvm/nest.java create mode 100644 test/files/jvm/nest.scala create mode 100644 test/files/jvm/outerEnum.scala delete mode 100644 test/files/jvm/outerEnum/enums.jar.desired.sha1 delete mode 100644 test/files/jvm/outerEnum/outerEnum.scala create mode 100644 test/files/jvm/t1652.check create mode 100644 test/files/lib/annotations.jar.desired.sha1 create mode 100644 test/files/lib/enums.jar.desired.sha1 create mode 100644 test/files/lib/genericNest.jar.desired.sha1 create mode 100644 test/files/lib/methvsfield.jar.desired.sha1 create mode 100644 test/files/lib/nest.jar.desired.sha1 create mode 100644 test/files/lib/scalacheck.jar.desired.sha1 delete mode 100644 test/files/pos/spec-traits.scala delete mode 100644 test/files/pos/t1254/t1254.java delete mode 100644 test/files/run/stream_length.scala delete mode 100644 test/files/run/t2946/Parsers.scala delete mode 100644 test/files/run/t2946/ResponseCommon.scala delete mode 100644 test/files/run/t2946/Test.scala delete mode 100644 test/files/scalacheck/redblack.scala create mode 100644 test/pending/jvm/t1464.check create mode 100644 test/pending/pos/t0644.scala create mode 100644 test/pending/run/bug2365/Test.scala create mode 100644 test/pending/run/bug2365/bug2365.javaopts create mode 100755 test/pending/run/bug2365/run create mode 100644 test/pending/run/subarray.check diff --git a/build.xml b/build.xml index 4df52769ec..5c2c8f0add 100644 --- a/build.xml +++ b/build.xml @@ -147,9 +147,7 @@ PROPERTIES - - - + @@ -178,7 +176,7 @@ PROPERTIES - + @@ -186,11 +184,13 @@ PROPERTIES - + + - - + @@ -1498,54 +1496,68 @@ BOOTRAPING TEST AND TEST SUITE + + + + + + + - - - - - - - - - - - - + + + + + + + + + + - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + @@ -1767,7 +1779,7 @@ POSITIONS - + @@ -1791,13 +1803,13 @@ POSITIONS - + - + - + diff --git a/lib/scalacheck.jar.desired.sha1 b/lib/scalacheck.jar.desired.sha1 deleted file mode 100644 index ed9c46c3db..0000000000 --- a/lib/scalacheck.jar.desired.sha1 +++ /dev/null @@ -1 +0,0 @@ -4c76385b1a9cb7cd619739776b940d98c4aadc6d ?scalacheck.jar 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 [] [ ...] + : 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 + --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-alternative/scala/tools/partest/Actions.scala b/src/partest-alternative/scala/tools/partest/Actions.scala new file mode 100644 index 0000000000..cb60152b71 --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/Actions.scala @@ -0,0 +1,231 @@ +/* __ *\ +** ________ ___ / / ___ Scala Parallel Testing ** +** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.tools +package partest + +import util._ +import nsc.io._ + +trait Actions { + partest: Universe => + + class TestSequence(val actions: List[TestStep]) extends AbsTestSequence { + } + + implicit def createSequence(xs: List[TestStep]) = new TestSequence(xs) + + trait ExecSupport { + self: TestEntity => + + def execEnv: Map[String, String] = { + val map = assembleEnvironment() + val cwd = execCwd.toList map ("CWD" -> _.path) + + map ++ cwd + } + def execCwd = if (commandFile.isFile) Some(sourcesDir) else None + + def runExec(args: List[String]): Boolean = { + val cmd = fromArgs(args) + + if (isVerbose) { + trace("runExec: " + execEnv.mkString("ENV(", "\n", "\n)")) + execCwd foreach (x => trace("CWD(" + x + ")")) + } + + trace("runExec: " + cmd) + isDryRun || execAndLog(cmd) + } + + /** Runs command redirecting standard out and + * error out to output file. + */ + private def runCommandOld(command: String, output: java.io.File): Int = { + import java.io._ + import nest.StreamAppender + + // 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 } + } + + /** Exec a process to run a command. Assumes 0 exit value is success. + * Of necessity, also treats no available exit value as success. + */ + protected def execAndLog(cmd: String): Boolean = { + runCommandOld(cmd, logFile.jfile) == 0 + + // var proc: Process = null + // + // val result = interruptMeIn(cmd, testTimeout) { + // loggingResult { + // proc = Process.exec(toArgs(cmd), execEnv, execCwd.orNull, true) + // proc.slurp() + // } + // proc != null && (proc.waitFor() == 0) + // } + // result getOrElse { + // warning("Process never terminated: '%s'" format cmd) + // if (proc != null) + // proc.destroy() + // + // false + // } + } + } + + trait ScriptableTest { + self: TestEntity => + + /** Translates a line from a .cmds file into a teststep. + */ + def customTestStep(line: String): TestStep = { + trace("customTestStep: " + line) + val (cmd, rest) = line span (x => !Character.isWhitespace(x)) + def qualify(name: String) = sourcesDir / name path + val args = toArgs(rest) map qualify + def fail: TestStep = (_: TestEntity) => error("Parse error: did not understand '%s'" format line) + + val f: TestEntity => Boolean = cmd match { + case "scalac" => _ scalac args + case "javac" => _ javac args + case "scala" => _ runScala args + case _ => fail + } + f + } + } + + trait CompilableTest extends CompileExecSupport { + self: TestEntity => + + def sourceFiles = location.walk collect { case f: File if isJavaOrScala(f) => f } toList + def allSources = sourceFiles map (_.path) + def scalaSources = sourceFiles filter isScala map (_.path) + def javaSources = sourceFiles filter isJava map (_.path) + + /** If there are mixed java and scala files, the standard compilation + * sequence is: + * + * scalac with all files + * javac with only java files + * scalac with only scala files + * + * This should be expanded to encompass other strategies so we know how + * well they're working or not working - notably, it would be very useful + * to know exactly when and how two-pass compilation fails. + */ + def compile() = { + trace("compile: " + sourceFiles) + + def compileJava() = javac(javaSources) + def compileScala() = scalac(scalaSources) + def compileAll() = scalac(allSources) + def compileMixed() = compileAll() && compileJava() && compileScala() + + if (scalaSources.nonEmpty && javaSources.nonEmpty) compileMixed() + else compileScala() + } + } + + trait DiffableTest { + self: TestEntity => + + def checkFile: File = withExtension("check").toFile + def checkFileRequired = + returning(checkFile.isFile)(res => if (!res) warnAndLog("A checkFile at '%s' is mandatory.\n" format checkFile.path)) + + lazy val sourceFileNames = sourceFiles map (_.name) + + /** Given the difficulty of verifying that any selective approach works + * everywhere, the algorithm now is to look for the name of any known + * source file for this test, and if seen, remove all the non-whitespace + * preceding it. (Paths with whitespace don't work anyway.) This should + * wipe out all slashes, backslashes, C:\, cygwin/windows differences, + * and whatever else makes a simple diff not simple. + * + * The log and check file are both transformed, which I don't think is + * correct -- only the log should be -- but doing it this way until I + * can clarify martin's comments in #3283. + */ + def normalizePaths(s: String) = + sourceFileNames.foldLeft(s)((res, name) => res.replaceAll("""\S+\Q%s\E""" format name, name)) + + /** The default cleanup normalizes paths relative to sourcesDir, + * absorbs line terminator differences by going to lines and back, + * and trims leading or trailing whitespace. + */ + def diffCleanup(f: File) = safeLines(f) map normalizePaths mkString "\n" trim + + /** diffFiles requires actual Files as arguments but the output we want + * is the post-processed versions of log/check, so we resort to tempfiles. + */ + lazy val diffOutput = { + if (!checkFile.exists) "" else { + val input = diffCleanup(checkFile) + val output = diffCleanup(logFile) + def asFile(s: String) = returning(File.makeTemp("partest-diff"))(_ writeAll s) + + if (input == output) "" + else diffFiles(asFile(input), asFile(output)) + } + } + private def checkTraceName = tracePath(checkFile) + private def logTraceName = tracePath(logFile) + private def isDiffConfirmed = checkFile.exists && (diffOutput == "") + + private def sendTraceMsg() { + def result = + if (isDryRun) "" + else if (isDiffConfirmed) " [passed]" + else if (checkFile.exists) " [failed]" + else " [unchecked]" + + trace("diff %s %s%s".format(checkTraceName, logTraceName, result)) + } + + /** If optional is true, a missing check file is considered + * a successful diff. Necessary since many categories use + * checkfiles in an ad hoc manner. + */ + def runDiff() = { + sendTraceMsg() + + def updateCheck = ( + isUpdateCheck && { + val formatStr = "** diff %s %s: " + ( + if (checkFile.exists) "failed, updating '%s' and marking as passed." + else if (diffOutput == "") "not creating checkFile at '%s' as there is no output." + else "was unchecked, creating '%s' for future tests." + ) + "\n" + + normal(formatStr.format(checkTraceName, logTraceName, checkFile.path)) + if (diffOutput != "") normal(diffOutput) + + checkFile.writeAll(diffCleanup(logFile), "\n") + true + } + ) + + isDryRun || isDiffConfirmed || (updateCheck || !checkFile.exists) + } + } +} diff --git a/src/partest-alternative/scala/tools/partest/Alarms.scala b/src/partest-alternative/scala/tools/partest/Alarms.scala new file mode 100644 index 0000000000..f38d8d6268 --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/Alarms.scala @@ -0,0 +1,86 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools +package partest + +import java.util.{ Timer, TimerTask } + +trait Alarms { + self: Universe => + + def interruptMeIn[T](debugMsg: String, seconds: Int)(body: => T): Option[T] = { + val thisThread = currentThread + val alarm = new SimpleAlarm(seconds * 1000) set thisThread.interrupt() + debug("interruptMeIn(%d) '%s'".format(seconds, debugMsg)) + + try { Some(body) } + catch { case _: InterruptedException => debug("Received interrupted exception.") ; None } + finally { debug("Cancelling interruptMeIn '%s'" format debugMsg) ; alarm.cancel() ; Thread.interrupted() } + } + + case class AlarmerAction(secs: Int, action: () => Unit) extends Runnable { + override def run() = action() + } + + /** Set any number of alarms up with tuples of the form: + * seconds to alarm -> Function0[Unit] to execute + */ + class Alarmer(alarms: AlarmerAction*) { + import java.util.concurrent._ + + val exec = Executors.newSingleThreadScheduledExecutor() + alarms foreach (x => exec.schedule(x, x.secs, TimeUnit.SECONDS)) + exec.shutdown() + + def cancelAll() = exec.shutdownNow() + } + + class SimpleAlarm(timeout: Long) { + private val alarm = new Timer + + /** Start a timer, running the given body if it goes off. + */ + def set(body: => Unit) = returning(new TimerTask { def run() = body })(alarm.schedule(_, timeout)) + + /** Cancel the timer. + */ + def cancel() = alarm.cancel() + } + + trait TestAlarms { + test: TestEntity => + + private def warning1 = AlarmerAction(testWarning, () => warning( + """|I've been waiting %s seconds for this to complete: + | %s + |It may be stuck, or if not, it should be broken into smaller tests. + |""".stripMargin.format(testWarning, test)) + ) + private def warning2 = AlarmerAction(testWarning * 2, () => warning( + """|Now I've been waiting %s seconds for this to complete: + | %s + |If partest seems hung it would be a good place to look. + |""".stripMargin.format(testWarning * 2, test)) + ) + + def startAlarms(onTimeout: => Unit) = + if (isNoAlarms) new Alarmer() // for alarm debugging + else new Alarmer(Seq(warning1, warning2, AlarmerAction(testTimeout, () => onTimeout)): _*) + } + + // Thread.setDefaultUncaughtExceptionHandler(new UncaughtException) + // class UncaughtException extends Thread.UncaughtExceptionHandler { + // def uncaughtException(t: Thread, e: Throwable) { + // Console.println("Uncaught in %s: %s".format(t, e)) + // } + // } + // + // lazy val logger = File("/tmp/partest.log").bufferedWriter() + // def flog(msg: String) = logger synchronized { + // logger write (msg + "\n") + // logger.flush() + // } +} diff --git a/src/partest-alternative/scala/tools/partest/BuildContributors.scala b/src/partest-alternative/scala/tools/partest/BuildContributors.scala new file mode 100644 index 0000000000..64c7e07bc3 --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/BuildContributors.scala @@ -0,0 +1,102 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + */ + +package scala.tools +package partest + +import nsc.io._ +import nsc.util.ClassPath + +trait BuildContributors { + universe: Universe => + + /** A trait mixed into types which contribute a portion of the values. + * The basic mechanism is the TestBuild, TestCategory, and TestEntity + * can each contribute to each value. They are assembled at the last + * moment by the ContributorAssembler (presently the TestEntity.) + */ + trait BuildContributor { + def javaFlags: List[String] + def scalacFlags: List[String] + def classpathPaths: List[Path] + def buildProperties: List[(String, Any)] + def buildEnvironment: Map[String, String] + } + + trait ContributorAssembler { + def contributors: List[BuildContributor] + def assemble[T](what: BuildContributor => List[T]): List[T] = contributors flatMap what + + /** !!! This will need work if we want to achieve real composability, + * but it can wait for the demand. + */ + def assembleScalacArgs(args: List[String]) = assemble(_.scalacFlags) ++ args + def assembleJavaArgs(args: List[String]) = assemble(_.javaFlags) ++ args + def assembleProperties() = assemble(_.buildProperties) + def assembleClasspaths(paths: List[Path]) = assemble(_.classpathPaths) ++ paths + def assembleEnvironment() = assemble(_.buildEnvironment.toList).toMap + + def createClasspathString() = ClassPath fromPaths (assembleClasspaths(Nil) : _*) + def createPropertyString() = assembleProperties() map { case (k, v) => "-D%s=%s".format(k, v.toString) } + } + + trait BuildContribution extends BuildContributor { + self: TestBuild => + + /** The base classpath and system properties. + * !!! TODO - this should adjust itself depending on the build + * being tested, because pack and quick at least need different jars. + */ + def classpathPaths = List[Path](library, compiler, partest, fjbg) ++ forkJoinPath + def buildProperties = List( + "scala.home" -> testBuildDir, + "partest.lib" -> library, // used in jvm/inner + "java.awt.headless" -> true, + "user.language" -> "en", + "user.country" -> "US", + "partest.debug" -> isDebug, + "partest.verbose" -> isVerbose + // Disabled because there are no natives tests. + // "java.library.path" -> srcLibDir + ) + def javaFlags: List[String] = toArgs(javaOpts) + def scalacFlags: List[String] = toArgs(scalacOpts) + + /** We put the build being tested's /bin directory in the front of the + * path so the scripts and such written to execute "scala" will use this + * build and not whatever happens to be on their path. + */ + private def modifiedPath = ClassPath.join(scalaBin.path, Properties.envOrElse("PATH", "")) + def buildEnvironment = Map("PATH" -> modifiedPath) + } + + trait CategoryContribution extends BuildContributor { + self: DirBasedCategory => + + /** Category-wide classpath additions placed in /lib. */ + private def libContents = root / "lib" ifDirectory (_.list.toList) + + def classpathPaths = libContents getOrElse Nil + def buildProperties = Nil + def javaFlags = Nil + def scalacFlags = Nil + def buildEnvironment = Map() + } + + trait TestContribution extends BuildContributor with ContributorAssembler { + self: TestEntity => + + def jarsInTestDir = location.walk collect { case f: File if f hasExtension "jar" => f } toList + + def contributors = List(build, category, self) + def javaFlags = safeArgs(javaOptsFile) + def scalacFlags = safeArgs(scalaOptsFile) + def classpathPaths = jarsInTestDir :+ outDir + def buildProperties = List( + "partest.output" -> outDir.toAbsolute, // used in jvm/inner + "partest.cwd" -> outDir.parent.toAbsolute // used in shootout tests + ) + def buildEnvironment = Map("JAVA_OPTS" -> fromArgs(assembleJavaArgs(Nil))) + } +} \ No newline at end of file diff --git a/src/partest-alternative/scala/tools/partest/Categories.scala b/src/partest-alternative/scala/tools/partest/Categories.scala new file mode 100644 index 0000000000..172cca74b4 --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/Categories.scala @@ -0,0 +1,70 @@ +/* __ *\ +** ________ ___ / / ___ Scala Parallel Testing ** +** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.tools +package partest + +import nsc.Settings +import nsc.io._ +import nsc.util.{ ClassPath } + +trait Categories { + self: Universe => + + trait TestCategory extends AbsTestCategory { + def kind: String + def startMessage: String = "Executing test group" + def testSequence: TestSequence + + class TestSettings(entity: TestEntity, error: String => Unit) extends Settings(error) { + def this(entity: TestEntity) = this(entity, Console println _) + + deprecation.value = false + encoding.value = "ISO-8859-1" + classpath.value = entity.testClasspath + outdir.value = entity.outDir.path + } + + def createSettings(entity: TestEntity): TestSettings = new TestSettings(entity) + def createTest(location: Path): TestEntity = + if (location.isFile) TestFile(this, location.toFile) + else if (location.isDirectory) TestDirectory(this, location.toDirectory) + else error("Failed to create test at '%s'" format location) + + /** Category test identification. + */ + def denotesTestFile(p: Path) = p.isFile && (p hasExtension "scala") + def denotesTestDir(p: Path) = p.isDirectory && !ignorePath(p) + def denotesTest(p: Path) = denotesTestDir(p) || denotesTestFile(p) + + /** This should verify that all necessary files are present. + * By default it delegates to denotesTest. + */ + def denotesValidTest(p: Path) = denotesTest(p) + } + + abstract class DirBasedCategory(val kind: String) extends TestCategory with CategoryContribution { + lazy val root = Directory(src / kind).normalize + def enumerate = root.list filter denotesTest map createTest toList + + /** Standard actions. These can be overridden either on the + * Category level or by individual tests. + */ + def compile: TestStep = (_: TestEntity).compile() + def checkFileRequired: TestStep = (_: TestEntity).checkFileRequired + def diff: TestStep = (_: TestEntity).diff() + def run: TestStep = (_: TestEntity).run() + def exec: TestStep = (_: TestEntity).exec() + + /** Combinators. + */ + def not(f: TestStep): TestStep = !f(_: TestEntity) + + override def toString = kind + } +} \ No newline at end of file diff --git a/src/partest-alternative/scala/tools/partest/Compilable.scala b/src/partest-alternative/scala/tools/partest/Compilable.scala new file mode 100644 index 0000000000..ddaa277842 --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/Compilable.scala @@ -0,0 +1,106 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + */ + +package scala.tools +package partest + +import scala.tools.nsc.io._ +import scala.tools.nsc.{ Global, Settings, CompilerCommand, FatalError } +import scala.tools.nsc.util.{ ClassPath } +import scala.tools.nsc.reporters.{ Reporter, ConsoleReporter } + +trait PartestCompilation { + self: Universe => + + trait CompileExecSupport extends ExecSupport { + self: TestEntity => + + def javacpArg = "-classpath " + testClasspath + def scalacpArg = "-usejavacp" + + /** Not used, requires tools.jar. + */ + // def javacInternal(args: List[String]) = { + // import com.sun.tools.javac.Main + // Main.compile(args.toArray, logWriter) + // } + + def javac(args: List[String]): Boolean = { + val allArgString = fromArgs(javacpArg :: javacOpts :: args) + + // javac -d outdir -classpath + val cmd = "%s -d %s %s".format(javacCmd, outDir, allArgString) + def traceMsg = + if (isVerbose) cmd + else "%s -d %s %s".format(tracePath(Path(javacCmd)), tracePath(outDir), fromArgs(args)) + + trace(traceMsg) + + isDryRun || execAndLog(cmd) + } + + def scalac(args: List[String]): Boolean = { + val allArgs = assembleScalacArgs(args) + val (global, files) = newGlobal(allArgs) + def nonFileArgs = if (isVerbose) global.settings.recreateArgs else assembleScalacArgs(Nil) + def traceArgs = fromArgs(nonFileArgs ++ (files map tracePath)) + def traceMsg = "scalac " + traceArgs + + trace(traceMsg) + isDryRun || global.partestCompile(files, true) + } + + /** Actually running the test, post compilation. + * Normally args will be List("Test", "jvm"), main class and arg to it. + */ + def runScala(args: List[String]): Boolean = { + val scalaRunnerClass = "scala.tools.nsc.MainGenericRunner" + + // java $JAVA_OPTS -classpath + val javaCmdAndOptions = javaCmd +: assembleJavaArgs(List(javacpArg)) + // MainGenericRunner -usejavacp Test jvm + val scalaCmdAndOptions = List(scalaRunnerClass, scalacpArg) ++ assembleScalacArgs(args) + // Assembled + val cmd = fromArgs(javaCmdAndOptions ++ createPropertyString() ++ scalaCmdAndOptions) + + def traceMsg = if (isVerbose) cmd else fromArgs(javaCmd :: args) + trace("runScala: " + traceMsg) + + isDryRun || execAndLog(cmd) + } + + def newReporter(settings: Settings) = new ConsoleReporter(settings, Console.in, logWriter) + + class PartestGlobal(settings: Settings, val creporter: ConsoleReporter) extends Global(settings, creporter) { + def partestCompile(files: List[String], printSummary: Boolean): Boolean = { + try { new Run compile files } + catch { + case FatalError(msg) => creporter.error(null, "fatal error: " + msg) + case ae: AssertionError => creporter.error(null, ""+ae) + case te: TypeError => creporter.error(null, ""+te) + case ex => + creporter.error(null, ""+ex) + throw ex + } + + if (printSummary) + creporter.printSummary + + creporter.flush() + !creporter.hasErrors + } + } + + def newGlobal(args: List[String]): (PartestGlobal, List[String]) = { + val settings = category createSettings self + val command = new CompilerCommand(args, settings) + val reporter = newReporter(settings) + + if (!command.ok) + debug("Error parsing arguments: '%s'".format(args mkString ", ")) + + (new PartestGlobal(command.settings, reporter), command.files) + } + } +} diff --git a/src/partest-alternative/scala/tools/partest/Config.scala b/src/partest-alternative/scala/tools/partest/Config.scala new file mode 100644 index 0000000000..288a3034e9 --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/Config.scala @@ -0,0 +1,129 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + */ + +package scala.tools +package partest + +import io._ +import nsc.io._ +import Properties._ + +trait Config { + universe: Universe => + + lazy val src = absolutize(srcDir).toDirectory + lazy val build = new TestBuild() + + def javaHomeEnv = envOrElse("JAVA_HOME", null) + def javaCmd = envOrElse("JAVACMD", "java") + def javacCmd = Option(javaHomeEnv) map (x => Path(x) / "bin" / "javac" path) getOrElse "javac" + + /** Values related to actors. The timeouts are in seconds. On a dry + * run we only allocate one worker so the output isn't interspersed. + */ + def workerTimeout = 3600 // 1 hour, probably overly generous + def numWorkers = if (isDryRun) 1 else propOrElse("partest.actors", "8").toInt + def expectedErrors = propOrElse("partest.errors", "0").toInt + def poolSize = (wrapAccessControl(propOrNone("actors.corePoolSize")) getOrElse "16").toInt + + def allScalaFiles = src.deepFiles filter (_ hasExtension "scala") + def allObjDirs = src.deepDirs filter (_ hasExtension "obj") + def allLogFiles = src.deepFiles filter (_ hasExtension "log") + def allClassFiles = src.deepFiles filter (_ hasExtension "class") + + class TestBuild() extends BuildContribution { + import nsc.util.ClassPath + + /** Scala core libs. + */ + val library = pathForComponent("library") + val compiler = pathForComponent("compiler") + val partest = pathForComponent("partest") + val scalap = pathForComponent("scalap", "%s.jar") + + /** Scala supplementary libs - these are not all needed for all build targets, + * and some of them are copied inside other jars in later targets. However quick + * for instance cannot be run without some of these. + */ + val fjbg = pathForLibrary("fjbg") + val msil = pathForLibrary("msil") + val forkjoin = pathForLibrary("forkjoin") + val scalacheck = pathForLibrary("scalacheck") + + /** Other interesting paths. + */ + val scalaBin = testBuildDir / "bin" + + /** A hack for now to get quick running. + */ + def needsForkJoin = { + val loader = nsc.util.ScalaClassLoader.fromURLs(List(library.toURL)) + val fjMarker = "scala.concurrent.forkjoin.ForkJoinTask" + val clazz = loader.tryToLoadClass(fjMarker) + + if (clazz.isDefined) debug("Loaded ForkJoinTask OK, don't need jar.") + else debug("Could not load ForkJoinTask, putting jar on classpath.") + + clazz.isEmpty + } + lazy val forkJoinPath: List[Path] = if (needsForkJoin) List(forkjoin) else Nil + + /** Internal **/ + private def repo = partestDir.parent.normalize + // XXX - is this needed? Where? + // + // private val pluginOptionString = "-Xplugin:" + // private def updatedPluginPath(options: String): String = { + // val (pluginArgs, rest) = toArgs(options) partition (_ startsWith pluginOptionString) + // // join all plugin paths as one classpath + // val pluginPaths = ClassPath.join(pluginArgs map (_ stripPrefix pluginOptionString): _*) + // // map all paths to absolute + // val newPath = ClassPath.map(pluginPaths, x => absolutize(x).path) + // // recreate option + // val pluginOption = if (newPath == "") None else Some(pluginOptionString + newPath) + // + // fromArgs(rest ::: pluginOption.toList) + // } + + private def pathForComponent(what: String, jarFormat: String = "scala-%s.jar"): Path = { + def asDir = testBuildDir / "classes" / what + def asJar = testBuildDir / "lib" / jarFormat.format(what) + + if (asDir.isDirectory) asDir + else if (asJar.isFile) asJar + else "" + } + private def pathForLibrary(what: String) = File(repo / "lib" / (what + ".jar")) + } + + def printConfigBanner() = { + debug("Java VM started with arguments: '%s'" format fromArgs(Process.javaVmArguments)) + debug("System Properties:\n" + util.allPropertiesString()) + + normal(configBanner()) + } + + /** Treat an access control failure as None. */ + private def wrapAccessControl[T](body: => Option[T]): Option[T] = + try body catch { case _: java.security.AccessControlException => None } + + private def configBanner() = { + val javaBin = Path(javaHome) / "bin" + val javaInfoString = "%s (build %s, %s)".format(javaVmName, javaVmVersion, javaVmInfo) + + List( + "Scala compiler classes in: " + testBuildDir, + "Scala version is: " + nsc.Properties.versionMsg, + "Scalac options are: " + universe.scalacOpts, + "Java binaries in: " + javaBin, + "Java runtime is: " + javaInfoString, + "Java runtime options: " + (Process.javaVmArguments mkString " "), + "Javac options are: " + universe.javacOpts, + "Java options are: " + universe.javaOpts, + "Source directory is: " + src, + "Selected categories: " + (selectedCategories mkString " "), + "" + ) mkString "\n" + } +} diff --git a/src/partest-alternative/scala/tools/partest/Dispatcher.scala b/src/partest-alternative/scala/tools/partest/Dispatcher.scala new file mode 100644 index 0000000000..2a9d99ab60 --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/Dispatcher.scala @@ -0,0 +1,162 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +package scala.tools +package partest + +import scala.tools.nsc.io._ +import scala.actors.{ Actor, TIMEOUT } +import scala.actors.Actor._ +import scala.collection.immutable +import scala.util.control.Exception.ultimately + +/** The machinery for concurrent execution of tests. Each Worker + * is given a bundle of tests, which it runs sequentially and then + * sends a report back to the dispatcher. + */ +trait Dispatcher { + partest: Universe => + + /** The public entry point. The given filter narrows down the list of + * tests to run. + */ + def runSelection(categories: List[TestCategory], filt: TestEntity => Boolean = _ => true): CombinedTestResults = { + // Setting scala.home informs tests where to obtain their jars. + setProp("scala.home", testBuildDir.path) + + val allTests = allCategories flatMap (_.enumerate) + val selected = allTests filter filt + val groups = selected groupBy (_.category) + val count = selected.size + + if (count == 0) return CombinedTestResults(0, 0, 0, Nil) + else if (count == allTests.size) verbose("Running all %d tests." format count) + else verbose("Running %d/%d tests: %s".format(count, allTests.size, toStringTrunc(selected map (_.label) mkString ", "))) + + allCategories collect { case x if groups contains x => runCategory(x, groups(x)) } reduceLeft (_ ++ _) + } + + private def parallelizeTests(tests: List[TestEntity]): immutable.Map[TestEntity, TestResult] = { + // propagate verbosity + if (isDebug) scala.actors.Debug.level = 3 + + // "If elected, I guarantee a slice of tests for every worker!" + val groups = tests grouped ((tests.size / numWorkers) + 1) toList + + // "Workers, line up for assignments!" + val workers = + for ((slice, workerNum) <- groups.zipWithIndex) yield { + returning(new Worker(workerNum)) { worker => + worker.start() + worker ! TestsToRun(slice) + } + } + + normal("Started %d workers with ~%d tests each.\n".format(groups.size, groups.head.size)) + + /** Listening for news from the proletariat. + */ + (workers map { w => + receiveWithin(workerTimeout * 1000) { + case ResultsOfRun(resultMap) => resultMap + case TIMEOUT => + warning("Worker %d timed out." format w.workerNum) + // mark all the worker's tests as having timed out - should be hard to miss + // immutable.Map[TestEntity, TestResult]() + groups(w.workerNum) map (x => (x -> new Timeout(x))) toMap + } + }) reduceLeft (_ ++ _) + } + + private def runCategory(category: TestCategory, tests: List[TestEntity]): CombinedTestResults = { + val kind = category.kind + normal("%s (%s tests in %s)\n".format(category.startMessage, tests.size, category)) + + val (milliSeconds, resultMap) = timed2(parallelizeTests(tests)) + val (passed, failed) = resultsToStatistics(resultMap mapValues (_.state)) + val failures = resultMap.values filterNot (_.passed) toList + + CombinedTestResults(passed, failed, milliSeconds, failures) + } + + /** A Worker is given a bundle of tests and runs them all sequentially. + */ + class Worker(val workerNum: Int) extends Actor { + def act() { + react { case TestsToRun(tests) => + val master = sender + runTests(tests)(results => master ! ResultsOfRun(results)) + } + } + + /** Runs the tests. Passes the result Map to onCompletion when done. + */ + private def runTests(tests: List[TestEntity])(onCompletion: immutable.Map[TestEntity, TestResult] => Unit) { + var results = new immutable.HashMap[TestEntity, TestResult] // maps tests to results + val numberOfTests = tests.size + val testIterator = tests.iterator + def processed = results.size + def isComplete = testIterator.isEmpty + + def atThreshold(num: Double) = { + require(num >= 0 && num <= 1.0) + ((processed - 1).toDouble / numberOfTests <= num) && (processed.toDouble / numberOfTests >= num) + } + + def extraMessage = { + // for now quiet for normal people + if (isVerbose || isTrace || isDebug) { + if (isComplete) "(#%d 100%%)" format workerNum + else if (isVerbose) "(#%d %d/%d)".format(workerNum, processed, numberOfTests) + else if (isTrace && atThreshold(0.5)) "(#%d 50%%)" format workerNum + else "" + } + else "" + } + + def countAndReport(result: TestResult) { + val TestResult(test, state) = result + // refuse to count an entity twice + if (results contains test) + return warning("Received duplicate result for %s: was %s, now %s".format(test, results(test), state)) + + // increment the counter for this result state + results += (test -> result) + + // show on screen + if (isDryRun) normal("\n") // blank line between dry run traces + else result show extraMessage + + // remove log if successful + if (result.passed) + test.deleteLog() + + // Respond to master if this Worker is complete + if (isComplete) + onCompletion(results) + } + + Actor.loopWhile(testIterator.hasNext) { + val parent = self + // pick a test and set some alarms + val test = testIterator.next + val alarmer = test startAlarms (parent ! new Timeout(test)) + + actor { + ultimately(alarmer.cancelAll()) { + // Calling isSuccess forces the lazy val "process" inside the test, running it. + val res = test.isSuccess + // Cancel the alarms and alert the media. + parent ! TestResult(test, res) + } + } + + react { + case x: TestResult => countAndReport(x) + } + } + } + } +} \ No newline at end of file diff --git a/src/partest-alternative/scala/tools/partest/Entities.scala b/src/partest-alternative/scala/tools/partest/Entities.scala new file mode 100644 index 0000000000..bea505b594 --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/Entities.scala @@ -0,0 +1,74 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +package scala.tools +package partest + +import nsc.io._ + +trait Entities { + self: Universe => + + abstract class TestEntity extends AbsTestEntity + with TestContribution + with TestHousekeeping + with TestAlarms + with EntityLogging + with CompilableTest + with ScriptableTest + with DiffableTest { + def location: Path + def category: TestCategory + + lazy val label = location.stripExtension + lazy val testClasspath = returning(createClasspathString())(x => vtrace("testClasspath: " + x)) + + /** Was this test successful? Calling this for the first time forces + * lazy val "process" which actually runs the test. + */ + def isSuccess = process + + /** Some standard files, which may or may not be present. + */ + def scalaOptsFile = withExtension("flags").toFile // opts to scalac + def javaOptsFile = withExtension("javaopts").toFile // opts to java (but not javac) + def commandFile = withExtension("cmds").toFile // sequence of commands to execute + def logFile = withExtension("log").toFile // collected output + + /** Some standard directories. + */ + def outDir = withExtension("obj").toDirectory // output dir, e.g. files/pos/t14.obj + def categoryDir = location.parent.normalize // category dir, e.g. files/pos/ + def sourcesDir = location ifDirectory (_.normalize) getOrElse categoryDir + + /** Standard arguments for run, exec, diff. + */ + def argumentsToRun = List("Test", "jvm") + def argumentsToExec = List(location.path) + + /** Using a .cmds file for a custom test sequence. + */ + def commandList = safeLines(commandFile) + def testSequence = + if (commandFile.isFile && commandList.nonEmpty) commandList map customTestStep + else category.testSequence + + def run() = runScala(argumentsToRun) + def exec() = runExec(argumentsToExec) + def diff() = runDiff() // checkFile, logFile + + /** The memoized result of the test run. + */ + private lazy val process = { + val outcome = runWrappers(testSequence.actions forall (f => f(this))) + + // an empty outcome means we've been interrupted and are shutting down. + outcome getOrElse false + } + } + + case class TestDirectory(category: TestCategory, location: Directory) extends TestEntity { } + case class TestFile(category: TestCategory, location: File) extends TestEntity { } +} diff --git a/src/partest-alternative/scala/tools/partest/Housekeeping.scala b/src/partest-alternative/scala/tools/partest/Housekeeping.scala new file mode 100644 index 0000000000..a624ca8adb --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/Housekeeping.scala @@ -0,0 +1,187 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + */ + +package scala.tools +package partest + +import scala.util.control.Exception.catching +import util._ +import nsc.io._ +import Process.runtime +import Properties._ + +/** An agglomeration of code which is low on thrills. Hopefully + * it operates so quietly in the background that you never have to + * look at this file. + */ +trait Housekeeping { + self: Universe => + + /** Orderly shutdown on ctrl-C. */ + @volatile private var _shuttingDown = false + protected def setShuttingDown() = { + /** Whatever we want to do as shutdown begins goes here. */ + if (!_shuttingDown) { + warning("Received shutdown signal, partest is cleaning up...\n") + _shuttingDown = true + } + } + def isShuttingDown = _shuttingDown + + /** Execute some code with a shutdown hook in place. This is + * motivated by the desire not to leave the filesystem full of + * junk when someone ctrl-Cs a test run. + */ + def withShutdownHook[T](hook: => Unit)(body: => T): Option[T] = + /** Java doesn't like it if you keep adding and removing shutdown + * hooks after shutdown has begun, so we trap the failure. + */ + catching(classOf[IllegalStateException]) opt { + val t = new Thread() { + override def run() = { + setShuttingDown() + hook + } + } + runtime addShutdownHook t + + try body + finally runtime removeShutdownHook t + } + + /** Search for a directory, possibly given only a name, by starting + * at the current dir and walking upward looking for it at each level. + */ + protected def searchForDir(name: String): Directory = { + val result = Path(name) ifDirectory (x => x.normalize) orElse { + val cwd = Directory.Current getOrElse error("user.dir property not set") + val dirs = cwd :: cwd.parents map (_ / name) + + Path onlyDirs dirs map (_.normalize) headOption + } + + result getOrElse error("Fatal: could not find directory '%s'" format name) + } + + /** Paths we ignore for most purposes. + */ + def ignorePath(x: Path) = { + (x.name startsWith ".") || + (x.isDirectory && ((x.name == "lib") || x.hasExtension("obj", "svn"))) + } + /** Make a possibly relative path absolute using partestDir as the base. + */ + def absolutize(path: String) = Path(path) toAbsoluteWithRoot partestDir + + /** Go on a deleting binge. + */ + def cleanupAll() { + if (isNoCleanup) + return + + val (dirCount, fileCount) = (cleanupObjDirs(), cleanupLogs() + cleanupJunk()) + if (dirCount + fileCount > 0) + normal("Cleaned up %d directories and %d files.\n".format(dirCount, fileCount)) + } + + def cleanupObjDirs() = countTrue(allObjDirs collect { case x if x.exists => x.deleteRecursively() }) + def cleanupJunk() = countTrue(allClassFiles collect { case x if x.exists => x.delete() }) + def cleanupLogs() = countTrue(allLogFiles collect { case x if x.exists => x.delete() }) + + /** Look through every file in the partest directory and ask around + * to make sure someone knows him. Complain about strangers. + */ + def validateAll() { + def denotesTest(p: Path) = allCategories exists (_ denotesTest p) + def isMSILcheck(p: Path) = p.name endsWith "-msil.check" + + def analyzeCategory(cat: DirBasedCategory) = { + val allTests = cat.enumerate + val otherPaths = cat.root walkFilter (x => !ignorePath(x)) filterNot (cat denotesTest _) filterNot isMSILcheck toList + val count = otherPaths.size + + println("Validating %d non-test paths in %s.".format(count, cat.kind)) + + for (path <- otherPaths) { + (allTests find (_ acknowledges path)) match { + case Some(test) => if (isVerbose) println(" OK: '%s' is claimed by '%s'".format(path, test.label)) + case _ => println(">> Unknown path '%s'" format path) + } + } + } + + allCategories collect { case x: DirBasedCategory => analyzeCategory(x) } + } + + trait TestHousekeeping { + self: TestEntity => + + /** Calculating derived files. Given a test like + * files/run/foo.scala or files/run/foo/ + * This creates paths like foo.check, foo.flags, etc. + */ + def withExtension(extension: String) = categoryDir / "%s.%s".format(label, extension) + + /** True for a path if this test acknowledges it belongs to this test. + * Overridden by some categories. + */ + def acknowledges(path: Path): Boolean = { + val loc = location.normalize + val knownPaths = List(scalaOptsFile, javaOptsFile, commandFile, logFile, checkFile) ++ jarsInTestDir + def isContainedSource = location.isDirectory && isJavaOrScala(path) && (path.normalize startsWith loc) + + (knownPaths exists (_ isSame path)) || isContainedSource + } + + /** This test "responds to" this String. This could mean anything -- it's a + * way of specifying ad-hoc collections of tests to exercise only a subset of tests. + * At present it looks for the given String in all the test sources. + */ + def respondsToString(str: String) = containsString(str) + def containsString(str: String) = { + debug("Checking %s for \"%s\"".format(sourceFiles mkString ", ", str)) + sourceFiles map safeSlurp exists (_ contains str) + } + + def possiblyTimed[T](body: => T): T = { + if (isStats) timed(recordTestTiming(label, _))(body) + else body + } + + private def prepareForTestRun() = { + // make sure we have a clean slate + deleteLog(force = true) + if (outDir.exists) + outDir.deleteRecursively() + + // recreate object dir + outDir createDirectory true + } + def deleteOutDir() = outDir.deleteRecursively() + def deleteShutdownHook() = { debug("Shutdown hook deleting " + outDir) ; deleteOutDir() } + + protected def runWrappers[T](body: => T): Option[T] = { + prepareForTestRun() + + withShutdownHook(deleteShutdownHook()) { + loggingOutAndErr { + val result = possiblyTimed { body } + if (!isNoCleanup) + deleteOutDir() + + result + } + } + } + + override def toString = location.path + override def equals(other: Any) = other match { + case x: TestEntity => location.normalize == x.location.normalize + case _ => false + } + override def hashCode = location.normalize.hashCode + } + + private def countTrue(f: => Iterator[Boolean]) = f filter (_ == true) length +} \ No newline at end of file diff --git a/src/partest-alternative/scala/tools/partest/Partest.scala b/src/partest-alternative/scala/tools/partest/Partest.scala new file mode 100644 index 0000000000..b3fe9a98ef --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/Partest.scala @@ -0,0 +1,81 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + */ + +package scala.tools +package partest + +import nsc.io._ +import nsc.util._ +import category.AllCategories + +/** Global object for a Partest run. It is completely configured by the list + * of arguments passed to the constructor (although there are a few properties + * and environment variables which can influence matters.) See PartestSpec.scala + * for the complete list. + */ +class Partest(args: List[String]) extends { + val parsed = PartestSpec(args: _*) +} with Universe with PartestSpec with cmd.Instance with AllCategories { + + if (parsed.propertyArgs.nonEmpty) + debug("Partest property args: " + fromArgs(parsed.propertyArgs)) + + debug("Partest created with args: " + fromArgs(args)) + + def helpMsg = PartestSpec.helpMsg + + // The abstract values from Universe. + lazy val testBuildDir = searchForDir(buildDir) + lazy val partestDir = searchForDir(rootDir) + lazy val allCategories = List(Pos, Neg, Run, Jvm, Res, Shootout, Scalap, Scalacheck, BuildManager, Script) + lazy val selectedCategories = if (isAllImplied) allCategories else specifiedCats + + def specifiedTests = parsed.residualArgs map (x => Path(x).normalize) + def specifiedKinds = testKinds filter (x => isSet(x) || (runSets contains x)) + def specifiedCats = specifiedKinds flatMap (x => allCategories find (_.kind == x)) + def isAllImplied = isAll || (specifiedTests.isEmpty && specifiedKinds.isEmpty) + + /** Assembles a filter based on command line options which restrict the test set + * --grep limits to only matching tests + * --failed limits to only recently failed tests (log file is present) + * -- limits to only the given tests and categories (but --all overrides) + * path/to/Test limits to only the given tests and categories + */ + lazy val filter = { + def indivFilter(test: TestEntity) = specifiedTests contains test.location.normalize + def categoryFilter(test: TestEntity) = specifiedCats contains test.category + def indivOrCat(test: TestEntity) = isAllImplied || indivFilter(test) || categoryFilter(test) // combines previous two + + def failedFilter(test: TestEntity) = !isFailed || (test.logFile exists) + def grepFilter(test: TestEntity) = grepExpr.isEmpty || (test containsString grepExpr.get) + def combinedFilter(x: TestEntity) = indivOrCat(x) && failedFilter(x) && grepFilter(x) // combines previous three + + combinedFilter _ + } + + def launchTestSuite() = { + def onTimeout() = { + warning("Partest test run timed out after " + timeout + " seconds.\n") + System.exit(-1) + } + val alarm = new Alarmer(AlarmerAction(timeout, () => onTimeout())) + + try runSelection(selectedCategories, filter) + finally alarm.cancelAll() + } +} + +object Partest { + def fromBuild(dir: String, args: String*): Partest = apply("--builddir" +: dir +: args: _*) + def apply(args: String*): Partest = new Partest(args.toList) + + // builds without partest jars won't actually work + def starr() = fromBuild("") + def locker() = fromBuild("build/locker") + def quick() = fromBuild("build/quick") + def pack() = fromBuild("build/pack") + def strap() = fromBuild("build/strap") + def dist() = fromBuild("dists/latest") +} + diff --git a/src/partest-alternative/scala/tools/partest/PartestSpec.scala b/src/partest-alternative/scala/tools/partest/PartestSpec.scala new file mode 100644 index 0000000000..c25119b3af --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/PartestSpec.scala @@ -0,0 +1,104 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools +package partest + +import nsc.io._ +import cmd._ + +/** This takes advantage of bits of scala goodness to fully define a command + * line program with a minimum of duplicated code. When the specification object + * is created, the vals are evaluated in order and each of them side effects + * a private accumulator. What emerges is a full list of the valid unary + * and binary arguments, as well as autogenerated help. + */ +trait PartestSpec extends Spec with Meta.StdOpts with Interpolation { + def referenceSpec = PartestSpec + def programInfo = Spec.Info("partest", "", "scala.tools.partest.Runner") + private val kind = new Spec.Accumulator[String]() + protected def testKinds = kind.get + + private implicit val tokenizeString = FromString.ArgumentsFromString // String => List[String] + + help(""" + |# Pro Tip! Instant bash completion: `partest --bash` (note backticks) + |Usage: partest [] [ ...] + | : a path to a test designator, typically a .scala file or a directory. + | Examples: files/pos/test1.scala, files/res/bug785 + | + | Test categories:""".stripMargin) + + val isAll = ("all" / "run all tests (default, unless no options given)" --?) + (kind("pos") / "Compile files that are expected to build" --?) + (kind("neg") / "Compile files that are expected to fail" --?) + (kind("run") / "Test JVM backend" --?) + (kind("jvm") / "Test JVM backend" --?) + (kind("res") / "Run resident compiler scenarii" --?) + (kind("buildmanager") / "Run Build Manager scenarii" --?) + (kind("scalacheck") / "Run Scalacheck tests" --?) + (kind("script") / "Run script files" --?) + (kind("shootout") / "Run shootout tests" --?) + (kind("scalap") / "Run scalap tests" --?) + + heading ("""Test "smart" categories:""") + val grepExpr = "grep" / "run all tests with a source file containing " --| + val isFailed = "failed" / "run all tests which failed on the last run" --? + + heading ("Specifying paths and additional flags, ~ means repository root:") + + val rootDir = "rootdir" / "path from ~ to partest" defaultTo "test" + val buildDir = "builddir" / "path from ~ to test build" defaultTo "build/pack" + val srcDir = "srcdir" / "path from --rootdir to sources" defaultTo "files" + val javaOpts = "javaopts" / "flags to java on all runs" defaultToEnv "JAVA_OPTS" + val javacOpts = "javacopts" / "flags to javac on all runs" defaultToEnv "JAVAC_OPTS" + val scalacOpts = "scalacopts" / "flags to scalac on all tests" defaultToEnv "SCALAC_OPTS" + + "pack" / "" expandTo ("--builddir", "build/pack") + "quick" / "" expandTo ("--builddir", "build/quick") + + heading ("Options influencing output:") + val isTrace = "trace" / "show the individual steps taken by each test" --? + val isShowDiff = "show-diff" / "show diff between log and check file" --? + val isShowLog = "show-log" / "show log on failures" --? + val isDryRun = "dry-run" / "do not run tests, only show their traces." --? + val isTerse = "terse" / "be less verbose (almost silent except for failures)" --? + val isVerbose = "verbose" / "be more verbose (additive with --trace)" --? + val isDebug = "debug" / "maximum debugging output" --? + val isAnsi = "ansi" / "print output in color" --? + + heading ("Other options:") + val timeout = "timeout" / "Overall timeout in seconds" defaultTo 7200 + val testWarning = "test-warning" / "Test warning in seconds" defaultTo 90 + val testTimeout = "test-timeout" / "Test timeout in seconds" defaultTo 900 + val isCleanup = "cleanup" / "delete all stale files and dirs before run" --? + val isNoCleanup = "nocleanup" / "do not delete any logfiles or object dirs" --? + val isStats = "stats" / "collect and print statistics about the tests" --? + val isValidate = "validate" / "examine test filesystem for inconsistencies" --? + val isUpdateCheck = "update-check" / "overwrite checkFile if diff fails" --? + + "version" / "print version" --> runAndExit(println(Properties.versionMsg)) + + // no help for anything below this line - secret options + // mostly intended for property configuration. + val runSets = ("runsets" --^) getOrElse Nil + val isNoAlarms = "noalarms" --? + val isInsideAnt = "is-in-ant" --? +} + +object PartestSpec extends PartestSpec with Property { + lazy val propMapper = new PropertyMapper(PartestSpec) { + override def isPassThrough(key: String) = key == "partest.options" + } + + type ThisCommandLine = PartestCommandLine + class PartestCommandLine(args: List[String]) extends SpecCommandLine(args) { + override def errorFn(msg: String) = printAndExit("Error: " + msg) + + def propertyArgs = PartestSpec.propertyArgs + } + + override def creator(args: List[String]): PartestCommandLine = new PartestCommandLine(args) +} diff --git a/src/partest-alternative/scala/tools/partest/Properties.scala b/src/partest-alternative/scala/tools/partest/Properties.scala new file mode 100644 index 0000000000..4eeb0359ec --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/Properties.scala @@ -0,0 +1,18 @@ +/* __ *\ +** ________ ___ / / ___ Scala Parallel Testing ** +** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.tools +package partest + +/** Loads partest.properties from the jar. */ +object Properties extends scala.util.PropertiesTrait { + protected def propCategory = "partest" + protected def pickJarBasedOn = classOf[Application] +} diff --git a/src/partest-alternative/scala/tools/partest/Results.scala b/src/partest-alternative/scala/tools/partest/Results.scala new file mode 100644 index 0000000000..5d0e300136 --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/Results.scala @@ -0,0 +1,121 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + */ + +package scala.tools +package partest + +import scala.collection.immutable + +trait Results { + self: Universe => + + /** A collection of tests for a Worker. + */ + case class TestsToRun(entities: List[TestEntity]) + + /** The response from a Worker who has been given TestsToRun. + */ + case class ResultsOfRun(results: immutable.Map[TestEntity, TestResult]) + + /** The result of a single test. (0: OK, 1: FAILED, 2: TIMEOUT) + */ + sealed abstract class TestResult(val state: Int, val description: String) { + def entity: TestEntity + + def passed = state == 0 + def colorize(s: String): String + def show(msg: String) = + if (!isShuttingDown) + showResult(colorize(description), msg) + + private def outputPrefix = if (isInsideAnt) "" else markNormal("partest: ") + private def name = src relativize entity.location // e.g. "neg/test.scala" + private def showResult(status: String, extraMsg: String) = + normal(outputPrefix + "[...]/%-40s [%s] %s\n".format(name, status, extraMsg)) + + override def equals(other: Any) = other match { + case x: TestResult => entity == x.entity + case _ => false + } + override def hashCode = entity.hashCode + override def toString = "%s [%s]".format(entity, description) + } + + class Success(val entity: TestEntity) extends TestResult(0, " OK ") { + def colorize(s: String) = markSuccess(s) + override def show(msg: String) = if (!isTerse) super.show(msg) + } + class Failure(val entity: TestEntity) extends TestResult(1, " FAILED ") { + def colorize(s: String) = markFailure(s) + + override def show(msg: String) = { + super.show(msg) + + if (isShowDiff || isTrace) + normal(entity.diffOutput) + + if (isShowLog || isTrace) + normal(toStringTrunc(entity.failureMessage(), 1600)) + } + override def toString = List(super.toString, toStringTrunc(entity.failureMessage(), 400)) mkString "\n" + } + class Timeout(val entity: TestEntity) extends TestResult(2, "TIME OUT") { + def colorize(s: String) = markFailure(s) + } + + object TestResult { + def apply(entity: TestEntity, success: Boolean) = + if (success) new Success(entity) + else new Failure(entity) + + def apply(entity: TestEntity, state: Int) = state match { + case 0 => new Success(entity) + case 1 => new Failure(entity) + case 2 => new Timeout(entity) + } + def unapply(x: Any) = x match { + case x: TestResult => Some((x.entity, x.state)) + case _ => None + } + } + + /** The combined results of any number of tests. + */ + case class CombinedTestResults( + passed: Int, + failed: Int, + elapsedMilliseconds: Long, + failures: List[TestResult] + ) { + // housekeeping + val elapsedSecs = elapsedMilliseconds / 1000 + val elapsedMins = elapsedSecs / 60 + val elapsedHrs = elapsedMins / 60 + val dispMins = elapsedMins - elapsedHrs * 60 + val dispSecs = elapsedSecs - elapsedMins * 60 + + def total = passed + failed + def hasFailures = failed > 0 + def exitCode = if (expectedErrors == failed) 0 else 1 + + def ++(x: CombinedTestResults) = CombinedTestResults( + passed + x.passed, + failed + x.failed, + elapsedMilliseconds + x.elapsedMilliseconds, + failures ::: x.failures + ) + + def elapsedString = "%02d:%02d:%02d".format(elapsedHrs, dispMins, dispSecs) + def failuresString = { + if (failures.isEmpty) "" + else "Summary of failures:" :: failures mkString ("\n", "\n", "") + } + + override def toString = + if (total == 0) "There were no tests to run." + else if (isDryRun) "%d tests would be run." format total + else if (hasFailures) "%d of %d tests failed (elapsed time: %s)".format(failed, total, elapsedString) + failuresString + else "All %d tests were successful (elapsed time: %s)".format(total, elapsedString) + } +} \ No newline at end of file diff --git a/src/partest-alternative/scala/tools/partest/Runner.scala b/src/partest-alternative/scala/tools/partest/Runner.scala new file mode 100644 index 0000000000..1a28e60896 --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/Runner.scala @@ -0,0 +1,36 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +package scala.tools +package partest + +import nsc.io._ + +object Runner { + def main(args: Array[String]) { + val runner = Partest(args: _*) + import runner._ + + if (args.isEmpty) return println(helpMsg) + if (isValidate) return validateAll() + + printConfigBanner() + + if (isCleanup) + cleanupAll() + + val result = launchTestSuite() + val exitCode = result.exitCode + val message = "\n" + result + "\n" + + if (exitCode == 0) success(message) + else failure(message) + + if (isStats) + showTestStatistics() + + System exit exitCode + } +} diff --git a/src/partest-alternative/scala/tools/partest/Statistics.scala b/src/partest-alternative/scala/tools/partest/Statistics.scala new file mode 100644 index 0000000000..2ea3c6e8f0 --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/Statistics.scala @@ -0,0 +1,46 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +package scala.tools +package partest + +import scala.collection.mutable.HashMap + +trait Statistics { + /** Only collected when --stats is given. */ + lazy val testStatistics = new HashMap[String, Long] + + /** Given function and block of code, evaluates code block, + * calls function with milliseconds elapsed, and returns block result. + */ + def timed[T](f: Long => Unit)(body: => T): T = { + val start = System.currentTimeMillis + val result = body + val end = System.currentTimeMillis + + f(end - start) + result + } + /** Times body and returns both values. + */ + def timed2[T](body: => T): (Long, T) = { + var milliSeconds = 0L + val result = timed(x => milliSeconds = x)(body) + + (milliSeconds, result) + } + + def resultsToStatistics(results: Iterable[(_, Int)]): (Int, Int) = + (results partition (_._2 == 0)) match { + case (winners, losers) => (winners.size, losers.size) + } + + def recordTestTiming(name: String, milliseconds: Long) = + synchronized { testStatistics(name) = milliseconds } + + def showTestStatistics() { + testStatistics.toList sortBy (-_._2) foreach { case (k, v) => println("%s: %.2f seconds".format(k, (v.toDouble / 1000))) } + } +} diff --git a/src/partest-alternative/scala/tools/partest/Universe.scala b/src/partest-alternative/scala/tools/partest/Universe.scala new file mode 100644 index 0000000000..942fc1a8be --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/Universe.scala @@ -0,0 +1,96 @@ +/* __ *\ +** ________ ___ / / ___ Scala Parallel Testing ** +** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.tools +package partest + +import nsc.io._ +import category.AllCategories +import io.Logging + +/** The high level view of the partest infrastructure. + */ +abstract class Universe + extends Entities + with BuildContributors + with Logging + with Dispatcher + with Statistics + with Housekeeping + with Results + with PartestCompilation + with PartestSpec + with Config + with Alarms + with Actions + with Categories { + + /** The abstract values from which all else is derived. */ + def partestDir: Directory + def testBuildDir: Directory + def allCategories: List[TestCategory] + def selectedCategories: List[TestCategory] + + /** Some plausibly abstract types. */ + type TestBuild <: BuildContributor // e.g. quick, pack + type TestCategory <: AbsTestCategory // e.g. pos, neg, run + type TestEntity <: AbsTestEntity // e.g. files/pos/test25.scala + type TestSequence <: AbsTestSequence // e.g. compile, run, diff + + /** Although TestStep isn't much more than Function1 right now, + * it exists this way so it can become more capable. + */ + implicit def f1ToTestStep(f: TestEntity => Boolean): TestStep = + new TestStep { def apply(test: TestEntity) = f(test) } + + abstract class TestStep extends (TestEntity => Boolean) { + def apply(test: TestEntity): Boolean + } + + /** An umbrella category of tests, such as "pos" or "run". + */ + trait AbsTestCategory extends BuildContributor { + type TestSettings + + def kind: String + def testSequence: TestSequence + def denotesTest(location: Path): Boolean + + def createTest(location: Path): TestEntity + def createSettings(entity: TestEntity): TestSettings + def enumerate: List[TestEntity] + } + + /** A single test. It may involve multiple files, but only a + * single path is used to designate it. + */ + trait AbsTestEntity extends BuildContributor { + def category: TestCategory + def location: Path + def onException(x: Throwable): Unit + def testClasspath: String + + /** Most tests will use the sequence defined by the category, + * but the test can override and define a custom sequence. + */ + def testSequence: TestSequence + + /** True if this test recognizes the given path as a piece of it. + * For validation purposes. + */ + def acknowledges(path: Path): Boolean + } + + /** Every TestEntity is partly characterized by a series of actions + * which are applied to the TestEntity in the given order. The test + * passes if all those actions return true, fails otherwise. + */ + trait AbsTestSequence { + def actions: List[TestStep] + } +} \ No newline at end of file diff --git a/src/partest-alternative/scala/tools/partest/ant/JavaTask.scala b/src/partest-alternative/scala/tools/partest/ant/JavaTask.scala new file mode 100644 index 0000000000..6740554dd8 --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/ant/JavaTask.scala @@ -0,0 +1,57 @@ +/* __ *\ +** ________ ___ / / ___ Scala Parallel Testing ** +** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.tools +package partest +package ant + +import org.apache.tools.ant.Task +import org.apache.tools.ant.taskdefs.Java +import org.apache.tools.ant.types.Environment + +import scala.tools.nsc.io._ +import scala.tools.nsc.util.ClassPath +import cmd.Spec._ + +class JavaTask extends Java { + override def getTaskName() = "partest" + private val scalaRunnerClass = "scala.tools.nsc.MainGenericRunner" + private val partestRunnerClass = "scala.tools.partest.Runner" + def defaultJvmArgs = "-Xms64M -Xmx768M -Xss768K -XX:MaxPermSize=96M" + + protected def rootDir = prop("partest.rootdir") getOrElse (baseDir / "test").path + protected def partestJVMArgs = prop("partest.jvm.args") getOrElse defaultJvmArgs + protected def runnerArgs = List("-usejavacp", partestRunnerClass, "--javaopts", partestJVMArgs) + + private def baseDir = Directory(getProject.getBaseDir) + private def prop(s: String) = Option(getProject getProperty s) + private def jvmline(s: String) = returning(createJvmarg())(_ setLine s) + private def addArg(s: String) = returning(createArg())(_ setValue s) + + private def newKeyValue(key: String, value: String) = + returning(new Environment.Variable)(x => { x setKey key ; x setValue value }) + + def setDefaults() { + setFork(true) + setFailonerror(true) + getProject.setSystemProperties() + setClassname(scalaRunnerClass) + addSysproperty(newKeyValue("partest.is-in-ant", "true")) + jvmline(partestJVMArgs) + runnerArgs foreach addArg + + // do we want basedir or rootDir to be the cwd? + // setDir(Path(rootDir).jfile) + } + + override def init() = { + super.init() + setDefaults() + } +} + 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 @@ + + + diff --git a/src/partest-alternative/scala/tools/partest/category/AllCategories.scala b/src/partest-alternative/scala/tools/partest/category/AllCategories.scala new file mode 100644 index 0000000000..953f80324b --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/category/AllCategories.scala @@ -0,0 +1,20 @@ +/* __ *\ +** ________ ___ / / ___ Scala Parallel Testing ** +** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.tools +package partest +package category + +trait AllCategories extends Compiler with Analysis with Runner { + self: Universe => + + object Pos extends DirBasedCategory("pos") { lazy val testSequence: TestSequence = List(compile) } + object Neg extends DirBasedCategory("neg") { lazy val testSequence: TestSequence = List(checkFileRequired, not(compile), diff) } + object Run extends DirBasedCategory("run") { lazy val testSequence: TestSequence = List(compile, run, diff) } + object Jvm extends DirBasedCategory("jvm") { lazy val testSequence: TestSequence = List(compile, run, diff) } +} diff --git a/src/partest-alternative/scala/tools/partest/category/Analysis.scala b/src/partest-alternative/scala/tools/partest/category/Analysis.scala new file mode 100644 index 0000000000..2c6c208ee5 --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/category/Analysis.scala @@ -0,0 +1,64 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + */ + +package scala.tools +package partest +package category + +import java.lang.{ ClassLoader => JavaClassLoader } +import java.net.URL +import nsc.util.ScalaClassLoader +import nsc.io._ + +class PartestClassLoader(urls: Array[URL], parent: JavaClassLoader) extends ScalaClassLoader.URLClassLoader(urls, parent) { + def this(urls: Array[URL]) = this(urls, null) + def bytes(path: String) = findBytesForClassName(path) + def singleton(path: String) = tryToInitializeClass(path).get getField "MODULE$" get null + + /** Calls a method in an object via reflection. + */ + def apply[T](className: String, methodName: String)(args: Any*): T = { + def fail = error("Reflection failed on %s.%s".format(className, methodName)) + val clazz = tryToLoadClass(className) getOrElse fail + val obj = singleton(className) + val m = clazz.getMethods find (x => x.getName == methodName && x.getParameterTypes.size == args.size) getOrElse fail + + m.invoke(obj, args map (_.asInstanceOf[AnyRef]): _*).asInstanceOf[T] + } +} + +trait Analysis { + self: Universe => + + object Scalap extends DirBasedCategory("scalap") { + val testSequence: TestSequence = List(checkFileRequired, compile, run, diff) + override def denotesTest(p: Path) = p.isDirectory && (p.toDirectory.files exists (_.name == "result.test")) + override def createTest(location: Path) = new ScalapTest(location) + + class ScalapTest(val location: Path) extends TestEntity { + val category = Scalap + val scalapMain = "scala.tools.scalap.Main$" + val scalapMethod = "decompileScala" + + override def classpathPaths = super.classpathPaths :+ build.scalap + override def checkFile = File(location / "result.test") + + private def runnerURLs = build.classpathPaths ::: classpathPaths map (_.toURL) + private def createClassLoader = new PartestClassLoader(runnerURLs.toArray, this.getClass.getClassLoader) + + val isPackageObject = containsString("package object") + val suffix = if (isPackageObject) ".package" else "" + val className = location.name.capitalize + suffix + + override def run() = loggingResult { + def loader = createClassLoader + def bytes = loader.bytes(className) + + trace("scalap %s".format(className)) + if (isDryRun) "" + else loader[String](scalapMain, scalapMethod)(bytes, isPackageObject) + } + } + } +} diff --git a/src/partest-alternative/scala/tools/partest/category/Compiler.scala b/src/partest-alternative/scala/tools/partest/category/Compiler.scala new file mode 100644 index 0000000000..49775d5031 --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/category/Compiler.scala @@ -0,0 +1,140 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + */ + +package scala.tools +package partest +package category + +import nsc.io._ +import nsc.reporters._ +import nsc.{ Settings, CompilerCommand } +import scala.tools.nsc.interactive.RefinedBuildManager +import util.copyPath + +trait Compiler { + self: Universe => + + /** Resident Compiler. + * $SCALAC -d dir.obj -Xresident -sourcepath . "$@" + */ + object Res extends DirBasedCategory("res") { + lazy val testSequence: TestSequence = List(checkFileRequired, compile, diff) + + override def denotesTest(p: Path) = p.isDirectory && resFile(p).isFile + override def createTest(location: Path) = new ResidentTest(location.toDirectory) + + override def createSettings(entity: TestEntity): TestSettings = + returning(super.createSettings(entity)) { settings => + settings.resident.value = true + settings.sourcepath.value = entity.sourcesDir.path + } + + class ResidentTest(val location: Directory) extends TestEntity { + val category = Res + override def sourcesDir = categoryDir + + override def acknowledges(p: Path) = + super.acknowledges(p) || (resFile(location) isSame p) + + private def residentCompilerCommands = safeLines(resFile(location)) + private def compileResident(global: PartestGlobal, lines: List[String]) = { + def printPrompt = global inform "nsc> " + val results = + lines map { line => + printPrompt + trace("compile " + line) + isDryRun || global.partestCompile(toArgs(line) map (categoryDir / _ path), false) + } + + printPrompt + + /** Note - some res tests are really "neg" style tests, so we can't + * use the return value of the compile. The diff catches failures. + */ + true // results forall (_ == true) + } + + override def compile() = compileResident(newGlobal(Nil)._1, residentCompilerCommands) + } + private[Res] def resFile(p: Path) = p.toFile addExtension "res" + } + + object BuildManager extends DirBasedCategory("buildmanager") { + lazy val testSequence: TestSequence = List(checkFileRequired, compile, diff) + override def denotesTest(p: Path) = p.isDirectory && testFile(p).isFile + override def createTest(location: Path) = new BuildManagerTest(location.toDirectory) + + override def createSettings(entity: TestEntity): TestSettings = + returning[TestSettings](super.createSettings(entity)) { settings => + settings.Ybuildmanagerdebug.value = true + settings.sourcepath.value = entity.sourcesDir.path + } + + class PartestBuildManager(settings: Settings, val reporter: ConsoleReporter) extends RefinedBuildManager(settings) { + def errorFn(msg: String) = Console println msg + + override protected def newCompiler(newSettings: Settings) = + new BuilderGlobal(newSettings, reporter) + + private def filesToSet(pre: String, fs: List[String]): Set[AbstractFile] = + fs flatMap (s => Option(AbstractFile getFile (Path(settings.sourcepath.value) / s path))) toSet + + def buildManagerCompile(line: String): Boolean = { + val prompt = "builder > " + reporter printMessage (prompt + line) + val command = new CompilerCommand(toArgs(line), settings) + val files = filesToSet(settings.sourcepath.value, command.files) + + update(files, Set.empty) + true + } + } + + private[BuildManager] def testFile(p: Path) = (p / p.name addExtension "test").toFile + + class BuildManagerTest(val location: Directory) extends TestEntity { + val category = BuildManager + + override def sourcesDir = outDir + override def sourceFiles = Path onlyFiles (location walkFilter (_ != changesDir) filter isJavaOrScala toList) + override def checkFile = File(location / location.name addExtension "check") + + override def acknowledges(p: Path) = super.acknowledges(p) || (p isSame testFile(location)) + + def buildManagerCommands = safeLines(testFile(location)) + def changesDir = Directory(location / (location.name + ".changes")) + + override def compile() = { + val settings = createSettings(this) + val pbm = new PartestBuildManager(settings, newReporter(settings)) + + // copy files + for (source <- sourceFiles) { + val target = outDir / (location.normalize relativize source) + copyPath(source, target.toFile) + } + + def runUpdate(line: String) = { + val Array(srcName, replacement) = line split "=>" + copyPath(File(changesDir / replacement), File(outDir / srcName)) + } + + def sendCommand(line: String): Boolean = { + val compileRegex = """^>>compile (.*)$""".r + val updateRegex = """^>>update\s+(.*)""".r + trace("send: " + (line drop 2)) + + isDryRun || (line match { + case compileRegex(xs) => pbm.buildManagerCompile(xs) + case updateRegex(line) => runUpdate(line) + }) + } + + // send each line to the build manager + buildManagerCommands forall sendCommand + } + } + } +} + diff --git a/src/partest-alternative/scala/tools/partest/category/Runner.scala b/src/partest-alternative/scala/tools/partest/category/Runner.scala new file mode 100644 index 0000000000..10bf5794a9 --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/category/Runner.scala @@ -0,0 +1,108 @@ +/* __ *\ +** ________ ___ / / ___ Scala Parallel Testing ** +** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.tools +package partest +package category + +import nsc.io._ + +trait Runner { + self: Universe => + + /** Shootout. + */ + object Shootout extends DirBasedCategory("shootout") { + lazy val testSequence: TestSequence = List(compile, run, diff) + + override def denotesTest(p: Path) = isScala(p) && runner(p).isFile + override def createTest(location: Path) = new ShootoutTest(location.toFile) + + class ShootoutTest(val location: File) extends TestEntity { + val category = Shootout + // The files in shootout are very free form, so acknowledge anything close. + override def acknowledges(p: Path) = + (p.parent.normalize isSame Shootout.root) && (p.name startsWith label) + + private def generated = File(outDir / "test.scala") + private def runnerFile = runner(location) + override def sourceFiles = List(generated) + + override def compile() = { + trace("generate %s from %s, %s".format(tracePath(generated), tracePath(location), tracePath(runnerFile))) + // generate source file (even on dry run, we need the path) + generated.writeAll(location.slurp(), runnerFile.slurp()) + + // compile generated file + super.compile() + } + } + + private[Shootout] def runner(p: Path) = p addExtension "runner" toFile + } + + object Scalacheck extends DirBasedCategory("scalacheck") { + lazy val testSequence: TestSequence = List(compile, run) + override def createTest(location: Path) = new ScalacheckTest(location) + + class ScalacheckTest(val location: Path) extends TestEntity { + val category = Scalacheck + + import build.{ scalacheck, forkjoin } + import org.scalacheck.Properties + import org.scalacheck.Test.{ checkProperties, defaultParams, Result } + + override def classpathPaths = super.classpathPaths ::: List(scalacheck, forkjoin) + private def arrayURLs = Array(scalacheck, outDir) map (_.toURL) + + /** For reasons I'm not entirely clear on, I've written all this + * to avoid a source dependency on scalacheck. + */ + class ScalacheckClassLoader extends PartestClassLoader(arrayURLs, this.getClass.getClassLoader) { + type ScalacheckResult = { def passed: Boolean } + + def propCallback(name: String, passed: Int, discarded: Int): Unit = () + def testCallback(name: String, result: AnyRef): Unit = () + + val test = singleton("Test$") + val params = apply[AnyRef]("org.scalacheck.Test$", "defaultParams")() + val result = apply[Seq[(String, AnyRef)]]("org.scalacheck.Test$", "checkProperties")(test, params, propCallback _, testCallback _) + + def allResults() = + for ((prop, res) <- result) yield { + ScalacheckTest.this.trace("%s: %s".format(prop, res)) + res.asInstanceOf[ScalacheckResult].passed + } + + def check() = allResults forall (_ == true) + } + + override def run() = { + trace("scalacheck runs via classloader with: %s".format(arrayURLs mkString ", ")) + isDryRun || (new ScalacheckClassLoader check) + } + } + } + + object Script extends DirBasedCategory("script") { + val testSequence: TestSequence = List(exec, diff) + override def createTest(location: Path) = new ScriptTest(location) + + class ScriptTest(val location: Path) extends TestEntity { + val category = Script + val scriptFile = if (location.isDirectory) location / (label + ".scala") else location + val argsFile = withExtension("args").toFile + def batFile = scriptFile changeExtension "bat" + def script = if (Properties.isWin) batFile else scriptFile + + override def acknowledges(p: Path) = super.acknowledges(p) || (List(argsFile, batFile) exists (_ isSame p)) + override def execCwd = Some(sourcesDir) + override def argumentsToExec = script.path :: safeArgs(argsFile) + } + } +} \ No newline at end of file diff --git a/src/partest-alternative/scala/tools/partest/io/ANSIWriter.scala b/src/partest-alternative/scala/tools/partest/io/ANSIWriter.scala new file mode 100644 index 0000000000..0ddcd97a5f --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/io/ANSIWriter.scala @@ -0,0 +1,58 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +package scala.tools +package partest +package io + +import java.io.{ Writer, PrintWriter, OutputStream, OutputStreamWriter } + +object ANSIWriter { + val NONE = 0 + val SOME = 1 + val MANY = 2 + + def apply(isAnsi: Boolean) = if (isAnsi) MANY else NONE +} +import ANSIWriter._ + +class ANSIWriter(writer: Writer) extends PrintWriter(writer, true) { + def this(out: OutputStream) = this(new OutputStreamWriter(out)) + def colorful: Int = NONE + + protected val manyColors = List( + Console.BOLD + Console.BLACK, + Console.BOLD + Console.GREEN, + Console.BOLD + Console.RED, + Console.BOLD + Console.YELLOW, + Console.RESET + ) + protected val someColors = List( + Console.BOLD + Console.BLACK, + Console.RESET, + Console.BOLD + Console.BLACK, + Console.BOLD + Console.BLACK, + Console.RESET + ) + protected val noColors = List("", "", "", "", "") + + lazy val List(_outline, _success, _failure, _warning, _default) = colorful match { + case NONE => noColors + case SOME => someColors + case MANY => manyColors + case _ => noColors + } + + private def wrprint(msg: String): Unit = synchronized { + print(msg) + flush() + } + + def outline(msg: String) = wrprint(_outline + msg + _default) + def success(msg: String) = wrprint(_success + msg + _default) + def failure(msg: String) = wrprint(_failure + msg + _default) + def warning(msg: String) = wrprint(_warning + msg + _default) + def normal(msg: String) = wrprint(_default + msg) +} diff --git a/src/partest-alternative/scala/tools/partest/io/Diff.java b/src/partest-alternative/scala/tools/partest/io/Diff.java new file mode 100644 index 0000000000..c7a3d42f30 --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/io/Diff.java @@ -0,0 +1,874 @@ +// $Id$ + +package scala.tools.partest.io; + +import java.util.Hashtable; + +/** A class to compare IndexedSeqs of objects. The result of comparison + is a list of change 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 equals + and hashcode methods for the objects compared. +

+ The basic algorithm is described in:
+ "An O(ND) Difference Algorithm and its Variations", Eugene Myers, + Algorithmica Vol. 1 No. 2, 1986, p 251. +

+ 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. +

+ 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. +

+ 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. +

+ 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 Diff { + + /** Prepare to find differences between two arrays. Each element of + the arrays is translated to an "equivalence number" based on + the result of equals. 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. +

+ 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-alternative/scala/tools/partest/io/DiffPrint.java b/src/partest-alternative/scala/tools/partest/io/DiffPrint.java new file mode 100644 index 0000000000..2b2ad93ec7 --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/io/DiffPrint.java @@ -0,0 +1,607 @@ +// $Id$ + +package scala.tools.partest.io; + +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 Diff. + @see bmsi.util.Diff + @author Stuart D. Gathman + Copyright (C) 2000 Business Management Systems, Inc. +

+ 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. +

+ 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. +

+ 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 + print_hunk 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 ed. + The edit script must be generated with the reverse option to + be useful as actual ed 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-alternative/scala/tools/partest/io/JUnitReport.scala b/src/partest-alternative/scala/tools/partest/io/JUnitReport.scala new file mode 100644 index 0000000000..63ae200020 --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/io/JUnitReport.scala @@ -0,0 +1,38 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + */ + +package scala.tools +package partest +package io + +/** This is disabled for the moment but I can fix it up if anyone + * is using it. + */ +class JUnitReport { + // create JUnit Report xml files if directory was specified + // def junitReport(dir: Directory) = { + // dir.mkdir() + // val report = testReport(set.kind, results, succs, fails) + // XML.save("%s/%s.xml".format(d.toAbsolute.path, set.kind), report) + // } + + // def oneResult(res: (TestEntity, Int)) = + // { + // res._2 match { + // case 0 => scala.xml.NodeSeq.Empty + // case 1 => + // case 2 => + // } + // } + // + // def testReport(kind: String, results: Iterable[(TestEntity, Int)], succs: Int, fails: Int) = { + // + // + // { + // results.map(oneResult(_)) + // } + // + // } + // +} \ No newline at end of file diff --git a/src/partest-alternative/scala/tools/partest/io/Logging.scala b/src/partest-alternative/scala/tools/partest/io/Logging.scala new file mode 100644 index 0000000000..52239ffb2c --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/io/Logging.scala @@ -0,0 +1,137 @@ +package scala.tools +package partest +package io + +import java.io.{ StringWriter, PrintWriter, Writer } +import scala.tools.nsc.io._ +import scala.util.control.ControlThrowable + +trait Logging { + universe: Universe => + + class PartestANSIWriter extends ANSIWriter(Console.out) { + override def colorful: Int = ANSIWriter(universe.isAnsi) + private def printIf(cond: Boolean, msg: String) = + if (cond) { outline("debug: ") ; println(msg) } + + val verbose = printIf(isVerbose || isDebug, _: String) + val debug = printIf(isDebug, _: String) + } + + lazy val NestUI = new PartestANSIWriter() + + import NestUI.{ _outline, _success, _failure, _warning, _default } + + def markOutline(msg: String) = _outline + msg + _default + def markSuccess(msg: String) = _success + msg + _default + def markFailure(msg: String) = _failure + msg + _default + def markWarning(msg: String) = _warning + msg + _default + def markNormal(msg: String) = _default + msg + + def outline(msg: String) = NestUI outline msg + def success(msg: String) = NestUI success msg + def failure(msg: String) = NestUI failure msg + def warning(msg: String) = NestUI warning msg + def normal(msg: String) = NestUI normal msg + + def verbose(msg: String) = NestUI verbose msg + def debug(msg: String) = NestUI debug msg + + trait EntityLogging { + self: TestEntity => + + lazy val logWriter = new LogWriter(logFile) + + /** Redirect stdout and stderr to logFile, run body, return result. + */ + def loggingOutAndErr[T](body: => T): T = { + val log = logFile.printStream(append = true) + + try Console.withOut(log) { + Console.withErr(log) { + body + } + } + finally log.close() + } + + /** What to print in a failure summary. + */ + def failureMessage() = if (diffOutput != "") diffOutput else safeSlurp(logFile) + + /** For tracing. Outputs a line describing the next action. tracePath + * is a path wrapper which prints name or full path depending on verbosity. + */ + def trace(msg: String) = if (isTrace || isDryRun) System.err.println(">> [%s] %s".format(label, msg)) + + def tracePath(path: Path): String = if (isVerbose) path.path else path.name + def tracePath(path: String): String = tracePath(Path(path)) + + /** v == verbose. + */ + def vtrace(msg: String) = if (isVerbose) trace(msg) + + /** Run body, writes result to logFile. Any throwable is + * caught, stringified, and written to the log. + */ + def loggingResult(body: => String) = + try returning(true)(_ => logFile writeAll body) + catch { + case x: ControlThrowable => throw x + case x: InterruptedException => debug(this + " received interrupt, failing.\n") ; false + case x: Throwable => logException(x) + } + + def throwableToString(x: Throwable): String = { + val w = new StringWriter + x.printStackTrace(new PrintWriter(w)) + w.toString + } + + def warnAndLog(str: String) = { + warning(toStringTrunc(str, 800)) + logWriter append str + } + + def warnAndLogException(msg: String, ex: Throwable) = + warnAndLog(msg + throwableToString(ex)) + + def deleteLog(force: Boolean = false) = + if (universe.isNoCleanup && !force) debug("Not cleaning up " + logFile) + else logFile.deleteIfExists() + + def onException(x: Throwable) { logException(x) } + def logException(x: Throwable) = { + val msg = throwableToString(x) + if (!isTerse) + normal(msg) + + logWriter append msg + false + } + } + + /** A writer which doesn't create the file until a write comes in. + */ + class LazilyCreatedWriter(log: File) extends Writer { + @volatile private var isCreated = false + private lazy val underlying = { + isCreated = true + log.bufferedWriter() + } + + def flush() = if (isCreated) underlying.flush() + def close() = if (isCreated) underlying.close() + def write(chars: Array[Char], off: Int, len: Int) = { + underlying.write(chars, off, len) + underlying.flush() + } + } + + class LogWriter(log: File) extends PrintWriter(new LazilyCreatedWriter(log), true) { + override def print(s: String) = { + super.print(s) + flush() + } + } +} \ No newline at end of file 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-alternative/scala/tools/partest/util/package.scala b/src/partest-alternative/scala/tools/partest/util/package.scala new file mode 100644 index 0000000000..bc5470ba5d --- /dev/null +++ b/src/partest-alternative/scala/tools/partest/util/package.scala @@ -0,0 +1,61 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + */ + +package scala.tools +package partest + +import java.util.{ Timer, TimerTask } +import java.io.StringWriter +import nsc.io._ + +/** Misc code still looking for a good home. + */ +package object util { + + def allPropertiesString() = javaHashtableToString(System.getProperties) + + private def javaHashtableToString(table: java.util.Hashtable[_,_]) = { + import collection.JavaConversions._ + (table.toList map { case (k, v) => "%s -> %s\n".format(k, v) }).sorted mkString + } + + def filesToSet(pre: String, fs: List[String]): Set[AbstractFile] = + fs flatMap (x => Option(AbstractFile getFile (Path(pre) / x).path)) toSet + + /** Copies one Path to another Path, trying to be sensible when one or the + * other is a Directory. Returns true if it believes it succeeded. + */ + def copyPath(from: Path, to: Path): Boolean = { + if (!to.parent.isDirectory) + to.parent.createDirectory(force = true) + + def copyDir = { + val sub = to / from.name createDirectory true + from.toDirectory.list forall (x => copyPath(x, sub)) + } + (from.isDirectory, to.isDirectory) match { + case (true, true) => copyDir + case (true, false) => false + case (false, true) => from.toFile copyTo (to / from.name) + case (false, false) => from.toFile copyTo to + } + } + + /** + * 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 diffFiles(f1: File, f2: File): String = { + val diffWriter = new StringWriter + val args = Array(f1.toAbsolute.path, f2.toAbsolute.path) + + io.DiffPrint.doDiff(args, diffWriter) + val result = diffWriter.toString + if (result == "No differences") "" else result + } +} 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 [] [ ...] - : 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 - --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/Actions.scala b/src/partest/scala/tools/partest/Actions.scala deleted file mode 100644 index cb60152b71..0000000000 --- a/src/partest/scala/tools/partest/Actions.scala +++ /dev/null @@ -1,231 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala Parallel Testing ** -** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala.tools -package partest - -import util._ -import nsc.io._ - -trait Actions { - partest: Universe => - - class TestSequence(val actions: List[TestStep]) extends AbsTestSequence { - } - - implicit def createSequence(xs: List[TestStep]) = new TestSequence(xs) - - trait ExecSupport { - self: TestEntity => - - def execEnv: Map[String, String] = { - val map = assembleEnvironment() - val cwd = execCwd.toList map ("CWD" -> _.path) - - map ++ cwd - } - def execCwd = if (commandFile.isFile) Some(sourcesDir) else None - - def runExec(args: List[String]): Boolean = { - val cmd = fromArgs(args) - - if (isVerbose) { - trace("runExec: " + execEnv.mkString("ENV(", "\n", "\n)")) - execCwd foreach (x => trace("CWD(" + x + ")")) - } - - trace("runExec: " + cmd) - isDryRun || execAndLog(cmd) - } - - /** Runs command redirecting standard out and - * error out to output file. - */ - private def runCommandOld(command: String, output: java.io.File): Int = { - import java.io._ - import nest.StreamAppender - - // 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 } - } - - /** Exec a process to run a command. Assumes 0 exit value is success. - * Of necessity, also treats no available exit value as success. - */ - protected def execAndLog(cmd: String): Boolean = { - runCommandOld(cmd, logFile.jfile) == 0 - - // var proc: Process = null - // - // val result = interruptMeIn(cmd, testTimeout) { - // loggingResult { - // proc = Process.exec(toArgs(cmd), execEnv, execCwd.orNull, true) - // proc.slurp() - // } - // proc != null && (proc.waitFor() == 0) - // } - // result getOrElse { - // warning("Process never terminated: '%s'" format cmd) - // if (proc != null) - // proc.destroy() - // - // false - // } - } - } - - trait ScriptableTest { - self: TestEntity => - - /** Translates a line from a .cmds file into a teststep. - */ - def customTestStep(line: String): TestStep = { - trace("customTestStep: " + line) - val (cmd, rest) = line span (x => !Character.isWhitespace(x)) - def qualify(name: String) = sourcesDir / name path - val args = toArgs(rest) map qualify - def fail: TestStep = (_: TestEntity) => error("Parse error: did not understand '%s'" format line) - - val f: TestEntity => Boolean = cmd match { - case "scalac" => _ scalac args - case "javac" => _ javac args - case "scala" => _ runScala args - case _ => fail - } - f - } - } - - trait CompilableTest extends CompileExecSupport { - self: TestEntity => - - def sourceFiles = location.walk collect { case f: File if isJavaOrScala(f) => f } toList - def allSources = sourceFiles map (_.path) - def scalaSources = sourceFiles filter isScala map (_.path) - def javaSources = sourceFiles filter isJava map (_.path) - - /** If there are mixed java and scala files, the standard compilation - * sequence is: - * - * scalac with all files - * javac with only java files - * scalac with only scala files - * - * This should be expanded to encompass other strategies so we know how - * well they're working or not working - notably, it would be very useful - * to know exactly when and how two-pass compilation fails. - */ - def compile() = { - trace("compile: " + sourceFiles) - - def compileJava() = javac(javaSources) - def compileScala() = scalac(scalaSources) - def compileAll() = scalac(allSources) - def compileMixed() = compileAll() && compileJava() && compileScala() - - if (scalaSources.nonEmpty && javaSources.nonEmpty) compileMixed() - else compileScala() - } - } - - trait DiffableTest { - self: TestEntity => - - def checkFile: File = withExtension("check").toFile - def checkFileRequired = - returning(checkFile.isFile)(res => if (!res) warnAndLog("A checkFile at '%s' is mandatory.\n" format checkFile.path)) - - lazy val sourceFileNames = sourceFiles map (_.name) - - /** Given the difficulty of verifying that any selective approach works - * everywhere, the algorithm now is to look for the name of any known - * source file for this test, and if seen, remove all the non-whitespace - * preceding it. (Paths with whitespace don't work anyway.) This should - * wipe out all slashes, backslashes, C:\, cygwin/windows differences, - * and whatever else makes a simple diff not simple. - * - * The log and check file are both transformed, which I don't think is - * correct -- only the log should be -- but doing it this way until I - * can clarify martin's comments in #3283. - */ - def normalizePaths(s: String) = - sourceFileNames.foldLeft(s)((res, name) => res.replaceAll("""\S+\Q%s\E""" format name, name)) - - /** The default cleanup normalizes paths relative to sourcesDir, - * absorbs line terminator differences by going to lines and back, - * and trims leading or trailing whitespace. - */ - def diffCleanup(f: File) = safeLines(f) map normalizePaths mkString "\n" trim - - /** diffFiles requires actual Files as arguments but the output we want - * is the post-processed versions of log/check, so we resort to tempfiles. - */ - lazy val diffOutput = { - if (!checkFile.exists) "" else { - val input = diffCleanup(checkFile) - val output = diffCleanup(logFile) - def asFile(s: String) = returning(File.makeTemp("partest-diff"))(_ writeAll s) - - if (input == output) "" - else diffFiles(asFile(input), asFile(output)) - } - } - private def checkTraceName = tracePath(checkFile) - private def logTraceName = tracePath(logFile) - private def isDiffConfirmed = checkFile.exists && (diffOutput == "") - - private def sendTraceMsg() { - def result = - if (isDryRun) "" - else if (isDiffConfirmed) " [passed]" - else if (checkFile.exists) " [failed]" - else " [unchecked]" - - trace("diff %s %s%s".format(checkTraceName, logTraceName, result)) - } - - /** If optional is true, a missing check file is considered - * a successful diff. Necessary since many categories use - * checkfiles in an ad hoc manner. - */ - def runDiff() = { - sendTraceMsg() - - def updateCheck = ( - isUpdateCheck && { - val formatStr = "** diff %s %s: " + ( - if (checkFile.exists) "failed, updating '%s' and marking as passed." - else if (diffOutput == "") "not creating checkFile at '%s' as there is no output." - else "was unchecked, creating '%s' for future tests." - ) + "\n" - - normal(formatStr.format(checkTraceName, logTraceName, checkFile.path)) - if (diffOutput != "") normal(diffOutput) - - checkFile.writeAll(diffCleanup(logFile), "\n") - true - } - ) - - isDryRun || isDiffConfirmed || (updateCheck || !checkFile.exists) - } - } -} diff --git a/src/partest/scala/tools/partest/Alarms.scala b/src/partest/scala/tools/partest/Alarms.scala deleted file mode 100644 index f38d8d6268..0000000000 --- a/src/partest/scala/tools/partest/Alarms.scala +++ /dev/null @@ -1,86 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools -package partest - -import java.util.{ Timer, TimerTask } - -trait Alarms { - self: Universe => - - def interruptMeIn[T](debugMsg: String, seconds: Int)(body: => T): Option[T] = { - val thisThread = currentThread - val alarm = new SimpleAlarm(seconds * 1000) set thisThread.interrupt() - debug("interruptMeIn(%d) '%s'".format(seconds, debugMsg)) - - try { Some(body) } - catch { case _: InterruptedException => debug("Received interrupted exception.") ; None } - finally { debug("Cancelling interruptMeIn '%s'" format debugMsg) ; alarm.cancel() ; Thread.interrupted() } - } - - case class AlarmerAction(secs: Int, action: () => Unit) extends Runnable { - override def run() = action() - } - - /** Set any number of alarms up with tuples of the form: - * seconds to alarm -> Function0[Unit] to execute - */ - class Alarmer(alarms: AlarmerAction*) { - import java.util.concurrent._ - - val exec = Executors.newSingleThreadScheduledExecutor() - alarms foreach (x => exec.schedule(x, x.secs, TimeUnit.SECONDS)) - exec.shutdown() - - def cancelAll() = exec.shutdownNow() - } - - class SimpleAlarm(timeout: Long) { - private val alarm = new Timer - - /** Start a timer, running the given body if it goes off. - */ - def set(body: => Unit) = returning(new TimerTask { def run() = body })(alarm.schedule(_, timeout)) - - /** Cancel the timer. - */ - def cancel() = alarm.cancel() - } - - trait TestAlarms { - test: TestEntity => - - private def warning1 = AlarmerAction(testWarning, () => warning( - """|I've been waiting %s seconds for this to complete: - | %s - |It may be stuck, or if not, it should be broken into smaller tests. - |""".stripMargin.format(testWarning, test)) - ) - private def warning2 = AlarmerAction(testWarning * 2, () => warning( - """|Now I've been waiting %s seconds for this to complete: - | %s - |If partest seems hung it would be a good place to look. - |""".stripMargin.format(testWarning * 2, test)) - ) - - def startAlarms(onTimeout: => Unit) = - if (isNoAlarms) new Alarmer() // for alarm debugging - else new Alarmer(Seq(warning1, warning2, AlarmerAction(testTimeout, () => onTimeout)): _*) - } - - // Thread.setDefaultUncaughtExceptionHandler(new UncaughtException) - // class UncaughtException extends Thread.UncaughtExceptionHandler { - // def uncaughtException(t: Thread, e: Throwable) { - // Console.println("Uncaught in %s: %s".format(t, e)) - // } - // } - // - // lazy val logger = File("/tmp/partest.log").bufferedWriter() - // def flog(msg: String) = logger synchronized { - // logger write (msg + "\n") - // logger.flush() - // } -} diff --git a/src/partest/scala/tools/partest/BuildContributors.scala b/src/partest/scala/tools/partest/BuildContributors.scala deleted file mode 100644 index 64c7e07bc3..0000000000 --- a/src/partest/scala/tools/partest/BuildContributors.scala +++ /dev/null @@ -1,102 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - */ - -package scala.tools -package partest - -import nsc.io._ -import nsc.util.ClassPath - -trait BuildContributors { - universe: Universe => - - /** A trait mixed into types which contribute a portion of the values. - * The basic mechanism is the TestBuild, TestCategory, and TestEntity - * can each contribute to each value. They are assembled at the last - * moment by the ContributorAssembler (presently the TestEntity.) - */ - trait BuildContributor { - def javaFlags: List[String] - def scalacFlags: List[String] - def classpathPaths: List[Path] - def buildProperties: List[(String, Any)] - def buildEnvironment: Map[String, String] - } - - trait ContributorAssembler { - def contributors: List[BuildContributor] - def assemble[T](what: BuildContributor => List[T]): List[T] = contributors flatMap what - - /** !!! This will need work if we want to achieve real composability, - * but it can wait for the demand. - */ - def assembleScalacArgs(args: List[String]) = assemble(_.scalacFlags) ++ args - def assembleJavaArgs(args: List[String]) = assemble(_.javaFlags) ++ args - def assembleProperties() = assemble(_.buildProperties) - def assembleClasspaths(paths: List[Path]) = assemble(_.classpathPaths) ++ paths - def assembleEnvironment() = assemble(_.buildEnvironment.toList).toMap - - def createClasspathString() = ClassPath fromPaths (assembleClasspaths(Nil) : _*) - def createPropertyString() = assembleProperties() map { case (k, v) => "-D%s=%s".format(k, v.toString) } - } - - trait BuildContribution extends BuildContributor { - self: TestBuild => - - /** The base classpath and system properties. - * !!! TODO - this should adjust itself depending on the build - * being tested, because pack and quick at least need different jars. - */ - def classpathPaths = List[Path](library, compiler, partest, fjbg) ++ forkJoinPath - def buildProperties = List( - "scala.home" -> testBuildDir, - "partest.lib" -> library, // used in jvm/inner - "java.awt.headless" -> true, - "user.language" -> "en", - "user.country" -> "US", - "partest.debug" -> isDebug, - "partest.verbose" -> isVerbose - // Disabled because there are no natives tests. - // "java.library.path" -> srcLibDir - ) - def javaFlags: List[String] = toArgs(javaOpts) - def scalacFlags: List[String] = toArgs(scalacOpts) - - /** We put the build being tested's /bin directory in the front of the - * path so the scripts and such written to execute "scala" will use this - * build and not whatever happens to be on their path. - */ - private def modifiedPath = ClassPath.join(scalaBin.path, Properties.envOrElse("PATH", "")) - def buildEnvironment = Map("PATH" -> modifiedPath) - } - - trait CategoryContribution extends BuildContributor { - self: DirBasedCategory => - - /** Category-wide classpath additions placed in /lib. */ - private def libContents = root / "lib" ifDirectory (_.list.toList) - - def classpathPaths = libContents getOrElse Nil - def buildProperties = Nil - def javaFlags = Nil - def scalacFlags = Nil - def buildEnvironment = Map() - } - - trait TestContribution extends BuildContributor with ContributorAssembler { - self: TestEntity => - - def jarsInTestDir = location.walk collect { case f: File if f hasExtension "jar" => f } toList - - def contributors = List(build, category, self) - def javaFlags = safeArgs(javaOptsFile) - def scalacFlags = safeArgs(scalaOptsFile) - def classpathPaths = jarsInTestDir :+ outDir - def buildProperties = List( - "partest.output" -> outDir.toAbsolute, // used in jvm/inner - "partest.cwd" -> outDir.parent.toAbsolute // used in shootout tests - ) - def buildEnvironment = Map("JAVA_OPTS" -> fromArgs(assembleJavaArgs(Nil))) - } -} \ No newline at end of file diff --git a/src/partest/scala/tools/partest/Categories.scala b/src/partest/scala/tools/partest/Categories.scala deleted file mode 100644 index 172cca74b4..0000000000 --- a/src/partest/scala/tools/partest/Categories.scala +++ /dev/null @@ -1,70 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala Parallel Testing ** -** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala.tools -package partest - -import nsc.Settings -import nsc.io._ -import nsc.util.{ ClassPath } - -trait Categories { - self: Universe => - - trait TestCategory extends AbsTestCategory { - def kind: String - def startMessage: String = "Executing test group" - def testSequence: TestSequence - - class TestSettings(entity: TestEntity, error: String => Unit) extends Settings(error) { - def this(entity: TestEntity) = this(entity, Console println _) - - deprecation.value = false - encoding.value = "ISO-8859-1" - classpath.value = entity.testClasspath - outdir.value = entity.outDir.path - } - - def createSettings(entity: TestEntity): TestSettings = new TestSettings(entity) - def createTest(location: Path): TestEntity = - if (location.isFile) TestFile(this, location.toFile) - else if (location.isDirectory) TestDirectory(this, location.toDirectory) - else error("Failed to create test at '%s'" format location) - - /** Category test identification. - */ - def denotesTestFile(p: Path) = p.isFile && (p hasExtension "scala") - def denotesTestDir(p: Path) = p.isDirectory && !ignorePath(p) - def denotesTest(p: Path) = denotesTestDir(p) || denotesTestFile(p) - - /** This should verify that all necessary files are present. - * By default it delegates to denotesTest. - */ - def denotesValidTest(p: Path) = denotesTest(p) - } - - abstract class DirBasedCategory(val kind: String) extends TestCategory with CategoryContribution { - lazy val root = Directory(src / kind).normalize - def enumerate = root.list filter denotesTest map createTest toList - - /** Standard actions. These can be overridden either on the - * Category level or by individual tests. - */ - def compile: TestStep = (_: TestEntity).compile() - def checkFileRequired: TestStep = (_: TestEntity).checkFileRequired - def diff: TestStep = (_: TestEntity).diff() - def run: TestStep = (_: TestEntity).run() - def exec: TestStep = (_: TestEntity).exec() - - /** Combinators. - */ - def not(f: TestStep): TestStep = !f(_: TestEntity) - - override def toString = kind - } -} \ No newline at end of file diff --git a/src/partest/scala/tools/partest/Compilable.scala b/src/partest/scala/tools/partest/Compilable.scala deleted file mode 100644 index ddaa277842..0000000000 --- a/src/partest/scala/tools/partest/Compilable.scala +++ /dev/null @@ -1,106 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - */ - -package scala.tools -package partest - -import scala.tools.nsc.io._ -import scala.tools.nsc.{ Global, Settings, CompilerCommand, FatalError } -import scala.tools.nsc.util.{ ClassPath } -import scala.tools.nsc.reporters.{ Reporter, ConsoleReporter } - -trait PartestCompilation { - self: Universe => - - trait CompileExecSupport extends ExecSupport { - self: TestEntity => - - def javacpArg = "-classpath " + testClasspath - def scalacpArg = "-usejavacp" - - /** Not used, requires tools.jar. - */ - // def javacInternal(args: List[String]) = { - // import com.sun.tools.javac.Main - // Main.compile(args.toArray, logWriter) - // } - - def javac(args: List[String]): Boolean = { - val allArgString = fromArgs(javacpArg :: javacOpts :: args) - - // javac -d outdir -classpath - val cmd = "%s -d %s %s".format(javacCmd, outDir, allArgString) - def traceMsg = - if (isVerbose) cmd - else "%s -d %s %s".format(tracePath(Path(javacCmd)), tracePath(outDir), fromArgs(args)) - - trace(traceMsg) - - isDryRun || execAndLog(cmd) - } - - def scalac(args: List[String]): Boolean = { - val allArgs = assembleScalacArgs(args) - val (global, files) = newGlobal(allArgs) - def nonFileArgs = if (isVerbose) global.settings.recreateArgs else assembleScalacArgs(Nil) - def traceArgs = fromArgs(nonFileArgs ++ (files map tracePath)) - def traceMsg = "scalac " + traceArgs - - trace(traceMsg) - isDryRun || global.partestCompile(files, true) - } - - /** Actually running the test, post compilation. - * Normally args will be List("Test", "jvm"), main class and arg to it. - */ - def runScala(args: List[String]): Boolean = { - val scalaRunnerClass = "scala.tools.nsc.MainGenericRunner" - - // java $JAVA_OPTS -classpath - val javaCmdAndOptions = javaCmd +: assembleJavaArgs(List(javacpArg)) - // MainGenericRunner -usejavacp Test jvm - val scalaCmdAndOptions = List(scalaRunnerClass, scalacpArg) ++ assembleScalacArgs(args) - // Assembled - val cmd = fromArgs(javaCmdAndOptions ++ createPropertyString() ++ scalaCmdAndOptions) - - def traceMsg = if (isVerbose) cmd else fromArgs(javaCmd :: args) - trace("runScala: " + traceMsg) - - isDryRun || execAndLog(cmd) - } - - def newReporter(settings: Settings) = new ConsoleReporter(settings, Console.in, logWriter) - - class PartestGlobal(settings: Settings, val creporter: ConsoleReporter) extends Global(settings, creporter) { - def partestCompile(files: List[String], printSummary: Boolean): Boolean = { - try { new Run compile files } - catch { - case FatalError(msg) => creporter.error(null, "fatal error: " + msg) - case ae: AssertionError => creporter.error(null, ""+ae) - case te: TypeError => creporter.error(null, ""+te) - case ex => - creporter.error(null, ""+ex) - throw ex - } - - if (printSummary) - creporter.printSummary - - creporter.flush() - !creporter.hasErrors - } - } - - def newGlobal(args: List[String]): (PartestGlobal, List[String]) = { - val settings = category createSettings self - val command = new CompilerCommand(args, settings) - val reporter = newReporter(settings) - - if (!command.ok) - debug("Error parsing arguments: '%s'".format(args mkString ", ")) - - (new PartestGlobal(command.settings, reporter), command.files) - } - } -} diff --git a/src/partest/scala/tools/partest/Config.scala b/src/partest/scala/tools/partest/Config.scala deleted file mode 100644 index 288a3034e9..0000000000 --- a/src/partest/scala/tools/partest/Config.scala +++ /dev/null @@ -1,129 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - */ - -package scala.tools -package partest - -import io._ -import nsc.io._ -import Properties._ - -trait Config { - universe: Universe => - - lazy val src = absolutize(srcDir).toDirectory - lazy val build = new TestBuild() - - def javaHomeEnv = envOrElse("JAVA_HOME", null) - def javaCmd = envOrElse("JAVACMD", "java") - def javacCmd = Option(javaHomeEnv) map (x => Path(x) / "bin" / "javac" path) getOrElse "javac" - - /** Values related to actors. The timeouts are in seconds. On a dry - * run we only allocate one worker so the output isn't interspersed. - */ - def workerTimeout = 3600 // 1 hour, probably overly generous - def numWorkers = if (isDryRun) 1 else propOrElse("partest.actors", "8").toInt - def expectedErrors = propOrElse("partest.errors", "0").toInt - def poolSize = (wrapAccessControl(propOrNone("actors.corePoolSize")) getOrElse "16").toInt - - def allScalaFiles = src.deepFiles filter (_ hasExtension "scala") - def allObjDirs = src.deepDirs filter (_ hasExtension "obj") - def allLogFiles = src.deepFiles filter (_ hasExtension "log") - def allClassFiles = src.deepFiles filter (_ hasExtension "class") - - class TestBuild() extends BuildContribution { - import nsc.util.ClassPath - - /** Scala core libs. - */ - val library = pathForComponent("library") - val compiler = pathForComponent("compiler") - val partest = pathForComponent("partest") - val scalap = pathForComponent("scalap", "%s.jar") - - /** Scala supplementary libs - these are not all needed for all build targets, - * and some of them are copied inside other jars in later targets. However quick - * for instance cannot be run without some of these. - */ - val fjbg = pathForLibrary("fjbg") - val msil = pathForLibrary("msil") - val forkjoin = pathForLibrary("forkjoin") - val scalacheck = pathForLibrary("scalacheck") - - /** Other interesting paths. - */ - val scalaBin = testBuildDir / "bin" - - /** A hack for now to get quick running. - */ - def needsForkJoin = { - val loader = nsc.util.ScalaClassLoader.fromURLs(List(library.toURL)) - val fjMarker = "scala.concurrent.forkjoin.ForkJoinTask" - val clazz = loader.tryToLoadClass(fjMarker) - - if (clazz.isDefined) debug("Loaded ForkJoinTask OK, don't need jar.") - else debug("Could not load ForkJoinTask, putting jar on classpath.") - - clazz.isEmpty - } - lazy val forkJoinPath: List[Path] = if (needsForkJoin) List(forkjoin) else Nil - - /** Internal **/ - private def repo = partestDir.parent.normalize - // XXX - is this needed? Where? - // - // private val pluginOptionString = "-Xplugin:" - // private def updatedPluginPath(options: String): String = { - // val (pluginArgs, rest) = toArgs(options) partition (_ startsWith pluginOptionString) - // // join all plugin paths as one classpath - // val pluginPaths = ClassPath.join(pluginArgs map (_ stripPrefix pluginOptionString): _*) - // // map all paths to absolute - // val newPath = ClassPath.map(pluginPaths, x => absolutize(x).path) - // // recreate option - // val pluginOption = if (newPath == "") None else Some(pluginOptionString + newPath) - // - // fromArgs(rest ::: pluginOption.toList) - // } - - private def pathForComponent(what: String, jarFormat: String = "scala-%s.jar"): Path = { - def asDir = testBuildDir / "classes" / what - def asJar = testBuildDir / "lib" / jarFormat.format(what) - - if (asDir.isDirectory) asDir - else if (asJar.isFile) asJar - else "" - } - private def pathForLibrary(what: String) = File(repo / "lib" / (what + ".jar")) - } - - def printConfigBanner() = { - debug("Java VM started with arguments: '%s'" format fromArgs(Process.javaVmArguments)) - debug("System Properties:\n" + util.allPropertiesString()) - - normal(configBanner()) - } - - /** Treat an access control failure as None. */ - private def wrapAccessControl[T](body: => Option[T]): Option[T] = - try body catch { case _: java.security.AccessControlException => None } - - private def configBanner() = { - val javaBin = Path(javaHome) / "bin" - val javaInfoString = "%s (build %s, %s)".format(javaVmName, javaVmVersion, javaVmInfo) - - List( - "Scala compiler classes in: " + testBuildDir, - "Scala version is: " + nsc.Properties.versionMsg, - "Scalac options are: " + universe.scalacOpts, - "Java binaries in: " + javaBin, - "Java runtime is: " + javaInfoString, - "Java runtime options: " + (Process.javaVmArguments mkString " "), - "Javac options are: " + universe.javacOpts, - "Java options are: " + universe.javaOpts, - "Source directory is: " + src, - "Selected categories: " + (selectedCategories mkString " "), - "" - ) mkString "\n" - } -} diff --git a/src/partest/scala/tools/partest/Dispatcher.scala b/src/partest/scala/tools/partest/Dispatcher.scala deleted file mode 100644 index 2a9d99ab60..0000000000 --- a/src/partest/scala/tools/partest/Dispatcher.scala +++ /dev/null @@ -1,162 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - * @author Philipp Haller - */ - -package scala.tools -package partest - -import scala.tools.nsc.io._ -import scala.actors.{ Actor, TIMEOUT } -import scala.actors.Actor._ -import scala.collection.immutable -import scala.util.control.Exception.ultimately - -/** The machinery for concurrent execution of tests. Each Worker - * is given a bundle of tests, which it runs sequentially and then - * sends a report back to the dispatcher. - */ -trait Dispatcher { - partest: Universe => - - /** The public entry point. The given filter narrows down the list of - * tests to run. - */ - def runSelection(categories: List[TestCategory], filt: TestEntity => Boolean = _ => true): CombinedTestResults = { - // Setting scala.home informs tests where to obtain their jars. - setProp("scala.home", testBuildDir.path) - - val allTests = allCategories flatMap (_.enumerate) - val selected = allTests filter filt - val groups = selected groupBy (_.category) - val count = selected.size - - if (count == 0) return CombinedTestResults(0, 0, 0, Nil) - else if (count == allTests.size) verbose("Running all %d tests." format count) - else verbose("Running %d/%d tests: %s".format(count, allTests.size, toStringTrunc(selected map (_.label) mkString ", "))) - - allCategories collect { case x if groups contains x => runCategory(x, groups(x)) } reduceLeft (_ ++ _) - } - - private def parallelizeTests(tests: List[TestEntity]): immutable.Map[TestEntity, TestResult] = { - // propagate verbosity - if (isDebug) scala.actors.Debug.level = 3 - - // "If elected, I guarantee a slice of tests for every worker!" - val groups = tests grouped ((tests.size / numWorkers) + 1) toList - - // "Workers, line up for assignments!" - val workers = - for ((slice, workerNum) <- groups.zipWithIndex) yield { - returning(new Worker(workerNum)) { worker => - worker.start() - worker ! TestsToRun(slice) - } - } - - normal("Started %d workers with ~%d tests each.\n".format(groups.size, groups.head.size)) - - /** Listening for news from the proletariat. - */ - (workers map { w => - receiveWithin(workerTimeout * 1000) { - case ResultsOfRun(resultMap) => resultMap - case TIMEOUT => - warning("Worker %d timed out." format w.workerNum) - // mark all the worker's tests as having timed out - should be hard to miss - // immutable.Map[TestEntity, TestResult]() - groups(w.workerNum) map (x => (x -> new Timeout(x))) toMap - } - }) reduceLeft (_ ++ _) - } - - private def runCategory(category: TestCategory, tests: List[TestEntity]): CombinedTestResults = { - val kind = category.kind - normal("%s (%s tests in %s)\n".format(category.startMessage, tests.size, category)) - - val (milliSeconds, resultMap) = timed2(parallelizeTests(tests)) - val (passed, failed) = resultsToStatistics(resultMap mapValues (_.state)) - val failures = resultMap.values filterNot (_.passed) toList - - CombinedTestResults(passed, failed, milliSeconds, failures) - } - - /** A Worker is given a bundle of tests and runs them all sequentially. - */ - class Worker(val workerNum: Int) extends Actor { - def act() { - react { case TestsToRun(tests) => - val master = sender - runTests(tests)(results => master ! ResultsOfRun(results)) - } - } - - /** Runs the tests. Passes the result Map to onCompletion when done. - */ - private def runTests(tests: List[TestEntity])(onCompletion: immutable.Map[TestEntity, TestResult] => Unit) { - var results = new immutable.HashMap[TestEntity, TestResult] // maps tests to results - val numberOfTests = tests.size - val testIterator = tests.iterator - def processed = results.size - def isComplete = testIterator.isEmpty - - def atThreshold(num: Double) = { - require(num >= 0 && num <= 1.0) - ((processed - 1).toDouble / numberOfTests <= num) && (processed.toDouble / numberOfTests >= num) - } - - def extraMessage = { - // for now quiet for normal people - if (isVerbose || isTrace || isDebug) { - if (isComplete) "(#%d 100%%)" format workerNum - else if (isVerbose) "(#%d %d/%d)".format(workerNum, processed, numberOfTests) - else if (isTrace && atThreshold(0.5)) "(#%d 50%%)" format workerNum - else "" - } - else "" - } - - def countAndReport(result: TestResult) { - val TestResult(test, state) = result - // refuse to count an entity twice - if (results contains test) - return warning("Received duplicate result for %s: was %s, now %s".format(test, results(test), state)) - - // increment the counter for this result state - results += (test -> result) - - // show on screen - if (isDryRun) normal("\n") // blank line between dry run traces - else result show extraMessage - - // remove log if successful - if (result.passed) - test.deleteLog() - - // Respond to master if this Worker is complete - if (isComplete) - onCompletion(results) - } - - Actor.loopWhile(testIterator.hasNext) { - val parent = self - // pick a test and set some alarms - val test = testIterator.next - val alarmer = test startAlarms (parent ! new Timeout(test)) - - actor { - ultimately(alarmer.cancelAll()) { - // Calling isSuccess forces the lazy val "process" inside the test, running it. - val res = test.isSuccess - // Cancel the alarms and alert the media. - parent ! TestResult(test, res) - } - } - - react { - case x: TestResult => countAndReport(x) - } - } - } - } -} \ No newline at end of file diff --git a/src/partest/scala/tools/partest/Entities.scala b/src/partest/scala/tools/partest/Entities.scala deleted file mode 100644 index bea505b594..0000000000 --- a/src/partest/scala/tools/partest/Entities.scala +++ /dev/null @@ -1,74 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - * @author Philipp Haller - */ - -package scala.tools -package partest - -import nsc.io._ - -trait Entities { - self: Universe => - - abstract class TestEntity extends AbsTestEntity - with TestContribution - with TestHousekeeping - with TestAlarms - with EntityLogging - with CompilableTest - with ScriptableTest - with DiffableTest { - def location: Path - def category: TestCategory - - lazy val label = location.stripExtension - lazy val testClasspath = returning(createClasspathString())(x => vtrace("testClasspath: " + x)) - - /** Was this test successful? Calling this for the first time forces - * lazy val "process" which actually runs the test. - */ - def isSuccess = process - - /** Some standard files, which may or may not be present. - */ - def scalaOptsFile = withExtension("flags").toFile // opts to scalac - def javaOptsFile = withExtension("javaopts").toFile // opts to java (but not javac) - def commandFile = withExtension("cmds").toFile // sequence of commands to execute - def logFile = withExtension("log").toFile // collected output - - /** Some standard directories. - */ - def outDir = withExtension("obj").toDirectory // output dir, e.g. files/pos/t14.obj - def categoryDir = location.parent.normalize // category dir, e.g. files/pos/ - def sourcesDir = location ifDirectory (_.normalize) getOrElse categoryDir - - /** Standard arguments for run, exec, diff. - */ - def argumentsToRun = List("Test", "jvm") - def argumentsToExec = List(location.path) - - /** Using a .cmds file for a custom test sequence. - */ - def commandList = safeLines(commandFile) - def testSequence = - if (commandFile.isFile && commandList.nonEmpty) commandList map customTestStep - else category.testSequence - - def run() = runScala(argumentsToRun) - def exec() = runExec(argumentsToExec) - def diff() = runDiff() // checkFile, logFile - - /** The memoized result of the test run. - */ - private lazy val process = { - val outcome = runWrappers(testSequence.actions forall (f => f(this))) - - // an empty outcome means we've been interrupted and are shutting down. - outcome getOrElse false - } - } - - case class TestDirectory(category: TestCategory, location: Directory) extends TestEntity { } - case class TestFile(category: TestCategory, location: File) extends TestEntity { } -} diff --git a/src/partest/scala/tools/partest/Housekeeping.scala b/src/partest/scala/tools/partest/Housekeeping.scala deleted file mode 100644 index a624ca8adb..0000000000 --- a/src/partest/scala/tools/partest/Housekeeping.scala +++ /dev/null @@ -1,187 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - */ - -package scala.tools -package partest - -import scala.util.control.Exception.catching -import util._ -import nsc.io._ -import Process.runtime -import Properties._ - -/** An agglomeration of code which is low on thrills. Hopefully - * it operates so quietly in the background that you never have to - * look at this file. - */ -trait Housekeeping { - self: Universe => - - /** Orderly shutdown on ctrl-C. */ - @volatile private var _shuttingDown = false - protected def setShuttingDown() = { - /** Whatever we want to do as shutdown begins goes here. */ - if (!_shuttingDown) { - warning("Received shutdown signal, partest is cleaning up...\n") - _shuttingDown = true - } - } - def isShuttingDown = _shuttingDown - - /** Execute some code with a shutdown hook in place. This is - * motivated by the desire not to leave the filesystem full of - * junk when someone ctrl-Cs a test run. - */ - def withShutdownHook[T](hook: => Unit)(body: => T): Option[T] = - /** Java doesn't like it if you keep adding and removing shutdown - * hooks after shutdown has begun, so we trap the failure. - */ - catching(classOf[IllegalStateException]) opt { - val t = new Thread() { - override def run() = { - setShuttingDown() - hook - } - } - runtime addShutdownHook t - - try body - finally runtime removeShutdownHook t - } - - /** Search for a directory, possibly given only a name, by starting - * at the current dir and walking upward looking for it at each level. - */ - protected def searchForDir(name: String): Directory = { - val result = Path(name) ifDirectory (x => x.normalize) orElse { - val cwd = Directory.Current getOrElse error("user.dir property not set") - val dirs = cwd :: cwd.parents map (_ / name) - - Path onlyDirs dirs map (_.normalize) headOption - } - - result getOrElse error("Fatal: could not find directory '%s'" format name) - } - - /** Paths we ignore for most purposes. - */ - def ignorePath(x: Path) = { - (x.name startsWith ".") || - (x.isDirectory && ((x.name == "lib") || x.hasExtension("obj", "svn"))) - } - /** Make a possibly relative path absolute using partestDir as the base. - */ - def absolutize(path: String) = Path(path) toAbsoluteWithRoot partestDir - - /** Go on a deleting binge. - */ - def cleanupAll() { - if (isNoCleanup) - return - - val (dirCount, fileCount) = (cleanupObjDirs(), cleanupLogs() + cleanupJunk()) - if (dirCount + fileCount > 0) - normal("Cleaned up %d directories and %d files.\n".format(dirCount, fileCount)) - } - - def cleanupObjDirs() = countTrue(allObjDirs collect { case x if x.exists => x.deleteRecursively() }) - def cleanupJunk() = countTrue(allClassFiles collect { case x if x.exists => x.delete() }) - def cleanupLogs() = countTrue(allLogFiles collect { case x if x.exists => x.delete() }) - - /** Look through every file in the partest directory and ask around - * to make sure someone knows him. Complain about strangers. - */ - def validateAll() { - def denotesTest(p: Path) = allCategories exists (_ denotesTest p) - def isMSILcheck(p: Path) = p.name endsWith "-msil.check" - - def analyzeCategory(cat: DirBasedCategory) = { - val allTests = cat.enumerate - val otherPaths = cat.root walkFilter (x => !ignorePath(x)) filterNot (cat denotesTest _) filterNot isMSILcheck toList - val count = otherPaths.size - - println("Validating %d non-test paths in %s.".format(count, cat.kind)) - - for (path <- otherPaths) { - (allTests find (_ acknowledges path)) match { - case Some(test) => if (isVerbose) println(" OK: '%s' is claimed by '%s'".format(path, test.label)) - case _ => println(">> Unknown path '%s'" format path) - } - } - } - - allCategories collect { case x: DirBasedCategory => analyzeCategory(x) } - } - - trait TestHousekeeping { - self: TestEntity => - - /** Calculating derived files. Given a test like - * files/run/foo.scala or files/run/foo/ - * This creates paths like foo.check, foo.flags, etc. - */ - def withExtension(extension: String) = categoryDir / "%s.%s".format(label, extension) - - /** True for a path if this test acknowledges it belongs to this test. - * Overridden by some categories. - */ - def acknowledges(path: Path): Boolean = { - val loc = location.normalize - val knownPaths = List(scalaOptsFile, javaOptsFile, commandFile, logFile, checkFile) ++ jarsInTestDir - def isContainedSource = location.isDirectory && isJavaOrScala(path) && (path.normalize startsWith loc) - - (knownPaths exists (_ isSame path)) || isContainedSource - } - - /** This test "responds to" this String. This could mean anything -- it's a - * way of specifying ad-hoc collections of tests to exercise only a subset of tests. - * At present it looks for the given String in all the test sources. - */ - def respondsToString(str: String) = containsString(str) - def containsString(str: String) = { - debug("Checking %s for \"%s\"".format(sourceFiles mkString ", ", str)) - sourceFiles map safeSlurp exists (_ contains str) - } - - def possiblyTimed[T](body: => T): T = { - if (isStats) timed(recordTestTiming(label, _))(body) - else body - } - - private def prepareForTestRun() = { - // make sure we have a clean slate - deleteLog(force = true) - if (outDir.exists) - outDir.deleteRecursively() - - // recreate object dir - outDir createDirectory true - } - def deleteOutDir() = outDir.deleteRecursively() - def deleteShutdownHook() = { debug("Shutdown hook deleting " + outDir) ; deleteOutDir() } - - protected def runWrappers[T](body: => T): Option[T] = { - prepareForTestRun() - - withShutdownHook(deleteShutdownHook()) { - loggingOutAndErr { - val result = possiblyTimed { body } - if (!isNoCleanup) - deleteOutDir() - - result - } - } - } - - override def toString = location.path - override def equals(other: Any) = other match { - case x: TestEntity => location.normalize == x.location.normalize - case _ => false - } - override def hashCode = location.normalize.hashCode - } - - private def countTrue(f: => Iterator[Boolean]) = f filter (_ == true) length -} \ No newline at end of file diff --git a/src/partest/scala/tools/partest/Partest.scala b/src/partest/scala/tools/partest/Partest.scala deleted file mode 100644 index b3fe9a98ef..0000000000 --- a/src/partest/scala/tools/partest/Partest.scala +++ /dev/null @@ -1,81 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - */ - -package scala.tools -package partest - -import nsc.io._ -import nsc.util._ -import category.AllCategories - -/** Global object for a Partest run. It is completely configured by the list - * of arguments passed to the constructor (although there are a few properties - * and environment variables which can influence matters.) See PartestSpec.scala - * for the complete list. - */ -class Partest(args: List[String]) extends { - val parsed = PartestSpec(args: _*) -} with Universe with PartestSpec with cmd.Instance with AllCategories { - - if (parsed.propertyArgs.nonEmpty) - debug("Partest property args: " + fromArgs(parsed.propertyArgs)) - - debug("Partest created with args: " + fromArgs(args)) - - def helpMsg = PartestSpec.helpMsg - - // The abstract values from Universe. - lazy val testBuildDir = searchForDir(buildDir) - lazy val partestDir = searchForDir(rootDir) - lazy val allCategories = List(Pos, Neg, Run, Jvm, Res, Shootout, Scalap, Scalacheck, BuildManager, Script) - lazy val selectedCategories = if (isAllImplied) allCategories else specifiedCats - - def specifiedTests = parsed.residualArgs map (x => Path(x).normalize) - def specifiedKinds = testKinds filter (x => isSet(x) || (runSets contains x)) - def specifiedCats = specifiedKinds flatMap (x => allCategories find (_.kind == x)) - def isAllImplied = isAll || (specifiedTests.isEmpty && specifiedKinds.isEmpty) - - /** Assembles a filter based on command line options which restrict the test set - * --grep limits to only matching tests - * --failed limits to only recently failed tests (log file is present) - * -- limits to only the given tests and categories (but --all overrides) - * path/to/Test limits to only the given tests and categories - */ - lazy val filter = { - def indivFilter(test: TestEntity) = specifiedTests contains test.location.normalize - def categoryFilter(test: TestEntity) = specifiedCats contains test.category - def indivOrCat(test: TestEntity) = isAllImplied || indivFilter(test) || categoryFilter(test) // combines previous two - - def failedFilter(test: TestEntity) = !isFailed || (test.logFile exists) - def grepFilter(test: TestEntity) = grepExpr.isEmpty || (test containsString grepExpr.get) - def combinedFilter(x: TestEntity) = indivOrCat(x) && failedFilter(x) && grepFilter(x) // combines previous three - - combinedFilter _ - } - - def launchTestSuite() = { - def onTimeout() = { - warning("Partest test run timed out after " + timeout + " seconds.\n") - System.exit(-1) - } - val alarm = new Alarmer(AlarmerAction(timeout, () => onTimeout())) - - try runSelection(selectedCategories, filter) - finally alarm.cancelAll() - } -} - -object Partest { - def fromBuild(dir: String, args: String*): Partest = apply("--builddir" +: dir +: args: _*) - def apply(args: String*): Partest = new Partest(args.toList) - - // builds without partest jars won't actually work - def starr() = fromBuild("") - def locker() = fromBuild("build/locker") - def quick() = fromBuild("build/quick") - def pack() = fromBuild("build/pack") - def strap() = fromBuild("build/strap") - def dist() = fromBuild("dists/latest") -} - 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/PartestSpec.scala b/src/partest/scala/tools/partest/PartestSpec.scala deleted file mode 100644 index c25119b3af..0000000000 --- a/src/partest/scala/tools/partest/PartestSpec.scala +++ /dev/null @@ -1,104 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools -package partest - -import nsc.io._ -import cmd._ - -/** This takes advantage of bits of scala goodness to fully define a command - * line program with a minimum of duplicated code. When the specification object - * is created, the vals are evaluated in order and each of them side effects - * a private accumulator. What emerges is a full list of the valid unary - * and binary arguments, as well as autogenerated help. - */ -trait PartestSpec extends Spec with Meta.StdOpts with Interpolation { - def referenceSpec = PartestSpec - def programInfo = Spec.Info("partest", "", "scala.tools.partest.Runner") - private val kind = new Spec.Accumulator[String]() - protected def testKinds = kind.get - - private implicit val tokenizeString = FromString.ArgumentsFromString // String => List[String] - - help(""" - |# Pro Tip! Instant bash completion: `partest --bash` (note backticks) - |Usage: partest [] [ ...] - | : a path to a test designator, typically a .scala file or a directory. - | Examples: files/pos/test1.scala, files/res/bug785 - | - | Test categories:""".stripMargin) - - val isAll = ("all" / "run all tests (default, unless no options given)" --?) - (kind("pos") / "Compile files that are expected to build" --?) - (kind("neg") / "Compile files that are expected to fail" --?) - (kind("run") / "Test JVM backend" --?) - (kind("jvm") / "Test JVM backend" --?) - (kind("res") / "Run resident compiler scenarii" --?) - (kind("buildmanager") / "Run Build Manager scenarii" --?) - (kind("scalacheck") / "Run Scalacheck tests" --?) - (kind("script") / "Run script files" --?) - (kind("shootout") / "Run shootout tests" --?) - (kind("scalap") / "Run scalap tests" --?) - - heading ("""Test "smart" categories:""") - val grepExpr = "grep" / "run all tests with a source file containing " --| - val isFailed = "failed" / "run all tests which failed on the last run" --? - - heading ("Specifying paths and additional flags, ~ means repository root:") - - val rootDir = "rootdir" / "path from ~ to partest" defaultTo "test" - val buildDir = "builddir" / "path from ~ to test build" defaultTo "build/pack" - val srcDir = "srcdir" / "path from --rootdir to sources" defaultTo "files" - val javaOpts = "javaopts" / "flags to java on all runs" defaultToEnv "JAVA_OPTS" - val javacOpts = "javacopts" / "flags to javac on all runs" defaultToEnv "JAVAC_OPTS" - val scalacOpts = "scalacopts" / "flags to scalac on all tests" defaultToEnv "SCALAC_OPTS" - - "pack" / "" expandTo ("--builddir", "build/pack") - "quick" / "" expandTo ("--builddir", "build/quick") - - heading ("Options influencing output:") - val isTrace = "trace" / "show the individual steps taken by each test" --? - val isShowDiff = "show-diff" / "show diff between log and check file" --? - val isShowLog = "show-log" / "show log on failures" --? - val isDryRun = "dry-run" / "do not run tests, only show their traces." --? - val isTerse = "terse" / "be less verbose (almost silent except for failures)" --? - val isVerbose = "verbose" / "be more verbose (additive with --trace)" --? - val isDebug = "debug" / "maximum debugging output" --? - val isAnsi = "ansi" / "print output in color" --? - - heading ("Other options:") - val timeout = "timeout" / "Overall timeout in seconds" defaultTo 7200 - val testWarning = "test-warning" / "Test warning in seconds" defaultTo 90 - val testTimeout = "test-timeout" / "Test timeout in seconds" defaultTo 900 - val isCleanup = "cleanup" / "delete all stale files and dirs before run" --? - val isNoCleanup = "nocleanup" / "do not delete any logfiles or object dirs" --? - val isStats = "stats" / "collect and print statistics about the tests" --? - val isValidate = "validate" / "examine test filesystem for inconsistencies" --? - val isUpdateCheck = "update-check" / "overwrite checkFile if diff fails" --? - - "version" / "print version" --> runAndExit(println(Properties.versionMsg)) - - // no help for anything below this line - secret options - // mostly intended for property configuration. - val runSets = ("runsets" --^) getOrElse Nil - val isNoAlarms = "noalarms" --? - val isInsideAnt = "is-in-ant" --? -} - -object PartestSpec extends PartestSpec with Property { - lazy val propMapper = new PropertyMapper(PartestSpec) { - override def isPassThrough(key: String) = key == "partest.options" - } - - type ThisCommandLine = PartestCommandLine - class PartestCommandLine(args: List[String]) extends SpecCommandLine(args) { - override def errorFn(msg: String) = printAndExit("Error: " + msg) - - def propertyArgs = PartestSpec.propertyArgs - } - - override def creator(args: List[String]): PartestCommandLine = new PartestCommandLine(args) -} 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)) = + { + res._2 match { + case 0 => scala.xml.NodeSeq.Empty + case 1 => + case 2 => + } + } + + def testReport(kind: String, results: Iterable[(String, Int)], succs: Int, fails: Int) = + + + { + results.map(oneResult(_)) + } + +} diff --git a/src/partest/scala/tools/partest/Properties.scala b/src/partest/scala/tools/partest/Properties.scala deleted file mode 100644 index 4eeb0359ec..0000000000 --- a/src/partest/scala/tools/partest/Properties.scala +++ /dev/null @@ -1,18 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala Parallel Testing ** -** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -// $Id$ - -package scala.tools -package partest - -/** Loads partest.properties from the jar. */ -object Properties extends scala.util.PropertiesTrait { - protected def propCategory = "partest" - protected def pickJarBasedOn = classOf[Application] -} diff --git a/src/partest/scala/tools/partest/Results.scala b/src/partest/scala/tools/partest/Results.scala deleted file mode 100644 index 5d0e300136..0000000000 --- a/src/partest/scala/tools/partest/Results.scala +++ /dev/null @@ -1,121 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - */ - -package scala.tools -package partest - -import scala.collection.immutable - -trait Results { - self: Universe => - - /** A collection of tests for a Worker. - */ - case class TestsToRun(entities: List[TestEntity]) - - /** The response from a Worker who has been given TestsToRun. - */ - case class ResultsOfRun(results: immutable.Map[TestEntity, TestResult]) - - /** The result of a single test. (0: OK, 1: FAILED, 2: TIMEOUT) - */ - sealed abstract class TestResult(val state: Int, val description: String) { - def entity: TestEntity - - def passed = state == 0 - def colorize(s: String): String - def show(msg: String) = - if (!isShuttingDown) - showResult(colorize(description), msg) - - private def outputPrefix = if (isInsideAnt) "" else markNormal("partest: ") - private def name = src relativize entity.location // e.g. "neg/test.scala" - private def showResult(status: String, extraMsg: String) = - normal(outputPrefix + "[...]/%-40s [%s] %s\n".format(name, status, extraMsg)) - - override def equals(other: Any) = other match { - case x: TestResult => entity == x.entity - case _ => false - } - override def hashCode = entity.hashCode - override def toString = "%s [%s]".format(entity, description) - } - - class Success(val entity: TestEntity) extends TestResult(0, " OK ") { - def colorize(s: String) = markSuccess(s) - override def show(msg: String) = if (!isTerse) super.show(msg) - } - class Failure(val entity: TestEntity) extends TestResult(1, " FAILED ") { - def colorize(s: String) = markFailure(s) - - override def show(msg: String) = { - super.show(msg) - - if (isShowDiff || isTrace) - normal(entity.diffOutput) - - if (isShowLog || isTrace) - normal(toStringTrunc(entity.failureMessage(), 1600)) - } - override def toString = List(super.toString, toStringTrunc(entity.failureMessage(), 400)) mkString "\n" - } - class Timeout(val entity: TestEntity) extends TestResult(2, "TIME OUT") { - def colorize(s: String) = markFailure(s) - } - - object TestResult { - def apply(entity: TestEntity, success: Boolean) = - if (success) new Success(entity) - else new Failure(entity) - - def apply(entity: TestEntity, state: Int) = state match { - case 0 => new Success(entity) - case 1 => new Failure(entity) - case 2 => new Timeout(entity) - } - def unapply(x: Any) = x match { - case x: TestResult => Some((x.entity, x.state)) - case _ => None - } - } - - /** The combined results of any number of tests. - */ - case class CombinedTestResults( - passed: Int, - failed: Int, - elapsedMilliseconds: Long, - failures: List[TestResult] - ) { - // housekeeping - val elapsedSecs = elapsedMilliseconds / 1000 - val elapsedMins = elapsedSecs / 60 - val elapsedHrs = elapsedMins / 60 - val dispMins = elapsedMins - elapsedHrs * 60 - val dispSecs = elapsedSecs - elapsedMins * 60 - - def total = passed + failed - def hasFailures = failed > 0 - def exitCode = if (expectedErrors == failed) 0 else 1 - - def ++(x: CombinedTestResults) = CombinedTestResults( - passed + x.passed, - failed + x.failed, - elapsedMilliseconds + x.elapsedMilliseconds, - failures ::: x.failures - ) - - def elapsedString = "%02d:%02d:%02d".format(elapsedHrs, dispMins, dispSecs) - def failuresString = { - if (failures.isEmpty) "" - else "Summary of failures:" :: failures mkString ("\n", "\n", "") - } - - override def toString = - if (total == 0) "There were no tests to run." - else if (isDryRun) "%d tests would be run." format total - else if (hasFailures) "%d of %d tests failed (elapsed time: %s)".format(failed, total, elapsedString) + failuresString - else "All %d tests were successful (elapsed time: %s)".format(total, elapsedString) - } -} \ No newline at end of file diff --git a/src/partest/scala/tools/partest/Runner.scala b/src/partest/scala/tools/partest/Runner.scala deleted file mode 100644 index 1a28e60896..0000000000 --- a/src/partest/scala/tools/partest/Runner.scala +++ /dev/null @@ -1,36 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - * @author Philipp Haller - */ - -package scala.tools -package partest - -import nsc.io._ - -object Runner { - def main(args: Array[String]) { - val runner = Partest(args: _*) - import runner._ - - if (args.isEmpty) return println(helpMsg) - if (isValidate) return validateAll() - - printConfigBanner() - - if (isCleanup) - cleanupAll() - - val result = launchTestSuite() - val exitCode = result.exitCode - val message = "\n" + result + "\n" - - if (exitCode == 0) success(message) - else failure(message) - - if (isStats) - showTestStatistics() - - System exit exitCode - } -} diff --git a/src/partest/scala/tools/partest/Statistics.scala b/src/partest/scala/tools/partest/Statistics.scala deleted file mode 100644 index 2ea3c6e8f0..0000000000 --- a/src/partest/scala/tools/partest/Statistics.scala +++ /dev/null @@ -1,46 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - * @author Philipp Haller - */ - -package scala.tools -package partest - -import scala.collection.mutable.HashMap - -trait Statistics { - /** Only collected when --stats is given. */ - lazy val testStatistics = new HashMap[String, Long] - - /** Given function and block of code, evaluates code block, - * calls function with milliseconds elapsed, and returns block result. - */ - def timed[T](f: Long => Unit)(body: => T): T = { - val start = System.currentTimeMillis - val result = body - val end = System.currentTimeMillis - - f(end - start) - result - } - /** Times body and returns both values. - */ - def timed2[T](body: => T): (Long, T) = { - var milliSeconds = 0L - val result = timed(x => milliSeconds = x)(body) - - (milliSeconds, result) - } - - def resultsToStatistics(results: Iterable[(_, Int)]): (Int, Int) = - (results partition (_._2 == 0)) match { - case (winners, losers) => (winners.size, losers.size) - } - - def recordTestTiming(name: String, milliseconds: Long) = - synchronized { testStatistics(name) = milliseconds } - - def showTestStatistics() { - testStatistics.toList sortBy (-_._2) foreach { case (k, v) => println("%s: %.2f seconds".format(k, (v.toDouble / 1000))) } - } -} diff --git a/src/partest/scala/tools/partest/Universe.scala b/src/partest/scala/tools/partest/Universe.scala deleted file mode 100644 index 942fc1a8be..0000000000 --- a/src/partest/scala/tools/partest/Universe.scala +++ /dev/null @@ -1,96 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala Parallel Testing ** -** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala.tools -package partest - -import nsc.io._ -import category.AllCategories -import io.Logging - -/** The high level view of the partest infrastructure. - */ -abstract class Universe - extends Entities - with BuildContributors - with Logging - with Dispatcher - with Statistics - with Housekeeping - with Results - with PartestCompilation - with PartestSpec - with Config - with Alarms - with Actions - with Categories { - - /** The abstract values from which all else is derived. */ - def partestDir: Directory - def testBuildDir: Directory - def allCategories: List[TestCategory] - def selectedCategories: List[TestCategory] - - /** Some plausibly abstract types. */ - type TestBuild <: BuildContributor // e.g. quick, pack - type TestCategory <: AbsTestCategory // e.g. pos, neg, run - type TestEntity <: AbsTestEntity // e.g. files/pos/test25.scala - type TestSequence <: AbsTestSequence // e.g. compile, run, diff - - /** Although TestStep isn't much more than Function1 right now, - * it exists this way so it can become more capable. - */ - implicit def f1ToTestStep(f: TestEntity => Boolean): TestStep = - new TestStep { def apply(test: TestEntity) = f(test) } - - abstract class TestStep extends (TestEntity => Boolean) { - def apply(test: TestEntity): Boolean - } - - /** An umbrella category of tests, such as "pos" or "run". - */ - trait AbsTestCategory extends BuildContributor { - type TestSettings - - def kind: String - def testSequence: TestSequence - def denotesTest(location: Path): Boolean - - def createTest(location: Path): TestEntity - def createSettings(entity: TestEntity): TestSettings - def enumerate: List[TestEntity] - } - - /** A single test. It may involve multiple files, but only a - * single path is used to designate it. - */ - trait AbsTestEntity extends BuildContributor { - def category: TestCategory - def location: Path - def onException(x: Throwable): Unit - def testClasspath: String - - /** Most tests will use the sequence defined by the category, - * but the test can override and define a custom sequence. - */ - def testSequence: TestSequence - - /** True if this test recognizes the given path as a piece of it. - * For validation purposes. - */ - def acknowledges(path: Path): Boolean - } - - /** Every TestEntity is partly characterized by a series of actions - * which are applied to the TestEntity in the given order. The test - * passes if all those actions return true, fails otherwise. - */ - trait AbsTestSequence { - def actions: List[TestStep] - } -} \ No newline at end of file diff --git a/src/partest/scala/tools/partest/ant/JavaTask.scala b/src/partest/scala/tools/partest/ant/JavaTask.scala deleted file mode 100644 index 6740554dd8..0000000000 --- a/src/partest/scala/tools/partest/ant/JavaTask.scala +++ /dev/null @@ -1,57 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala Parallel Testing ** -** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala.tools -package partest -package ant - -import org.apache.tools.ant.Task -import org.apache.tools.ant.taskdefs.Java -import org.apache.tools.ant.types.Environment - -import scala.tools.nsc.io._ -import scala.tools.nsc.util.ClassPath -import cmd.Spec._ - -class JavaTask extends Java { - override def getTaskName() = "partest" - private val scalaRunnerClass = "scala.tools.nsc.MainGenericRunner" - private val partestRunnerClass = "scala.tools.partest.Runner" - def defaultJvmArgs = "-Xms64M -Xmx768M -Xss768K -XX:MaxPermSize=96M" - - protected def rootDir = prop("partest.rootdir") getOrElse (baseDir / "test").path - protected def partestJVMArgs = prop("partest.jvm.args") getOrElse defaultJvmArgs - protected def runnerArgs = List("-usejavacp", partestRunnerClass, "--javaopts", partestJVMArgs) - - private def baseDir = Directory(getProject.getBaseDir) - private def prop(s: String) = Option(getProject getProperty s) - private def jvmline(s: String) = returning(createJvmarg())(_ setLine s) - private def addArg(s: String) = returning(createArg())(_ setValue s) - - private def newKeyValue(key: String, value: String) = - returning(new Environment.Variable)(x => { x setKey key ; x setValue value }) - - def setDefaults() { - setFork(true) - setFailonerror(true) - getProject.setSystemProperties() - setClassname(scalaRunnerClass) - addSysproperty(newKeyValue("partest.is-in-ant", "true")) - jvmline(partestJVMArgs) - runnerArgs foreach addArg - - // do we want basedir or rootDir to be the cwd? - // setDir(Path(rootDir).jfile) - } - - override def init() = { - super.init() - setDefaults() - } -} - 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 @@ - + diff --git a/src/partest/scala/tools/partest/category/AllCategories.scala b/src/partest/scala/tools/partest/category/AllCategories.scala deleted file mode 100644 index 953f80324b..0000000000 --- a/src/partest/scala/tools/partest/category/AllCategories.scala +++ /dev/null @@ -1,20 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala Parallel Testing ** -** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala.tools -package partest -package category - -trait AllCategories extends Compiler with Analysis with Runner { - self: Universe => - - object Pos extends DirBasedCategory("pos") { lazy val testSequence: TestSequence = List(compile) } - object Neg extends DirBasedCategory("neg") { lazy val testSequence: TestSequence = List(checkFileRequired, not(compile), diff) } - object Run extends DirBasedCategory("run") { lazy val testSequence: TestSequence = List(compile, run, diff) } - object Jvm extends DirBasedCategory("jvm") { lazy val testSequence: TestSequence = List(compile, run, diff) } -} diff --git a/src/partest/scala/tools/partest/category/Analysis.scala b/src/partest/scala/tools/partest/category/Analysis.scala deleted file mode 100644 index 2c6c208ee5..0000000000 --- a/src/partest/scala/tools/partest/category/Analysis.scala +++ /dev/null @@ -1,64 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - */ - -package scala.tools -package partest -package category - -import java.lang.{ ClassLoader => JavaClassLoader } -import java.net.URL -import nsc.util.ScalaClassLoader -import nsc.io._ - -class PartestClassLoader(urls: Array[URL], parent: JavaClassLoader) extends ScalaClassLoader.URLClassLoader(urls, parent) { - def this(urls: Array[URL]) = this(urls, null) - def bytes(path: String) = findBytesForClassName(path) - def singleton(path: String) = tryToInitializeClass(path).get getField "MODULE$" get null - - /** Calls a method in an object via reflection. - */ - def apply[T](className: String, methodName: String)(args: Any*): T = { - def fail = error("Reflection failed on %s.%s".format(className, methodName)) - val clazz = tryToLoadClass(className) getOrElse fail - val obj = singleton(className) - val m = clazz.getMethods find (x => x.getName == methodName && x.getParameterTypes.size == args.size) getOrElse fail - - m.invoke(obj, args map (_.asInstanceOf[AnyRef]): _*).asInstanceOf[T] - } -} - -trait Analysis { - self: Universe => - - object Scalap extends DirBasedCategory("scalap") { - val testSequence: TestSequence = List(checkFileRequired, compile, run, diff) - override def denotesTest(p: Path) = p.isDirectory && (p.toDirectory.files exists (_.name == "result.test")) - override def createTest(location: Path) = new ScalapTest(location) - - class ScalapTest(val location: Path) extends TestEntity { - val category = Scalap - val scalapMain = "scala.tools.scalap.Main$" - val scalapMethod = "decompileScala" - - override def classpathPaths = super.classpathPaths :+ build.scalap - override def checkFile = File(location / "result.test") - - private def runnerURLs = build.classpathPaths ::: classpathPaths map (_.toURL) - private def createClassLoader = new PartestClassLoader(runnerURLs.toArray, this.getClass.getClassLoader) - - val isPackageObject = containsString("package object") - val suffix = if (isPackageObject) ".package" else "" - val className = location.name.capitalize + suffix - - override def run() = loggingResult { - def loader = createClassLoader - def bytes = loader.bytes(className) - - trace("scalap %s".format(className)) - if (isDryRun) "" - else loader[String](scalapMain, scalapMethod)(bytes, isPackageObject) - } - } - } -} diff --git a/src/partest/scala/tools/partest/category/Compiler.scala b/src/partest/scala/tools/partest/category/Compiler.scala deleted file mode 100644 index 49775d5031..0000000000 --- a/src/partest/scala/tools/partest/category/Compiler.scala +++ /dev/null @@ -1,140 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - */ - -package scala.tools -package partest -package category - -import nsc.io._ -import nsc.reporters._ -import nsc.{ Settings, CompilerCommand } -import scala.tools.nsc.interactive.RefinedBuildManager -import util.copyPath - -trait Compiler { - self: Universe => - - /** Resident Compiler. - * $SCALAC -d dir.obj -Xresident -sourcepath . "$@" - */ - object Res extends DirBasedCategory("res") { - lazy val testSequence: TestSequence = List(checkFileRequired, compile, diff) - - override def denotesTest(p: Path) = p.isDirectory && resFile(p).isFile - override def createTest(location: Path) = new ResidentTest(location.toDirectory) - - override def createSettings(entity: TestEntity): TestSettings = - returning(super.createSettings(entity)) { settings => - settings.resident.value = true - settings.sourcepath.value = entity.sourcesDir.path - } - - class ResidentTest(val location: Directory) extends TestEntity { - val category = Res - override def sourcesDir = categoryDir - - override def acknowledges(p: Path) = - super.acknowledges(p) || (resFile(location) isSame p) - - private def residentCompilerCommands = safeLines(resFile(location)) - private def compileResident(global: PartestGlobal, lines: List[String]) = { - def printPrompt = global inform "nsc> " - val results = - lines map { line => - printPrompt - trace("compile " + line) - isDryRun || global.partestCompile(toArgs(line) map (categoryDir / _ path), false) - } - - printPrompt - - /** Note - some res tests are really "neg" style tests, so we can't - * use the return value of the compile. The diff catches failures. - */ - true // results forall (_ == true) - } - - override def compile() = compileResident(newGlobal(Nil)._1, residentCompilerCommands) - } - private[Res] def resFile(p: Path) = p.toFile addExtension "res" - } - - object BuildManager extends DirBasedCategory("buildmanager") { - lazy val testSequence: TestSequence = List(checkFileRequired, compile, diff) - override def denotesTest(p: Path) = p.isDirectory && testFile(p).isFile - override def createTest(location: Path) = new BuildManagerTest(location.toDirectory) - - override def createSettings(entity: TestEntity): TestSettings = - returning[TestSettings](super.createSettings(entity)) { settings => - settings.Ybuildmanagerdebug.value = true - settings.sourcepath.value = entity.sourcesDir.path - } - - class PartestBuildManager(settings: Settings, val reporter: ConsoleReporter) extends RefinedBuildManager(settings) { - def errorFn(msg: String) = Console println msg - - override protected def newCompiler(newSettings: Settings) = - new BuilderGlobal(newSettings, reporter) - - private def filesToSet(pre: String, fs: List[String]): Set[AbstractFile] = - fs flatMap (s => Option(AbstractFile getFile (Path(settings.sourcepath.value) / s path))) toSet - - def buildManagerCompile(line: String): Boolean = { - val prompt = "builder > " - reporter printMessage (prompt + line) - val command = new CompilerCommand(toArgs(line), settings) - val files = filesToSet(settings.sourcepath.value, command.files) - - update(files, Set.empty) - true - } - } - - private[BuildManager] def testFile(p: Path) = (p / p.name addExtension "test").toFile - - class BuildManagerTest(val location: Directory) extends TestEntity { - val category = BuildManager - - override def sourcesDir = outDir - override def sourceFiles = Path onlyFiles (location walkFilter (_ != changesDir) filter isJavaOrScala toList) - override def checkFile = File(location / location.name addExtension "check") - - override def acknowledges(p: Path) = super.acknowledges(p) || (p isSame testFile(location)) - - def buildManagerCommands = safeLines(testFile(location)) - def changesDir = Directory(location / (location.name + ".changes")) - - override def compile() = { - val settings = createSettings(this) - val pbm = new PartestBuildManager(settings, newReporter(settings)) - - // copy files - for (source <- sourceFiles) { - val target = outDir / (location.normalize relativize source) - copyPath(source, target.toFile) - } - - def runUpdate(line: String) = { - val Array(srcName, replacement) = line split "=>" - copyPath(File(changesDir / replacement), File(outDir / srcName)) - } - - def sendCommand(line: String): Boolean = { - val compileRegex = """^>>compile (.*)$""".r - val updateRegex = """^>>update\s+(.*)""".r - trace("send: " + (line drop 2)) - - isDryRun || (line match { - case compileRegex(xs) => pbm.buildManagerCompile(xs) - case updateRegex(line) => runUpdate(line) - }) - } - - // send each line to the build manager - buildManagerCommands forall sendCommand - } - } - } -} - diff --git a/src/partest/scala/tools/partest/category/Runner.scala b/src/partest/scala/tools/partest/category/Runner.scala deleted file mode 100644 index 10bf5794a9..0000000000 --- a/src/partest/scala/tools/partest/category/Runner.scala +++ /dev/null @@ -1,108 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala Parallel Testing ** -** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala.tools -package partest -package category - -import nsc.io._ - -trait Runner { - self: Universe => - - /** Shootout. - */ - object Shootout extends DirBasedCategory("shootout") { - lazy val testSequence: TestSequence = List(compile, run, diff) - - override def denotesTest(p: Path) = isScala(p) && runner(p).isFile - override def createTest(location: Path) = new ShootoutTest(location.toFile) - - class ShootoutTest(val location: File) extends TestEntity { - val category = Shootout - // The files in shootout are very free form, so acknowledge anything close. - override def acknowledges(p: Path) = - (p.parent.normalize isSame Shootout.root) && (p.name startsWith label) - - private def generated = File(outDir / "test.scala") - private def runnerFile = runner(location) - override def sourceFiles = List(generated) - - override def compile() = { - trace("generate %s from %s, %s".format(tracePath(generated), tracePath(location), tracePath(runnerFile))) - // generate source file (even on dry run, we need the path) - generated.writeAll(location.slurp(), runnerFile.slurp()) - - // compile generated file - super.compile() - } - } - - private[Shootout] def runner(p: Path) = p addExtension "runner" toFile - } - - object Scalacheck extends DirBasedCategory("scalacheck") { - lazy val testSequence: TestSequence = List(compile, run) - override def createTest(location: Path) = new ScalacheckTest(location) - - class ScalacheckTest(val location: Path) extends TestEntity { - val category = Scalacheck - - import build.{ scalacheck, forkjoin } - import org.scalacheck.Properties - import org.scalacheck.Test.{ checkProperties, defaultParams, Result } - - override def classpathPaths = super.classpathPaths ::: List(scalacheck, forkjoin) - private def arrayURLs = Array(scalacheck, outDir) map (_.toURL) - - /** For reasons I'm not entirely clear on, I've written all this - * to avoid a source dependency on scalacheck. - */ - class ScalacheckClassLoader extends PartestClassLoader(arrayURLs, this.getClass.getClassLoader) { - type ScalacheckResult = { def passed: Boolean } - - def propCallback(name: String, passed: Int, discarded: Int): Unit = () - def testCallback(name: String, result: AnyRef): Unit = () - - val test = singleton("Test$") - val params = apply[AnyRef]("org.scalacheck.Test$", "defaultParams")() - val result = apply[Seq[(String, AnyRef)]]("org.scalacheck.Test$", "checkProperties")(test, params, propCallback _, testCallback _) - - def allResults() = - for ((prop, res) <- result) yield { - ScalacheckTest.this.trace("%s: %s".format(prop, res)) - res.asInstanceOf[ScalacheckResult].passed - } - - def check() = allResults forall (_ == true) - } - - override def run() = { - trace("scalacheck runs via classloader with: %s".format(arrayURLs mkString ", ")) - isDryRun || (new ScalacheckClassLoader check) - } - } - } - - object Script extends DirBasedCategory("script") { - val testSequence: TestSequence = List(exec, diff) - override def createTest(location: Path) = new ScriptTest(location) - - class ScriptTest(val location: Path) extends TestEntity { - val category = Script - val scriptFile = if (location.isDirectory) location / (label + ".scala") else location - val argsFile = withExtension("args").toFile - def batFile = scriptFile changeExtension "bat" - def script = if (Properties.isWin) batFile else scriptFile - - override def acknowledges(p: Path) = super.acknowledges(p) || (List(argsFile, batFile) exists (_ isSame p)) - override def execCwd = Some(sourcesDir) - override def argumentsToExec = script.path :: safeArgs(argsFile) - } - } -} \ No newline at end of file diff --git a/src/partest/scala/tools/partest/io/ANSIWriter.scala b/src/partest/scala/tools/partest/io/ANSIWriter.scala deleted file mode 100644 index 0ddcd97a5f..0000000000 --- a/src/partest/scala/tools/partest/io/ANSIWriter.scala +++ /dev/null @@ -1,58 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - * @author Philipp Haller - */ - -package scala.tools -package partest -package io - -import java.io.{ Writer, PrintWriter, OutputStream, OutputStreamWriter } - -object ANSIWriter { - val NONE = 0 - val SOME = 1 - val MANY = 2 - - def apply(isAnsi: Boolean) = if (isAnsi) MANY else NONE -} -import ANSIWriter._ - -class ANSIWriter(writer: Writer) extends PrintWriter(writer, true) { - def this(out: OutputStream) = this(new OutputStreamWriter(out)) - def colorful: Int = NONE - - protected val manyColors = List( - Console.BOLD + Console.BLACK, - Console.BOLD + Console.GREEN, - Console.BOLD + Console.RED, - Console.BOLD + Console.YELLOW, - Console.RESET - ) - protected val someColors = List( - Console.BOLD + Console.BLACK, - Console.RESET, - Console.BOLD + Console.BLACK, - Console.BOLD + Console.BLACK, - Console.RESET - ) - protected val noColors = List("", "", "", "", "") - - lazy val List(_outline, _success, _failure, _warning, _default) = colorful match { - case NONE => noColors - case SOME => someColors - case MANY => manyColors - case _ => noColors - } - - private def wrprint(msg: String): Unit = synchronized { - print(msg) - flush() - } - - def outline(msg: String) = wrprint(_outline + msg + _default) - def success(msg: String) = wrprint(_success + msg + _default) - def failure(msg: String) = wrprint(_failure + msg + _default) - def warning(msg: String) = wrprint(_warning + msg + _default) - def normal(msg: String) = wrprint(_default + msg) -} diff --git a/src/partest/scala/tools/partest/io/Diff.java b/src/partest/scala/tools/partest/io/Diff.java deleted file mode 100644 index c7a3d42f30..0000000000 --- a/src/partest/scala/tools/partest/io/Diff.java +++ /dev/null @@ -1,874 +0,0 @@ -// $Id$ - -package scala.tools.partest.io; - -import java.util.Hashtable; - -/** A class to compare IndexedSeqs of objects. The result of comparison - is a list of change 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 equals - and hashcode methods for the objects compared. -

- The basic algorithm is described in:
- "An O(ND) Difference Algorithm and its Variations", Eugene Myers, - Algorithmica Vol. 1 No. 2, 1986, p 251. -

- 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. -

- 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. -

- 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. -

- 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 Diff { - - /** Prepare to find differences between two arrays. Each element of - the arrays is translated to an "equivalence number" based on - the result of equals. 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. -

- 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/io/DiffPrint.java b/src/partest/scala/tools/partest/io/DiffPrint.java deleted file mode 100644 index 2b2ad93ec7..0000000000 --- a/src/partest/scala/tools/partest/io/DiffPrint.java +++ /dev/null @@ -1,607 +0,0 @@ -// $Id$ - -package scala.tools.partest.io; - -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 Diff. - @see bmsi.util.Diff - @author Stuart D. Gathman - Copyright (C) 2000 Business Management Systems, Inc. -

- 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. -

- 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. -

- 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 - print_hunk 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 ed. - The edit script must be generated with the reverse option to - be useful as actual ed 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/io/JUnitReport.scala b/src/partest/scala/tools/partest/io/JUnitReport.scala deleted file mode 100644 index 63ae200020..0000000000 --- a/src/partest/scala/tools/partest/io/JUnitReport.scala +++ /dev/null @@ -1,38 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - */ - -package scala.tools -package partest -package io - -/** This is disabled for the moment but I can fix it up if anyone - * is using it. - */ -class JUnitReport { - // create JUnit Report xml files if directory was specified - // def junitReport(dir: Directory) = { - // dir.mkdir() - // val report = testReport(set.kind, results, succs, fails) - // XML.save("%s/%s.xml".format(d.toAbsolute.path, set.kind), report) - // } - - // def oneResult(res: (TestEntity, Int)) = - // { - // res._2 match { - // case 0 => scala.xml.NodeSeq.Empty - // case 1 => - // case 2 => - // } - // } - // - // def testReport(kind: String, results: Iterable[(TestEntity, Int)], succs: Int, fails: Int) = { - // - // - // { - // results.map(oneResult(_)) - // } - // - // } - // -} \ No newline at end of file diff --git a/src/partest/scala/tools/partest/io/Logging.scala b/src/partest/scala/tools/partest/io/Logging.scala deleted file mode 100644 index 52239ffb2c..0000000000 --- a/src/partest/scala/tools/partest/io/Logging.scala +++ /dev/null @@ -1,137 +0,0 @@ -package scala.tools -package partest -package io - -import java.io.{ StringWriter, PrintWriter, Writer } -import scala.tools.nsc.io._ -import scala.util.control.ControlThrowable - -trait Logging { - universe: Universe => - - class PartestANSIWriter extends ANSIWriter(Console.out) { - override def colorful: Int = ANSIWriter(universe.isAnsi) - private def printIf(cond: Boolean, msg: String) = - if (cond) { outline("debug: ") ; println(msg) } - - val verbose = printIf(isVerbose || isDebug, _: String) - val debug = printIf(isDebug, _: String) - } - - lazy val NestUI = new PartestANSIWriter() - - import NestUI.{ _outline, _success, _failure, _warning, _default } - - def markOutline(msg: String) = _outline + msg + _default - def markSuccess(msg: String) = _success + msg + _default - def markFailure(msg: String) = _failure + msg + _default - def markWarning(msg: String) = _warning + msg + _default - def markNormal(msg: String) = _default + msg - - def outline(msg: String) = NestUI outline msg - def success(msg: String) = NestUI success msg - def failure(msg: String) = NestUI failure msg - def warning(msg: String) = NestUI warning msg - def normal(msg: String) = NestUI normal msg - - def verbose(msg: String) = NestUI verbose msg - def debug(msg: String) = NestUI debug msg - - trait EntityLogging { - self: TestEntity => - - lazy val logWriter = new LogWriter(logFile) - - /** Redirect stdout and stderr to logFile, run body, return result. - */ - def loggingOutAndErr[T](body: => T): T = { - val log = logFile.printStream(append = true) - - try Console.withOut(log) { - Console.withErr(log) { - body - } - } - finally log.close() - } - - /** What to print in a failure summary. - */ - def failureMessage() = if (diffOutput != "") diffOutput else safeSlurp(logFile) - - /** For tracing. Outputs a line describing the next action. tracePath - * is a path wrapper which prints name or full path depending on verbosity. - */ - def trace(msg: String) = if (isTrace || isDryRun) System.err.println(">> [%s] %s".format(label, msg)) - - def tracePath(path: Path): String = if (isVerbose) path.path else path.name - def tracePath(path: String): String = tracePath(Path(path)) - - /** v == verbose. - */ - def vtrace(msg: String) = if (isVerbose) trace(msg) - - /** Run body, writes result to logFile. Any throwable is - * caught, stringified, and written to the log. - */ - def loggingResult(body: => String) = - try returning(true)(_ => logFile writeAll body) - catch { - case x: ControlThrowable => throw x - case x: InterruptedException => debug(this + " received interrupt, failing.\n") ; false - case x: Throwable => logException(x) - } - - def throwableToString(x: Throwable): String = { - val w = new StringWriter - x.printStackTrace(new PrintWriter(w)) - w.toString - } - - def warnAndLog(str: String) = { - warning(toStringTrunc(str, 800)) - logWriter append str - } - - def warnAndLogException(msg: String, ex: Throwable) = - warnAndLog(msg + throwableToString(ex)) - - def deleteLog(force: Boolean = false) = - if (universe.isNoCleanup && !force) debug("Not cleaning up " + logFile) - else logFile.deleteIfExists() - - def onException(x: Throwable) { logException(x) } - def logException(x: Throwable) = { - val msg = throwableToString(x) - if (!isTerse) - normal(msg) - - logWriter append msg - false - } - } - - /** A writer which doesn't create the file until a write comes in. - */ - class LazilyCreatedWriter(log: File) extends Writer { - @volatile private var isCreated = false - private lazy val underlying = { - isCreated = true - log.bufferedWriter() - } - - def flush() = if (isCreated) underlying.flush() - def close() = if (isCreated) underlying.close() - def write(chars: Array[Char], off: Int, len: Int) = { - underlying.write(chars, off, len) - underlying.flush() - } - } - - class LogWriter(log: File) extends PrintWriter(new LazilyCreatedWriter(log), true) { - override def print(s: String) = { - super.print(s) - flush() - } - } -} \ No newline at end of file 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 change 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 equals + and hashcode methods for the objects compared. +

+ The basic algorithm is described in:
+ "An O(ND) Difference Algorithm and its Variations", Eugene Myers, + Algorithmica Vol. 1 No. 2, 1986, p 251. +

+ 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. +

+ 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. +

+ 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. +

+ 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 Diff { + + /** Prepare to find differences between two arrays. Each element of + the arrays is translated to an "equivalence number" based on + the result of equals. 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. +

+ 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 Diff. + @see bmsi.util.Diff + @author Stuart D. Gathman + Copyright (C) 2000 Business Management Systems, Inc. +

+ 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. +

+ 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. +

+ 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 + print_hunk 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 ed. + The edit script must be generated with the reverse option to + be useful as actual ed 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 [] [ ..] []") + println(" : list of files ending in '.scala'") + println(" : a file not ending in '.scala'") + println(" :") + 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 run all tests whose source file contains ") + 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 /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 /test/files + lazy val srcDir = Directory(testRoot / srcDirName normalize) + + // Directory /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 command redirecting standard out and + * error out to output 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 script 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/util/package.scala b/src/partest/scala/tools/partest/util/package.scala deleted file mode 100644 index bc5470ba5d..0000000000 --- a/src/partest/scala/tools/partest/util/package.scala +++ /dev/null @@ -1,61 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - */ - -package scala.tools -package partest - -import java.util.{ Timer, TimerTask } -import java.io.StringWriter -import nsc.io._ - -/** Misc code still looking for a good home. - */ -package object util { - - def allPropertiesString() = javaHashtableToString(System.getProperties) - - private def javaHashtableToString(table: java.util.Hashtable[_,_]) = { - import collection.JavaConversions._ - (table.toList map { case (k, v) => "%s -> %s\n".format(k, v) }).sorted mkString - } - - def filesToSet(pre: String, fs: List[String]): Set[AbstractFile] = - fs flatMap (x => Option(AbstractFile getFile (Path(pre) / x).path)) toSet - - /** Copies one Path to another Path, trying to be sensible when one or the - * other is a Directory. Returns true if it believes it succeeded. - */ - def copyPath(from: Path, to: Path): Boolean = { - if (!to.parent.isDirectory) - to.parent.createDirectory(force = true) - - def copyDir = { - val sub = to / from.name createDirectory true - from.toDirectory.list forall (x => copyPath(x, sub)) - } - (from.isDirectory, to.isDirectory) match { - case (true, true) => copyDir - case (true, false) => false - case (false, true) => from.toFile copyTo (to / from.name) - case (false, false) => from.toFile copyTo to - } - } - - /** - * 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 diffFiles(f1: File, f2: File): String = { - val diffWriter = new StringWriter - val args = Array(f1.toAbsolute.path, f2.toAbsolute.path) - - io.DiffPrint.doDiff(args, diffWriter) - val result = diffWriter.toString - if (result == "No differences") "" else result - } -} 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/continuations/neg/function0.check b/test/continuations/neg/function0.check deleted file mode 100644 index 0a66763a0f..0000000000 --- a/test/continuations/neg/function0.check +++ /dev/null @@ -1,6 +0,0 @@ -function0.scala:11: error: type mismatch; - found : () => Int @scala.util.continuations.cpsParam[Int,Int] - required: () => Int - val g: () => Int = f - ^ -one error found diff --git a/test/continuations/neg/function0.scala b/test/continuations/neg/function0.scala deleted file mode 100644 index 4112ee3835..0000000000 --- a/test/continuations/neg/function0.scala +++ /dev/null @@ -1,16 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def main(args: Array[String]): Any = { - - val f = () => shift { k: (Int=>Int) => k(7) } - val g: () => Int = f - - println(reset(g())) - } - -} \ No newline at end of file diff --git a/test/continuations/neg/function2.check b/test/continuations/neg/function2.check deleted file mode 100644 index 4833057652..0000000000 --- a/test/continuations/neg/function2.check +++ /dev/null @@ -1,6 +0,0 @@ -function2.scala:11: error: type mismatch; - found : () => Int - required: () => Int @util.continuations.package.cps[Int] - val g: () => Int @cps[Int] = f - ^ -one error found diff --git a/test/continuations/neg/function2.scala b/test/continuations/neg/function2.scala deleted file mode 100644 index ae0fda509d..0000000000 --- a/test/continuations/neg/function2.scala +++ /dev/null @@ -1,16 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def main(args: Array[String]): Any = { - - val f = () => 7 - val g: () => Int @cps[Int] = f - - println(reset(g())) - } - -} \ No newline at end of file diff --git a/test/continuations/neg/function3.check b/test/continuations/neg/function3.check deleted file mode 100644 index 4705ad9ed9..0000000000 --- a/test/continuations/neg/function3.check +++ /dev/null @@ -1,6 +0,0 @@ -function3.scala:10: error: type mismatch; - found : Int @scala.util.continuations.cpsParam[Int,Int] - required: Int - val g: () => Int = () => shift { k: (Int=>Int) => k(7) } - ^ -one error found diff --git a/test/continuations/neg/function3.scala b/test/continuations/neg/function3.scala deleted file mode 100644 index 0c3f1667e5..0000000000 --- a/test/continuations/neg/function3.scala +++ /dev/null @@ -1,15 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def main(args: Array[String]): Any = { - - val g: () => Int = () => shift { k: (Int=>Int) => k(7) } - - println(reset(g())) - } - -} \ No newline at end of file diff --git a/test/continuations/neg/infer0.check b/test/continuations/neg/infer0.check deleted file mode 100644 index 1dd072ef09..0000000000 --- a/test/continuations/neg/infer0.check +++ /dev/null @@ -1,4 +0,0 @@ -infer0.scala:11: error: cannot cps-transform expression 8: type arguments [Int(8),String,Int] do not conform to method shiftUnit's type parameter bounds [A,B,C >: B] - test(8) - ^ -one error found diff --git a/test/continuations/neg/infer0.scala b/test/continuations/neg/infer0.scala deleted file mode 100644 index 894d5228b1..0000000000 --- a/test/continuations/neg/infer0.scala +++ /dev/null @@ -1,14 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def test(x: => Int @cpsParam[String,Int]) = 7 - - def main(args: Array[String]): Any = { - test(8) - } - -} \ No newline at end of file diff --git a/test/continuations/neg/infer2.check b/test/continuations/neg/infer2.check deleted file mode 100644 index 59eb670bc3..0000000000 --- a/test/continuations/neg/infer2.check +++ /dev/null @@ -1,4 +0,0 @@ -infer2.scala:14: error: illegal answer type modification: scala.util.continuations.cpsParam[String,Int] andThen scala.util.continuations.cpsParam[String,Int] - test { sym(); sym() } - ^ -one error found diff --git a/test/continuations/neg/infer2.scala b/test/continuations/neg/infer2.scala deleted file mode 100644 index a890ac1fc4..0000000000 --- a/test/continuations/neg/infer2.scala +++ /dev/null @@ -1,19 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def test(x: => Int @cpsParam[String,Int]) = 7 - - def sym() = shift { k: (Int => String) => 9 } - - - def main(args: Array[String]): Any = { - test { sym(); sym() } - } - -} - - diff --git a/test/continuations/neg/lazy.check b/test/continuations/neg/lazy.check deleted file mode 100644 index bfa44c59a4..0000000000 --- a/test/continuations/neg/lazy.check +++ /dev/null @@ -1,6 +0,0 @@ -lazy.scala:5: error: type mismatch; - found : Unit @scala.util.continuations.cpsParam[Unit,Unit] - required: Unit - def foo = { - ^ -one error found diff --git a/test/continuations/neg/lazy.scala b/test/continuations/neg/lazy.scala deleted file mode 100644 index dffc57ffa0..0000000000 --- a/test/continuations/neg/lazy.scala +++ /dev/null @@ -1,16 +0,0 @@ -import scala.util.continuations._ - -object Test { - - def foo = { - lazy val x = shift((k:Unit=>Unit)=>k()) - println(x) - } - - def main(args: Array[String]) { - reset { - foo - } - } - -} \ No newline at end of file diff --git a/test/continuations/neg/t1929.check b/test/continuations/neg/t1929.check deleted file mode 100644 index f42c3a1e15..0000000000 --- a/test/continuations/neg/t1929.check +++ /dev/null @@ -1,6 +0,0 @@ -t1929.scala:8: error: type mismatch; - found : Int @scala.util.continuations.cpsParam[String,java.lang.String] @scala.util.continuations.cpsSynth - required: Int @scala.util.continuations.cpsParam[Int,java.lang.String] - reset { - ^ -one error found diff --git a/test/continuations/neg/t1929.scala b/test/continuations/neg/t1929.scala deleted file mode 100644 index 02eda9170d..0000000000 --- a/test/continuations/neg/t1929.scala +++ /dev/null @@ -1,17 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - def main(args : Array[String]) { - reset { - println("up") - val x = shift((k:Int=>String) => k(8) + k(2)) - println("down " + x) - val y = shift((k:Int=>String) => k(3)) - println("down2 " + y) - y + x - } - } -} \ No newline at end of file diff --git a/test/continuations/neg/t2285.check b/test/continuations/neg/t2285.check deleted file mode 100644 index d5dff6a4f2..0000000000 --- a/test/continuations/neg/t2285.check +++ /dev/null @@ -1,6 +0,0 @@ -t2285.scala:9: error: type mismatch; - found : Int @scala.util.continuations.cpsParam[String,String] @scala.util.continuations.cpsSynth - required: Int @scala.util.continuations.cpsParam[Int,String] - def foo() = reset { bar(); 7 } - ^ -one error found diff --git a/test/continuations/neg/t2285.scala b/test/continuations/neg/t2285.scala deleted file mode 100644 index f3c7f4c89c..0000000000 --- a/test/continuations/neg/t2285.scala +++ /dev/null @@ -1,11 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - -object Test { - - def bar() = shift { k: (String => String) => k("1") } - - def foo() = reset { bar(); 7 } - -} diff --git a/test/continuations/neg/t2949.check b/test/continuations/neg/t2949.check deleted file mode 100644 index dd9768807c..0000000000 --- a/test/continuations/neg/t2949.check +++ /dev/null @@ -1,6 +0,0 @@ -t2949.scala:13: error: type mismatch; - found : Int - required: ? @scala.util.continuations.cpsParam[List[?],Any] - x * y - ^ -one error found diff --git a/test/continuations/neg/t2949.scala b/test/continuations/neg/t2949.scala deleted file mode 100644 index ce27c7c0e8..0000000000 --- a/test/continuations/neg/t2949.scala +++ /dev/null @@ -1,15 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - -object Test { - - def reflect[A,B](xs : List[A]) = shift{ xs.flatMap[B, List[B]] } - def reify[A, B](x : A @cpsParam[List[A], B]) = reset{ List(x) } - - def main(args: Array[String]): Unit = println(reify { - val x = reflect[Int, Int](List(1,2,3)) - val y = reflect[Int, Int](List(2,4,8)) - x * y - }) -} diff --git a/test/continuations/neg/trycatch2.check b/test/continuations/neg/trycatch2.check deleted file mode 100644 index 5ff2838bad..0000000000 --- a/test/continuations/neg/trycatch2.check +++ /dev/null @@ -1,7 +0,0 @@ -trycatch2.scala:11: error: only simple cps types allowed in try/catch blocks (found: Int @scala.util.continuations.cpsParam[String,Int]) - def foo1 = try { - ^ -trycatch2.scala:19: error: only simple cps types allowed in try/catch blocks (found: Int @scala.util.continuations.cpsParam[String,Int]) - def foo2 = try { - ^ -two errors found diff --git a/test/continuations/neg/trycatch2.scala b/test/continuations/neg/trycatch2.scala deleted file mode 100644 index 761cee52ac..0000000000 --- a/test/continuations/neg/trycatch2.scala +++ /dev/null @@ -1,33 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - -object Test { - - def fatal[T]: T = throw new Exception - def cpsIntStringInt = shift { k:(Int=>String) => k(3); 7 } - def cpsIntIntString = shift { k:(Int=>Int) => k(3); "7" } - - def foo1 = try { - fatal[Int] - cpsIntStringInt - } catch { - case ex => - cpsIntStringInt - } - - def foo2 = try { - fatal[Int] - cpsIntStringInt - } catch { - case ex => - cpsIntStringInt - } - - - def main(args: Array[String]): Unit = { - println(reset { foo1; "3" }) - println(reset { foo2; "3" }) - } - -} \ No newline at end of file diff --git a/test/continuations/run/basics.check b/test/continuations/run/basics.check deleted file mode 100755 index 54c059fdcb..0000000000 --- a/test/continuations/run/basics.check +++ /dev/null @@ -1,2 +0,0 @@ -28 -28 \ No newline at end of file diff --git a/test/continuations/run/basics.scala b/test/continuations/run/basics.scala deleted file mode 100755 index 9df209b11c..0000000000 --- a/test/continuations/run/basics.scala +++ /dev/null @@ -1,23 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def m0() = { - shift((k:Int => Int) => k(k(7))) * 2 - } - - def m1() = { - 2 * shift((k:Int => Int) => k(k(7))) - } - - def main(args: Array[String]) = { - - println(reset(m0())) - println(reset(m1())) - - } - -} diff --git a/test/continuations/run/function1.check b/test/continuations/run/function1.check deleted file mode 100644 index 7f8f011eb7..0000000000 --- a/test/continuations/run/function1.check +++ /dev/null @@ -1 +0,0 @@ -7 diff --git a/test/continuations/run/function1.scala b/test/continuations/run/function1.scala deleted file mode 100644 index 3b39722e3a..0000000000 --- a/test/continuations/run/function1.scala +++ /dev/null @@ -1,16 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def main(args: Array[String]): Any = { - - val f = () => shift { k: (Int=>Int) => k(7) } - val g: () => Int @cps[Int] = f - - println(reset(g())) - } - -} \ No newline at end of file diff --git a/test/continuations/run/function4.check b/test/continuations/run/function4.check deleted file mode 100644 index c7930257df..0000000000 --- a/test/continuations/run/function4.check +++ /dev/null @@ -1 +0,0 @@ -7 \ No newline at end of file diff --git a/test/continuations/run/function4.scala b/test/continuations/run/function4.scala deleted file mode 100644 index b73eedb02c..0000000000 --- a/test/continuations/run/function4.scala +++ /dev/null @@ -1,15 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def main(args: Array[String]): Any = { - - val g: () => Int @cps[Int] = () => shift { k: (Int=>Int) => k(7) } - - println(reset(g())) - } - -} \ No newline at end of file diff --git a/test/continuations/run/function5.check b/test/continuations/run/function5.check deleted file mode 100644 index c7930257df..0000000000 --- a/test/continuations/run/function5.check +++ /dev/null @@ -1 +0,0 @@ -7 \ No newline at end of file diff --git a/test/continuations/run/function5.scala b/test/continuations/run/function5.scala deleted file mode 100644 index a689ccf243..0000000000 --- a/test/continuations/run/function5.scala +++ /dev/null @@ -1,15 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def main(args: Array[String]): Any = { - - val g: () => Int @cps[Int] = () => 7 - - println(reset(g())) - } - -} \ No newline at end of file diff --git a/test/continuations/run/function6.check b/test/continuations/run/function6.check deleted file mode 100644 index c7930257df..0000000000 --- a/test/continuations/run/function6.check +++ /dev/null @@ -1 +0,0 @@ -7 \ No newline at end of file diff --git a/test/continuations/run/function6.scala b/test/continuations/run/function6.scala deleted file mode 100644 index 1a2792370a..0000000000 --- a/test/continuations/run/function6.scala +++ /dev/null @@ -1,16 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def main(args: Array[String]): Any = { - - val g: PartialFunction[Int, Int @cps[Int]] = { case x => 7 } - - println(reset(g(2))) - - } - -} \ No newline at end of file diff --git a/test/continuations/run/ifelse0.check b/test/continuations/run/ifelse0.check deleted file mode 100644 index f8bc79860d..0000000000 --- a/test/continuations/run/ifelse0.check +++ /dev/null @@ -1,2 +0,0 @@ -10 -9 \ No newline at end of file diff --git a/test/continuations/run/ifelse0.scala b/test/continuations/run/ifelse0.scala deleted file mode 100644 index e34b86ee84..0000000000 --- a/test/continuations/run/ifelse0.scala +++ /dev/null @@ -1,18 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def test(x:Int) = if (x <= 7) - shift { k: (Int=>Int) => k(k(k(x))) } - else - shift { k: (Int=>Int) => k(x) } - - def main(args: Array[String]): Any = { - println(reset(1 + test(7))) - println(reset(1 + test(8))) - } - -} \ No newline at end of file diff --git a/test/continuations/run/ifelse1.check b/test/continuations/run/ifelse1.check deleted file mode 100644 index 86a3fbc0c1..0000000000 --- a/test/continuations/run/ifelse1.check +++ /dev/null @@ -1,4 +0,0 @@ -10 -9 -8 -11 \ No newline at end of file diff --git a/test/continuations/run/ifelse1.scala b/test/continuations/run/ifelse1.scala deleted file mode 100644 index 2ccc1ed730..0000000000 --- a/test/continuations/run/ifelse1.scala +++ /dev/null @@ -1,25 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def test1(x:Int) = if (x <= 7) - shift { k: (Int=>Int) => k(k(k(x))) } - else - x - - def test2(x:Int) = if (x <= 7) - x - else - shift { k: (Int=>Int) => k(k(k(x))) } - - def main(args: Array[String]): Any = { - println(reset(1 + test1(7))) - println(reset(1 + test1(8))) - println(reset(1 + test2(7))) - println(reset(1 + test2(8))) - } - -} \ No newline at end of file diff --git a/test/continuations/run/ifelse2.check b/test/continuations/run/ifelse2.check deleted file mode 100644 index f97a95b08d..0000000000 --- a/test/continuations/run/ifelse2.check +++ /dev/null @@ -1,4 +0,0 @@ -abort -() -alive -() diff --git a/test/continuations/run/ifelse2.scala b/test/continuations/run/ifelse2.scala deleted file mode 100644 index 536e350190..0000000000 --- a/test/continuations/run/ifelse2.scala +++ /dev/null @@ -1,16 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def test(x:Int) = if (x <= 7) - shift { k: (Unit=>Unit) => println("abort") } - - def main(args: Array[String]): Any = { - println(reset{ test(7); println("alive") }) - println(reset{ test(8); println("alive") }) - } - -} \ No newline at end of file diff --git a/test/continuations/run/ifelse3.check b/test/continuations/run/ifelse3.check deleted file mode 100644 index 95b562c8e6..0000000000 --- a/test/continuations/run/ifelse3.check +++ /dev/null @@ -1,2 +0,0 @@ -6 -9 diff --git a/test/continuations/run/ifelse3.scala b/test/continuations/run/ifelse3.scala deleted file mode 100644 index 5dbd079d1c..0000000000 --- a/test/continuations/run/ifelse3.scala +++ /dev/null @@ -1,21 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def util(x: Boolean) = shift { k: (Boolean=>Int) => k(x) } - - def test(x:Int) = if (util(x <= 7)) - x - 1 - else - x + 1 - - - def main(args: Array[String]): Any = { - println(reset(test(7))) - println(reset(test(8))) - } - -} \ No newline at end of file diff --git a/test/continuations/run/infer1.scala b/test/continuations/run/infer1.scala deleted file mode 100644 index a6c6c07215..0000000000 --- a/test/continuations/run/infer1.scala +++ /dev/null @@ -1,33 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def test(x: => Int @cpsParam[String,Int]) = 7 - - def test2() = { - val x = shift { k: (Int => String) => 9 } - x - } - - def test3(x: => Int @cpsParam[Int,Int]) = 7 - - - def util() = shift { k: (String => String) => "7" } - - def main(args: Array[String]): Any = { - test { shift { k: (Int => String) => 9 } } - test { shift { k: (Int => String) => 9 }; 2 } -// test { shift { k: (Int => String) => 9 }; util() } <-- doesn't work - test { shift { k: (Int => String) => 9 }; util(); 2 } - - - test { shift { k: (Int => String) => 9 }; { test3(0); 2 } } - - test3 { { test3(0); 2 } } - - } - -} \ No newline at end of file diff --git a/test/continuations/run/match0.check b/test/continuations/run/match0.check deleted file mode 100644 index f8bc79860d..0000000000 --- a/test/continuations/run/match0.check +++ /dev/null @@ -1,2 +0,0 @@ -10 -9 \ No newline at end of file diff --git a/test/continuations/run/match0.scala b/test/continuations/run/match0.scala deleted file mode 100644 index bd36238d7f..0000000000 --- a/test/continuations/run/match0.scala +++ /dev/null @@ -1,18 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def test(x:Int) = x match { - case 7 => shift { k: (Int=>Int) => k(k(k(x))) } - case 8 => shift { k: (Int=>Int) => k(x) } - } - - def main(args: Array[String]): Any = { - println(reset(1 + test(7))) - println(reset(1 + test(8))) - } - -} \ No newline at end of file diff --git a/test/continuations/run/match1.check b/test/continuations/run/match1.check deleted file mode 100644 index 73053d3f4f..0000000000 --- a/test/continuations/run/match1.check +++ /dev/null @@ -1,2 +0,0 @@ -10 -9 diff --git a/test/continuations/run/match1.scala b/test/continuations/run/match1.scala deleted file mode 100644 index ea4e219666..0000000000 --- a/test/continuations/run/match1.scala +++ /dev/null @@ -1,18 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def test(x:Int) = x match { - case 7 => shift { k: (Int=>Int) => k(k(k(x))) } - case _ => x - } - - def main(args: Array[String]): Any = { - println(reset(1 + test(7))) - println(reset(1 + test(8))) - } - -} \ No newline at end of file diff --git a/test/continuations/run/match2.check b/test/continuations/run/match2.check deleted file mode 100644 index cbf91349cc..0000000000 --- a/test/continuations/run/match2.check +++ /dev/null @@ -1,2 +0,0 @@ -B -B diff --git a/test/continuations/run/match2.scala b/test/continuations/run/match2.scala deleted file mode 100644 index 8d4f04870f..0000000000 --- a/test/continuations/run/match2.scala +++ /dev/null @@ -1,26 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def test1() = { - val (a, b) = shift { k: (((String,String)) => String) => k("A","B") } - b - } - - case class Elem[T,U](a: T, b: U) - - def test2() = { - val Elem(a,b) = shift { k: (Elem[String,String] => String) => k(Elem("A","B")) } - b - } - - - def main(args: Array[String]): Any = { - println(reset(test1())) - println(reset(test2())) - } - -} \ No newline at end of file diff --git a/test/continuations/run/t1807.check b/test/continuations/run/t1807.check deleted file mode 100644 index 56a6051ca2..0000000000 --- a/test/continuations/run/t1807.check +++ /dev/null @@ -1 +0,0 @@ -1 \ No newline at end of file diff --git a/test/continuations/run/t1807.scala b/test/continuations/run/t1807.scala deleted file mode 100644 index 278b3a9936..0000000000 --- a/test/continuations/run/t1807.scala +++ /dev/null @@ -1,14 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - def main(args: Array[String]): Unit = { - val z = reset { - val f: (() => Int @cps[Int]) = () => 1 - f() - } - println(z) - } -} \ No newline at end of file diff --git a/test/continuations/run/t1808.scala b/test/continuations/run/t1808.scala deleted file mode 100644 index 125c7c1cdf..0000000000 --- a/test/continuations/run/t1808.scala +++ /dev/null @@ -1,10 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - def main(args: Array[String]): Unit = { - reset0 { 0 } - } -} \ No newline at end of file diff --git a/test/continuations/run/t1820.scala b/test/continuations/run/t1820.scala deleted file mode 100644 index 893ddab6d1..0000000000 --- a/test/continuations/run/t1820.scala +++ /dev/null @@ -1,14 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - def shifted: Unit @suspendable = shift { (k: Unit => Unit) => () } - def test1(b: => Boolean) = { - reset { - if (b) shifted - } - } - def main(args: Array[String]) = test1(true) -} \ No newline at end of file diff --git a/test/continuations/run/t1821.check b/test/continuations/run/t1821.check deleted file mode 100644 index f7b76115db..0000000000 --- a/test/continuations/run/t1821.check +++ /dev/null @@ -1,4 +0,0 @@ -() -() -() -() \ No newline at end of file diff --git a/test/continuations/run/t1821.scala b/test/continuations/run/t1821.scala deleted file mode 100644 index 0d5fb553be..0000000000 --- a/test/continuations/run/t1821.scala +++ /dev/null @@ -1,20 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - def suspended[A](x: A): A @suspendable = x - def test1[A](x: A): A @suspendable = suspended(x) match { case x => x } - def test2[A](x: List[A]): A @suspendable = suspended(x) match { case List(x) => x } - - def test3[A](x: A): A @suspendable = x match { case x => x } - def test4[A](x: List[A]): A @suspendable = x match { case List(x) => x } - - def main(args: Array[String]) = { - println(reset(test1())) - println(reset(test2(List(())))) - println(reset(test3())) - println(reset(test4(List(())))) - } -} \ No newline at end of file diff --git a/test/continuations/run/t2864.check b/test/continuations/run/t2864.check deleted file mode 100644 index d411bb7c1a..0000000000 --- a/test/continuations/run/t2864.check +++ /dev/null @@ -1 +0,0 @@ -400 diff --git a/test/continuations/run/t2864.scala b/test/continuations/run/t2864.scala deleted file mode 100644 index 7a2579e45c..0000000000 --- a/test/continuations/run/t2864.scala +++ /dev/null @@ -1,30 +0,0 @@ -import scala.util.continuations._ -object Test { - - def double[B](n : Int)(k : Int => B) : B = k(n * 2) - - def main(args : Array[String]) { - reset { - val result1 = shift(double[Unit](100)) - val result2 = shift(double[Unit](result1)) - println(result2) - } - } - - def foo: Int @cps[Int] = { - val a0 = shift((k:Int=>Int) => k(0)) - val x0 = 2 - val a1 = shift((k:Int=>Int) => x0) - 0 - } - -/* - def bar: ControlContext[Int,Int,Int] = { - shiftR((k:Int=>Int) => k(0)).flatMap { a0 => - val x0 = 2 - shiftR((k:Int=>Int) => x0).map { a1 => - 0 - }} - } -*/ -} \ No newline at end of file diff --git a/test/continuations/run/t2934.check b/test/continuations/run/t2934.check deleted file mode 100644 index a92586538e..0000000000 --- a/test/continuations/run/t2934.check +++ /dev/null @@ -1 +0,0 @@ -List(3, 4, 5) diff --git a/test/continuations/run/t2934.scala b/test/continuations/run/t2934.scala deleted file mode 100644 index a1b8ca9e04..0000000000 --- a/test/continuations/run/t2934.scala +++ /dev/null @@ -1,10 +0,0 @@ -import scala.util.continuations._ - -object Test { - def main(args : Array[String]) { - println(reset { - val x = shift(List(1,2,3).flatMap[Int, List[Int]]) - List(x + 2) - }) - } -} diff --git a/test/continuations/run/t3199.check b/test/continuations/run/t3199.check deleted file mode 100644 index a065247b8c..0000000000 --- a/test/continuations/run/t3199.check +++ /dev/null @@ -1 +0,0 @@ -Right(7) diff --git a/test/continuations/run/t3199.scala b/test/continuations/run/t3199.scala deleted file mode 100644 index 3fd2f1959a..0000000000 --- a/test/continuations/run/t3199.scala +++ /dev/null @@ -1,20 +0,0 @@ -import _root_.scala.collection.Seq -import _root_.scala.util.control.Exception -import _root_.scala.util.continuations._ - -object Test { - - trait AbstractResource[+R <: AnyRef] { - def reflect[B] : R @cpsParam[B,Either[Throwable, B]] = shift(acquireFor) - def acquireFor[B](f : R => B) : Either[Throwable, B] = { - import Exception._ - catching(List(classOf[Throwable]) : _*) either (f(null.asInstanceOf[R])) - } - } - - def main(args: Array[String]) : Unit = { - val x = new AbstractResource[String] { } - val result = x.acquireFor( x => 7 ) - println(result) - } - } diff --git a/test/continuations/run/t3199b.check b/test/continuations/run/t3199b.check deleted file mode 100644 index b5d8bb58d9..0000000000 --- a/test/continuations/run/t3199b.check +++ /dev/null @@ -1 +0,0 @@ -[1, 2, 3] diff --git a/test/continuations/run/t3199b.scala b/test/continuations/run/t3199b.scala deleted file mode 100644 index 950c584153..0000000000 --- a/test/continuations/run/t3199b.scala +++ /dev/null @@ -1,11 +0,0 @@ -object Test { - - def test() = { - java.util.Arrays.asList(Array(1,2,3):_*) - } - - def main(args: Array[String]) = { - println(test()) - } - -} \ No newline at end of file diff --git a/test/continuations/run/t3223.check b/test/continuations/run/t3223.check deleted file mode 100644 index ec635144f6..0000000000 --- a/test/continuations/run/t3223.check +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/test/continuations/run/t3223.scala b/test/continuations/run/t3223.scala deleted file mode 100644 index 4e510178e6..0000000000 --- a/test/continuations/run/t3223.scala +++ /dev/null @@ -1,19 +0,0 @@ -import scala.util.continuations._ -object Test { - - def foo(x:Int) = { - try { - throw new Exception - shiftUnit0[Int,Int](7) - } catch { - case ex => - val g = (a:Int)=>a - 9 - } - } - - def main(args: Array[String]) { - println(reset(foo(0))) - } - -} \ No newline at end of file diff --git a/test/continuations/run/t3225.check b/test/continuations/run/t3225.check deleted file mode 100644 index df1a8a9ce4..0000000000 --- a/test/continuations/run/t3225.check +++ /dev/null @@ -1,12 +0,0 @@ -8 -8 -9 -9 -8 -9 -8 -8 -9 -9 -8 -9 diff --git a/test/continuations/run/t3225.scala b/test/continuations/run/t3225.scala deleted file mode 100644 index ecfde279cf..0000000000 --- a/test/continuations/run/t3225.scala +++ /dev/null @@ -1,56 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - class Bla { - val x = 8 - def y[T] = 9 - } - -/* - def bla[A] = shift { k:(Bla=>A) => k(new Bla) } -*/ - - def bla1 = shift { k:(Bla=>Bla) => k(new Bla) } - def bla2 = shift { k:(Bla=>Int) => k(new Bla) } - - def fooA = bla2.x - def fooB[T] = bla2.y[T] - - def testMono() = { - println(reset(bla1).x) - println(reset(bla2.x)) - println(reset(bla2.y[Int])) - println(reset(bla2.y)) - println(reset(fooA)) - println(reset(fooB)) - 0 - } - - def blaX[A] = shift { k:(Bla=>A) => k(new Bla) } - - def fooX[A] = blaX[A].x - def fooY[A] = blaX[A].y[A] - - def testPoly() = { - println(reset(blaX[Bla]).x) - println(reset(blaX[Int].x)) - println(reset(blaX[Int].y[Int])) - println(reset(blaX[Int].y)) - println(reset(fooX[Int])) - println(reset(fooY[Int])) - 0 - } - - - // TODO: check whether this also applies to a::shift { k => ... } - - def main(args: Array[String]) = { - testMono() - testPoly() - } - -} diff --git a/test/continuations/run/trycatch0.check b/test/continuations/run/trycatch0.check deleted file mode 100644 index 36806909d0..0000000000 --- a/test/continuations/run/trycatch0.check +++ /dev/null @@ -1,2 +0,0 @@ -10 -10 \ No newline at end of file diff --git a/test/continuations/run/trycatch0.scala b/test/continuations/run/trycatch0.scala deleted file mode 100644 index 74a078b5ef..0000000000 --- a/test/continuations/run/trycatch0.scala +++ /dev/null @@ -1,25 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - -object Test { - - def foo = try { - shift((k: Int=>Int) => k(7)) - } catch { - case ex => - 9 - } - - def bar = try { - 7 - } catch { - case ex => - shiftUnit0[Int,Int](9) - } - - def main(args: Array[String]): Unit = { - println(reset { foo + 3 }) - println(reset { bar + 3 }) - } -} \ No newline at end of file diff --git a/test/continuations/run/trycatch1.check b/test/continuations/run/trycatch1.check deleted file mode 100644 index a028d2b1e1..0000000000 --- a/test/continuations/run/trycatch1.check +++ /dev/null @@ -1,4 +0,0 @@ -12 -12 -12 -12 \ No newline at end of file diff --git a/test/continuations/run/trycatch1.scala b/test/continuations/run/trycatch1.scala deleted file mode 100644 index ade13794e3..0000000000 --- a/test/continuations/run/trycatch1.scala +++ /dev/null @@ -1,48 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - -object Test { - - def fatal: Int = throw new Exception() - - def foo1 = try { - fatal - shift((k: Int=>Int) => k(7)) - } catch { - case ex => - 9 - } - - def foo2 = try { - shift((k: Int=>Int) => k(7)) - fatal - } catch { - case ex => - 9 - } - - def bar1 = try { - fatal - 7 - } catch { - case ex => - shiftUnit0[Int,Int](9) // regular shift causes no-symbol doesn't have owner - } - - def bar2 = try { - 7 - fatal - } catch { - case ex => - shiftUnit0[Int,Int](9) // regular shift causes no-symbol doesn't have owner - } - - def main(args: Array[String]): Unit = { - println(reset { foo1 + 3 }) - println(reset { foo2 + 3 }) - println(reset { bar1 + 3 }) - println(reset { bar2 + 3 }) - } - -} \ No newline at end of file diff --git a/test/continuations/run/while0.check b/test/continuations/run/while0.check deleted file mode 100644 index d58c55a31d..0000000000 --- a/test/continuations/run/while0.check +++ /dev/null @@ -1 +0,0 @@ -9000 diff --git a/test/continuations/run/while0.scala b/test/continuations/run/while0.scala deleted file mode 100644 index 9735f9d2c3..0000000000 --- a/test/continuations/run/while0.scala +++ /dev/null @@ -1,22 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def foo(): Int @cps[Unit] = 2 - - def test(): Unit @cps[Unit] = { - var x = 0 - while (x < 9000) { // pick number large enough to require tail-call opt - x += foo() - } - println(x) - } - - def main(args: Array[String]): Any = { - reset(test()) - } - -} \ No newline at end of file diff --git a/test/continuations/run/while1.check b/test/continuations/run/while1.check deleted file mode 100644 index 3d5f0b9a46..0000000000 --- a/test/continuations/run/while1.check +++ /dev/null @@ -1,11 +0,0 @@ -up -up -up -up -up -10 -down -down -down -down -down diff --git a/test/continuations/run/while1.scala b/test/continuations/run/while1.scala deleted file mode 100644 index fb5dc0079a..0000000000 --- a/test/continuations/run/while1.scala +++ /dev/null @@ -1,22 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def foo(): Int @cps[Unit] = shift { k => println("up"); k(2); println("down") } - - def test(): Unit @cps[Unit] = { - var x = 0 - while (x < 9) { - x += foo() - } - println(x) - } - - def main(args: Array[String]): Any = { - reset(test()) - } - -} \ No newline at end of file diff --git a/test/continuations/run/while2.check b/test/continuations/run/while2.check deleted file mode 100644 index 9fe515181b..0000000000 --- a/test/continuations/run/while2.check +++ /dev/null @@ -1,19 +0,0 @@ -up -up -up -up -up -up -up -up -up -9000 -down -down -down -down -down -down -down -down -down diff --git a/test/continuations/run/while2.scala b/test/continuations/run/while2.scala deleted file mode 100644 index f36288929e..0000000000 --- a/test/continuations/run/while2.scala +++ /dev/null @@ -1,23 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def foo1(): Int @cps[Unit] = 2 - def foo2(): Int @cps[Unit] = shift { k => println("up"); k(2); println("down") } - - def test(): Unit @cps[Unit] = { - var x = 0 - while (x < 9000) { // pick number large enough to require tail-call opt - x += (if (x % 1000 != 0) foo1() else foo2()) - } - println(x) - } - - def main(args: Array[String]): Any = { - reset(test()) - } - -} \ No newline at end of file diff --git a/test/disabled/pos/spec-traits.scala b/test/disabled/pos/spec-traits.scala new file mode 100644 index 0000000000..9e339a14ad --- /dev/null +++ b/test/disabled/pos/spec-traits.scala @@ -0,0 +1,83 @@ +trait A[@specialized(Int) T] { def foo: T } +class B extends A[Int] { val foo = 10 } +class C extends B + +// issue 3309 +class Lazy { + def test[U](block: => U): Unit = { block } + + test { lazy val x = 1 } +} + +// issue 3307 +class Bug3307 { + def f[Z](block: String => Z) { + block("abc") + } + + ({ () => + f { implicit x => println(x) } })() +} + +// issue 3301 + trait T[X] + +class Bug3301 { + def t[A]: T[A] = error("stub") + + () => { + type X = Int + + def foo[X] = t[X] + () + } +} +// issue 3299 +object Failure { + def thunk() { + for (i <- 1 to 2) { + val Array(a, b) = Array(1,2) + () + } + } +} + +// issue 3296 + +object AA +{ + def f(block: => Unit) {} + + object BB + { + f { + object CC + + () + } + } + + def foo[T](x: T) = { object A; false } +} + +// issue 3292 +import scala.swing._ +import scala.swing.GridBagPanel._ + +object Grid { + + def later(code : => Unit) = + javax.swing.SwingUtilities.invokeLater(new Runnable { def run { code }}) + + def test = later { + val frame = new Dialog { + contents = new GridBagPanel { + val c = new Constraints + } + } + } + +} + +// issue 3325 +object O { def f[@specialized T] { for(k <- Nil: List[T]) { } } } diff --git a/test/disabled/pos/t1254/t1254.java b/test/disabled/pos/t1254/t1254.java new file mode 100644 index 0000000000..25b733cf28 --- /dev/null +++ b/test/disabled/pos/t1254/t1254.java @@ -0,0 +1,28 @@ +/* Taken from ticket #1254. Tests Java signatures in mirror classes and that + Nothing is translated to Nothing$. +*/ + +import scala.None; + +// This compiles with javac but fails with Eclipse java compiler: +// 'The type scala.Nothing cannot be resolved. It is indirectly referenced from required .class files' +class NothingBug3 { + public NothingBug3() { + scala.Option o = scala.None$.MODULE$; + + test(o); + None.toLeft(new scala.Function0() { + public Integer apply() { return 0; } + }); + } + + public void test(scala.Option f) {} +} + +// This compiles with javac but fails with Eclipse java compiler: +// 'The type scala.Nothing cannot be resolved. It is indirectly referenced from required .class files' +class NothingBug4 { + public NothingBug4() { + scala.Option o = scala.None$.MODULE$; + } +} 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/disabled/run/stream_length.scala b/test/disabled/run/stream_length.scala new file mode 100644 index 0000000000..68e9cad5ac --- /dev/null +++ b/test/disabled/run/stream_length.scala @@ -0,0 +1,15 @@ + + +object Test { + def walk(depth: Int, bias: String): Stream[String] = { + if (depth == 0) + Stream(bias) + else { + Stream.concat(Stream.range(1, 100).map((x: Int) => walk(depth-1, bias + x))) + } + } + + def main(args: Array[String]) { + println("Length: " + walk(3, "---").length) + } +} diff --git a/test/disabled/run/t2946/Parsers.scala b/test/disabled/run/t2946/Parsers.scala new file mode 100644 index 0000000000..c0961034c4 --- /dev/null +++ b/test/disabled/run/t2946/Parsers.scala @@ -0,0 +1,4 @@ +class Parser { + def parse(t: Any): Unit = { + } +} diff --git a/test/disabled/run/t2946/ResponseCommon.scala b/test/disabled/run/t2946/ResponseCommon.scala new file mode 100644 index 0000000000..fa9d8acccb --- /dev/null +++ b/test/disabled/run/t2946/ResponseCommon.scala @@ -0,0 +1,14 @@ +trait ResponseCommon extends Parser { + private[this] var paramsParser: Parser = null + def withParamsParser(parser: Parser) = {paramsParser = parser; this} + + class Foo { + println(paramsParser) + } + + override abstract def parse(t: Any): Unit = t match { + case ("params", value: List[_]) => value.foreach {paramsParser.parse(_)} + case _ => super.parse(t) + } +} + diff --git a/test/disabled/run/t2946/Test.scala b/test/disabled/run/t2946/Test.scala new file mode 100644 index 0000000000..e9d9896a0e --- /dev/null +++ b/test/disabled/run/t2946/Test.scala @@ -0,0 +1,7 @@ +class Test extends Parser with ResponseCommon + +object Test { + def main(args: Array[String]) { + new Test + } +} diff --git a/test/disabled/scalacheck/redblack.scala b/test/disabled/scalacheck/redblack.scala new file mode 100644 index 0000000000..0334c1218d --- /dev/null +++ b/test/disabled/scalacheck/redblack.scala @@ -0,0 +1,157 @@ +import org.scalacheck._ +import Prop._ +import Gen._ + +/* +Properties of a Red & Black Tree: + +A node is either red or black. +The root is black. (This rule is used in some definitions and not others. Since the +root can always be changed from red to black but not necessarily vice-versa this +rule has little effect on analysis.) +All leaves are black. +Both children of every red node are black. +Every simple path from a given node to any of its descendant leaves contains the same number of black nodes. +*/ + +abstract class RedBlackTest extends Properties("RedBlack") { + object RedBlackTest extends scala.collection.immutable.RedBlack[Int] { + def isSmaller(x: Int, y: Int) = x < y + } + + import RedBlackTest._ + + def rootIsBlack[A](t: Tree[A]) = t.isBlack + + def areAllLeavesBlack[A](t: Tree[A]): Boolean = t match { + case Empty => t.isBlack + case ne: NonEmpty[_] => List(ne.left, ne.right) forall areAllLeavesBlack + } + + def areRedNodeChildrenBlack[A](t: Tree[A]): Boolean = t match { + case RedTree(_, _, left, right) => List(left, right) forall (t => t.isBlack && areRedNodeChildrenBlack(t)) + case BlackTree(_, _, left, right) => List(left, right) forall areRedNodeChildrenBlack + case Empty => true + } + + def blackNodesToLeaves[A](t: Tree[A]): List[Int] = t match { + case Empty => List(1) + case BlackTree(_, _, left, right) => List(left, right) flatMap blackNodesToLeaves map (_ + 1) + case RedTree(_, _, left, right) => List(left, right) flatMap blackNodesToLeaves + } + + def areBlackNodesToLeavesEqual[A](t: Tree[A]): Boolean = t match { + case Empty => true + case ne: NonEmpty[_] => + ( + blackNodesToLeaves(ne).removeDuplicates.size == 1 + && areBlackNodesToLeavesEqual(ne.left) + && areBlackNodesToLeavesEqual(ne.right) + ) + } + + def orderIsPreserved[A](t: Tree[A]): Boolean = t match { + case Empty => true + case ne: NonEmpty[_] => + ( + (ne.left.iterator map (_._1) forall (isSmaller(_, ne.key))) + && (ne.right.iterator map (_._1) forall (isSmaller(ne.key, _))) + && (List(ne.left, ne.right) forall orderIsPreserved) + ) + } + + def setup(l: List[Int], invariant: Tree[Unit] => Boolean): (Boolean, Tree[Unit]) + + def listNoRepetitions(size: Int) = for { + s <- Gen.choose(1, size) + l <- Gen.listOfN(size, Gen.choose(0, Int.MaxValue)) suchThat (l => l.size == l.removeDuplicates.size) + } yield l + def listFewRepetitions(size: Int) = for { + s <- Gen.choose(1, size) + l <- Gen.listOfN(s, Gen.choose(0, size * 4)) suchThat (l => l.size != l.removeDuplicates.size) + } yield l + def listManyRepetitions(size: Int) = for { + s <- Gen.choose(1, size) + l <- Gen.listOfN(s, Gen.choose(0, size)) suchThat (l => l.size != l.removeDuplicates.size) + } yield l + def listEvenRepetitions(size: Int) = listFewRepetitions(size) map (x => + scala.util.Random.shuffle(x zip x flatMap { case (a, b) => List(a, b) }) + ) + + // Arbitrarily weighted list distribution types + val seqType: Gen[Int => Gen[List[Int]]] + + def myGen(sized: Int) = for { + size <- Gen.choose(0, sized) + seq <- seqType + list <- seq(size) + } yield list + + property("root is black") = forAll(myGen(10)) { l => + setup(l, rootIsBlack)._1 :| setup(l, rootIsBlack)._2.toString + } + property("all leaves are black") = forAll(myGen(50)) { l => + setup(l, areAllLeavesBlack)._1 :| setup(l, areAllLeavesBlack)._2.toString + } + property("children of red nodes are black") = forAll(myGen(50)) { l => + setup(l, areRedNodeChildrenBlack)._1 :| setup(l, areRedNodeChildrenBlack)._2.toString + } + property("Every path from a node to its descendant leaves contains the same number of black nodes") = forAll(myGen(50)) { l => + setup(l, areBlackNodesToLeavesEqual)._1 :| setup(l, areBlackNodesToLeavesEqual)._2.toString + } + property("Ordering of keys is preserved") = forAll(myGen(50)) { l => + setup(l, orderIsPreserved)._1 :| setup(l, orderIsPreserved)._2.toString + } +} + +object TestInsertion extends RedBlackTest { + import RedBlackTest._ + override val seqType = Gen.frequency( + (1, listNoRepetitions _), + (1, listManyRepetitions _) + ) + + property("update adds elements") = forAll(myGen(50)) { l => + val tree = l.foldLeft(Empty: Tree[Unit])((acc, n) => acc update (n, ())) + forAll(Gen.pick(1, l)) ( n => !(tree lookup n.head isEmpty) :| "Tree: "+tree+" N: "+n.head ) + } + + override def setup(l: List[Int], invariant: Tree[Unit] => Boolean) = l.foldLeft((true, Empty: Tree[Unit])) { + case ((true, acc), n) => + val newRoot = acc update (n, ()) + (invariant(newRoot), newRoot) + case (failed, _) => failed + } +} + +object TestDeletion extends RedBlackTest { + import RedBlackTest._ + override val seqType = Gen.frequency( + (2, listFewRepetitions _), + (3, listManyRepetitions _), + (1, listEvenRepetitions _) + ) + + property("delete removes elements") = forAll(myGen(50)) { l => + val tree = l.foldLeft(Empty: Tree[Unit])((acc, n) => acc update (n, ())) + forAll(Gen.choose(1, l.size)) { numberOfElementsToRemove => + forAll(Gen.pick(numberOfElementsToRemove, l)) { elementsToRemove => + val newTree = elementsToRemove.foldLeft(tree)((acc, n) => acc delete n) + (elementsToRemove forall (n => newTree lookup n isEmpty)) :| "Tree: "+tree+"New Tree: "+newTree+" Elements to Remove: "+elementsToRemove + } + } + } + + override def setup(l: List[Int], invariant: Tree[Unit] => Boolean) = l.foldLeft((true, Empty: Tree[Unit])) { + case ((true, acc), n) => + val newRoot = if (acc lookup n isEmpty) acc update (n, ()) else acc delete n + (invariant(newRoot), newRoot) + case (failed, _) => failed + } +} + +object Test extends Properties("RedBlack") { + include(TestInsertion) + include(TestDeletion) +} + diff --git a/test/files/continuations-neg/function0.check b/test/files/continuations-neg/function0.check new file mode 100644 index 0000000000..0a66763a0f --- /dev/null +++ b/test/files/continuations-neg/function0.check @@ -0,0 +1,6 @@ +function0.scala:11: error: type mismatch; + found : () => Int @scala.util.continuations.cpsParam[Int,Int] + required: () => Int + val g: () => Int = f + ^ +one error found diff --git a/test/files/continuations-neg/function0.scala b/test/files/continuations-neg/function0.scala new file mode 100644 index 0000000000..4112ee3835 --- /dev/null +++ b/test/files/continuations-neg/function0.scala @@ -0,0 +1,16 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + def main(args: Array[String]): Any = { + + val f = () => shift { k: (Int=>Int) => k(7) } + val g: () => Int = f + + println(reset(g())) + } + +} \ No newline at end of file diff --git a/test/files/continuations-neg/function2.check b/test/files/continuations-neg/function2.check new file mode 100644 index 0000000000..4833057652 --- /dev/null +++ b/test/files/continuations-neg/function2.check @@ -0,0 +1,6 @@ +function2.scala:11: error: type mismatch; + found : () => Int + required: () => Int @util.continuations.package.cps[Int] + val g: () => Int @cps[Int] = f + ^ +one error found diff --git a/test/files/continuations-neg/function2.scala b/test/files/continuations-neg/function2.scala new file mode 100644 index 0000000000..ae0fda509d --- /dev/null +++ b/test/files/continuations-neg/function2.scala @@ -0,0 +1,16 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + def main(args: Array[String]): Any = { + + val f = () => 7 + val g: () => Int @cps[Int] = f + + println(reset(g())) + } + +} \ No newline at end of file diff --git a/test/files/continuations-neg/function3.check b/test/files/continuations-neg/function3.check new file mode 100644 index 0000000000..4705ad9ed9 --- /dev/null +++ b/test/files/continuations-neg/function3.check @@ -0,0 +1,6 @@ +function3.scala:10: error: type mismatch; + found : Int @scala.util.continuations.cpsParam[Int,Int] + required: Int + val g: () => Int = () => shift { k: (Int=>Int) => k(7) } + ^ +one error found diff --git a/test/files/continuations-neg/function3.scala b/test/files/continuations-neg/function3.scala new file mode 100644 index 0000000000..0c3f1667e5 --- /dev/null +++ b/test/files/continuations-neg/function3.scala @@ -0,0 +1,15 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + def main(args: Array[String]): Any = { + + val g: () => Int = () => shift { k: (Int=>Int) => k(7) } + + println(reset(g())) + } + +} \ No newline at end of file diff --git a/test/files/continuations-neg/infer0.check b/test/files/continuations-neg/infer0.check new file mode 100644 index 0000000000..1dd072ef09 --- /dev/null +++ b/test/files/continuations-neg/infer0.check @@ -0,0 +1,4 @@ +infer0.scala:11: error: cannot cps-transform expression 8: type arguments [Int(8),String,Int] do not conform to method shiftUnit's type parameter bounds [A,B,C >: B] + test(8) + ^ +one error found diff --git a/test/files/continuations-neg/infer0.scala b/test/files/continuations-neg/infer0.scala new file mode 100644 index 0000000000..894d5228b1 --- /dev/null +++ b/test/files/continuations-neg/infer0.scala @@ -0,0 +1,14 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + def test(x: => Int @cpsParam[String,Int]) = 7 + + def main(args: Array[String]): Any = { + test(8) + } + +} \ No newline at end of file diff --git a/test/files/continuations-neg/infer2.check b/test/files/continuations-neg/infer2.check new file mode 100644 index 0000000000..59eb670bc3 --- /dev/null +++ b/test/files/continuations-neg/infer2.check @@ -0,0 +1,4 @@ +infer2.scala:14: error: illegal answer type modification: scala.util.continuations.cpsParam[String,Int] andThen scala.util.continuations.cpsParam[String,Int] + test { sym(); sym() } + ^ +one error found diff --git a/test/files/continuations-neg/infer2.scala b/test/files/continuations-neg/infer2.scala new file mode 100644 index 0000000000..a890ac1fc4 --- /dev/null +++ b/test/files/continuations-neg/infer2.scala @@ -0,0 +1,19 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + def test(x: => Int @cpsParam[String,Int]) = 7 + + def sym() = shift { k: (Int => String) => 9 } + + + def main(args: Array[String]): Any = { + test { sym(); sym() } + } + +} + + diff --git a/test/files/continuations-neg/lazy.check b/test/files/continuations-neg/lazy.check new file mode 100644 index 0000000000..bfa44c59a4 --- /dev/null +++ b/test/files/continuations-neg/lazy.check @@ -0,0 +1,6 @@ +lazy.scala:5: error: type mismatch; + found : Unit @scala.util.continuations.cpsParam[Unit,Unit] + required: Unit + def foo = { + ^ +one error found diff --git a/test/files/continuations-neg/lazy.scala b/test/files/continuations-neg/lazy.scala new file mode 100644 index 0000000000..dffc57ffa0 --- /dev/null +++ b/test/files/continuations-neg/lazy.scala @@ -0,0 +1,16 @@ +import scala.util.continuations._ + +object Test { + + def foo = { + lazy val x = shift((k:Unit=>Unit)=>k()) + println(x) + } + + def main(args: Array[String]) { + reset { + foo + } + } + +} \ No newline at end of file diff --git a/test/files/continuations-neg/t1929.check b/test/files/continuations-neg/t1929.check new file mode 100644 index 0000000000..f42c3a1e15 --- /dev/null +++ b/test/files/continuations-neg/t1929.check @@ -0,0 +1,6 @@ +t1929.scala:8: error: type mismatch; + found : Int @scala.util.continuations.cpsParam[String,java.lang.String] @scala.util.continuations.cpsSynth + required: Int @scala.util.continuations.cpsParam[Int,java.lang.String] + reset { + ^ +one error found diff --git a/test/files/continuations-neg/t1929.scala b/test/files/continuations-neg/t1929.scala new file mode 100644 index 0000000000..02eda9170d --- /dev/null +++ b/test/files/continuations-neg/t1929.scala @@ -0,0 +1,17 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + def main(args : Array[String]) { + reset { + println("up") + val x = shift((k:Int=>String) => k(8) + k(2)) + println("down " + x) + val y = shift((k:Int=>String) => k(3)) + println("down2 " + y) + y + x + } + } +} \ No newline at end of file diff --git a/test/files/continuations-neg/t2285.check b/test/files/continuations-neg/t2285.check new file mode 100644 index 0000000000..d5dff6a4f2 --- /dev/null +++ b/test/files/continuations-neg/t2285.check @@ -0,0 +1,6 @@ +t2285.scala:9: error: type mismatch; + found : Int @scala.util.continuations.cpsParam[String,String] @scala.util.continuations.cpsSynth + required: Int @scala.util.continuations.cpsParam[Int,String] + def foo() = reset { bar(); 7 } + ^ +one error found diff --git a/test/files/continuations-neg/t2285.scala b/test/files/continuations-neg/t2285.scala new file mode 100644 index 0000000000..f3c7f4c89c --- /dev/null +++ b/test/files/continuations-neg/t2285.scala @@ -0,0 +1,11 @@ +// $Id$ + +import scala.util.continuations._ + +object Test { + + def bar() = shift { k: (String => String) => k("1") } + + def foo() = reset { bar(); 7 } + +} diff --git a/test/files/continuations-neg/t2949.check b/test/files/continuations-neg/t2949.check new file mode 100644 index 0000000000..dd9768807c --- /dev/null +++ b/test/files/continuations-neg/t2949.check @@ -0,0 +1,6 @@ +t2949.scala:13: error: type mismatch; + found : Int + required: ? @scala.util.continuations.cpsParam[List[?],Any] + x * y + ^ +one error found diff --git a/test/files/continuations-neg/t2949.scala b/test/files/continuations-neg/t2949.scala new file mode 100644 index 0000000000..ce27c7c0e8 --- /dev/null +++ b/test/files/continuations-neg/t2949.scala @@ -0,0 +1,15 @@ +// $Id$ + +import scala.util.continuations._ + +object Test { + + def reflect[A,B](xs : List[A]) = shift{ xs.flatMap[B, List[B]] } + def reify[A, B](x : A @cpsParam[List[A], B]) = reset{ List(x) } + + def main(args: Array[String]): Unit = println(reify { + val x = reflect[Int, Int](List(1,2,3)) + val y = reflect[Int, Int](List(2,4,8)) + x * y + }) +} diff --git a/test/files/continuations-neg/trycatch2.check b/test/files/continuations-neg/trycatch2.check new file mode 100644 index 0000000000..5ff2838bad --- /dev/null +++ b/test/files/continuations-neg/trycatch2.check @@ -0,0 +1,7 @@ +trycatch2.scala:11: error: only simple cps types allowed in try/catch blocks (found: Int @scala.util.continuations.cpsParam[String,Int]) + def foo1 = try { + ^ +trycatch2.scala:19: error: only simple cps types allowed in try/catch blocks (found: Int @scala.util.continuations.cpsParam[String,Int]) + def foo2 = try { + ^ +two errors found diff --git a/test/files/continuations-neg/trycatch2.scala b/test/files/continuations-neg/trycatch2.scala new file mode 100644 index 0000000000..761cee52ac --- /dev/null +++ b/test/files/continuations-neg/trycatch2.scala @@ -0,0 +1,33 @@ +// $Id$ + +import scala.util.continuations._ + +object Test { + + def fatal[T]: T = throw new Exception + def cpsIntStringInt = shift { k:(Int=>String) => k(3); 7 } + def cpsIntIntString = shift { k:(Int=>Int) => k(3); "7" } + + def foo1 = try { + fatal[Int] + cpsIntStringInt + } catch { + case ex => + cpsIntStringInt + } + + def foo2 = try { + fatal[Int] + cpsIntStringInt + } catch { + case ex => + cpsIntStringInt + } + + + def main(args: Array[String]): Unit = { + println(reset { foo1; "3" }) + println(reset { foo2; "3" }) + } + +} \ No newline at end of file diff --git a/test/files/continuations-run/basics.check b/test/files/continuations-run/basics.check new file mode 100755 index 0000000000..54c059fdcb --- /dev/null +++ b/test/files/continuations-run/basics.check @@ -0,0 +1,2 @@ +28 +28 \ No newline at end of file diff --git a/test/files/continuations-run/basics.scala b/test/files/continuations-run/basics.scala new file mode 100755 index 0000000000..9df209b11c --- /dev/null +++ b/test/files/continuations-run/basics.scala @@ -0,0 +1,23 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + def m0() = { + shift((k:Int => Int) => k(k(7))) * 2 + } + + def m1() = { + 2 * shift((k:Int => Int) => k(k(7))) + } + + def main(args: Array[String]) = { + + println(reset(m0())) + println(reset(m1())) + + } + +} diff --git a/test/files/continuations-run/function1.check b/test/files/continuations-run/function1.check new file mode 100644 index 0000000000..7f8f011eb7 --- /dev/null +++ b/test/files/continuations-run/function1.check @@ -0,0 +1 @@ +7 diff --git a/test/files/continuations-run/function1.scala b/test/files/continuations-run/function1.scala new file mode 100644 index 0000000000..3b39722e3a --- /dev/null +++ b/test/files/continuations-run/function1.scala @@ -0,0 +1,16 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + def main(args: Array[String]): Any = { + + val f = () => shift { k: (Int=>Int) => k(7) } + val g: () => Int @cps[Int] = f + + println(reset(g())) + } + +} \ No newline at end of file diff --git a/test/files/continuations-run/function4.check b/test/files/continuations-run/function4.check new file mode 100644 index 0000000000..c7930257df --- /dev/null +++ b/test/files/continuations-run/function4.check @@ -0,0 +1 @@ +7 \ No newline at end of file diff --git a/test/files/continuations-run/function4.scala b/test/files/continuations-run/function4.scala new file mode 100644 index 0000000000..b73eedb02c --- /dev/null +++ b/test/files/continuations-run/function4.scala @@ -0,0 +1,15 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + def main(args: Array[String]): Any = { + + val g: () => Int @cps[Int] = () => shift { k: (Int=>Int) => k(7) } + + println(reset(g())) + } + +} \ No newline at end of file diff --git a/test/files/continuations-run/function5.check b/test/files/continuations-run/function5.check new file mode 100644 index 0000000000..c7930257df --- /dev/null +++ b/test/files/continuations-run/function5.check @@ -0,0 +1 @@ +7 \ No newline at end of file diff --git a/test/files/continuations-run/function5.scala b/test/files/continuations-run/function5.scala new file mode 100644 index 0000000000..a689ccf243 --- /dev/null +++ b/test/files/continuations-run/function5.scala @@ -0,0 +1,15 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + def main(args: Array[String]): Any = { + + val g: () => Int @cps[Int] = () => 7 + + println(reset(g())) + } + +} \ No newline at end of file diff --git a/test/files/continuations-run/function6.check b/test/files/continuations-run/function6.check new file mode 100644 index 0000000000..c7930257df --- /dev/null +++ b/test/files/continuations-run/function6.check @@ -0,0 +1 @@ +7 \ No newline at end of file diff --git a/test/files/continuations-run/function6.scala b/test/files/continuations-run/function6.scala new file mode 100644 index 0000000000..1a2792370a --- /dev/null +++ b/test/files/continuations-run/function6.scala @@ -0,0 +1,16 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + def main(args: Array[String]): Any = { + + val g: PartialFunction[Int, Int @cps[Int]] = { case x => 7 } + + println(reset(g(2))) + + } + +} \ No newline at end of file diff --git a/test/files/continuations-run/ifelse0.check b/test/files/continuations-run/ifelse0.check new file mode 100644 index 0000000000..f8bc79860d --- /dev/null +++ b/test/files/continuations-run/ifelse0.check @@ -0,0 +1,2 @@ +10 +9 \ No newline at end of file diff --git a/test/files/continuations-run/ifelse0.scala b/test/files/continuations-run/ifelse0.scala new file mode 100644 index 0000000000..e34b86ee84 --- /dev/null +++ b/test/files/continuations-run/ifelse0.scala @@ -0,0 +1,18 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + def test(x:Int) = if (x <= 7) + shift { k: (Int=>Int) => k(k(k(x))) } + else + shift { k: (Int=>Int) => k(x) } + + def main(args: Array[String]): Any = { + println(reset(1 + test(7))) + println(reset(1 + test(8))) + } + +} \ No newline at end of file diff --git a/test/files/continuations-run/ifelse1.check b/test/files/continuations-run/ifelse1.check new file mode 100644 index 0000000000..86a3fbc0c1 --- /dev/null +++ b/test/files/continuations-run/ifelse1.check @@ -0,0 +1,4 @@ +10 +9 +8 +11 \ No newline at end of file diff --git a/test/files/continuations-run/ifelse1.scala b/test/files/continuations-run/ifelse1.scala new file mode 100644 index 0000000000..2ccc1ed730 --- /dev/null +++ b/test/files/continuations-run/ifelse1.scala @@ -0,0 +1,25 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + def test1(x:Int) = if (x <= 7) + shift { k: (Int=>Int) => k(k(k(x))) } + else + x + + def test2(x:Int) = if (x <= 7) + x + else + shift { k: (Int=>Int) => k(k(k(x))) } + + def main(args: Array[String]): Any = { + println(reset(1 + test1(7))) + println(reset(1 + test1(8))) + println(reset(1 + test2(7))) + println(reset(1 + test2(8))) + } + +} \ No newline at end of file diff --git a/test/files/continuations-run/ifelse2.check b/test/files/continuations-run/ifelse2.check new file mode 100644 index 0000000000..f97a95b08d --- /dev/null +++ b/test/files/continuations-run/ifelse2.check @@ -0,0 +1,4 @@ +abort +() +alive +() diff --git a/test/files/continuations-run/ifelse2.scala b/test/files/continuations-run/ifelse2.scala new file mode 100644 index 0000000000..536e350190 --- /dev/null +++ b/test/files/continuations-run/ifelse2.scala @@ -0,0 +1,16 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + def test(x:Int) = if (x <= 7) + shift { k: (Unit=>Unit) => println("abort") } + + def main(args: Array[String]): Any = { + println(reset{ test(7); println("alive") }) + println(reset{ test(8); println("alive") }) + } + +} \ No newline at end of file diff --git a/test/files/continuations-run/ifelse3.check b/test/files/continuations-run/ifelse3.check new file mode 100644 index 0000000000..95b562c8e6 --- /dev/null +++ b/test/files/continuations-run/ifelse3.check @@ -0,0 +1,2 @@ +6 +9 diff --git a/test/files/continuations-run/ifelse3.scala b/test/files/continuations-run/ifelse3.scala new file mode 100644 index 0000000000..5dbd079d1c --- /dev/null +++ b/test/files/continuations-run/ifelse3.scala @@ -0,0 +1,21 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + def util(x: Boolean) = shift { k: (Boolean=>Int) => k(x) } + + def test(x:Int) = if (util(x <= 7)) + x - 1 + else + x + 1 + + + def main(args: Array[String]): Any = { + println(reset(test(7))) + println(reset(test(8))) + } + +} \ No newline at end of file diff --git a/test/files/continuations-run/infer1.scala b/test/files/continuations-run/infer1.scala new file mode 100644 index 0000000000..a6c6c07215 --- /dev/null +++ b/test/files/continuations-run/infer1.scala @@ -0,0 +1,33 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + def test(x: => Int @cpsParam[String,Int]) = 7 + + def test2() = { + val x = shift { k: (Int => String) => 9 } + x + } + + def test3(x: => Int @cpsParam[Int,Int]) = 7 + + + def util() = shift { k: (String => String) => "7" } + + def main(args: Array[String]): Any = { + test { shift { k: (Int => String) => 9 } } + test { shift { k: (Int => String) => 9 }; 2 } +// test { shift { k: (Int => String) => 9 }; util() } <-- doesn't work + test { shift { k: (Int => String) => 9 }; util(); 2 } + + + test { shift { k: (Int => String) => 9 }; { test3(0); 2 } } + + test3 { { test3(0); 2 } } + + } + +} \ No newline at end of file diff --git a/test/files/continuations-run/match0.check b/test/files/continuations-run/match0.check new file mode 100644 index 0000000000..f8bc79860d --- /dev/null +++ b/test/files/continuations-run/match0.check @@ -0,0 +1,2 @@ +10 +9 \ No newline at end of file diff --git a/test/files/continuations-run/match0.scala b/test/files/continuations-run/match0.scala new file mode 100644 index 0000000000..bd36238d7f --- /dev/null +++ b/test/files/continuations-run/match0.scala @@ -0,0 +1,18 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + def test(x:Int) = x match { + case 7 => shift { k: (Int=>Int) => k(k(k(x))) } + case 8 => shift { k: (Int=>Int) => k(x) } + } + + def main(args: Array[String]): Any = { + println(reset(1 + test(7))) + println(reset(1 + test(8))) + } + +} \ No newline at end of file diff --git a/test/files/continuations-run/match1.check b/test/files/continuations-run/match1.check new file mode 100644 index 0000000000..73053d3f4f --- /dev/null +++ b/test/files/continuations-run/match1.check @@ -0,0 +1,2 @@ +10 +9 diff --git a/test/files/continuations-run/match1.scala b/test/files/continuations-run/match1.scala new file mode 100644 index 0000000000..ea4e219666 --- /dev/null +++ b/test/files/continuations-run/match1.scala @@ -0,0 +1,18 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + def test(x:Int) = x match { + case 7 => shift { k: (Int=>Int) => k(k(k(x))) } + case _ => x + } + + def main(args: Array[String]): Any = { + println(reset(1 + test(7))) + println(reset(1 + test(8))) + } + +} \ No newline at end of file diff --git a/test/files/continuations-run/match2.check b/test/files/continuations-run/match2.check new file mode 100644 index 0000000000..cbf91349cc --- /dev/null +++ b/test/files/continuations-run/match2.check @@ -0,0 +1,2 @@ +B +B diff --git a/test/files/continuations-run/match2.scala b/test/files/continuations-run/match2.scala new file mode 100644 index 0000000000..8d4f04870f --- /dev/null +++ b/test/files/continuations-run/match2.scala @@ -0,0 +1,26 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + def test1() = { + val (a, b) = shift { k: (((String,String)) => String) => k("A","B") } + b + } + + case class Elem[T,U](a: T, b: U) + + def test2() = { + val Elem(a,b) = shift { k: (Elem[String,String] => String) => k(Elem("A","B")) } + b + } + + + def main(args: Array[String]): Any = { + println(reset(test1())) + println(reset(test2())) + } + +} \ No newline at end of file diff --git a/test/files/continuations-run/t1807.check b/test/files/continuations-run/t1807.check new file mode 100644 index 0000000000..56a6051ca2 --- /dev/null +++ b/test/files/continuations-run/t1807.check @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/test/files/continuations-run/t1807.scala b/test/files/continuations-run/t1807.scala new file mode 100644 index 0000000000..278b3a9936 --- /dev/null +++ b/test/files/continuations-run/t1807.scala @@ -0,0 +1,14 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + def main(args: Array[String]): Unit = { + val z = reset { + val f: (() => Int @cps[Int]) = () => 1 + f() + } + println(z) + } +} \ No newline at end of file diff --git a/test/files/continuations-run/t1808.scala b/test/files/continuations-run/t1808.scala new file mode 100644 index 0000000000..125c7c1cdf --- /dev/null +++ b/test/files/continuations-run/t1808.scala @@ -0,0 +1,10 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + def main(args: Array[String]): Unit = { + reset0 { 0 } + } +} \ No newline at end of file diff --git a/test/files/continuations-run/t1820.scala b/test/files/continuations-run/t1820.scala new file mode 100644 index 0000000000..893ddab6d1 --- /dev/null +++ b/test/files/continuations-run/t1820.scala @@ -0,0 +1,14 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + def shifted: Unit @suspendable = shift { (k: Unit => Unit) => () } + def test1(b: => Boolean) = { + reset { + if (b) shifted + } + } + def main(args: Array[String]) = test1(true) +} \ No newline at end of file diff --git a/test/files/continuations-run/t1821.check b/test/files/continuations-run/t1821.check new file mode 100644 index 0000000000..f7b76115db --- /dev/null +++ b/test/files/continuations-run/t1821.check @@ -0,0 +1,4 @@ +() +() +() +() \ No newline at end of file diff --git a/test/files/continuations-run/t1821.scala b/test/files/continuations-run/t1821.scala new file mode 100644 index 0000000000..0d5fb553be --- /dev/null +++ b/test/files/continuations-run/t1821.scala @@ -0,0 +1,20 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + def suspended[A](x: A): A @suspendable = x + def test1[A](x: A): A @suspendable = suspended(x) match { case x => x } + def test2[A](x: List[A]): A @suspendable = suspended(x) match { case List(x) => x } + + def test3[A](x: A): A @suspendable = x match { case x => x } + def test4[A](x: List[A]): A @suspendable = x match { case List(x) => x } + + def main(args: Array[String]) = { + println(reset(test1())) + println(reset(test2(List(())))) + println(reset(test3())) + println(reset(test4(List(())))) + } +} \ No newline at end of file diff --git a/test/files/continuations-run/t2864.check b/test/files/continuations-run/t2864.check new file mode 100644 index 0000000000..d411bb7c1a --- /dev/null +++ b/test/files/continuations-run/t2864.check @@ -0,0 +1 @@ +400 diff --git a/test/files/continuations-run/t2864.scala b/test/files/continuations-run/t2864.scala new file mode 100644 index 0000000000..7a2579e45c --- /dev/null +++ b/test/files/continuations-run/t2864.scala @@ -0,0 +1,30 @@ +import scala.util.continuations._ +object Test { + + def double[B](n : Int)(k : Int => B) : B = k(n * 2) + + def main(args : Array[String]) { + reset { + val result1 = shift(double[Unit](100)) + val result2 = shift(double[Unit](result1)) + println(result2) + } + } + + def foo: Int @cps[Int] = { + val a0 = shift((k:Int=>Int) => k(0)) + val x0 = 2 + val a1 = shift((k:Int=>Int) => x0) + 0 + } + +/* + def bar: ControlContext[Int,Int,Int] = { + shiftR((k:Int=>Int) => k(0)).flatMap { a0 => + val x0 = 2 + shiftR((k:Int=>Int) => x0).map { a1 => + 0 + }} + } +*/ +} \ No newline at end of file diff --git a/test/files/continuations-run/t2934.check b/test/files/continuations-run/t2934.check new file mode 100644 index 0000000000..a92586538e --- /dev/null +++ b/test/files/continuations-run/t2934.check @@ -0,0 +1 @@ +List(3, 4, 5) diff --git a/test/files/continuations-run/t2934.scala b/test/files/continuations-run/t2934.scala new file mode 100644 index 0000000000..a1b8ca9e04 --- /dev/null +++ b/test/files/continuations-run/t2934.scala @@ -0,0 +1,10 @@ +import scala.util.continuations._ + +object Test { + def main(args : Array[String]) { + println(reset { + val x = shift(List(1,2,3).flatMap[Int, List[Int]]) + List(x + 2) + }) + } +} diff --git a/test/files/continuations-run/t3199.check b/test/files/continuations-run/t3199.check new file mode 100644 index 0000000000..a065247b8c --- /dev/null +++ b/test/files/continuations-run/t3199.check @@ -0,0 +1 @@ +Right(7) diff --git a/test/files/continuations-run/t3199.scala b/test/files/continuations-run/t3199.scala new file mode 100644 index 0000000000..3fd2f1959a --- /dev/null +++ b/test/files/continuations-run/t3199.scala @@ -0,0 +1,20 @@ +import _root_.scala.collection.Seq +import _root_.scala.util.control.Exception +import _root_.scala.util.continuations._ + +object Test { + + trait AbstractResource[+R <: AnyRef] { + def reflect[B] : R @cpsParam[B,Either[Throwable, B]] = shift(acquireFor) + def acquireFor[B](f : R => B) : Either[Throwable, B] = { + import Exception._ + catching(List(classOf[Throwable]) : _*) either (f(null.asInstanceOf[R])) + } + } + + def main(args: Array[String]) : Unit = { + val x = new AbstractResource[String] { } + val result = x.acquireFor( x => 7 ) + println(result) + } + } diff --git a/test/files/continuations-run/t3199b.check b/test/files/continuations-run/t3199b.check new file mode 100644 index 0000000000..b5d8bb58d9 --- /dev/null +++ b/test/files/continuations-run/t3199b.check @@ -0,0 +1 @@ +[1, 2, 3] diff --git a/test/files/continuations-run/t3199b.scala b/test/files/continuations-run/t3199b.scala new file mode 100644 index 0000000000..950c584153 --- /dev/null +++ b/test/files/continuations-run/t3199b.scala @@ -0,0 +1,11 @@ +object Test { + + def test() = { + java.util.Arrays.asList(Array(1,2,3):_*) + } + + def main(args: Array[String]) = { + println(test()) + } + +} \ No newline at end of file diff --git a/test/files/continuations-run/t3223.check b/test/files/continuations-run/t3223.check new file mode 100644 index 0000000000..ec635144f6 --- /dev/null +++ b/test/files/continuations-run/t3223.check @@ -0,0 +1 @@ +9 diff --git a/test/files/continuations-run/t3223.scala b/test/files/continuations-run/t3223.scala new file mode 100644 index 0000000000..4e510178e6 --- /dev/null +++ b/test/files/continuations-run/t3223.scala @@ -0,0 +1,19 @@ +import scala.util.continuations._ +object Test { + + def foo(x:Int) = { + try { + throw new Exception + shiftUnit0[Int,Int](7) + } catch { + case ex => + val g = (a:Int)=>a + 9 + } + } + + def main(args: Array[String]) { + println(reset(foo(0))) + } + +} \ No newline at end of file diff --git a/test/files/continuations-run/t3225.check b/test/files/continuations-run/t3225.check new file mode 100644 index 0000000000..df1a8a9ce4 --- /dev/null +++ b/test/files/continuations-run/t3225.check @@ -0,0 +1,12 @@ +8 +8 +9 +9 +8 +9 +8 +8 +9 +9 +8 +9 diff --git a/test/files/continuations-run/t3225.scala b/test/files/continuations-run/t3225.scala new file mode 100644 index 0000000000..ecfde279cf --- /dev/null +++ b/test/files/continuations-run/t3225.scala @@ -0,0 +1,56 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + class Bla { + val x = 8 + def y[T] = 9 + } + +/* + def bla[A] = shift { k:(Bla=>A) => k(new Bla) } +*/ + + def bla1 = shift { k:(Bla=>Bla) => k(new Bla) } + def bla2 = shift { k:(Bla=>Int) => k(new Bla) } + + def fooA = bla2.x + def fooB[T] = bla2.y[T] + + def testMono() = { + println(reset(bla1).x) + println(reset(bla2.x)) + println(reset(bla2.y[Int])) + println(reset(bla2.y)) + println(reset(fooA)) + println(reset(fooB)) + 0 + } + + def blaX[A] = shift { k:(Bla=>A) => k(new Bla) } + + def fooX[A] = blaX[A].x + def fooY[A] = blaX[A].y[A] + + def testPoly() = { + println(reset(blaX[Bla]).x) + println(reset(blaX[Int].x)) + println(reset(blaX[Int].y[Int])) + println(reset(blaX[Int].y)) + println(reset(fooX[Int])) + println(reset(fooY[Int])) + 0 + } + + + // TODO: check whether this also applies to a::shift { k => ... } + + def main(args: Array[String]) = { + testMono() + testPoly() + } + +} diff --git a/test/files/continuations-run/trycatch0.check b/test/files/continuations-run/trycatch0.check new file mode 100644 index 0000000000..36806909d0 --- /dev/null +++ b/test/files/continuations-run/trycatch0.check @@ -0,0 +1,2 @@ +10 +10 \ No newline at end of file diff --git a/test/files/continuations-run/trycatch0.scala b/test/files/continuations-run/trycatch0.scala new file mode 100644 index 0000000000..74a078b5ef --- /dev/null +++ b/test/files/continuations-run/trycatch0.scala @@ -0,0 +1,25 @@ +// $Id$ + +import scala.util.continuations._ + +object Test { + + def foo = try { + shift((k: Int=>Int) => k(7)) + } catch { + case ex => + 9 + } + + def bar = try { + 7 + } catch { + case ex => + shiftUnit0[Int,Int](9) + } + + def main(args: Array[String]): Unit = { + println(reset { foo + 3 }) + println(reset { bar + 3 }) + } +} \ No newline at end of file diff --git a/test/files/continuations-run/trycatch1.check b/test/files/continuations-run/trycatch1.check new file mode 100644 index 0000000000..a028d2b1e1 --- /dev/null +++ b/test/files/continuations-run/trycatch1.check @@ -0,0 +1,4 @@ +12 +12 +12 +12 \ No newline at end of file diff --git a/test/files/continuations-run/trycatch1.scala b/test/files/continuations-run/trycatch1.scala new file mode 100644 index 0000000000..ade13794e3 --- /dev/null +++ b/test/files/continuations-run/trycatch1.scala @@ -0,0 +1,48 @@ +// $Id$ + +import scala.util.continuations._ + +object Test { + + def fatal: Int = throw new Exception() + + def foo1 = try { + fatal + shift((k: Int=>Int) => k(7)) + } catch { + case ex => + 9 + } + + def foo2 = try { + shift((k: Int=>Int) => k(7)) + fatal + } catch { + case ex => + 9 + } + + def bar1 = try { + fatal + 7 + } catch { + case ex => + shiftUnit0[Int,Int](9) // regular shift causes no-symbol doesn't have owner + } + + def bar2 = try { + 7 + fatal + } catch { + case ex => + shiftUnit0[Int,Int](9) // regular shift causes no-symbol doesn't have owner + } + + def main(args: Array[String]): Unit = { + println(reset { foo1 + 3 }) + println(reset { foo2 + 3 }) + println(reset { bar1 + 3 }) + println(reset { bar2 + 3 }) + } + +} \ No newline at end of file diff --git a/test/files/continuations-run/while0.check b/test/files/continuations-run/while0.check new file mode 100644 index 0000000000..d58c55a31d --- /dev/null +++ b/test/files/continuations-run/while0.check @@ -0,0 +1 @@ +9000 diff --git a/test/files/continuations-run/while0.scala b/test/files/continuations-run/while0.scala new file mode 100644 index 0000000000..9735f9d2c3 --- /dev/null +++ b/test/files/continuations-run/while0.scala @@ -0,0 +1,22 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + def foo(): Int @cps[Unit] = 2 + + def test(): Unit @cps[Unit] = { + var x = 0 + while (x < 9000) { // pick number large enough to require tail-call opt + x += foo() + } + println(x) + } + + def main(args: Array[String]): Any = { + reset(test()) + } + +} \ No newline at end of file diff --git a/test/files/continuations-run/while1.check b/test/files/continuations-run/while1.check new file mode 100644 index 0000000000..3d5f0b9a46 --- /dev/null +++ b/test/files/continuations-run/while1.check @@ -0,0 +1,11 @@ +up +up +up +up +up +10 +down +down +down +down +down diff --git a/test/files/continuations-run/while1.scala b/test/files/continuations-run/while1.scala new file mode 100644 index 0000000000..fb5dc0079a --- /dev/null +++ b/test/files/continuations-run/while1.scala @@ -0,0 +1,22 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + def foo(): Int @cps[Unit] = shift { k => println("up"); k(2); println("down") } + + def test(): Unit @cps[Unit] = { + var x = 0 + while (x < 9) { + x += foo() + } + println(x) + } + + def main(args: Array[String]): Any = { + reset(test()) + } + +} \ No newline at end of file diff --git a/test/files/continuations-run/while2.check b/test/files/continuations-run/while2.check new file mode 100644 index 0000000000..9fe515181b --- /dev/null +++ b/test/files/continuations-run/while2.check @@ -0,0 +1,19 @@ +up +up +up +up +up +up +up +up +up +9000 +down +down +down +down +down +down +down +down +down diff --git a/test/files/continuations-run/while2.scala b/test/files/continuations-run/while2.scala new file mode 100644 index 0000000000..f36288929e --- /dev/null +++ b/test/files/continuations-run/while2.scala @@ -0,0 +1,23 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + + def foo1(): Int @cps[Unit] = 2 + def foo2(): Int @cps[Unit] = shift { k => println("up"); k(2); println("down") } + + def test(): Unit @cps[Unit] = { + var x = 0 + while (x < 9000) { // pick number large enough to require tail-call opt + x += (if (x % 1000 != 0) foo1() else foo2()) + } + println(x) + } + + def main(args: Array[String]): Any = { + reset(test()) + } + +} \ No newline at end of file diff --git a/test/files/jvm/genericNest.scala b/test/files/jvm/genericNest.scala new file mode 100644 index 0000000000..c1b0210117 --- /dev/null +++ b/test/files/jvm/genericNest.scala @@ -0,0 +1,13 @@ +/** found in genericNest.jar, compiled from OuterTParams.java */ +import nestpkg._; + +// bug #695 +object ForceParse extends OuterTParams[AnyRef] { + // Force import of HarderToParse.InnerClass, + // which has confusing method signature. + var field: InnerClass = null +} + +object Test extends Application { + ForceParse +} diff --git a/test/files/jvm/genericNest/genericNest.jar.desired.sha1 b/test/files/jvm/genericNest/genericNest.jar.desired.sha1 deleted file mode 100644 index e9321262f2..0000000000 --- a/test/files/jvm/genericNest/genericNest.jar.desired.sha1 +++ /dev/null @@ -1 +0,0 @@ -b1ec8a095cec4902b3609d74d274c04365c59c04 ?genericNest.jar diff --git a/test/files/jvm/genericNest/genericNest.scala b/test/files/jvm/genericNest/genericNest.scala deleted file mode 100644 index c1b0210117..0000000000 --- a/test/files/jvm/genericNest/genericNest.scala +++ /dev/null @@ -1,13 +0,0 @@ -/** found in genericNest.jar, compiled from OuterTParams.java */ -import nestpkg._; - -// bug #695 -object ForceParse extends OuterTParams[AnyRef] { - // Force import of HarderToParse.InnerClass, - // which has confusing method signature. - var field: InnerClass = null -} - -object Test extends Application { - ForceParse -} diff --git a/test/files/jvm/lib/annotations.jar.desired.sha1 b/test/files/jvm/lib/annotations.jar.desired.sha1 deleted file mode 100644 index 2b4292d796..0000000000 --- a/test/files/jvm/lib/annotations.jar.desired.sha1 +++ /dev/null @@ -1 +0,0 @@ -02fe2ed93766323a13f22c7a7e2ecdcd84259b6c ?annotations.jar diff --git a/test/files/jvm/lib/nest.jar.desired.sha1 b/test/files/jvm/lib/nest.jar.desired.sha1 deleted file mode 100644 index 674ca79a5b..0000000000 --- a/test/files/jvm/lib/nest.jar.desired.sha1 +++ /dev/null @@ -1 +0,0 @@ -cd33e0a0ea249eb42363a2f8ba531186345ff68c ?nest.jar 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.scala b/test/files/jvm/outerEnum.scala new file mode 100644 index 0000000000..18794b7dbe --- /dev/null +++ b/test/files/jvm/outerEnum.scala @@ -0,0 +1,9 @@ +import enums._ + +object Test extends Application { + def foo { + val res: OuterEnum.Foo = OuterEnum.Foo.Bar + println(res) + } + foo +} diff --git a/test/files/jvm/outerEnum/enums.jar.desired.sha1 b/test/files/jvm/outerEnum/enums.jar.desired.sha1 deleted file mode 100644 index 46cd8e92cf..0000000000 --- a/test/files/jvm/outerEnum/enums.jar.desired.sha1 +++ /dev/null @@ -1 +0,0 @@ -981392dbd1f727b152cd1c908c5fce60ad9d07f7 ?enums.jar diff --git a/test/files/jvm/outerEnum/outerEnum.scala b/test/files/jvm/outerEnum/outerEnum.scala deleted file mode 100644 index 18794b7dbe..0000000000 --- a/test/files/jvm/outerEnum/outerEnum.scala +++ /dev/null @@ -1,9 +0,0 @@ -import enums._ - -object Test extends Application { - def foo { - val res: OuterEnum.Foo = OuterEnum.Foo.Bar - println(res) - } - foo -} 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/lib/annotations.jar.desired.sha1 b/test/files/lib/annotations.jar.desired.sha1 new file mode 100644 index 0000000000..2b4292d796 --- /dev/null +++ b/test/files/lib/annotations.jar.desired.sha1 @@ -0,0 +1 @@ +02fe2ed93766323a13f22c7a7e2ecdcd84259b6c ?annotations.jar diff --git a/test/files/lib/enums.jar.desired.sha1 b/test/files/lib/enums.jar.desired.sha1 new file mode 100644 index 0000000000..46cd8e92cf --- /dev/null +++ b/test/files/lib/enums.jar.desired.sha1 @@ -0,0 +1 @@ +981392dbd1f727b152cd1c908c5fce60ad9d07f7 ?enums.jar diff --git a/test/files/lib/genericNest.jar.desired.sha1 b/test/files/lib/genericNest.jar.desired.sha1 new file mode 100644 index 0000000000..e9321262f2 --- /dev/null +++ b/test/files/lib/genericNest.jar.desired.sha1 @@ -0,0 +1 @@ +b1ec8a095cec4902b3609d74d274c04365c59c04 ?genericNest.jar 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/lib/nest.jar.desired.sha1 b/test/files/lib/nest.jar.desired.sha1 new file mode 100644 index 0000000000..674ca79a5b --- /dev/null +++ b/test/files/lib/nest.jar.desired.sha1 @@ -0,0 +1 @@ +cd33e0a0ea249eb42363a2f8ba531186345ff68c ?nest.jar diff --git a/test/files/lib/scalacheck.jar.desired.sha1 b/test/files/lib/scalacheck.jar.desired.sha1 new file mode 100644 index 0000000000..ed9c46c3db --- /dev/null +++ b/test/files/lib/scalacheck.jar.desired.sha1 @@ -0,0 +1 @@ +4c76385b1a9cb7cd619739776b940d98c4aadc6d ?scalacheck.jar 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/pos/spec-traits.scala b/test/files/pos/spec-traits.scala deleted file mode 100644 index 9e339a14ad..0000000000 --- a/test/files/pos/spec-traits.scala +++ /dev/null @@ -1,83 +0,0 @@ -trait A[@specialized(Int) T] { def foo: T } -class B extends A[Int] { val foo = 10 } -class C extends B - -// issue 3309 -class Lazy { - def test[U](block: => U): Unit = { block } - - test { lazy val x = 1 } -} - -// issue 3307 -class Bug3307 { - def f[Z](block: String => Z) { - block("abc") - } - - ({ () => - f { implicit x => println(x) } })() -} - -// issue 3301 - trait T[X] - -class Bug3301 { - def t[A]: T[A] = error("stub") - - () => { - type X = Int - - def foo[X] = t[X] - () - } -} -// issue 3299 -object Failure { - def thunk() { - for (i <- 1 to 2) { - val Array(a, b) = Array(1,2) - () - } - } -} - -// issue 3296 - -object AA -{ - def f(block: => Unit) {} - - object BB - { - f { - object CC - - () - } - } - - def foo[T](x: T) = { object A; false } -} - -// issue 3292 -import scala.swing._ -import scala.swing.GridBagPanel._ - -object Grid { - - def later(code : => Unit) = - javax.swing.SwingUtilities.invokeLater(new Runnable { def run { code }}) - - def test = later { - val frame = new Dialog { - contents = new GridBagPanel { - val c = new Constraints - } - } - } - -} - -// issue 3325 -object O { def f[@specialized T] { for(k <- Nil: List[T]) { } } } diff --git a/test/files/pos/t1254/t1254.java b/test/files/pos/t1254/t1254.java deleted file mode 100644 index 25b733cf28..0000000000 --- a/test/files/pos/t1254/t1254.java +++ /dev/null @@ -1,28 +0,0 @@ -/* Taken from ticket #1254. Tests Java signatures in mirror classes and that - Nothing is translated to Nothing$. -*/ - -import scala.None; - -// This compiles with javac but fails with Eclipse java compiler: -// 'The type scala.Nothing cannot be resolved. It is indirectly referenced from required .class files' -class NothingBug3 { - public NothingBug3() { - scala.Option o = scala.None$.MODULE$; - - test(o); - None.toLeft(new scala.Function0() { - public Integer apply() { return 0; } - }); - } - - public void test(scala.Option f) {} -} - -// This compiles with javac but fails with Eclipse java compiler: -// 'The type scala.Nothing cannot be resolved. It is indirectly referenced from required .class files' -class NothingBug4 { - public NothingBug4() { - scala.Option o = scala.None$.MODULE$; - } -} 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/files/run/stream_length.scala b/test/files/run/stream_length.scala deleted file mode 100644 index 68e9cad5ac..0000000000 --- a/test/files/run/stream_length.scala +++ /dev/null @@ -1,15 +0,0 @@ - - -object Test { - def walk(depth: Int, bias: String): Stream[String] = { - if (depth == 0) - Stream(bias) - else { - Stream.concat(Stream.range(1, 100).map((x: Int) => walk(depth-1, bias + x))) - } - } - - def main(args: Array[String]) { - println("Length: " + walk(3, "---").length) - } -} diff --git a/test/files/run/t2946/Parsers.scala b/test/files/run/t2946/Parsers.scala deleted file mode 100644 index c0961034c4..0000000000 --- a/test/files/run/t2946/Parsers.scala +++ /dev/null @@ -1,4 +0,0 @@ -class Parser { - def parse(t: Any): Unit = { - } -} diff --git a/test/files/run/t2946/ResponseCommon.scala b/test/files/run/t2946/ResponseCommon.scala deleted file mode 100644 index fa9d8acccb..0000000000 --- a/test/files/run/t2946/ResponseCommon.scala +++ /dev/null @@ -1,14 +0,0 @@ -trait ResponseCommon extends Parser { - private[this] var paramsParser: Parser = null - def withParamsParser(parser: Parser) = {paramsParser = parser; this} - - class Foo { - println(paramsParser) - } - - override abstract def parse(t: Any): Unit = t match { - case ("params", value: List[_]) => value.foreach {paramsParser.parse(_)} - case _ => super.parse(t) - } -} - diff --git a/test/files/run/t2946/Test.scala b/test/files/run/t2946/Test.scala deleted file mode 100644 index e9d9896a0e..0000000000 --- a/test/files/run/t2946/Test.scala +++ /dev/null @@ -1,7 +0,0 @@ -class Test extends Parser with ResponseCommon - -object Test { - def main(args: Array[String]) { - new Test - } -} diff --git a/test/files/scalacheck/redblack.scala b/test/files/scalacheck/redblack.scala deleted file mode 100644 index 0334c1218d..0000000000 --- a/test/files/scalacheck/redblack.scala +++ /dev/null @@ -1,157 +0,0 @@ -import org.scalacheck._ -import Prop._ -import Gen._ - -/* -Properties of a Red & Black Tree: - -A node is either red or black. -The root is black. (This rule is used in some definitions and not others. Since the -root can always be changed from red to black but not necessarily vice-versa this -rule has little effect on analysis.) -All leaves are black. -Both children of every red node are black. -Every simple path from a given node to any of its descendant leaves contains the same number of black nodes. -*/ - -abstract class RedBlackTest extends Properties("RedBlack") { - object RedBlackTest extends scala.collection.immutable.RedBlack[Int] { - def isSmaller(x: Int, y: Int) = x < y - } - - import RedBlackTest._ - - def rootIsBlack[A](t: Tree[A]) = t.isBlack - - def areAllLeavesBlack[A](t: Tree[A]): Boolean = t match { - case Empty => t.isBlack - case ne: NonEmpty[_] => List(ne.left, ne.right) forall areAllLeavesBlack - } - - def areRedNodeChildrenBlack[A](t: Tree[A]): Boolean = t match { - case RedTree(_, _, left, right) => List(left, right) forall (t => t.isBlack && areRedNodeChildrenBlack(t)) - case BlackTree(_, _, left, right) => List(left, right) forall areRedNodeChildrenBlack - case Empty => true - } - - def blackNodesToLeaves[A](t: Tree[A]): List[Int] = t match { - case Empty => List(1) - case BlackTree(_, _, left, right) => List(left, right) flatMap blackNodesToLeaves map (_ + 1) - case RedTree(_, _, left, right) => List(left, right) flatMap blackNodesToLeaves - } - - def areBlackNodesToLeavesEqual[A](t: Tree[A]): Boolean = t match { - case Empty => true - case ne: NonEmpty[_] => - ( - blackNodesToLeaves(ne).removeDuplicates.size == 1 - && areBlackNodesToLeavesEqual(ne.left) - && areBlackNodesToLeavesEqual(ne.right) - ) - } - - def orderIsPreserved[A](t: Tree[A]): Boolean = t match { - case Empty => true - case ne: NonEmpty[_] => - ( - (ne.left.iterator map (_._1) forall (isSmaller(_, ne.key))) - && (ne.right.iterator map (_._1) forall (isSmaller(ne.key, _))) - && (List(ne.left, ne.right) forall orderIsPreserved) - ) - } - - def setup(l: List[Int], invariant: Tree[Unit] => Boolean): (Boolean, Tree[Unit]) - - def listNoRepetitions(size: Int) = for { - s <- Gen.choose(1, size) - l <- Gen.listOfN(size, Gen.choose(0, Int.MaxValue)) suchThat (l => l.size == l.removeDuplicates.size) - } yield l - def listFewRepetitions(size: Int) = for { - s <- Gen.choose(1, size) - l <- Gen.listOfN(s, Gen.choose(0, size * 4)) suchThat (l => l.size != l.removeDuplicates.size) - } yield l - def listManyRepetitions(size: Int) = for { - s <- Gen.choose(1, size) - l <- Gen.listOfN(s, Gen.choose(0, size)) suchThat (l => l.size != l.removeDuplicates.size) - } yield l - def listEvenRepetitions(size: Int) = listFewRepetitions(size) map (x => - scala.util.Random.shuffle(x zip x flatMap { case (a, b) => List(a, b) }) - ) - - // Arbitrarily weighted list distribution types - val seqType: Gen[Int => Gen[List[Int]]] - - def myGen(sized: Int) = for { - size <- Gen.choose(0, sized) - seq <- seqType - list <- seq(size) - } yield list - - property("root is black") = forAll(myGen(10)) { l => - setup(l, rootIsBlack)._1 :| setup(l, rootIsBlack)._2.toString - } - property("all leaves are black") = forAll(myGen(50)) { l => - setup(l, areAllLeavesBlack)._1 :| setup(l, areAllLeavesBlack)._2.toString - } - property("children of red nodes are black") = forAll(myGen(50)) { l => - setup(l, areRedNodeChildrenBlack)._1 :| setup(l, areRedNodeChildrenBlack)._2.toString - } - property("Every path from a node to its descendant leaves contains the same number of black nodes") = forAll(myGen(50)) { l => - setup(l, areBlackNodesToLeavesEqual)._1 :| setup(l, areBlackNodesToLeavesEqual)._2.toString - } - property("Ordering of keys is preserved") = forAll(myGen(50)) { l => - setup(l, orderIsPreserved)._1 :| setup(l, orderIsPreserved)._2.toString - } -} - -object TestInsertion extends RedBlackTest { - import RedBlackTest._ - override val seqType = Gen.frequency( - (1, listNoRepetitions _), - (1, listManyRepetitions _) - ) - - property("update adds elements") = forAll(myGen(50)) { l => - val tree = l.foldLeft(Empty: Tree[Unit])((acc, n) => acc update (n, ())) - forAll(Gen.pick(1, l)) ( n => !(tree lookup n.head isEmpty) :| "Tree: "+tree+" N: "+n.head ) - } - - override def setup(l: List[Int], invariant: Tree[Unit] => Boolean) = l.foldLeft((true, Empty: Tree[Unit])) { - case ((true, acc), n) => - val newRoot = acc update (n, ()) - (invariant(newRoot), newRoot) - case (failed, _) => failed - } -} - -object TestDeletion extends RedBlackTest { - import RedBlackTest._ - override val seqType = Gen.frequency( - (2, listFewRepetitions _), - (3, listManyRepetitions _), - (1, listEvenRepetitions _) - ) - - property("delete removes elements") = forAll(myGen(50)) { l => - val tree = l.foldLeft(Empty: Tree[Unit])((acc, n) => acc update (n, ())) - forAll(Gen.choose(1, l.size)) { numberOfElementsToRemove => - forAll(Gen.pick(numberOfElementsToRemove, l)) { elementsToRemove => - val newTree = elementsToRemove.foldLeft(tree)((acc, n) => acc delete n) - (elementsToRemove forall (n => newTree lookup n isEmpty)) :| "Tree: "+tree+"New Tree: "+newTree+" Elements to Remove: "+elementsToRemove - } - } - } - - override def setup(l: List[Int], invariant: Tree[Unit] => Boolean) = l.foldLeft((true, Empty: Tree[Unit])) { - case ((true, acc), n) => - val newRoot = if (acc lookup n isEmpty) acc update (n, ()) else acc delete n - (invariant(newRoot), newRoot) - case (failed, _) => failed - } -} - -object Test extends Properties("RedBlack") { - include(TestInsertion) - include(TestDeletion) -} - 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 -- cgit v1.2.3