summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.xml6
-rw-r--r--src/partest/scala/tools/partest/nest/CompileManager.scala63
-rw-r--r--src/partest/scala/tools/partest/nest/Diff.java894
-rw-r--r--src/partest/scala/tools/partest/nest/DiffPrint.java578
-rw-r--r--src/partest/scala/tools/partest/nest/FileManager.scala50
-rw-r--r--src/partest/scala/tools/partest/nest/NestRunner.scala23
-rw-r--r--src/partest/scala/tools/partest/nest/StreamAppender.scala32
-rw-r--r--src/partest/scala/tools/partest/nest/Worker.scala223
8 files changed, 1736 insertions, 133 deletions
diff --git a/build.xml b/build.xml
index a6fc4a8486..e08d5ad991 100644
--- a/build.xml
+++ b/build.xml
@@ -596,6 +596,12 @@ BUILD QUICK-TEST LAYER
/>
<!-- Build partest -->
<mkdir dir="${quick.dir}/lib/partest"/>
+ <javac
+ srcdir="${src.dir}/partest"
+ destdir="${quick.dir}/lib/partest"
+ source="1.4" target="1.4" deprecation="yes"
+ classpath="${quick.dir}/lib/partest"
+ includes="scala/tools/partest/**/*.java"/>
<locker
srcdir="${src.dir}/partest"
destdir="${quick.dir}/lib/partest"
diff --git a/src/partest/scala/tools/partest/nest/CompileManager.scala b/src/partest/scala/tools/partest/nest/CompileManager.scala
index b31653ffde..81bbf076db 100644
--- a/src/partest/scala/tools/partest/nest/CompileManager.scala
+++ b/src/partest/scala/tools/partest/nest/CompileManager.scala
@@ -19,6 +19,7 @@ class ExtConsoleReporter(override val settings: Settings, reader: BufferedReader
abstract class SimpleCompiler {
def compile(file: File, kind: String): Boolean
+ def compile(file: File, kind: String, log: File): Boolean
}
class DirectCompiler extends SimpleCompiler {
@@ -45,11 +46,40 @@ class DirectCompiler extends SimpleCompiler {
Console.in,
new PrintWriter(new FileWriter("scalac-out")))
- val testSettings = newSettings
- val testRep = newReporter(testSettings)
- val global = newGlobal(testSettings, testRep)
+ def compile(file: File, kind: String, log: File): Boolean = {
+ val testSettings = newSettings
+ val global = newGlobal(testSettings, log)
+ val testRep: ExtConsoleReporter = global.reporter.asInstanceOf[ExtConsoleReporter]
+
+ val test: TestFile = kind match {
+ case "pos" => PosTestFile(file)
+ case "neg" => NegTestFile(file)
+ case "run" => RunTestFile(file)
+ case "jvm" => JvmTestFile(file)
+ case "jvm5" => Jvm5TestFile(file)
+ case "shootout" => ShootoutTestFile(file)
+ }
+ test.defineSettings(testSettings)
+
+ val toCompile = List(file.getPath)
+ try {
+ (new global.Run) compile toCompile
+ testRep.printSummary
+ testRep.writer.flush
+ testRep.writer.close
+ } catch {
+ case e: Exception =>
+ e.printStackTrace()
+ false
+ }
+ !testRep.hasErrors
+ }
def compile(file: File, kind: String): Boolean = {
+ val testSettings = newSettings
+ val testRep = newReporter(testSettings)
+ val global = newGlobal(testSettings, testRep)
+
val test: TestFile = kind match {
case "pos" => PosTestFile(file)
case "neg" => NegTestFile(file)
@@ -83,18 +113,27 @@ class ReflectiveCompiler extends SimpleCompiler {
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", Array(fileClass, stringClass))
- val sepCompiler = sepCompilerClass.newInstance()
+ val sepCompileMethod2 =
+ sepCompilerClass.getMethod("compile", Array(fileClass, stringClass, fileClass))
def compile(file: File, kind: String): Boolean = {
val fileArgs: Array[AnyRef] = Array(file, kind)
val res = sepCompileMethod.invoke(sepCompiler, fileArgs).asInstanceOf[java.lang.Boolean]
res.booleanValue()
}
+
+ def compile(file: File, kind: String, log: File): Boolean = {
+ val fileArgs: Array[AnyRef] = Array(file, kind, log)
+ val res = sepCompileMethod2.invoke(sepCompiler, fileArgs).asInstanceOf[java.lang.Boolean]
+ res.booleanValue()
+ }
}
class CompileManager {
@@ -106,16 +145,20 @@ class CompileManager {
compiler = new ReflectiveCompiler
}
- def shouldCompile(file: File, kind: String): Boolean =
- compiler.compile(file, kind) || {
+ def shouldCompile(file: File, kind: String): Boolean = {
+ createSeparateCompiler()
+ compiler.compile(file, kind)
+
+ /*compiler.compile(file, kind) || {
NestUI.verbose("creating new separate compiler")
createSeparateCompiler()
compiler.compile(file, kind)
- }
+ }*/
+ }
- def shouldFailCompile(file: File, kind: String): Boolean = {
- // always create separate compiler
+ def shouldFailCompile(file: File, kind: String, log: File): Boolean = {
+ // always create new separate compiler
createSeparateCompiler()
- !compiler.compile(file, kind)
+ !compiler.compile(file, kind, log)
}
}
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..cfb7d89722
--- /dev/null
+++ b/src/partest/scala/tools/partest/nest/Diff.java
@@ -0,0 +1,894 @@
+/*
+ * $Log$
+ * Revision 1.1 2003/10/13 16:00:03 michelou
+ * - Java implementation of GNU diff (GPL licence)
+ *
+ * Revision 1.6 2003/03/06 22:51:32 stuart
+ * Convert to CVS
+ *
+ * Revision 1.5 2002/07/19 19:14:40 stuart
+ * fix reverseScript, make change ctor public, update docs
+ *
+ * Revision 1.4 2002/04/09 17:53:39 stuart
+ * More flexible interface for diff() function.
+ *
+ * Revision 1.3 2000/03/03 21:58:03 stuart
+ * move discard_confusing_lines and shift_boundaries to class file_data
+ *
+ * Revision 1.2 2000/03/02 16:37:38 stuart
+ * Add GPL and copyright
+ *
+ */
+//package bmsi.util;
+package scala.tools.partest.nest;
+
+import java.util.Hashtable;
+
+/** A class to compare vectors of objects. The result of comparison
+ is a list of <code>change</code> objects which form an
+ edit script. The objects compared are traditionally lines
+ of text from two files. Comparison options such as "ignore
+ whitespace" are implemented by modifying the <code>equals</code>
+ and <code>hashcode</code> methods for the objects compared.
+<p>
+ The basic algorithm is described in: </br>
+ "An O(ND) Difference Algorithm and its Variations", Eugene Myers,
+ Algorithmica Vol. 1 No. 2, 1986, p 251.
+<p>
+ This class outputs different results from GNU diff 1.15 on some
+ inputs. Our results are actually better (smaller change list, smaller
+ total size of changes), but it would be nice to know why. Perhaps
+ there is a memory overwrite bug in GNU diff 1.15.
+
+ @author Stuart D. Gathman, translated from GNU diff 1.15
+ Copyright (C) 2000 Business Management Systems, Inc.
+<p>
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+<p>
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+<p>
+ You should have received a copy of the <a href=COPYING.txt>
+ GNU General Public License</a>
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+public class Diff {
+
+ /** Prepare to find differences between two arrays. Each element of
+ the arrays is translated to an "equivalence number" based on
+ the result of <code>equals</code>. The original Object arrays
+ are no longer needed for computing the differences. They will
+ be needed again later to print the results of the comparison as
+ an edit script, if desired.
+ */
+ public Diff(Object[] a,Object[] b) {
+ Hashtable h = new Hashtable(a.length + b.length);
+ filevec[0] = new file_data(a,h);
+ filevec[1] = new file_data(b,h);
+ }
+
+ /** 1 more than the maximum equivalence value used for this or its
+ sibling file. */
+ private int equiv_max = 1;
+
+ /** When set to true, the comparison uses a heuristic to speed it up.
+ With this heuristic, for files with a constant small density
+ of changes, the algorithm is linear in the file size. */
+ public boolean heuristic = false;
+
+ /** When set to true, the algorithm returns a guarranteed minimal
+ set of changes. This makes things slower, sometimes much slower. */
+ public boolean no_discards = false;
+
+ private int[] xvec, yvec; /* Vectors being compared. */
+ private int[] fdiag; /* Vector, 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; /* Vector, 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 vectors 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 vector.
+ */
+ changed_flag = new boolean[buffered_lines + 2];
+ }
+
+ /** Return equiv_count[I] as the number of lines in this file
+ that fall in equivalence class I.
+ @return the array of equivalence class counts.
+ */
+ int[] equivCount() {
+ int[] equiv_count = new int[equiv_max];
+ for (int i = 0; i < buffered_lines; ++i)
+ ++equiv_count[equivs[i]];
+ return equiv_count;
+ }
+
+ /** Discard lines that have no matches in another file.
+
+ A line which is discarded will not be considered by the actual
+ comparison algorithm; it will be as if that line were not in the file.
+ The file's `realindexes' table maps virtual line numbers
+ (which don't count the discarded lines) into real line numbers;
+ this is how the actual comparison algorithm produces results
+ that are comprehensible when the discarded lines are counted.
+<p>
+ When we discard a line, we also mark it as a deletion or insertion
+ so that it will be printed in the output.
+ @param f the other file
+ */
+ void discard_confusing_lines(file_data f) {
+ clear();
+ /* Set up table of which lines are going to be discarded. */
+ final byte[] discarded = discardable(f.equivCount());
+
+ /* Don't really discard the provisional lines except when they occur
+ in a run of discardables, with nonprovisionals at the beginning
+ and end. */
+ filterDiscards(discarded);
+
+ /* Actually discard the lines. */
+ discard(discarded);
+ }
+
+ /** Mark to be discarded each line that matches no line of another file.
+ If a line matches many lines, mark it as provisionally discardable.
+ @see equivCount()
+ @param counts The count of each equivalence number for the other file.
+ @return 0=nondiscardable, 1=discardable or 2=provisionally discardable
+ for each line
+ */
+
+ private byte[] discardable(final int[] counts) {
+ final int end = buffered_lines;
+ final byte[] discards = new byte[end];
+ final int[] equivs = this.equivs;
+ int many = 5;
+ int tem = end / 64;
+
+ /* Multiply MANY by approximate square root of number of lines.
+ That is the threshold for provisionally discardable lines. */
+ while ((tem = tem >> 2) > 0)
+ many *= 2;
+
+ for (int i = 0; i < end; i++)
+ {
+ int nmatch;
+ if (equivs[i] == 0)
+ continue;
+ nmatch = counts[equivs[i]];
+ if (nmatch == 0)
+ discards[i] = 1;
+ else if (nmatch > many)
+ discards[i] = 2;
+ }
+ return discards;
+ }
+
+ /** Don't really discard the provisional lines except when they occur
+ in a run of discardables, with nonprovisionals at the beginning
+ and end. */
+
+ private void filterDiscards(final byte[] discards) {
+ final int end = buffered_lines;
+
+ for (int i = 0; i < end; i++)
+ {
+ /* Cancel provisional discards not in middle of run of discards. */
+ if (discards[i] == 2)
+ discards[i] = 0;
+ else if (discards[i] != 0)
+ {
+ /* We have found a nonprovisional discard. */
+ int j;
+ int length;
+ int provisional = 0;
+
+ /* Find end of this run of discardable lines.
+ Count how many are provisionally discardable. */
+ for (j = i; j < end; j++)
+ {
+ if (discards[j] == 0)
+ break;
+ if (discards[j] == 2)
+ ++provisional;
+ }
+
+ /* Cancel provisional discards at end, and shrink the run. */
+ while (j > i && discards[j - 1] == 2) {
+ discards[--j] = 0; --provisional;
+ }
+
+ /* Now we have the length of a run of discardable lines
+ whose first and last are not provisional. */
+ length = j - i;
+
+ /* If 1/4 of the lines in the run are provisional,
+ cancel discarding of all provisional lines in the run. */
+ if (provisional * 4 > length)
+ {
+ while (j > i)
+ if (discards[--j] == 2)
+ discards[j] = 0;
+ }
+ else
+ {
+ int consec;
+ int minimum = 1;
+ int tem = length / 4;
+
+ /* MINIMUM is approximate square root of LENGTH/4.
+ A subrun of two or more provisionals can stand
+ when LENGTH is at least 16.
+ A subrun of 4 or more can stand when LENGTH >= 64. */
+ while ((tem = tem >> 2) > 0)
+ minimum *= 2;
+ minimum++;
+
+ /* Cancel any subrun of MINIMUM or more provisionals
+ within the larger run. */
+ for (j = 0, consec = 0; j < length; j++)
+ if (discards[i + j] != 2)
+ consec = 0;
+ else if (minimum == ++consec)
+ /* Back up to start of subrun, to cancel it all. */
+ j -= consec;
+ else if (minimum < consec)
+ discards[i + j] = 0;
+
+ /* Scan from beginning of run
+ until we find 3 or more nonprovisionals in a row
+ or until the first nonprovisional at least 8 lines in.
+ Until that point, cancel any provisionals. */
+ for (j = 0, consec = 0; j < length; j++)
+ {
+ if (j >= 8 && discards[i + j] == 1)
+ break;
+ if (discards[i + j] == 2) {
+ consec = 0; discards[i + j] = 0;
+ }
+ else if (discards[i + j] == 0)
+ consec = 0;
+ else
+ consec++;
+ if (consec == 3)
+ break;
+ }
+
+ /* I advances to the last line of the run. */
+ i += length - 1;
+
+ /* Same thing, from end. */
+ for (j = 0, consec = 0; j < length; j++)
+ {
+ if (j >= 8 && discards[i - j] == 1)
+ break;
+ if (discards[i - j] == 2) {
+ consec = 0; discards[i - j] = 0;
+ }
+ else if (discards[i - j] == 0)
+ consec = 0;
+ else
+ consec++;
+ if (consec == 3)
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /** Actually discard the lines.
+ @param discards flags lines to be discarded
+ */
+ private void discard(final byte[] discards) {
+ final int end = buffered_lines;
+ int j = 0;
+ for (int i = 0; i < end; ++i)
+ if (no_discards || discards[i] == 0)
+ {
+ undiscarded[j] = equivs[i];
+ realindexes[j++] = i;
+ }
+ else
+ changed_flag[1+i] = true;
+ nondiscarded_lines = j;
+ }
+
+ file_data(Object[] data,Hashtable h) {
+ buffered_lines = data.length;
+
+ equivs = new int[buffered_lines];
+ undiscarded = new int[buffered_lines];
+ realindexes = new int[buffered_lines];
+
+ for (int i = 0; i < data.length; ++i) {
+ Integer ir = (Integer)h.get(data[i]);
+ if (ir == null)
+ h.put(data[i],new Integer(equivs[i] = equiv_max++));
+ else
+ equivs[i] = ir.intValue();
+ }
+ }
+
+ /** Adjust inserts/deletes of blank lines to join changes
+ as much as possible.
+
+ We do something when a run of changed lines include a blank
+ line at one end and have an excluded blank line at the other.
+ We are free to choose which blank line is included.
+ `compareseq' always chooses the one at the beginning,
+ but usually it is cleaner to consider the following blank line
+ to be the "change". The only exception is if the preceding blank line
+ would join this change to other changes.
+ @param f the file being compared against
+ */
+
+ void shift_boundaries(file_data f) {
+ final boolean[] changed = changed_flag;
+ final boolean[] other_changed = f.changed_flag;
+ int i = 0;
+ int j = 0;
+ int i_end = buffered_lines;
+ int preceding = -1;
+ int other_preceding = -1;
+
+ for (;;)
+ {
+ int start, end, other_start;
+
+ /* Scan forwards to find beginning of another run of changes.
+ Also keep track of the corresponding point in the other file. */
+
+ while (i < i_end && !changed[1+i])
+ {
+ while (other_changed[1+j++])
+ /* Non-corresponding lines in the other file
+ will count as the preceding batch of changes. */
+ other_preceding = j;
+ i++;
+ }
+
+ if (i == i_end)
+ break;
+
+ start = i;
+ other_start = j;
+
+ for (;;)
+ {
+ /* Now find the end of this run of changes. */
+
+ while (i < i_end && changed[1+i]) i++;
+ end = i;
+
+ /* If the first changed line matches the following unchanged one,
+ and this run does not follow right after a previous run,
+ and there are no lines deleted from the other file here,
+ then classify the first changed line as unchanged
+ and the following line as changed in its place. */
+
+ /* You might ask, how could this run follow right after another?
+ Only because the previous run was shifted here. */
+
+ if (end != i_end
+ && equivs[start] == equivs[end]
+ && !other_changed[1+j]
+ && end != i_end
+ && !((preceding >= 0 && start == preceding)
+ || (other_preceding >= 0
+ && other_start == other_preceding)))
+ {
+ changed[1+end++] = true;
+ changed[1+start++] = false;
+ ++i;
+ /* Since one line-that-matches is now before this run
+ instead of after, we must advance in the other file
+ to keep in synch. */
+ ++j;
+ }
+ else
+ break;
+ }
+
+ preceding = i;
+ other_preceding = j;
+ }
+ }
+
+ /** Number of elements (lines) in this file. */
+ final int buffered_lines;
+
+ /** Vector, indexed by line number, containing an equivalence code for
+ each line. It is this vector that is actually compared with that
+ of another file to generate differences. */
+ private final int[] equivs;
+
+ /** Vector, like the previous one except that
+ the elements for discarded lines have been squeezed out. */
+ final int[] undiscarded;
+
+ /** Vector 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..7fc8eacb0d
--- /dev/null
+++ b/src/partest/scala/tools/partest/nest/DiffPrint.java
@@ -0,0 +1,578 @@
+/* $Log$
+ * Revision 1.1 2003/10/13 16:00:11 michelou
+ * - Java implementation of GNU diff (GPL licence)
+ *
+ * Revision 1.4 2003/04/22 01:50:47 stuart
+ * add Unified format diff
+ *
+ * Revision 1.3 2003/04/22 01:00:32 stuart
+ * added context diff format
+ *
+ * Revision 1.2 2000/03/02 16:59:54 stuart
+ * add GPL
+ *
+ */
+
+//package bmsi.util;
+package scala.tools.partest.nest;
+
+import java.io.*;
+import java.util.Vector;
+import java.util.Date;
+//import bmsi.util.Diff;
+//import com.objectspace.jgl.predicates.UnaryPredicate;
+
+interface UnaryPredicate {
+ boolean execute(Object obj);
+}
+
+/** A simple framework for printing change lists produced by <code>Diff</code>.
+ @see bmsi.util.Diff
+ @author Stuart D. Gathman
+ Copyright (C) 2000 Business Management Systems, Inc.
+<p>
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+<p>
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+<p>
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+public class DiffPrint {
+ /** A Base class for printing edit scripts produced by Diff.
+ This class divides the change list into "hunks", and calls
+ <code>print_hunk</code> for each hunk. Various utility methods
+ are provided as well.
+ */
+ public static abstract class Base {
+ protected Base(Object[] a,Object[] b) {
+ outfile = new PrintWriter(new OutputStreamWriter(System.out));
+ 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) {
+ super(a,b);
+ }
+
+ /** Print a hunk of a normal diff.
+ This is a contiguous portion of a complete edit script,
+ describing changes in consecutive lines. */
+
+ protected void print_hunk (Diff.change hunk) {
+
+ /* Determine range of line numbers involved in each file. */
+ analyze_hunk(hunk);
+ if (deletes == 0 && inserts == 0)
+ return;
+
+ /* Print out the line number header for this hunk */
+ print_number_range (',', first0, last0);
+ outfile.print(change_letter(inserts, deletes));
+ print_number_range (',', first1, last1);
+ outfile.println();
+
+ /* Print the lines that the first file has. */
+ if (deletes != 0)
+ for (int i = first0; i <= last0; i++)
+ print_1_line ("< ", file0[i]);
+
+ if (inserts != 0 && deletes != 0)
+ outfile.println("---");
+
+ /* Print the lines that the second file has. */
+ if (inserts != 0)
+ for (int i = first1; i <= last1; i++)
+ print_1_line ("> ", file1[i]);
+ }
+ }
+
+ /** Prints an edit script in a format suitable for input to <code>ed</code>.
+ The edit script must be generated with the reverse option to
+ be useful as actual <code>ed</code> input.
+ */
+ public static class EdPrint extends Base {
+
+ public EdPrint(Object[] a,Object[] b) {
+ super(a,b);
+ }
+
+ /** 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) {
+ super(a,b);
+ }
+
+ 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) {
+ super(a,b);
+ }
+
+ 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;
+ switch (style) {
+ case 'e':
+ p = new EdPrint(a,b); break;
+ case 'c':
+ p = new ContextPrint(a,b); break;
+ case 'u':
+ p = new UnifiedPrint(a,b); break;
+ default:
+ p = new NormalPrint(a,b);
+ }
+ p.print_header(filea,fileb);
+ p.print_script(script);
+ }
+ }
+
+}
diff --git a/src/partest/scala/tools/partest/nest/FileManager.scala b/src/partest/scala/tools/partest/nest/FileManager.scala
index 2a205f42a0..ade5dd46b5 100644
--- a/src/partest/scala/tools/partest/nest/FileManager.scala
+++ b/src/partest/scala/tools/partest/nest/FileManager.scala
@@ -4,7 +4,8 @@
package scala.tools.partest.nest
-import java.io.{File, FilenameFilter}
+import java.io.{File, FilenameFilter, ByteArrayOutputStream, PrintStream,
+ IOException, BufferedOutputStream}
import java.net.URI
object FileManager {
@@ -12,7 +13,7 @@ object FileManager {
val PATH_SEP = File.pathSeparatorChar
val CLASSPATH = System.getProperty("java.class.path", ".")
val PREFIX = System.getProperty("user.dir", ".")+"/.."
- val JAVACMD = System.getProperty("nest.javacmd", "java")
+ val JAVACMD = System.getProperty("scalatest.javacmd", "java")
/*
if [ -d "$PREFIX/test" ]; then
@@ -112,7 +113,7 @@ elif [ -d "$PREFIX/bin" ]; then
var SCALA: String = ""
var SCALAC_CMD: String = ""
- val SCALAC_OPTS = System.getProperty("nest.scalac_opts", "-deprecation -encoding utf8")
+ val SCALAC_OPTS = System.getProperty("scalatest.scalac_opts", "-deprecation -encoding utf8")
var latestFile: File = _
var latestLibFile: File = _
@@ -122,7 +123,7 @@ elif [ -d "$PREFIX/bin" ]; then
findLatest()
val srcDir = {
- val dirname = System.getProperty("nest.cwd", "")
+ val dirname = System.getProperty("scalatest.cwd", "")
val dir = if (dirname.isEmpty) { // guess
val libDir = new File(new URI(classOf[Test].getResource("/").toString))
val path = libDir.getAbsolutePath
@@ -142,12 +143,53 @@ elif [ -d "$PREFIX/bin" ]; then
exit(1)
}
+ private def basename(name: String): String = {
+ val inx = name.lastIndexOf(".")
+ if (inx < 0) name else name.substring(0, inx)
+ }
+
+ def getLogFile(file: File, kind: String) = {
+ val dir = file.getParentFile
+ val fileBase = basename(file.getName)
+ new File(dir, fileBase + "-" + kind + ".log")
+ }
+
def deleteRecursive(dir: File) {
if (dir.isDirectory) {
for (file <- dir.list) deleteRecursive(new File(dir, file))
}
dir.delete
}
+
+ /**
+ * Compares two files using a Java implementation of the GNU diff
+ * available at http://www.bmsi.com/java/#diff.
+ *
+ * @param f1
+ * @param f2
+ */
+ def compareFiles(f1: File, f2: File): String = {
+ var res = ""
+ try {
+ val diffStream = new ByteArrayOutputStream
+ val diffOutput = new PrintStream(
+ new BufferedOutputStream(diffStream), true)
+ System.setOut(diffOutput)
+ System.setErr(diffOutput)
+ val args = Array(f1.getCanonicalPath(), f2.getCanonicalPath())
+ DiffPrint.main(args)
+ System.setOut(System.out)
+ System.setErr(System.err)
+
+ res = diffStream.toString
+ if (res.startsWith("No"))
+ res = ""
+ } catch {
+ case e: IOException =>
+ e.printStackTrace()
+ }
+ res
+ }
}
class FileManager {
diff --git a/src/partest/scala/tools/partest/nest/NestRunner.scala b/src/partest/scala/tools/partest/nest/NestRunner.scala
index 07e2a2d335..abd155678c 100644
--- a/src/partest/scala/tools/partest/nest/NestRunner.scala
+++ b/src/partest/scala/tools/partest/nest/NestRunner.scala
@@ -22,12 +22,16 @@ object NestRunner {
private var conservative = false
+ var showDiff = false
+ var showLog = false
+ var failed = false
+
private var testFiles: List[File] = List()
private val con = new PrintStream(Console.out)
private var out = con
private val errors =
- Integer.parseInt(System.getProperty("nest.errors", "0"))
+ Integer.parseInt(System.getProperty("scalatest.errors", "0"))
def main(args: Array[String]) {
NestUI.initialize(NestUI.MANY)
@@ -45,13 +49,17 @@ object NestRunner {
case "--shootout" => shootoutCheck = true
case "--conservative" => conservative = true
case "--verbose" => NestUI._verbose = true
+ case "--show-diff" => showDiff = true
+ case "--show-log" => showLog = true
+ case "--failed" => failed = true
case "--version" => //todo: printVersion
case _ =>
if (arg endsWith ".scala") {
val file = new File(arg)
- if (file.isFile)
+ if (file.isFile) {
+ NestUI.verbose("adding test file "+file)
testFiles = file :: testFiles
- else {
+ } else {
NestUI.failure("File \"" + arg + "\" not found")
System.exit(1)
}
@@ -85,8 +93,8 @@ object NestRunner {
errApp.start()
val exitCode = proc.waitFor()
NestUI.verbose("exit code: "+exitCode)
- writer.flush()
- errWriter.flush()
+ appender.join()
+ errApp.join()
val scalaVersion = writer.toString + errWriter.toString
NestUI.outline("Scala version is : "+scalaVersion)
@@ -124,7 +132,10 @@ object NestRunner {
if (check) {
val fileMgr = new FileManager
val kindFiles =
- if (!testFiles.isEmpty) testFiles
+ if (!testFiles.isEmpty) {
+ NestUI.verbose("testing "+testFiles)
+ testFiles
+ }
else fileMgr.getFiles(kind, check)
if (!kindFiles.isEmpty) {
NestUI.outline("\n"+msg+"\n")
diff --git a/src/partest/scala/tools/partest/nest/StreamAppender.scala b/src/partest/scala/tools/partest/nest/StreamAppender.scala
index 4412d37ae7..eddf47cbfc 100644
--- a/src/partest/scala/tools/partest/nest/StreamAppender.scala
+++ b/src/partest/scala/tools/partest/nest/StreamAppender.scala
@@ -7,24 +7,20 @@ package scala.tools.partest.nest
import java.io.{Writer, PrintWriter, Reader, BufferedReader,
IOException}
-class StreamAppender(from: Reader, to: Writer) {
- private val thr = new Thread(new Runnable {
- def run() {
- try {
- val writer = new PrintWriter(to)
- val reader = new BufferedReader(from)
- var line = reader.readLine()
- while (line != null) {
- writer.println(line)
- line = reader.readLine()
- }
- writer.flush()
- } catch {
- case e: IOException =>
- e.printStackTrace()
+class StreamAppender(from: Reader, to: Writer) extends Thread {
+ override def run() {
+ try {
+ val writer = new PrintWriter(to)
+ val reader = new BufferedReader(from)
+ var line = reader.readLine()
+ while (line != null) {
+ writer.println(line)
+ line = reader.readLine()
}
+ writer.flush()
+ } catch {
+ case e: IOException =>
+ e.printStackTrace()
}
- })
-
- def start() { thr.start() }
+ }
}
diff --git a/src/partest/scala/tools/partest/nest/Worker.scala b/src/partest/scala/tools/partest/nest/Worker.scala
index 35a6331314..662a8708d2 100644
--- a/src/partest/scala/tools/partest/nest/Worker.scala
+++ b/src/partest/scala/tools/partest/nest/Worker.scala
@@ -5,7 +5,8 @@
package scala.tools.partest.nest
import java.io.{File, FileInputStream, FileOutputStream, PrintStream,
- PrintWriter, StringWriter}
+ PrintWriter, StringWriter, FileWriter, InputStreamReader,
+ FileReader}
import java.net.URL
@@ -36,11 +37,22 @@ class Worker extends Actor {
def printInfoStart(file: File, printer: PrintWriter) {
NestUI.outline("testing: ", printer)
- val dir = file.getParentFile
- val dirpath = dir.getAbsolutePath
- val name = file.getAbsolutePath.substring(dirpath.length)
- val WIDTH = 56
- NestUI.normal("[...]"+name+List.toString(List.make(WIDTH-name.length, ' ')), 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("[...]"+name+List.toString(List.make(totalWidth-name.length, ' ')), printer)
}
def printInfoEnd(success: boolean, printer: PrintWriter) {
@@ -59,6 +71,8 @@ class Worker extends Actor {
}
}
+ var log = ""
+
def execTest(outDir: File, logFile: File) {
val cmd =
JAVACMD+
@@ -70,8 +84,32 @@ class Worker extends Actor {
" scala.tools.nsc.MainGenericRunner"+
" Test jvm"
NestUI.verbose(cmd)
- val execution = Runtime.getRuntime.exec(cmd)
- //TODO: use buffered I/O
+
+ val proc = Runtime.getRuntime.exec(cmd)
+ val in = proc.getInputStream
+ val err = proc.getErrorStream
+ val writer = new FileWriter(logFile)
+ val inApp = new StreamAppender(new InputStreamReader(in), writer)
+ val errApp = new StreamAppender(new InputStreamReader(err), writer)
+ inApp.start()
+ errApp.start()
+ proc.waitFor()
+ inApp.join()
+ errApp.join()
+ writer.close()
+
+ if (NestRunner.showLog) {
+ // produce log as string in `log`
+ val reader = new FileReader(logFile)
+ val writer = new StringWriter
+ val appender = new StreamAppender(reader, writer)
+ appender.start()
+ appender.join()
+ reader.close()
+ log = writer.toString
+ }
+
+ /*val execution = Runtime.getRuntime.exec(cmd)
val in = execution.getInputStream
val out = new FileOutputStream(logFile)
var c = in.read
@@ -80,10 +118,10 @@ class Worker extends Actor {
c = in.read
}
in.close
- out.close
+ out.close*/
}
- def compareOutput(dir: File, fileBase: String, kind: String, logFile: File): Boolean = {
+ def compareOutput(dir: File, fileBase: String, kind: String, logFile: File): String = {
// if check file exists, compare with log file
val checkFile = {
val chkFile = new File(dir, fileBase + ".check")
@@ -92,108 +130,102 @@ class Worker extends Actor {
else
new File(dir, fileBase + "-" + kind + ".check")
}
- if (!checkFile.exists || !checkFile.canRead)
- true
- else {
- var success, equalNow = true
- val bufferSize = 1024
- val originBuffer, destBuffer = new Array[Byte](bufferSize)
- val originStream = new FileInputStream(logFile)
- val destStream = new FileInputStream(checkFile)
-
- var originSize = originStream.read(originBuffer)
- while (originSize >= 0) {
- if (originSize == destStream.read(destBuffer)) {
- for (idx <- 0 until originSize)
- equalNow = equalNow && (originBuffer(idx) == destBuffer(idx))
- if (!equalNow) {
- success = false
- NestUI.verbose("Diff1: diffs found")
- }
- } else {
- success = false
- NestUI.verbose("Diff1: diffs found")
- }
- originSize = originStream.read(originBuffer)
- }
- if (destStream.read(destBuffer) >= 0)
- success = false
- success
- }
+ if (!checkFile.exists || !checkFile.canRead) ""
+ else FileManager.compareFiles(logFile, checkFile)
}
def runJvmTests(kind: String, files: List[File]): (Int, Int) = {
+ NestUI.verbose("testing "+files)
val compileMgr = new CompileManager
var errors = 0
var success = true
+ var diff = ""
for (file <- files) {
- val swr = new StringWriter
- val wr = new PrintWriter(swr)
- success = true
- printInfoStart(file, wr)
+ val fileBase: String = basename(file.getName)
+ NestUI.verbose(this+" running test "+fileBase)
+ val dir = file.getParentFile
+ val outDir = new File(dir, fileBase + "-" + kind + ".obj")
+ val logFile = new File(dir, fileBase + "-" + kind + ".log")
- if (!compileMgr.shouldCompile(file, kind)) {
- NestUI.verbose("compilation of "+file+" failed\n")
- success = false
- } else {
- // -------- run test --------
- val fileBase: String = basename(file.getName)
- NestUI.verbose(this+" running test "+fileBase)
- val dir = file.getParentFile
- val dirpath = dir.getAbsolutePath
- val outDir = new File(dir, fileBase + "-" + kind + ".obj")
- val logFile = new File(dir, fileBase + "-" + kind + ".log")
- //TODO: detect whether we have to use Runtime.exec
- val useRuntime = true
+ // when option "--failed" is provided
+ // execute test only if log file is present
+ // (which means it failed before)
+ if (!NestRunner.failed || (logFile.exists && logFile.canRead)) {
+ val swr = new StringWriter
+ val wr = new PrintWriter(swr)
+ success = true
+ diff = ""
+ printInfoStart(file, wr)
- if (useRuntime) {
- execTest(outDir, logFile)
+ if (!compileMgr.shouldCompile(file, kind)) {
+ NestUI.verbose("compilation of "+file+" failed\n")
+ success = false
} else {
- val classpath: List[URL] =
- outDir.toURL ::
- List(file.getParentFile.toURL) :::
- (List.fromString(CLASSPATH, PATH_SEP) map { x =>
- (new File(x)).toURL }) :::
- (List.fromString(EXT_CLASSPATH, PATH_SEP) map { x =>
- (new File(x)).toURL })
- try {
- NestUI.verbose("classpath: "+classpath)
- val out = new FileOutputStream(logFile, true)
- Console.withOut(new PrintStream(out)) {
- ObjectRunner.run(classpath, "Test", List("jvm"))
+ // -------- run test --------
+
+ //TODO: detect whether we have to use Runtime.exec
+ val useRuntime = true
+
+ if (useRuntime) {
+ execTest(outDir, logFile)
+ } else {
+ val classpath: List[URL] =
+ outDir.toURL ::
+ List(file.getParentFile.toURL) :::
+ (List.fromString(CLASSPATH, PATH_SEP) map { x =>
+ (new File(x)).toURL }) :::
+ (List.fromString(EXT_CLASSPATH, PATH_SEP) map { x =>
+ (new File(x)).toURL })
+ try {
+ NestUI.verbose("classpath: "+classpath)
+ 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+")")
}
- out.flush
- out.close
- } catch {
- case e: Exception =>
- NestUI.verbose(e+" ("+file.getPath+")")
}
- }
- NestUI.verbose(this+" finished running "+fileBase)
+ NestUI.verbose(this+" finished running "+fileBase)
- if (!compareOutput(dir, fileBase, kind, logFile)) {
- NestUI.verbose("output differs from log file\n")
- success = false
- }
+ diff = compareOutput(dir, fileBase, kind, logFile)
+ if (!diff.equals("")) {
+ NestUI.verbose("output differs from log file\n")
+ success = false
+ }
- // delete log file and output dir
- FileManager.deleteRecursive(outDir)
- FileManager.deleteRecursive(logFile)
- } // successful compile
- if (!success) {
- errors += 1
- NestUI.verbose("incremented errors: "+errors)
+ // delete output dir
+ FileManager.deleteRecursive(outDir)
+
+ // delete log file only if test was successful
+ if (success)
+ FileManager.deleteRecursive(logFile)
+ } // successful compile
+ if (!success) {
+ errors += 1
+ NestUI.verbose("incremented errors: "+errors)
+ }
+ printInfoEnd(success, wr)
+ wr.flush()
+ swr.flush()
+ NestUI.normal(swr.toString)
+ if (!success && NestRunner.showDiff) NestUI.normal(diff)
+ if (!success && NestRunner.showLog) NestUI.normal(log)
}
- printInfoEnd(success, wr)
- wr.flush()
- swr.flush()
- NestUI.normal(swr.toString)
} // for each file
NestUI.verbose("finished testing "+kind+" with "+errors+" errors")
NestUI.verbose("created "+compileMgr.numSeparateCompilers+" separate compilers")
(files.length-errors, errors)
}
+ /** 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]): (Int, Int) = {
val compileMgr = new CompileManager
var errors = 0
@@ -228,18 +260,19 @@ class Worker extends Actor {
(files.length-errors, errors)
}
case "neg" => {
- def negTestRun(file: File, wr: PrintWriter) = new CompileTestRun(file, wr) {
+ def negTestRun(file: File, wr: PrintWriter, log: File) = new CompileTestRun(file, wr) {
def runTest() {
- if (!compileMgr.shouldFailCompile(file, kind)) {
+ if (!compileMgr.shouldFailCompile(file, kind, log)) {
succeeded = false
errors += 1
- }
+ } //TODO: else compare log file to check file
}
}
for (file <- files) {
+ val logFile = getLogFile(file, kind)
val swr = new StringWriter
val wr = new PrintWriter(swr)
- val testRun = negTestRun(file, wr)
+ val testRun = negTestRun(file, wr, logFile)
testRun.doAll()
wr.flush()
swr.flush()