Browse Source

initial version

tags/initial_CPL
wisberg 21 years ago
parent
commit
1025cde05a
100 changed files with 17437 additions and 0 deletions
  1. BIN
      lib/commons/commons-src.zip
  2. BIN
      lib/commons/commons.jar
  3. 5
    0
      lib/readme-lib-module.html
  4. 10
    0
      testing-client/.classpath
  5. 19
    0
      testing-client/.project
  6. 1
    0
      testing-client/src/.cvsignore
  7. 983
    0
      testing-client/src/org/aspectj/testing/Tester.java
  8. 31
    0
      testing-client/testsrc/TestingClientModuleTests.java
  9. 11
    0
      testing-util/.classpath
  10. 19
    0
      testing-util/.project
  11. 38
    0
      testing-util/TestUtilTest.launch
  12. 1
    0
      testing-util/src/.cvsignore
  13. 616
    0
      testing-util/src/org/aspectj/testing/util/TestUtil.java
  14. BIN
      testing-util/testdata/testCompareClassFiles/org/aspectj/testing/util/TestCompareClassFile$1.class
  15. BIN
      testing-util/testdata/testCompareClassFiles/org/aspectj/testing/util/TestCompareClassFile.class
  16. 95
    0
      testing-util/testdata/testCompareTextFiles/differentFile/actual/TestUtilTest.java
  17. 94
    0
      testing-util/testdata/testCompareTextFiles/differentFile/expected/TestUtilTest.java
  18. 94
    0
      testing-util/testdata/testCompareTextFiles/sameFile/actual/TestUtilTest.java
  19. 94
    0
      testing-util/testdata/testCompareTextFiles/sameFile/expected/TestUtilTest.java
  20. 32
    0
      testing-util/testsrc/TestingUtilModuleTests.java
  21. 148
    0
      testing-util/testsrc/org/aspectj/testing/util/TestCompareClassFile.java
  22. 116
    0
      testing-util/testsrc/org/aspectj/testing/util/TestUtilTest.java
  23. 32
    0
      testing-util/testsrc/org/aspectj/testing/util/UtilTests.java
  24. 18
    0
      testing/.classpath
  25. 21
    0
      testing/.project
  26. 1
    0
      testing/src/.cvsignore
  27. 1864
    0
      testing/src/org/aspectj/internal/tools/ant/taskdefs/Ajctest.java
  28. 181
    0
      testing/src/org/aspectj/internal/tools/ant/taskdefs/MainWrapper.java
  29. 53
    0
      testing/src/org/aspectj/testing/gui/resources/gui.properties
  30. BIN
      testing/src/org/aspectj/testing/gui/resources/images/Back16.gif
  31. BIN
      testing/src/org/aspectj/testing/gui/resources/images/Delete24.gif
  32. BIN
      testing/src/org/aspectj/testing/gui/resources/images/Forward16.gif
  33. BIN
      testing/src/org/aspectj/testing/gui/resources/images/Open16.gif
  34. BIN
      testing/src/org/aspectj/testing/gui/resources/images/Open24.gif
  35. BIN
      testing/src/org/aspectj/testing/gui/resources/images/Quit24.gif
  36. BIN
      testing/src/org/aspectj/testing/gui/resources/images/Reload24.gif
  37. BIN
      testing/src/org/aspectj/testing/gui/resources/images/Save16.gif
  38. BIN
      testing/src/org/aspectj/testing/gui/resources/images/Save24.gif
  39. BIN
      testing/src/org/aspectj/testing/gui/resources/images/SaveAs16.gif
  40. BIN
      testing/src/org/aspectj/testing/gui/resources/images/SaveAs24.gif
  41. BIN
      testing/src/org/aspectj/testing/gui/resources/images/Start16.gif
  42. BIN
      testing/src/org/aspectj/testing/gui/resources/images/Start24.gif
  43. BIN
      testing/src/org/aspectj/testing/gui/resources/images/Stop16.gif
  44. BIN
      testing/src/org/aspectj/testing/gui/resources/images/Stop24.gif
  45. BIN
      testing/src/org/aspectj/testing/gui/resources/images/Test16.gif
  46. BIN
      testing/src/org/aspectj/testing/gui/resources/images/Testfailed16.gif
  47. BIN
      testing/src/org/aspectj/testing/gui/resources/images/Testopen16.gif
  48. BIN
      testing/src/org/aspectj/testing/gui/resources/images/Testpassed16.gif
  49. 883
    0
      testing/src/org/aspectj/testing/harness/bridge/AbstractRunSpec.java
  50. 282
    0
      testing/src/org/aspectj/testing/harness/bridge/AjcMessageHandler.java
  51. 371
    0
      testing/src/org/aspectj/testing/harness/bridge/AjcTest.java
  52. 966
    0
      testing/src/org/aspectj/testing/harness/bridge/CompilerRun.java
  53. 522
    0
      testing/src/org/aspectj/testing/harness/bridge/DirChanges.java
  54. 377
    0
      testing/src/org/aspectj/testing/harness/bridge/FlatSuiteReader.java
  55. 94
    0
      testing/src/org/aspectj/testing/harness/bridge/Globals.java
  56. 44
    0
      testing/src/org/aspectj/testing/harness/bridge/IAjcRun.java
  57. 24
    0
      testing/src/org/aspectj/testing/harness/bridge/IRunSpec.java
  58. 444
    0
      testing/src/org/aspectj/testing/harness/bridge/IncCompilerRun.java
  59. 298
    0
      testing/src/org/aspectj/testing/harness/bridge/JavaRun.java
  60. 256
    0
      testing/src/org/aspectj/testing/harness/bridge/RunSpecIterator.java
  61. 386
    0
      testing/src/org/aspectj/testing/harness/bridge/Sandbox.java
  62. 557
    0
      testing/src/org/aspectj/testing/harness/bridge/Validator.java
  63. 62
    0
      testing/src/org/aspectj/testing/run/IRun.java
  64. 62
    0
      testing/src/org/aspectj/testing/run/IRunIterator.java
  65. 38
    0
      testing/src/org/aspectj/testing/run/IRunListener.java
  66. 175
    0
      testing/src/org/aspectj/testing/run/IRunStatus.java
  67. 28
    0
      testing/src/org/aspectj/testing/run/IRunValidator.java
  68. 136
    0
      testing/src/org/aspectj/testing/run/RunIterator.java
  69. 116
    0
      testing/src/org/aspectj/testing/run/RunListener.java
  70. 89
    0
      testing/src/org/aspectj/testing/run/RunListeners.java
  71. 408
    0
      testing/src/org/aspectj/testing/run/RunStatus.java
  72. 206
    0
      testing/src/org/aspectj/testing/run/RunValidator.java
  73. 510
    0
      testing/src/org/aspectj/testing/run/Runner.java
  74. 72
    0
      testing/src/org/aspectj/testing/run/WrappedRunIterator.java
  75. 53
    0
      testing/src/org/aspectj/testing/util/AccumulatingFileFilter.java
  76. 439
    0
      testing/src/org/aspectj/testing/util/BridgeUtil.java
  77. 88
    0
      testing/src/org/aspectj/testing/util/CollectorFileFilter.java
  78. 103
    0
      testing/src/org/aspectj/testing/util/Diffs.java
  79. 745
    0
      testing/src/org/aspectj/testing/util/FileUtil.java
  80. 118
    0
      testing/src/org/aspectj/testing/util/IntRange.java
  81. 21
    0
      testing/src/org/aspectj/testing/util/IntValidator.java
  82. 147
    0
      testing/src/org/aspectj/testing/util/IteratorWrapper.java
  83. 1192
    0
      testing/src/org/aspectj/testing/util/LangUtil.java
  84. 204
    0
      testing/src/org/aspectj/testing/util/LineReader.java
  85. 162
    0
      testing/src/org/aspectj/testing/util/Node.java
  86. 95
    0
      testing/src/org/aspectj/testing/util/NullPrintStream.java
  87. 38
    0
      testing/src/org/aspectj/testing/util/ObjectChecker.java
  88. 104
    0
      testing/src/org/aspectj/testing/util/ProxyPrintStream.java
  89. 368
    0
      testing/src/org/aspectj/testing/util/RunUtils.java
  90. 185
    0
      testing/src/org/aspectj/testing/util/SFileReader.java
  91. 127
    0
      testing/src/org/aspectj/testing/util/StandardObjectChecker.java
  92. 76
    0
      testing/src/org/aspectj/testing/util/StreamSniffer.java
  93. 217
    0
      testing/src/org/aspectj/testing/util/StreamsHandler.java
  94. 99
    0
      testing/src/org/aspectj/testing/util/StringAccumulator.java
  95. 28
    0
      testing/src/org/aspectj/testing/util/StringVisitor.java
  96. 154
    0
      testing/src/org/aspectj/testing/util/TestClassLoader.java
  97. 360
    0
      testing/src/org/aspectj/testing/util/TestDiffs.java
  98. 123
    0
      testing/src/org/aspectj/testing/util/ValidFileFilter.java
  99. 208
    0
      testing/src/org/aspectj/testing/util/WebInstall.java
  100. 0
    0
      testing/src/org/aspectj/testing/xml/AjcSpecXmlReader.java

BIN
lib/commons/commons-src.zip View File


BIN
lib/commons/commons.jar View File


+ 5
- 0
lib/readme-lib-module.html View File

@@ -24,6 +24,11 @@ This module also contains some bootstrap libraries and test sources.
</li>
<li><a href="build">build</a>: bootstrap libraries for the build process.
</li>
<li><a href="commons">commons</a>:
Commons 1.0 from
<a ="http:jakarta.apache.org/commons">http:jakarta.apache.org/commons</a>.
This is used only by the testing module.
</li>
<li><a href="eclipse2.0">eclipse2.0</a>:
The AspectJ compiler is a patch to the Eclipse 2.0 compiler.
These are the non-compiler binaries. For the eclipse compiler

+ 10
- 0
testing-client/.classpath View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="testsrc"/>
<classpathentry kind="var" path="JRE_LIB" rootpath="JRE_SRCROOT" sourcepath="JRE_SRC"/>
<classpathentry kind="src" path="/bridge"/>
<classpathentry kind="src" path="/util"/>
<classpathentry kind="lib" path="/lib/junit/junit.jar" sourcepath="/lib/junit/junit-src.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

+ 19
- 0
testing-client/.project View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>testing-client</name>
<comment></comment>
<projects>
<project>bridge</project>
<project>util</project>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

+ 1
- 0
testing-client/src/.cvsignore View File

@@ -0,0 +1 @@
*.lst

+ 983
- 0
testing-client/src/org/aspectj/testing/Tester.java View File

@@ -0,0 +1,983 @@
/* *******************************************************************
* Copyright (c) 1999-2000 Xerox Corporation.
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


package org.aspectj.testing; // XXX move to its own client package

import org.aspectj.bridge.AbortException;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.Message;
import org.aspectj.util.LangUtil;

import java.util.*;
import java.io.*;

/**
* Testing client interface for checking results and reporting
* to a delegate IMessageHandler.
* Harnesses providing this interface for test clients must
* set it up by calling
* {@link #setBASEDIR(File)}
* {@link #setMessageHandler(IMessageHandler)} and
* {@link #clear()} for each test, as appropriate.
* (That means that IMessageHandler must be loaded from a class
* loader common to the harness and Tester.)
* If clients submit a failing check, this registers the message
* and throws an AbortException holding the message; this
* AbortException <b>will not</b> have the correct stack trace;
* all the information should be encoded in the message.
* Find any original exception thrown in the message itself.
*/
// XXX consider creating exception for new API throwFailure(String m)
public class Tester {
/** delegate for reporting results */
private static IMessageHandler messageHandler;
/** base directory for calculating relative paths to event files */
private static File BASEDIR;
/**
* collection of notes submitted
*/
private static Set notes;
/** <code>List</code> to hold events submitted. */
private static List actualEvents = new ArrayList();
/** <code>List</code> to hold events we expect. */
private static List expectedEvents = new ArrayList();

static {
setBASEDIR(new File("."));
setMessageHandler(IMessageHandler.SYSTEM_ERR);
clear();
}

/**
* Set directory used for calculating relative paths
* (currently only to an events file)
* @param baseDir the File for an existing directory
*/
public static void setBASEDIR(File baseDir) {
if (null == baseDir) throw new IllegalArgumentException("null baseDir");
if (!baseDir.isDirectory()) throw new IllegalArgumentException("not a directory: " + baseDir);
BASEDIR = baseDir;
}

public static File getBASEDIR() {
return BASEDIR;
}
/**
* Set the message handler used for this Tester.
* When given a message of kind FAIL, this handler
* must complete abruptly or return false (i.e., not handled completely)
* so the Tester throws an AbortException.
* @see checkFailed(..).
*/
public static void setMessageHandler(IMessageHandler handler) {
if (null == handler) throw new IllegalArgumentException("null handler");
if (messageHandler != handler) messageHandler = handler;
}

public static void clear() {
clearNotes();
clearEvents();
}

/** XXX deprecated #clear() */
public static void clearNotes() {
notes = new HashSet();
}

/** XXX deprecated #clear() */
public static void clearEvents() {
actualEvents = new ArrayList();
expectedEvents = new ArrayList();
}

/** Add an actual event */
public static void event(String s) {
actualEvents.add(s);
}
/**
* Add a note to {@link #notes}.
* @param note Message to add.
* XXX deprecated event(String)
*/
public static void note(Object note) {
notes.add(note);
}

/**
* Checks that <code>note</code> was added using {@link #note},
* and fails using <code>note.toString()</code> is it wasn't found.
*
* @param note Message that should've been added using {@link #note}.
* XXX deprecated checkEvent(String)
*/
public static void check(Object note) {
check(note, "expected note \"" + note.toString() + "\"");
}

/**
* Checks that <code>note</code> was added using {@link #note},
* and fails using <code>message</code> is it wasn't found.
*
* @param note Message that should've been added using {@link #note}.
* @param message Message with which to fail if <code>node</code>
* wasn't added.
*/
public static void check(Object note, String message) {
check(notes.contains(note), message);
}

/**
* Reports that <code>t</code> shouldn't have been thrown.
* using <code>t</code> as the message.
*
* @param t Thrown exception.
* @see #throwable(Throwable,String)
*/
public static void throwable(Throwable t) {
throwable(t, null);
}

/**
* Reports that <code>t</code> shouldn't have been thrown.
* using <code>msg</code> as the message.
*
* @param thrown Thrown exception.
* @param msg Message with which to report error.
*/
public static void throwable(Throwable thrown, String msg) {
handle(msg, thrown, true);
}

/**
* Report the error <code>message</code> unconditionally.
*
* @param message Error to report.
*/
public static void checkFailed(String message) {
handle(message, null, true);
}

/**
* Check that <code>expectedNotes</code> is equal to {@link #notes}
* , fail with <code>msg</code> and create a new instance of {@link #notes}.
* <i>NOTE: <code>expectedNotes</code> is a <code>String</code>, so
* it must match with {@link java.util.HashSet#toString()}</i>.
*
* @param expectedNotes <code>String</code> we expect
* {@link #notes} to match.
* @param msg Message with which to fail.
*/
public static void checkAndClear(String expectedNotes, String msg) {
checkEqual(notes, expectedNotes, msg);
clearNotes();
}

/**
* Reports an error using <code>message</code> if
* <code>test == false</code>.
*
* @param test Determines whether we call {@link #checkFailed}.
* @param message Message to pass {@link #checkFailed} if
* <code>test == false</code>.
*/
public static void check(boolean test, String message) {
if (!test) checkFailed(message);
}

/**
* Checks that the values of <code>value</code> and
* <code>expectedValue</code> are equal. Both or either
* can be null. Calls {@link #checkFailed} with <code>message</code>
* if the arrays aren't equal.
*
* @param value One test set.
* @param expectedValue The other test set.
* @param message Message with which to fail.
*/
public static void checkEqual(Object[] value,
Object[] expectedValue,
String message)
{
if (value == null) {
if (expectedValue == null) return;
checkFailed(message+" null array found");
return;
}
int n = value.length;
if (n != expectedValue.length) {
checkFailed(message+" expected array of length "+n
+" got "+expectedValue.length);
return;
}
for(int i=0; i<n; i++) {
if (!value[i].equals(expectedValue[i])) {
checkFailed(message+": "+value[i]+" != "+
expectedValue[i]+" at index "+i);
}
}
}

/**
* Fails if <code>s == t</code>.
*
* @param s a known value.
* @param t another known value.
*/
public static void checkNotEqual(boolean s, boolean t) {
checkNotEqual(s, t, s + " shouldn't equal " + t);
}
/**
* Fails with message <code>msg</code> if <code>s == t</code>.
*
* @param s a known value.
* @param t another known value.
* @param msg the failure message.
*/
public static void checkNotEqual(boolean s, boolean t, String msg) {
if (s == t) checkFailed(msg);
}

/**
* Fails if <code>s == t</code>.
*
* @param s a known value.
* @param t another known value.
*/
public static void checkNotEqual(byte s, byte t) {
checkNotEqual(s, t, s + " shouldn't equal " + t);
}
/**
* Fails with message <code>msg</code> if <code>s == t</code>.
*
* @param s a known value.
* @param t another known value.
* @param msg the failure message.
*/
public static void checkNotEqual(byte s, byte t, String msg) {
if (s == t) checkFailed(msg);
}

/**
* Fails if <code>s == t</code>.
*
* @param s a known value.
* @param t another known value.
*/
public static void checkNotEqual(char s, char t) {
checkNotEqual(s, t, s + " shouldn't equal " + t);
}
/**
* Fails with message <code>msg</code> if <code>s == t</code>.
*
* @param s a known value.
* @param t another known value.
* @param msg the failure message.
*/
public static void checkNotEqual(char s, char t, String msg) {
if (s == t) checkFailed(msg);
}

/**
* Fails if <code>s == t</code>.
*
* @param s a known value.
* @param t another known value.
*/
public static void checkNotEqual(short s, short t) {
checkNotEqual(s, t, s + " shouldn't equal " + t);
}
/**
* Fails with message <code>msg</code> if <code>s == t</code>.
*
* @param s a known value.
* @param t another known value.
* @param msg the failure message.
*/
public static void checkNotEqual(short s, short t, String msg) {
if (s == t) checkFailed(msg);
}

/**
* Fails if <code>s == t</code>.
*
* @param s a known value.
* @param t another known value.
*/
public static void checkNotEqual(int s, int t) {
checkNotEqual(s, t, s + " shouldn't equal " + t);
}
/**
* Fails with message <code>msg</code> if <code>s == t</code>.
*
* @param s a known value.
* @param t another known value.
* @param msg the failure message.
*/
public static void checkNotEqual(int s, int t, String msg) {
if (s == t) checkFailed(msg);
}

/**
* Fails if <code>s == t</code>.
*
* @param s a known value.
* @param t another known value.
*/
public static void checkNotEqual(long s, long t) {
checkNotEqual(s, t, s + " shouldn't equal " + t);
}
/**
* Fails with message <code>msg</code> if <code>s == t</code>.
*
* @param s a known value.
* @param t another known value.
* @param msg the failure message.
*/
public static void checkNotEqual(long s, long t, String msg) {
if (s == t) checkFailed(msg);
}

/**
* Fails if <code>s == t</code>.
*
* @param s a known value.
* @param t another known value.
*/
public static void checkNotEqual(float s, float t) {
checkNotEqual(s, t, s + " shouldn't equal " + t);
}
/**
* Fails with message <code>msg</code> if <code>s == t</code>.
*
* @param s a known value.
* @param t another known value.
* @param msg the failure message.
*/
public static void checkNotEqual(float s, float t, String msg) {
if (s == t) checkFailed(msg);
}

/**
* Fails if <code>s == t</code>.
*
* @param s a known value.
* @param t another known value.
*/
public static void checkNotEqual(double s, double t) {
checkNotEqual(s, t, s + " shouldn't equal " + t);
}
/**
* Fails with message <code>msg</code> if <code>s == t</code>.
*
* @param s a known value.
* @param t another known value.
* @param msg the failure message.
*/
public static void checkNotEqual(double s, double t, String msg) {
if (s == t) checkFailed(msg);
}

/**
* Fails if <code>s == t</code>.
*
* @param s a known value.
* @param t another known value.
*/
public static void checkNotEqual(Object s, Object t) {
checkNotEqual(s, t, s + " shouldn't equal " + t);
}
/**
* Fails with message <code>msg</code> if <code>s == t</code>
* or both <code>s</code> and <code>t</code> are <code>null</code>.
*
* @param s a known value.
* @param t another known value.
* @param msg the failure message.
*/
public static void checkNotEqual(Object s, Object t, String msg) {
if ((s != null && s.equals(t)) ||
(t != null && t.equals(s)) ||
(s == null && t == null)) {
checkFailed(msg);
}
}

/**
* Compares <code>value</code> and <code>expectedValue</code>
* with failing message <code>"compare"</code>.
*
* @param value Unkown value.
* @param expectedValue Expected value.
* @see #checkEqual(int,int,String)
*/
public static void checkEqual(int value, int expectedValue) {
checkEqual(value, expectedValue, "compare");
}

/**
* Fails if the passed in value is <code>null</code>.
*
* @param o the expected non-null thing.
* @param name the name of <code>o</code>.
*/
public static void checkNonNull(Object o, String name) {
if (o == null) checkFailed(name + " shouldn't be null");
}

/**
* Compared <code>value</code> and <code>expectedValue</code>
* and fails with <code>message</code> if they aren't equal.
*
* @param value Unkown value.
* @param expectedValue Expected value.
* @param msg Message with which to fail.
*/
public static void checkEqual(int value, int expectedValue, String message) {
if (value == expectedValue) return;
if (value < expectedValue) {
message = message+": "+value+" < "+expectedValue;
} else {
message = message+": "+value+" > "+expectedValue;
}
checkFailed(message);
}

/**
* Compares <code>value</code> and <code>expectedValue</code>
* with failing message <code>"compare"</code>.
*
* @param value Unkown value.
* @param expectedValue Expected value.
* @see #checkEqual(float,float,String)
*/
public static void checkEqual(float value, float expectedValue) {
checkEqual(value, expectedValue, "compare");
}

/**
* Compared <code>value</code> and <code>expectedValue</code>
* and fails with <code>message</code> if they aren't equal.
*
* @param value Unkown value.
* @param expectedValue Expected value.
* @param msg Message with which to fail.
*/
public static void checkEqual(float value, float expectedValue, String msg) {
if (Float.isNaN(value) && Float.isNaN(expectedValue)) return;
if (value == expectedValue) return;
if (value < expectedValue) {
msg = msg+": "+value+" < "+expectedValue;
} else {
msg = msg+": "+value+" > "+expectedValue;
}
checkFailed(msg);
}

/**
* Compares <code>value</code> and <code>expectedValue</code>
* with failing message <code>"compare"</code>.
*
* @param value Unkown value.
* @param expectedValue Expected value.
* @see #checkEqual(long,long,String)
*/
public static void checkEqual(long value, long expectedValue) {
checkEqual(value, expectedValue, "compare");
}

/**
* Compared <code>value</code> and <code>expectedValue</code>
* and fails with <code>message</code> if they aren't equal.
*
* @param value Unkown value.
* @param expectedValue Expected value.
* @param msg Message with which to fail.
*/
public static void checkEqual(long value, long expectedValue, String msg) {
if (value == expectedValue) return;
if (value < expectedValue) {
msg = msg+": "+value+" < "+expectedValue;
} else {
msg = msg+": "+value+" > "+expectedValue;
}
checkFailed(msg);
}

/**
* Compares <code>value</code> and <code>expectedValue</code>
* with failing message <code>"compare"</code>.
*
* @param value Unkown value.
* @param expectedValue Expected value.
* @see #checkEqual(double,double,String)
*/
public static void checkEqual(double value, double expectedValue) {
checkEqual(value, expectedValue, "compare");
}

/**
* Compared <code>value</code> and <code>expectedValue</code>
* and fails with <code>message</code> if they aren't equal.
*
* @param value Unkown value.
* @param expectedValue Expected value.
* @param msg Message with which to fail.
*/
public static void checkEqual(double value, double expectedValue, String msg) {
if (Double.isNaN(value) && Double.isNaN(expectedValue)) return;
if (value == expectedValue) return;
if (value < expectedValue) {
msg = msg+": "+value+" < "+expectedValue;
} else {
msg = msg+": "+value+" > "+expectedValue;
}
checkFailed(msg);
}

/**
* Compares <code>value</code> and <code>expectedValue</code>
* with failing message <code>"compare"</code>.
*
* @param value Unkown value.
* @param expectedValue Expected value.
* @see #checkEqual(short,short,String)
*/
public static void checkEqual(short value, short expectedValue) {
checkEqual(value, expectedValue, "compare");
}

/**
* Compared <code>value</code> and <code>expectedValue</code>
* and fails with <code>message</code> if they aren't equal.
*
* @param value Unkown value.
* @param expectedValue Expected value.
* @param msg Message with which to fail.
*/
public static void checkEqual(short value, short expectedValue, String msg) {
if (value == expectedValue) return;
if (value < expectedValue) {
msg = msg+": "+value+" < "+expectedValue;
} else {
msg = msg+": "+value+" > "+expectedValue;
}
checkFailed(msg);
}

/**
* Compares <code>value</code> and <code>expectedValue</code>
* with failing message <code>"compare"</code>.
*
* @param value Unkown value.
* @param expectedValue Expected value.
* @see #checkEqual(byte,byte,String)
*/
public static void checkEqual(byte value, byte expectedValue) {
checkEqual(value, expectedValue, "compare");
}

/**
* Compares <code>value</code> and <code>expectedValue</code>
* with failing message <code>msg</code>.
*
* @param value Unkown value.
* @param expectedValue Expected value.
* @param msg Message with which to fail.
*/
public static void checkEqual(byte value, byte expectedValue, String msg) {
if (value == expectedValue) return;
if (value < expectedValue) {
msg = msg+": "+value+" < "+expectedValue;
} else {
msg = msg+": "+value+" > "+expectedValue;
}
checkFailed(msg);
}

/**
* Compares <code>value</code> and <code>expectedValue</code>
* with failing message <code>"compare"</code>.
*
* @param value Unkown value.
* @param expectedValue Expected value.
* @see #checkEqual(char,char,String)
*/
public static void checkEqual(char value, char expectedValue) {
checkEqual(value, expectedValue, "compare");
}

/**
* Compares <code>value</code> and <code>expectedValue</code>
* with failing message <code>msg</code>.
*
* @param value Unkown value.
* @param expectedValue Expected value.
* @param msg Message with which to fail.
*/
public static void checkEqual(char value, char expectedValue, String msg) {
if (value == expectedValue) return;
if (value < expectedValue) {
msg = msg+": "+value+" < "+expectedValue;
} else {
msg = msg+": "+value+" > "+expectedValue;
}
checkFailed(msg);
}

/**
* Compares <code>value</code> and <code>expectedValue</code>
* with failing message <code>"compare"</code>.
*
* @param value Unkown value.
* @param expectedValue Expected value.
* @see #checkEqual(boolean,boolean,String)
*/
public static void checkEqual(boolean value, boolean expectedValue) {
checkEqual(value, expectedValue, "compare");
}

/**
* Compares <code>value</code> and <code>expectedValue</code>
* with failing message <code>msg</code>.
*
* @param value Unkown value.
* @param expectedValue Expected value.
* @param msg Message with which to fail.
*/
public static void checkEqual(boolean value, boolean expectedValue, String msg) {
if (value == expectedValue) return;
msg = msg+": "+value+" != "+expectedValue;
checkFailed(msg);
}

/**
* Checks whether the entries of <code>set</code> are equal
* using <code>equals</code> to the corresponding String in
* <code>expectedSet</code> and fails with message <code>msg</code>.
*
* @param set Unkown set of values.
* @param expectedSet Expected <code>String</code> of values.
* @param msg Message with which to fail.
*/
public static void checkEqual(Collection set, String expectedSet, String msg) {
checkEqual(set, LangUtil.split(expectedSet), msg);
}
/**
* Checks whether the entries of <code>set</code> are equal
* using <code>equals</code> to the corresponding entry in
* <code>expectedSet</code> and fails with message <code>msg</code>,
* except that duplicate actual entries are ignored.
* This issues fail messages for each failure; when
* aborting on failure, only the first will be reported.
*
* @param set Unkown set of values.
* @param expectedSet Expected <code>String</code> of values.
* @param msg Message with which to fail.
*/
public static void checkEqualIgnoreDups(Collection set, String[] expected, String msg,
boolean ignoreDups) {
String[] diffs = diffIgnoreDups(set, expected, msg, ignoreDups);
if (0 < diffs.length) {
check(false, "" + Arrays.asList(diffs));
}
// for (int i = 0; i < diffs.length; i++) {
// check(false, diffs[i]);
// }
}
/** @return String[] of differences '{un}expected msg "..." {not} found' */
private static String[] diffIgnoreDups(Collection set, String[] expected, String msg,
boolean ignoreDups) {
ArrayList result = new ArrayList();
ArrayList actual = new ArrayList(set);
BitSet hits = new BitSet();
for (int i = 0; i < expected.length; i++) {
if (!actual.remove(expected[i])) {
result.add(" expected " + msg + " \"" + expected[i] + "\" not found");
} else {
hits.set(i);
if (ignoreDups) {
while (actual.remove(expected[i])) ; // remove all instances of it
}
}
}
for (Iterator iter = actual.iterator(); iter.hasNext();) {
String act = (String) iter.next();
result.add(" unexpected " + msg + " \"" + act + "\" found");
}
return (String[]) result.toArray(new String[0]);
}

/**
* Checks whether the entries of <code>set</code> are equal
* using <code>equals</code> to the corresponding entry in
* <code>expectedSet</code> and fails with message <code>msg</code>.
*
* @param set Unkown set of values.
* @param expectedSet Expected <code>String</code> of values.
* @param msg Message with which to fail.
*/
public static void checkEqual(Collection set, String[] expected, String msg) {
checkEqualIgnoreDups(set, expected, msg, false);
}

/**
* Compares <code>value</code> and <code>expectedValue</code>
* with failing message <code>"compare"</code>.
*
* @param value Unkown value.
* @param expectedValue Expected value.
* @see #checkEqual(Object,Object,String)
*/
public static void checkEqual(Object value, Object expectedValue) {
checkEqual(value, expectedValue, "compare");
}

/**
* Checks whether the entries of <code>set</code> are equal
* using <code>equals</code> to the corresponding String in
* <code>expectedSet</code> and fails with message <code>msg</code>.
*
* @param set Unkown set of values.
* @param expectedSet Expected <code>String</code> of values.
* @param msg Message with which to fail.
*/
public static void checkEqual(Object value, Object expectedValue, String msg) {
if (value == null && expectedValue == null) return;
if (value != null && value.equals(expectedValue)) return;
msg = msg+": "+value+" !equals "+expectedValue;
checkFailed(msg);
}

/**
* Checks whether the entries of <code>set</code> are equal
* using <code>equals</code> to the corresponding String in
* <code>expectedSet</code> and fails with message <code>msg</code>.
*
* @param set Unkown set of values.
* @param expectedSet Expected <code>String</code> of values.
* @param msg Message with which to fail.
*/
public static void checkEq(Object value, Object expectedValue, String msg) {
if (value == expectedValue) return;
msg = msg+": "+value+" != "+expectedValue;
checkFailed(msg);
}

/** add expected events */
public static void expectEvent(String s) {
if (null != s) {
expectedEvents.add(s);
}
}
/** add expected events */
public static void expectEvent(Object s) {
if (null != s) {
expectEvent(s.toString());
}
}
/**
* add expected events, parse out ; from string
* Expect those messages in <code>s</code> separated by
* <code>":;, "</code>.
*
* @param s String containg delimited,expected messages.
*/
public static void expectEventsInString(String s) {
if (null != s) {
StringTokenizer tok = new StringTokenizer(s, ":;, ");
while (tok.hasMoreTokens()) {
expectEvent(tok.nextToken());
}
}
}

public static void expectEventsInString(String[] ra) {
expectEvents((Object[]) ra);
}

/** add expected events */
public static void expectEvents(Object[] events) {
if (null != events) {
for (int i = 0; i < events.length; i++) {
if (null != events[i]) {
expectEvent(events[i].toString());
}
}
}
}

/** add expected events */
public static void expectEvents(String[] events) {
if (null != events) {
for (int i = 0; i < events.length; i++) {
if (null != events[i]) {
expectEvent(events[i].toString());
}
}
}
}
/** check actual and expected have same members */
public static void checkAllEvents() {
checkAndClearEvents((String[]) expectedEvents.toArray(new String[0]));
}
/** also ignore duplicate actual entries for expected */
public static void checkAllEventsIgnoreDups() {
final boolean ignoreDups = true;
final String[] exp = (String[]) expectedEvents.toArray(new String[0]);
checkEqualIgnoreDups(actualEvents, exp, "event", ignoreDups);
clearEvents();
}
/** Check events, file is line-delimited. If there is a non-match, signalls
* a single error for the first event that does not match the next event in
* the file. The equivalence is {@link #checkEqualLists}. Blank lines are
* ignored. lines that start with '//' are ignored. */
public static void checkEventsFromFile(String eventsFile) {
// XXX bug reads into current expected and checks all - separate read and check
try {
File file = new File(getBASEDIR(), eventsFile); // XXX TestDriver
BufferedReader in = new BufferedReader(new FileReader(file));
//final File parentDir = (null == file? null : file.getParentFile());
String line;
List expEvents = new ArrayList();
while ((line = in.readLine()) != null) {
line = line.trim();
if ((line.length() < 1) || (line.startsWith("//"))) continue;
expEvents.add(line);
}
checkEqualLists(actualEvents, expEvents, " from " + eventsFile);
} catch (IOException ioe) {
throwable(ioe);
}
}
/** Check to see that two lists of strings are the same. Order is important.
* Trimmable whitespace is not important. Case is important.
*
* @param actual one list to check
* @param expected another list
* @param message a context string for the resulting error message if the test fails.
*/
public static void checkEqualLists(List/*String*/ actual, List/*String*/ expected,
String message) {
Iterator a = actual.iterator();
Iterator e = expected.iterator();
int ai = 0;
int ei = 0;
for (; a.hasNext(); ) {
if (! e.hasNext()) {
checkFailed("unexpected [" + ai + "] \"" + a.next() + "\" " + message);
return;
}
String a0 = ((String) a.next()).trim();
String e0 = ((String) e.next()).trim();
if (! a0.equals(e0)) {
checkFailed("expected [" + ei + "] \"" + e0
+ "\"\n but found [" + ai + "] \"" + a0 + "\"\n " + message);
return;
}
ai++;
ei++;
}
while (e.hasNext()) {
checkFailed("expected [" + ei + "] \"" + e.next() + "\" " + message);
ei++;
}
}
/** Check events, expEvents is space delimited */
public static void checkEvents(String expEvents) {
checkEqual(actualEvents, expEvents, "event");
}

/** Check events, expEvents is an array */
public static void checkEvents(String[] expEvents) {
checkEqual(actualEvents, expEvents, "event");
}
/** Check events and clear after check*/
public static void checkAndClearEvents(String expEvents) {
checkEvents(expEvents);
clearEvents();
}
/** Check events and clear after check*/
public static void checkAndClearEvents(String[] expEvents) {
checkEvents(expEvents);
clearEvents();
}

/** XXX deprecated */
public static void printEvents() { // XXX no clients?
for (Iterator i = actualEvents.iterator(); i.hasNext(); ) {
System.out.println(i.next()); // XXX System.out
}
}

/**
* Report an uncaught exeption as an error
* @param thrown <code>Throwable</code> to print.
* @see #maxStackTrace
*/
public void unexpectedExceptionFailure(Throwable thrown) {
handle("unexpectedExceptionFailure", thrown, true);
}
/**
* Handle message by delegation to message handler, doing
* IMessage.FAIL if (fail || (thrown != null) and IMessage.INFO
* otherwise.
*/

private static void handle(String message, Throwable thrown, boolean fail) {
final boolean failed = fail || (null != thrown);
IMessage.Kind kind = (failed ? IMessage.FAIL : IMessage.INFO);
IMessage m = new Message(message, kind, thrown, null);
final boolean handled = messageHandler.handleMessage(m);
}
// private static void resofhandle(String message, Throwable thrown, boolean fail) {
// /* If FAIL and the message handler returns false (normally),
// * Then this preserves "abort" semantics by throwing an
// * abort exception.
// */
// if (failed) {
// if (handled) {
// String s = "Tester expecting handler to return false or "
// + "complete abruptly when passed a fail, for " + m;
// m = new Message(s, IMessage.DEBUG, null, null);
// messageHandler.handleMessage(m);
// } else {
// throw AbortException.borrowPorter(m);
// }
// }
// }

}

+ 31
- 0
testing-client/testsrc/TestingClientModuleTests.java View File

@@ -0,0 +1,31 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


// default package

import junit.framework.*;
import junit.framework.Test;

public class TestingClientModuleTests extends TestCase {

public static Test suite() {
TestSuite suite = new TestSuite(TestingClientModuleTests.class.getName());
suite.addTestSuite(TestingClientModuleTests.class); // minimum 1 test (testNothing)
return suite;
}

public TestingClientModuleTests(String name) { super(name); }

public void testNothing() {}
}

+ 11
- 0
testing-util/.classpath View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="testsrc"/>
<classpathentry kind="var" path="JRE_LIB" rootpath="JRE_SRCROOT" sourcepath="JRE_SRC"/>
<classpathentry kind="lib" path="/lib/junit/junit.jar" sourcepath="/lib/junit/junit-src.jar"/>
<classpathentry kind="src" path="/util"/>
<classpathentry kind="lib" path="/lib/jdiff/jdiff.jar"/>
<classpathentry kind="src" path="/bridge"/>
<classpathentry kind="output" path="bin"/>
</classpath>

+ 19
- 0
testing-util/.project View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>testing-util</name>
<comment></comment>
<projects>
<project>bridge</project>
<project>util</project>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

+ 38
- 0
testing-util/TestUtilTest.launch View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.aspectj.testing.util.TestUtilTest"/>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="testing-util"/>
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;runtimeClasspathEntry containerPath=&quot;JRE_LIB&quot; path=&quot;2&quot;
sourceAttachmentPath=&quot;JRE_SRC&quot; sourceRootPath=&quot;JRE_SRCROOT&quot; type=&quot;3&quot;/&gt;
"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;runtimeClasspathEntry path=&quot;3&quot; projectName=&quot;testing-util&quot; type=&quot;1&quot;/&gt;
"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;runtimeClasspathEntry internalArchive=&quot;/lib/junit/junit.jar&quot; path=&quot;3&quot;
sourceAttachmentPath=&quot;/lib/junit/junit-src.jar&quot; type=&quot;2&quot;/&gt;
"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;runtimeClasspathEntry path=&quot;3&quot; projectName=&quot;util&quot; type=&quot;1&quot;/&gt;
"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;runtimeClasspathEntry internalArchive=&quot;/lib/jdiff/jdiff.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;
"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;runtimeClasspathEntry path=&quot;3&quot; projectName=&quot;bridge&quot; type=&quot;1&quot;/&gt;
"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;runtimeClasspathEntry path=&quot;3&quot; projectName=&quot;bcweaver&quot; type=&quot;1&quot;/&gt;
"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;runtimeClasspathEntry internalArchive=&quot;/lib/bcel/bcel.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;
"/>
</listAttribute>
<stringAttribute key="org.eclipse.debug.ui.target_run_perspective" value="perspective_default"/>
<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.debug.ui.javaSourceLocator"/>
<stringAttribute key="org.eclipse.debug.ui.target_debug_perspective" value="perspective_default"/>
</launchConfiguration>

+ 1
- 0
testing-util/src/.cvsignore View File

@@ -0,0 +1 @@
*.lst

+ 616
- 0
testing-util/src/org/aspectj/testing/util/TestUtil.java View File

@@ -0,0 +1,616 @@
/* *******************************************************************
* Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.util;

import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.util.FileUtil;
import org.aspectj.util.LangUtil;
import org.aspectj.util.Reflection;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import jdiff.text.FileLine;
import jdiff.util.Diff;
import jdiff.util.DiffNormalOutput;
import junit.framework.Assert;
import junit.framework.TestCase;
import junit.runner.TestCaseClassLoader;

/**
* Things that junit should perhaps have, but doesn't.
* Note the file-comparison methods require JDiff to run,
* but JDiff types are not required to resolve this class.
* Also, the bytecode weaver is required to compare class
* files, but not to compare other files.
*/

public final class TestUtil {

private TestUtil() {
super();
}

// ---- arrays

public static void assertArrayEquals(String msg, Object[] expected, Object[] found) {
TestCase.assertEquals(msg, Arrays.asList(expected), Arrays.asList(found));
}

// ---- unordered

public static void assertSetEquals(Collection expected, Collection found) {
assertSetEquals(null, expected, found);
}

public static void assertSetEquals(String msg, Object[] expected, Object[] found) {
assertSetEquals(msg, Arrays.asList(expected), Arrays.asList(found));
}

public static void assertSetEquals(
String msg,
Collection expected,
Collection found) {
msg = (msg == null) ? "" : msg + ": ";

Set results1 = new HashSet(found);
results1.removeAll(expected);

Set results2 = new HashSet(expected);
results2.removeAll(found);

if (results1.isEmpty()) {
TestCase.assertTrue(
msg + "Expected but didn't find: " + results2.toString(),
results2.isEmpty());
} else if (results2.isEmpty()) {
TestCase.assertTrue(
msg + "Didn't expect: " + results1.toString(),
results1.isEmpty());
} else {
TestCase.assertTrue(
msg
+ "Expected but didn't find: "
+ results2.toString()
+ "\nDidn't expect: "
+ results1.toString(),
false);
}
}

// ---- objects

public static void assertCommutativeEquals(Object a, Object b, boolean should) {
TestCase.assertEquals(a + " equals " + b, should, a.equals(b));
TestCase.assertEquals(b + " equals " + a, should, b.equals(a));
assertHashEquals(a, b, should);
}

private static void assertHashEquals(Object s, Object t, boolean should) {
if (should) {
TestCase.assertTrue(
s + " does not hash to same as " + t,
s.hashCode() == t.hashCode());
} else {
if (s.hashCode() == t.hashCode()) {
System.err.println("warning: hash collision with hash = " + t.hashCode());
System.err.println(" for " + s);
System.err.println(" and " + t);
}
}
}
// -- reflective stuff

public static void runMain(String classPath, String className) {
runMethod(classPath, className, "main", new Object[] { new String[0] });
}


public static Object runMethod(String classPath, String className, String methodName, Object[] args) {
classPath += File.pathSeparator + System.getProperty("java.class.path");
ClassLoader loader = new TestCaseClassLoader(classPath);
Class c=null;
try {
c = loader.loadClass(className);
} catch (ClassNotFoundException e) {
Assert.assertTrue("unexpected exception: " + e, false);
}
return Reflection.invokestaticN(c, methodName, args);
}

/**
* Checks that two multi-line strings have the same value.
* Each line is trimmed before comparision
* Produces an error on the particular line of conflict
*/
public static void assertMultiLineStringEquals(String message, String s1, String s2) {
try {
BufferedReader r1 = new BufferedReader(new StringReader(s1));
BufferedReader r2 = new BufferedReader(new StringReader(s2));
List lines = new ArrayList();
String l1, l2;
int index = 1;
while(true) {
l1 = readNonBlankLine(r1);
l2 = readNonBlankLine(r2);
if (l1 == null || l2 == null) break;
if (l1.equals(l2)) {
lines.add(l1);
} else {
showContext(lines);
Assert.assertEquals(message +"(line " + index +")", l1, l2);
}
index++;
}
if (l1 != null) showContext(lines);
Assert.assertTrue(message + ": unexpected " + l1, l1 == null);
if (l2 != null) showContext(lines);
Assert.assertTrue(message + ": unexpected " + l2, l2 == null);
} catch (IOException ioe) {
Assert.assertTrue(message + ": caught " + ioe.getMessage(), false);
}
}
private static void showContext(List lines) {
int n = lines.size();
for (int i = Math.max(0, n - 8); i < n; i++) {
System.err.println(lines.get(i));
}
}
private static String readNonBlankLine(BufferedReader r) throws IOException {
String l = r.readLine();
if (l == null) return null;
l = l.trim();
// comment to include comments when reading
int commentLoc = l.indexOf("//");
if (-1 != commentLoc) {
l = l.substring(0, commentLoc).trim();
}
if ("".equals(l)) return readNonBlankLine(r);
return l;
}
/**
* If there is an expected dir, expect each file in its subtree
* to match a corresponding actual file in the base directory.
* @return boolean
*/
public static boolean sameDirectoryContents(
final IMessageHandler handler,
final File expectedBaseDir,
final File actualBaseDir,
final boolean fastFail) {
LangUtil.throwIaxIfNull(handler, "handler");
FileUtil.throwIaxUnlessCanReadDir(actualBaseDir, "actualBaseDir");
if (!FileUtil.canReadDir(expectedBaseDir)) {
MessageUtil.fail(handler, " expected dir not found: " + expectedBaseDir);
return false;
}
if (!FileUtil.canReadDir(actualBaseDir)) {
MessageUtil.fail(handler, " actual dir not found: " + actualBaseDir);
return false;
}
String[] paths = FileUtil.listFiles(expectedBaseDir);
boolean result = true;
for (int i = 0; i < paths.length; i++) {
if (!sameFiles(handler, expectedBaseDir, actualBaseDir, paths[i]) && !result) {
result = false;
if (fastFail) {
break;
}
}
}
return result;
}
//------------ File-comparison utilities (XXX need their own class...)
/**
* Compare two files, line by line, and report differences as one FAIL message
* if a handler is supplied. This preprocesses .class files by disassembling.
* @param handler the IMessageHandler for any FAIL messages (null to ignore)
* @param expectedFile the File path to the canonical file
* @param actualFile the File path to the actual file, if any
* @return true if the input files are the same, based on per-line comparisons
*/
public static boolean sameFiles (
IMessageHandler handler,
File expectedFile,
File actualFile) {
return doSameFile(handler, null, null, expectedFile, actualFile);
}

/**
* Compare two files, line by line, and report differences as one FAIL message
* if a handler is supplied. This preprocesses .class files by disassembling.
* This method assumes that the files are at the same offset from two
* respective base directories.
* @param handler the IMessageHandler for any FAIL messages (null to ignore)
* @param expectedBaseDir the File path to the canonical file base directory
* @param actualBaseDir the File path to the actual file base directory
* @param path the String path offset from the base directories
* @return true if the input files are the same, based on per-line comparisons
*/
public static boolean sameFiles (
IMessageHandler handler,
File expectedBaseDir,
File actualBaseDir,
String path) {
File actualFile = new File(actualBaseDir, path);
File expectedFile = new File(expectedBaseDir, path);
return doSameFile(handler, expectedBaseDir, actualBaseDir, expectedFile, actualFile);
}

/**
* This does the work, selecting a lineator subclass and converting public
* API's to JDiff APIs for comparison.
* Currently, all jdiff interfaces are method-local, so this class with load
* without it; if we do use it, we can avoid the duplication.
*/
private static boolean doSameFile(
IMessageHandler handler,
File expectedBaseDir,
File actualBaseDir,
File expectedFile,
File actualFile) {
String path = expectedFile.getPath();
// XXX permit user to specify lineator
ILineator lineator = Lineator.TEXT;
if (path.endsWith(".class")) {
if (ClassLineator.haveDisassembler() ) {
lineator = Lineator.CLASS;
} else {
MessageUtil.abort(handler, "skipping - dissassembler not available");
return false;
}
}
CanonicalLine[] actualLines = null;
CanonicalLine[] expectedLines = null;
try {
actualLines = lineator.getLines(handler, actualFile, actualBaseDir);
expectedLines = lineator.getLines(handler, expectedFile, expectedBaseDir);
} catch (IOException e) {
MessageUtil.fail(handler, "rendering lines ", e);
return false;
}
if (!LangUtil.isEmpty(actualLines) && !LangUtil.isEmpty(expectedLines)) {
// here's the transmutation back to jdiff - extract if publishing JDiff
CanonicalLine[][] clines = new CanonicalLine[][] { expectedLines, actualLines };
FileLine[][] flines = new FileLine[2][];
for (int i = 0; i < clines.length; i++) {
CanonicalLine[] cline = clines[i];
FileLine[] fline = new FileLine[cline.length];
for (int j = 0; j < fline.length; j++) {
fline[j] = new FileLine(cline[j].canonical, cline[j].line);
}
flines[i] = fline;
}

Diff.change edits = new Diff(flines[0], flines[1]).diff_2(false);
if ((null == edits) || (0 == (edits.inserted + edits.deleted))) {
// XXX confirm with jdiff that null means no edits
return true;
} else {
//String m = render(handler, edits, flines[0], flines[1]);
StringWriter writer = new StringWriter();
DiffNormalOutput out = new DiffNormalOutput(flines[0], flines[1]);
out.setOut(writer);
out.setLineSeparator(LangUtil.EOL);
try {
out.writeScript(edits);
} catch (IOException e) {
MessageUtil.fail(handler, "rendering edits", e);
} finally {
if (null != writer) {
try { writer.close(); }
catch (IOException e) {
MessageUtil.fail(handler, "closing after rendering edits", e);
}
}
}
String message = "diff between "
+ path
+ " in expected dir "
+ expectedBaseDir
+ " and actual dir "
+ actualBaseDir
+ LangUtil.EOL
+ writer.toString();
MessageUtil.fail(handler, message);
}
}
return false;
}

/** component that reduces file to CanonicalLine[] */
public static interface ILineator {
/** Lineator suitable for text files */
public static final ILineator TEXT = new TextLineator();

/** Lineator suitable for class files (disassembles first) */
public static final ILineator CLASS = new ClassLineator();

/**
* Reduce file to CanonicalLine[].
* @param handler the IMessageHandler for errors (may be null)
* @param file the File to render
* @param basedir the File for the base directory (may be null)
* @return CanonicalLine[] of lines - not null, but perhaps empty
*/
CanonicalLine[] getLines(
IMessageHandler handler,
File file,
File basedir) throws IOException;
}
/** alias for jdiff FileLine to avoid client binding */
public static class CanonicalLine {
public static final CanonicalLine[] NO_LINES = new CanonicalLine[0];
/** canonical variant of line for comparison */
public final String canonical;
/** actual line, for logging */
public final String line;
public CanonicalLine(String canonical, String line) {
this.canonical = canonical;
this.line = line;
}
public String toString() {
return line;
}
}
private abstract static class Lineator implements ILineator {
/**
* Reduce file to CanonicalLine[].
* @param handler the IMessageHandler for errors (may be null)
* @param file the File to render
* @param basedir the File for the base directory (may be null)
*/
public CanonicalLine[] getLines(
IMessageHandler handler,
File file,
File basedir)
throws IOException {
if (!file.canRead() || !file.isFile()) {
MessageUtil.error(handler, "not readable file: " + basedir + " - " + file);
return null;
}
// capture file as FileLine[]
InputStream in = null;
String path = FileUtil.normalizedPath(file, basedir);
LineStream capture = new LineStream();
try {
lineate(capture, handler, basedir, file);
} catch (IOException e) {
MessageUtil.fail(handler,
"NormalizedCompareFiles IOException reading " + file, e);
return null;
} finally {
if (null != in) {
try { in.close(); }
catch (IOException e) {} // ignore
}
capture.flush();
capture.close();
}
String missed = capture.getMissed();
if (!LangUtil.isEmpty(missed)) {
MessageUtil.warn(handler,
"NormalizedCompareFiles missed input: "
+ missed);
return null;
} else {
String[] lines = capture.getLines();
CanonicalLine[] result = new CanonicalLine[lines.length];
for (int i = 0; i < lines.length; i++) {
result[i] = new CanonicalLine(lines[i], lines[i]);
}
return result;
}
}
protected abstract void lineate(
PrintStream sink,
IMessageHandler handler,
File basedir,
File file) throws IOException;
}
private static class TextLineator extends Lineator {
protected void lineate(
PrintStream sink,
IMessageHandler handler,
File basedir,
File file) throws IOException {
InputStream in = null;
try {
in = new FileInputStream(file);
FileUtil.copyStream(new DataInputStream(in), sink);
} finally {
try { in.close(); }
catch (IOException e) {} // ignore
}
}
}

public static class ClassLineator extends Lineator {
protected void lineate(
PrintStream sink,
IMessageHandler handler,
File basedir,
File file) throws IOException {
String name = FileUtil.fileToClassName(basedir, file);
// XXX re-enable preflight?
// if ((null != basedir) && (path.length()-6 != name.length())) {
// MessageUtil.error(handler, "unexpected class name \""
// + name + "\" for path " + path);
// return null;
// }
disassemble(handler, basedir, name, sink);
}
public static boolean haveDisassembler() {
try {
return (null != Class.forName("org.aspectj.weaver.bcel.LazyClassGen"));
} catch (ClassNotFoundException e) {
// XXX fix
//System.err.println(e.getMessage());
//e.printStackTrace(System.err);
return false;
}
}
/** XXX dependency on bcweaver/bcel */
private static void disassemble(
IMessageHandler handler,
File basedir,
String name,
PrintStream out) throws IOException {
// LazyClassGen.disassemble(FileUtil.normalizedPath(basedir), name, capture);
Throwable thrown = null;
String basedirPath = FileUtil.normalizedPath(basedir);
// XXX use reflection utilities to invoke dissassembler?
try {
// XXX need test to detect when this is refactored
Class c = Class.forName("org.aspectj.weaver.bcel.LazyClassGen");
Method m = c.getMethod("disassemble",
new Class[] {String.class, String.class, PrintStream.class});
m.invoke(null, new Object[] { basedirPath, name, out});
} catch (ClassNotFoundException e) {
thrown = e;
} catch (NoSuchMethodException e) {
thrown = e;
} catch (IllegalAccessException e) {
thrown = e;
} catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
if (t instanceof IOException) {
throw (IOException) t;
}
thrown = t;
}
if (null != thrown) {
MessageUtil.fail(handler, "disassembling " + name + " path: " + basedirPath,
thrown);
}
}
}


/**
* Capture PrintStream output to String[]
* (delimiting component String on println()),
* also showing any missed text.
*/
public static class LineStream extends PrintStream {
StringBuffer sb = new StringBuffer();
ByteArrayOutputStream missed;
ArrayList sink;
public LineStream() {
super(new ByteArrayOutputStream());
this.sink = new ArrayList();
missed = (ByteArrayOutputStream) out;
}
/** @return any text not captured by our overrides */
public String getMissed() {
return missed.toString();
}
/** clear captured lines (but not missed text) */
public void clear() {
sink.clear();
}
/**
* Get String[] of lines printed,
* delimited by println(..) calls.
* @return lines printed, exclusive of any not yet terminated by newline
*/
public String[] getLines() {
return (String[]) sink.toArray(new String[0]);
}
// ---------- PrintStream overrides
public void println(Object x) {
println(x.toString());
}

public void print(Object obj) {
print(obj.toString());
}

public void println(char c) {
sb.append(c);
println();
}
public void println(char[] c) {
sb.append(c);
println();
}
public void print(char c) {
sb.append(c);
}

public void print(char[] c) {
sb.append(c);
}
public void println(String s) {
print(s);
println();
}
public void print(String s) {
sb.append(s);
}
public void println() {
String line = sb.toString();
sink.add(line);
sb.setLength(0);
}
}
}

BIN
testing-util/testdata/testCompareClassFiles/org/aspectj/testing/util/TestCompareClassFile$1.class View File


BIN
testing-util/testdata/testCompareClassFiles/org/aspectj/testing/util/TestCompareClassFile.class View File


+ 95
- 0
testing-util/testdata/testCompareTextFiles/differentFile/actual/TestUtilTest.java View File

@@ -0,0 +1,95 @@
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This file is part of the compiler and core tools for the AspectJ(tm)
* programming language; see http://aspectj.org
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* either http://www.mozilla.org/MPL/ or http://aspectj.org/MPL/.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is AspectJ.
*
* The Initial Developer of the Original Code is Xerox Corporation. Portions
* created by Xerox Corporation are Copyright (C) 1999-2002 Xerox Corporation.
* All Rights Reserved.
*
* Contributor(s):
*/
package org.aspectj.testing.util;

import org.aspectj.bridge.IMessageHolder;
import org.aspectj.bridge.MessageHandler;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.util.FileUtil;

import java.io.File;
import java.io.IOException;

import junit.framework.TestCase;

/**
*
*/
public class TestUtilTest extends TestCase {

public TestUtilTest(String name) {
super(name);
}
public void testFileCompareNonClass() throws IOException {
MessageHandler holder = new MessageHandler();
File thisFile = new File("testsrc/org/aspectj/testing/util/TestUtilTest.java");
//File thisFile = new File("src/testing-util.lst");
assertTrue(TestUtil.sameFiles(holder, thisFile, thisFile));
File tempFile = File.createTempFile("TestUtilTest", ".tmp");
FileUtil.copyFile(thisFile, tempFile);
long len = tempFile.length();
assertTrue(0 != len);
long tlen = thisFile.length();
assertEquals(tlen, len);
assertTrue(TestUtil.sameFiles(holder, tempFile, thisFile));
try {
String path = thisFile.getName();
File basedir = tempFile.getParentFile();
File renamed = new File(basedir, path);
if (!tempFile.renameTo(renamed)) {
MessageUtil.warn(holder, "unable to rename " + tempFile + " to " + renamed);
} else {
len = renamed.length();
assertEquals(tlen, len);
assertTrue(TestUtil.sameFiles(holder, basedir, thisFile.getParentFile(), path));
}
} finally {
if (0 < holder.numMessages(null, true)) {
MessageUtil.print(System.out, holder);
holder.clear();
}
tempFile.delete();
}
}

/** added line here */
public void testFileCompareClass() throws IOException {
MessageHandler holder = new MessageHandler();
File classBase = new File("testdata/testCompareClassFiles");
String path = "org/aspectj/testing/util/TestCompareClassFile.class";
File classFile = new File(classBase, path);
try {
assertTrue(TestUtil.sameFiles(holder, classFile, classFile));
assertTrue(TestUtil.sameFiles(holder, classBase, classBase, path));
} finally {
if (0 < holder.numMessages(null, true)) {
MessageUtil.print(System.out, holder);
}
}
}
}

+ 94
- 0
testing-util/testdata/testCompareTextFiles/differentFile/expected/TestUtilTest.java View File

@@ -0,0 +1,94 @@
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This file is part of the compiler and core tools for the AspectJ(tm)
* programming language; see http://aspectj.org
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* either http://www.mozilla.org/MPL/ or http://aspectj.org/MPL/.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is AspectJ.
*
* The Initial Developer of the Original Code is Xerox Corporation. Portions
* created by Xerox Corporation are Copyright (C) 1999-2002 Xerox Corporation.
* All Rights Reserved.
*
* Contributor(s):
*/
package org.aspectj.testing.util;

import org.aspectj.bridge.IMessageHolder;
import org.aspectj.bridge.MessageHandler;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.util.FileUtil;

import java.io.File;
import java.io.IOException;

import junit.framework.TestCase;

/**
*
*/
public class TestUtilTest extends TestCase {

public TestUtilTest(String name) {
super(name);
}
public void testFileCompareNonClass() throws IOException {
MessageHandler holder = new MessageHandler();
File thisFile = new File("testsrc/org/aspectj/testing/util/TestUtilTest.java");
//File thisFile = new File("src/testing-util.lst");
assertTrue(TestUtil.sameFiles(holder, thisFile, thisFile));
File tempFile = File.createTempFile("TestUtilTest", ".tmp");
FileUtil.copyFile(thisFile, tempFile);
long len = tempFile.length();
assertTrue(0 != len);
long tlen = thisFile.length();
assertEquals(tlen, len);
assertTrue(TestUtil.sameFiles(holder, tempFile, thisFile));
try {
String path = thisFile.getName();
File basedir = tempFile.getParentFile();
File renamed = new File(basedir, path);
if (!tempFile.renameTo(renamed)) {
MessageUtil.warn(holder, "unable to rename " + tempFile + " to " + renamed);
} else {
len = renamed.length();
assertEquals(tlen, len);
assertTrue(TestUtil.sameFiles(holder, basedir, thisFile.getParentFile(), path));
}
} finally {
if (0 < holder.numMessages(null, true)) {
MessageUtil.print(System.out, holder);
holder.clear();
}
tempFile.delete();
}
}

public void testFileCompareClass() throws IOException {
MessageHandler holder = new MessageHandler();
File classBase = new File("testdata/testCompareClassFiles");
String path = "org/aspectj/testing/util/TestCompareClassFile.class";
File classFile = new File(classBase, path);
try {
assertTrue(TestUtil.sameFiles(holder, classFile, classFile));
assertTrue(TestUtil.sameFiles(holder, classBase, classBase, path));
} finally {
if (0 < holder.numMessages(null, true)) {
MessageUtil.print(System.out, holder);
}
}
}
}

+ 94
- 0
testing-util/testdata/testCompareTextFiles/sameFile/actual/TestUtilTest.java View File

@@ -0,0 +1,94 @@
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This file is part of the compiler and core tools for the AspectJ(tm)
* programming language; see http://aspectj.org
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* either http://www.mozilla.org/MPL/ or http://aspectj.org/MPL/.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is AspectJ.
*
* The Initial Developer of the Original Code is Xerox Corporation. Portions
* created by Xerox Corporation are Copyright (C) 1999-2002 Xerox Corporation.
* All Rights Reserved.
*
* Contributor(s):
*/
package org.aspectj.testing.util;

import org.aspectj.bridge.IMessageHolder;
import org.aspectj.bridge.MessageHandler;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.util.FileUtil;

import java.io.File;
import java.io.IOException;

import junit.framework.TestCase;

/**
*
*/
public class TestUtilTest extends TestCase {

public TestUtilTest(String name) {
super(name);
}
public void testFileCompareNonClass() throws IOException {
MessageHandler holder = new MessageHandler();
File thisFile = new File("testsrc/org/aspectj/testing/util/TestUtilTest.java");
//File thisFile = new File("src/testing-util.lst");
assertTrue(TestUtil.sameFiles(holder, thisFile, thisFile));
File tempFile = File.createTempFile("TestUtilTest", ".tmp");
FileUtil.copyFile(thisFile, tempFile);
long len = tempFile.length();
assertTrue(0 != len);
long tlen = thisFile.length();
assertEquals(tlen, len);
assertTrue(TestUtil.sameFiles(holder, tempFile, thisFile));
try {
String path = thisFile.getName();
File basedir = tempFile.getParentFile();
File renamed = new File(basedir, path);
if (!tempFile.renameTo(renamed)) {
MessageUtil.warn(holder, "unable to rename " + tempFile + " to " + renamed);
} else {
len = renamed.length();
assertEquals(tlen, len);
assertTrue(TestUtil.sameFiles(holder, basedir, thisFile.getParentFile(), path));
}
} finally {
if (0 < holder.numMessages(null, true)) {
MessageUtil.print(System.out, holder);
holder.clear();
}
tempFile.delete();
}
}

public void testFileCompareClass() throws IOException {
MessageHandler holder = new MessageHandler();
File classBase = new File("testdata/testCompareClassFiles");
String path = "org/aspectj/testing/util/TestCompareClassFile.class";
File classFile = new File(classBase, path);
try {
assertTrue(TestUtil.sameFiles(holder, classFile, classFile));
assertTrue(TestUtil.sameFiles(holder, classBase, classBase, path));
} finally {
if (0 < holder.numMessages(null, true)) {
MessageUtil.print(System.out, holder);
}
}
}
}

+ 94
- 0
testing-util/testdata/testCompareTextFiles/sameFile/expected/TestUtilTest.java View File

@@ -0,0 +1,94 @@
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This file is part of the compiler and core tools for the AspectJ(tm)
* programming language; see http://aspectj.org
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* either http://www.mozilla.org/MPL/ or http://aspectj.org/MPL/.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is AspectJ.
*
* The Initial Developer of the Original Code is Xerox Corporation. Portions
* created by Xerox Corporation are Copyright (C) 1999-2002 Xerox Corporation.
* All Rights Reserved.
*
* Contributor(s):
*/
package org.aspectj.testing.util;

import org.aspectj.bridge.IMessageHolder;
import org.aspectj.bridge.MessageHandler;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.util.FileUtil;

import java.io.File;
import java.io.IOException;

import junit.framework.TestCase;

/**
*
*/
public class TestUtilTest extends TestCase {

public TestUtilTest(String name) {
super(name);
}
public void testFileCompareNonClass() throws IOException {
MessageHandler holder = new MessageHandler();
File thisFile = new File("testsrc/org/aspectj/testing/util/TestUtilTest.java");
//File thisFile = new File("src/testing-util.lst");
assertTrue(TestUtil.sameFiles(holder, thisFile, thisFile));
File tempFile = File.createTempFile("TestUtilTest", ".tmp");
FileUtil.copyFile(thisFile, tempFile);
long len = tempFile.length();
assertTrue(0 != len);
long tlen = thisFile.length();
assertEquals(tlen, len);
assertTrue(TestUtil.sameFiles(holder, tempFile, thisFile));
try {
String path = thisFile.getName();
File basedir = tempFile.getParentFile();
File renamed = new File(basedir, path);
if (!tempFile.renameTo(renamed)) {
MessageUtil.warn(holder, "unable to rename " + tempFile + " to " + renamed);
} else {
len = renamed.length();
assertEquals(tlen, len);
assertTrue(TestUtil.sameFiles(holder, basedir, thisFile.getParentFile(), path));
}
} finally {
if (0 < holder.numMessages(null, true)) {
MessageUtil.print(System.out, holder);
holder.clear();
}
tempFile.delete();
}
}

public void testFileCompareClass() throws IOException {
MessageHandler holder = new MessageHandler();
File classBase = new File("testdata/testCompareClassFiles");
String path = "org/aspectj/testing/util/TestCompareClassFile.class";
File classFile = new File(classBase, path);
try {
assertTrue(TestUtil.sameFiles(holder, classFile, classFile));
assertTrue(TestUtil.sameFiles(holder, classBase, classBase, path));
} finally {
if (0 < holder.numMessages(null, true)) {
MessageUtil.print(System.out, holder);
}
}
}
}

+ 32
- 0
testing-util/testsrc/TestingUtilModuleTests.java View File

@@ -0,0 +1,32 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


// default package

import org.aspectj.testing.util.UtilTests;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

public class TestingUtilModuleTests extends TestCase {

public static Test suite() {
TestSuite suite = new TestSuite(TestingUtilModuleTests.class.getName());
suite.addTest(UtilTests.suite());
return suite;
}

public TestingUtilModuleTests(String name) { super(name); }
}

+ 148
- 0
testing-util/testsrc/org/aspectj/testing/util/TestCompareClassFile.java View File

@@ -0,0 +1,148 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.util;

import org.aspectj.util.LangUtil;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.StringTokenizer;
import java.util.TreeSet;

/**
* This is source for a sample .class file.
* It is compiled and the corresponding .class files are
* checked in under the testdata directory.
* It has no other purpose.
*/
public class TestCompareClassFile implements Runnable {
public static final String STATIC_CONST = "STATIC_CONST";
public static void main(String[] args) {
// tc static references
long l = Math.abs(System.currentTimeMillis());
String s = STATIC_CONST + " is constant";
}
public static void runStatic() {
}
private static void privateRunStatic() {
}
static void defaultRunStatic() {
}
protected static void protectedRunStatic() {
}
private long privateLong;
private final Object privateFinalObject;
private TestCompareClassFile() {
super();
privateLong = System.currentTimeMillis();
// method-local inner class
privateFinalObject = new Runnable() { public void run(){}};
}

/** implement Runnable */
public void run() {
}
private void privateRun() {
}
void defaultRun() {
}
protected void protectedRun() {
}
// ------- misc stolen utility code
// Collections Util
public static List getListInMap(Map map, Object key) {
List list = (List)map.get(key);
if (list == null) {
list = new ArrayList();
map.put(key, list);
}
return list;
}

public static SortedSet getSortedSetInMap(Map map, Object key) {
SortedSet list = (SortedSet)map.get(key);
if (list == null) {
list = new TreeSet();
map.put(key, list);
}
return list;
}
// LangUtil
/**
* Make a copy of the array.
* @return an array with the same component type as source
* containing same elements, even if null.
* @throws IllegalArgumentException if source is null
*/
public static final Object[] copy(Object[] source) {
final Class c = source.getClass().getComponentType();
Object[] result = (Object[]) Array.newInstance(c, source.length);
System.arraycopy(source, 0, result, 0, result.length);
return result;
}
/**
* Trim ending lines from a StringBuffer,
* clipping to maxLines and further removing any number of
* trailing lines accepted by checker.
* @param checker returns true if trailing line should be elided.
* @param stack StringBuffer with lines to elide
* @param maxLines int for maximum number of resulting lines
*/
static void elideEndingLines(StringBuffer stack, int maxLines) {
if ((null == stack) || (0 == stack.length())) {
return;
}
final LinkedList lines = new LinkedList();
StringTokenizer st = new StringTokenizer(stack.toString(),"\n\r");
while (st.hasMoreTokens() && (0 < --maxLines)) {
lines.add(st.nextToken());
}
st = null;
String line;
int elided = 0;
while (!lines.isEmpty()) {
line = (String) lines.getLast();
if (null == line) {
break;
} else {
elided++;
lines.removeLast();
}
}
if ((elided > 0) || (maxLines < 1)) {
final int EOL_LEN = LangUtil.EOL.length();
int totalLength = 0;
while (!lines.isEmpty()) {
totalLength += EOL_LEN + ((String) lines.getFirst()).length();
lines.removeFirst();
}
if (stack.length() > totalLength) {
stack.setLength(totalLength);
if (elided > 0) {
stack.append(" (... " + elided + " lines...)");
}
}
}
}
}

+ 116
- 0
testing-util/testsrc/org/aspectj/testing/util/TestUtilTest.java View File

@@ -0,0 +1,116 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.util;

import org.aspectj.bridge.IMessageHolder;
import org.aspectj.bridge.MessageHandler;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.util.FileUtil;

import java.io.File;
import java.io.IOException;

import junit.framework.TestCase;

/**
*
*/
public class TestUtilTest extends TestCase {

public TestUtilTest(String name) {
super(name);
}
public void testFileCompareNonClass() throws IOException {
MessageHandler holder = new MessageHandler();
File thisFile = new File("testsrc/org/aspectj/testing/util/TestUtilTest.java");
//File thisFile = new File("src/testing-util.lst");
assertTrue(TestUtil.sameFiles(holder, thisFile, thisFile));
File tempFile = File.createTempFile("TestUtilTest", ".tmp");
FileUtil.copyFile(thisFile, tempFile);
long len = tempFile.length();
assertTrue(0 != len);
long tlen = thisFile.length();
assertEquals(tlen, len);
assertTrue(TestUtil.sameFiles(holder, tempFile, thisFile));
try {
String path = thisFile.getName();
File basedir = tempFile.getParentFile();
File renamed = new File(basedir, path);
if (!tempFile.renameTo(renamed)) {
MessageUtil.warn(holder, "unable to rename " + tempFile + " to " + renamed);
} else {
len = renamed.length();
assertEquals(tlen, len);
assertTrue(TestUtil.sameFiles(holder, basedir, thisFile.getParentFile(), path));
}
} finally {
if (0 < holder.numMessages(null, true)) {
MessageUtil.print(System.out, holder);
holder.clearMessages();
}
tempFile.delete();
}
}

public void testFileCompareNonClassStaticPositive() throws IOException {
MessageHandler holder = new MessageHandler();
File basedir = new File("testdata/testCompareTextFiles/sameFile");
File expectedBaseDir = new File(basedir, "expected");
File actualBaseDir = new File(basedir, "actual");
String filename = "TestUtilTest.java";
File expected = new File(expectedBaseDir, filename);
File actual = new File(actualBaseDir, filename);

assertTrue(TestUtil.sameFiles(holder, expected, actual));

assertTrue(TestUtil.sameFiles(holder, expectedBaseDir, actualBaseDir, filename));
}

public void testFileCompareNonClassStaticNegative() throws IOException {
MessageHandler holder = new MessageHandler();
File basedir = new File("testdata/testCompareTextFiles/differentFile");
File expectedBaseDir = new File(basedir, "expected");
File actualBaseDir = new File(basedir, "actual");
String filename = "TestUtilTest.java";
File expected = new File(expectedBaseDir, filename);
File actual = new File(actualBaseDir, filename);

assertTrue(!TestUtil.sameFiles(holder, expected, actual));

assertTrue(!TestUtil.sameFiles(holder, expectedBaseDir, actualBaseDir, filename));
}

public void testFileCompareClass() throws IOException {
if (!TestUtil.ClassLineator.haveDisassembler()) {
System.err.println("skipping testFileCompareClass - no disassembler on classpath");
return;
}
MessageHandler holder = new MessageHandler();
File classBase = new File("testdata/testCompareClassFiles");
String path = "org/aspectj/testing/util/TestCompareClassFile.class";
File classFile = new File(classBase, path);
try {
assertTrue(TestUtil.sameFiles(holder, classFile, classFile));
assertTrue(TestUtil.sameFiles(holder, classBase, classBase, path));
} finally {
if (0 < holder.numMessages(null, true)) {
MessageUtil.print(System.out, holder);
}
}
}
}

+ 32
- 0
testing-util/testsrc/org/aspectj/testing/util/UtilTests.java View File

@@ -0,0 +1,32 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


package org.aspectj.testing.util;

import junit.framework.*;

public class UtilTests extends TestCase {

public static Test suite() {
TestSuite suite = new TestSuite(UtilTests.class.getName());
// for now, do not include SuiteTest because it would take 15 minutes
//$JUnit-BEGIN$
suite.addTestSuite(TestUtilTest.class);
//$JUnit-END$
return suite;
}

public UtilTests(String name) { super(name); }

}

+ 18
- 0
testing/.classpath View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="testsrc"/>
<classpathentry kind="var" path="JRE_LIB" rootpath="JRE_SRCROOT" sourcepath="JRE_SRC"/>
<classpathentry kind="lib" path="/lib/ant/lib/ant.jar" sourcepath="/lib/ant/ant-src.zip"/>
<classpathentry kind="lib" path="/lib/junit/junit.jar" sourcepath="/lib/junit/junit-src.jar"/>
<classpathentry kind="lib" path="/lib/jdiff/jdiff.jar"/>
<classpathentry kind="lib" path="/lib/regexp/jakarta-regexp-1.2.jar"/>
<classpathentry kind="src" path="/bridge"/>
<classpathentry kind="src" path="/util"/>
<classpathentry kind="src" path="/testing-client"/>
<classpathentry kind="lib" path="/lib/ant/lib/xercesImpl.jar"/>
<classpathentry kind="lib" path="/lib/ant/lib/xml-apis.jar"/>
<classpathentry kind="lib" path="/lib/commons/commons.jar" sourcepath="/lib/commons/commons-src.zip"/>
<classpathentry kind="src" path="/testing-util"/>
<classpathentry kind="output" path="bin"/>
</classpath>

+ 21
- 0
testing/.project View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>testing</name>
<comment></comment>
<projects>
<project>bridge</project>
<project>testing-client</project>
<project>testing-util</project>
<project>util</project>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

+ 1
- 0
testing/src/.cvsignore View File

@@ -0,0 +1 @@
*.lst

+ 1864
- 0
testing/src/org/aspectj/internal/tools/ant/taskdefs/Ajctest.java
File diff suppressed because it is too large
View File


+ 181
- 0
testing/src/org/aspectj/internal/tools/ant/taskdefs/MainWrapper.java View File

@@ -0,0 +1,181 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/



package org.aspectj.internal.tools.ant.taskdefs;

import org.aspectj.testing.util.LangUtil; // todo config management here

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.InvocationTargetException;
import java.io.*;

/**
* Wrapper to invoke class identified by setting VM argument.
* Caller must set a system property "MainWrapper.classname"
* to the fully-qualified name of the target class to invoke,
* and the target class must be resolvable from the defining
* class loader of this class.
* VM argument name is available as <code>PROP_NAME</code>, but
* is set by adding the following to the command line:
* <code>-DMainWrapper.classname="fully.qualified.Classname"</code>.
* This returns -1 if unable to load the main method,
* 1 if the invoked method throws an exception, and 0 otherwise.
* TODO: prelminary
*/
public class MainWrapper {
/** MUST set the fully-qualified name of class to invoke using
* a VM property of this name
* tracked in Ajctest.java */
public static final String PROP_NAME = "MainWrapper.classname";
/** May set the path to a classes diretory,
* to interpret class names and load classes.
* Tracked in Ajctest.java */
public static final String CLASSDIR_NAME = "MainWrapper.classdir";

/** to disable returning int via System.exit, set to boolean true value (todo: ignored) */
public static final String SIGNAL_EXCEPTION_NAME = "MainWrapper.signalException";

/** to disable returning via System.exit on first Throwable, set to boolean true value (todo: ignored) */
public static final String FAIL_ON_EXCEPTION_NAME = "MainWrapper.failOnException";

/** quit on first exception */ // todo public class controls - yuck
public static boolean FAIL_ON_EXCEPTION = true;

/** signal number of exceptions with int return value */
public static boolean SIGNAL_EXCEPTION = true;

/** redirect messages for exceptions; if null, none printed */
public static PrintStream OUT_STREAM = System.err;

/** result accumulated, possibly from multiple threads */
private static int result;

/**
* Run target class's main(args), doing a System.exit() with
* a value > 0 for the number of Throwable that
* the target class threw that
* makes it through to a top-level ThreadGroup. (This is
* strictly speaking not correct since applications can live
* after their exceptions stop a thread.)
* Exit with a value < 0 if there were exceptions in loading
* the target class. Messages are printed to OUT_STREAM.
*/
public static void main(String[] args) {
String classname = "no property : " + PROP_NAME;
Method main = null;
// setup: this try block is for loading main method - return -1 if fail
try {
// access classname from jvm arg
classname = System.getProperty(PROP_NAME);
// this will fail if the class is not available from this classloader
Class cl = Class.forName(classname);
final Class[] argTypes = new Class[] {String[].class};
// will fail if no main method
main = cl.getMethod("main", argTypes);
if (!Modifier.isStatic(main.getModifiers())) {
PrintStream outStream = OUT_STREAM;
if (null != outStream) outStream.println("main is not static");
result = -1;
}
// if user also request loading of all classes...
String classesDir = System.getProperty(CLASSDIR_NAME);
if ((null != classesDir) && (0 < classesDir.length())) {
MainWrapper.loadAllClasses(new File(classesDir));
}
} catch (Throwable t) {
if (1 != result) result--;
reportException("setup Throwable invoking class " + classname, t);
}
// run: this try block is for running things - get Throwable from our thread here
if ((null != main) && (0 == result)) {
try {
runInOurThreadGroup(main, args);
} catch (Throwable t) {
if (result > -1) {
result++;
}
reportException("run Throwable invoking class " + classname, t);
}
}
if ((0 != result) && (SIGNAL_EXCEPTION)) {
System.exit(result);
}
}

static void runInOurThreadGroup(final Method main, final String[] args) {
final String classname = main.getDeclaringClass().getName();
ThreadGroup ourGroup = new ThreadGroup("MainWrapper ThreadGroup") {
public void uncaughtException(Thread t, Throwable e) {
reportException("uncaughtException invoking " + classname, e);
result++;
if (FAIL_ON_EXCEPTION) {
System.exit((SIGNAL_EXCEPTION ? result : 0));
}
}
};
Runnable runner = new Runnable() {
public void run() {
try {
main.invoke(null, new Object[] {args});
} catch (InvocationTargetException e) {
result = -1;
reportException("InvocationTargetException invoking " + classname, e);
} catch (IllegalAccessException e) {
result = -1;
reportException("IllegalAccessException invoking " + classname, e);
}
}
};
Thread newMain = new Thread(ourGroup, runner, "pseudo-main");
newMain.start();
try {
newMain.join();
} catch (InterruptedException e) {
result = -1; // todo: InterruptedException might be benign - retry?
reportException("Interrupted while waiting for to join " + newMain, e);
}
}

/**
* Try to load all classes in a directory.
* @throws Error if any failed
*/
static protected void loadAllClasses(File classesDir) {
if (null != classesDir) {
String[] names = LangUtil.classesIn(classesDir);
StringBuffer err = new StringBuffer();
LangUtil.loadClasses(names, null, err);
if (0 < err.length()) {
throw new Error("MainWrapper Errors loading classes: "
+ err.toString());
}
}
}

static void reportException(String context, Throwable t) {
PrintStream outStream = OUT_STREAM;
if (null != outStream) {
while ((null != t) &&
(InvocationTargetException.class.isAssignableFrom(t.getClass()))) {
t = ((InvocationTargetException) t).getTargetException();
}
outStream.println(" context: " + context);
outStream.println(" message: " + t.getMessage());
t.printStackTrace(outStream);
}
}

} // MainWrapper

+ 53
- 0
testing/src/org/aspectj/testing/gui/resources/gui.properties View File

@@ -0,0 +1,53 @@
gui.actions=quit delete reload open save saveas stop start


quit.class=org.aspectj.testing.gui.Gui$QuitAction
quit.icon=Quit24.gif
quit.tooltip=Quit

delete.class=org.aspectj.testing.gui.Gui$DeleteAction
delete.icon=Delete24.gif
delete.tooltip=Delete Test Cases

stop.class=org.aspectj.testing.gui.Gui$StopAction
stop.icon=Stop24.gif
stop.tooltip=Stop running test

reload.class=org.aspectj.testing.gui.Gui$ReloadAction
reload.icon=Reload24.gif
reload.tooltip=Reload current test

open.class=org.aspectj.testing.gui.Gui$OpenAction
open.icon=Open24.gif
open.tooltip=Open a test

save.class=org.aspectj.testing.gui.Gui$SaveAction
save.icon=Save24.gif
save.tooltip=Save current test

saveas.class=org.aspectj.testing.gui.Gui$SaveAsAction
saveas.icon=SaveAs24.gif
saveas.tooltip=Save current test as...

start.class=org.aspectj.testing.gui.Gui$StartAction
start.icon=Start24.gif
start.tooltip=Start test

editor.actions=openfile savefile savefileas

openfile.class=org.aspectj.testing.gui.Gui$OpenAction
openfile.icon=Open24.gif
openfile.tooltip=Open a file

savefile.class=org.aspectj.testing.gui.Gui$SaveAction
savefile.icon=Save24.gif
savefile.tooltip=Save currect file

savefileas.class=org.aspectj.testing.gui.Gui$SaveAsAction
savefileas.icon=SaveAs24.gif
savefileas.tooltip=Save current file as...

gui.width=700
gui.less.height=100
gui.top.divider.location=0.70
gui.center.divider.location=0.60

BIN
testing/src/org/aspectj/testing/gui/resources/images/Back16.gif View File


BIN
testing/src/org/aspectj/testing/gui/resources/images/Delete24.gif View File


BIN
testing/src/org/aspectj/testing/gui/resources/images/Forward16.gif View File


BIN
testing/src/org/aspectj/testing/gui/resources/images/Open16.gif View File


BIN
testing/src/org/aspectj/testing/gui/resources/images/Open24.gif View File


BIN
testing/src/org/aspectj/testing/gui/resources/images/Quit24.gif View File


BIN
testing/src/org/aspectj/testing/gui/resources/images/Reload24.gif View File


BIN
testing/src/org/aspectj/testing/gui/resources/images/Save16.gif View File


BIN
testing/src/org/aspectj/testing/gui/resources/images/Save24.gif View File


BIN
testing/src/org/aspectj/testing/gui/resources/images/SaveAs16.gif View File


BIN
testing/src/org/aspectj/testing/gui/resources/images/SaveAs24.gif View File


BIN
testing/src/org/aspectj/testing/gui/resources/images/Start16.gif View File


BIN
testing/src/org/aspectj/testing/gui/resources/images/Start24.gif View File


BIN
testing/src/org/aspectj/testing/gui/resources/images/Stop16.gif View File


BIN
testing/src/org/aspectj/testing/gui/resources/images/Stop24.gif View File


BIN
testing/src/org/aspectj/testing/gui/resources/images/Test16.gif View File


BIN
testing/src/org/aspectj/testing/gui/resources/images/Testfailed16.gif View File


BIN
testing/src/org/aspectj/testing/gui/resources/images/Testopen16.gif View File


BIN
testing/src/org/aspectj/testing/gui/resources/images/Testpassed16.gif View File


+ 883
- 0
testing/src/org/aspectj/testing/harness/bridge/AbstractRunSpec.java View File

@@ -0,0 +1,883 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.harness.bridge;

import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.bridge.MessageHandler;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.testing.run.IRunIterator;
import org.aspectj.testing.util.BridgeUtil;
import org.aspectj.testing.xml.IXmlWritable;
import org.aspectj.testing.xml.SoftMessage;
import org.aspectj.testing.xml.XMLWriter;
import org.aspectj.util.LangUtil;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

/**
* Base class for initialization of components expecting messages,
* options, files/paths, and source locations (resolved files),
* and potentially containing child Spec.
* <p>
* <u>initialization</u>: This defines bean/xml setters for all.
* This converts String to IMessage using
* {@link MessageUtil#readMessage(String)}
* and String to ISourceLocation using
* {@link BridgeUtil#makeSourceLocation(input)}.
* See those APIs for input form and limitations.
* A Spec also accepts (or rejects) runtime configuration from a parent
* in {@link adoptParentValues(RT, IMessageHandler)}.
* Since some children Spec may balk but this parent Spec continue,
* use {@link getChildren()} to get the full list of children Spec,
* but {@link getWorkingChildren()} to get the list of children that
* are not being skipped in accordance with runtime configuration.
* <p>
* <u>subclassing</u>: subclasses wishing other than
* the default behavior for reading String input should override the
* corresponding (bean) setter. They can also override the
* add{foo} methods to get notice or modify objects constructed
* from the input.
* <p>
* <u>bean properties</u>: because this is designed to work
* by standard Java bean introspection, take care to follow
* bean rules when adding methods. In particular, a property
* is illegal if the setter takes a different type than the
* getter returns. That means, e.g., that all List and array[] getters
* should be named "get{property}[List|Array]". Otherwise
* the XML readers will silently fail to set the property
* (perhaps with trace information that the property had
* no write method or was read-only).
* <p>
* <u>Coordination with writers</u>: because this reads the contents
* of values written by IXmlWritable, they should ensure their
* values are readable. When flattening and unflattening
* lists, the convention is to use the {un}flattenList(..) methods
* in XMLWriter.
* @see XMLWriter@unflattenList(String)
* @see XMLWriter@flattenList(List)
*/
abstract public class AbstractRunSpec implements IRunSpec { // XXX use MessageHandler?

/** true if we expect to use a staging directory */
boolean isStaging;
protected String description;
/** optional source location of the specification itself */
protected ISourceLocation sourceLocation;
private BitSet skipSet;
private boolean skipAll;
protected final String xmlElementName;
protected final ArrayList /*String*/ keywords;
protected final ArrayList /*IMessage*/ expectedMessages;
protected final ArrayList /*String*/ options;
protected final ArrayList /*String*/ paths;
protected final ArrayList /*ISourceLocation*/ sourceLocations; // XXX remove?
protected final ArrayList /*IRunSpec*/ children;
protected final ArrayList /*DirChanges.Spec*/ dirChanges;
protected XMLNames xmlNames;
protected String comment;
/** These options are 1:1 with spec, but set at runtime (not saved) */
public final RT runtime;

/** if true, then any child skip causes this to skip */
protected final boolean skipIfAnyChildSkipped;

public AbstractRunSpec(String xmlElementName) {
this(xmlElementName, true);
}
public AbstractRunSpec(String xmlElementName, boolean skipIfAnyChildSkipped) {
if (null == xmlElementName) {
xmlElementName = "spec";
}
this.xmlElementName = xmlElementName;
expectedMessages = new ArrayList();
options = new ArrayList();
paths = new ArrayList();
sourceLocations = new ArrayList();
keywords = new ArrayList();
children = new ArrayList();
dirChanges = new ArrayList();
xmlNames = XMLNames.DEFAULT;
runtime = new RT();
this.skipIfAnyChildSkipped = skipIfAnyChildSkipped;
}

/** @param comment ignored if null */
public void setComment(String comment) {
if (!LangUtil.isEmpty(comment)) {
this.comment = comment;
}
}
public void setStaging(boolean staging) {
isStaging = staging;
}
boolean isStaging() {
return isStaging;
}
// ------- description (title, label...)
public void setDescription(String description) {
this.description = description;
}

public String getDescription() {
return description;
}
// ------- source location of the spec
public void setSourceLocation(ISourceLocation sourceLocation) {
this.sourceLocation = sourceLocation;
}

public ISourceLocation getSourceLocation() {
return sourceLocation;
}
// ------- keywords
/** @param keyword added after trimming if not empty */
public void setKeyword(String keyword) {
addKeyword(keyword);
}
/** Add keyword if non-empty and not duplicate */
public void addKeyword(String keyword) {
if (!LangUtil.isEmptyTrimmed(keyword)) {
keyword = keyword.trim();
if (!keywords.contains(keyword)) {
keywords.add(keyword);
}
}
}
public void setKeywords(String items) {
addKeywords(items);
}
public void addKeywords(String items) {
if (null != items) {
addKeywords(XMLWriter.unflattenList(items));
}
}
public void addKeywords(String[] ra) {
if (null != ra) {
for (int i = 0; i < ra.length; i++) {
addKeyword(ra[i]);
}
}
}
public ArrayList getKeywordsList() {
return makeList(keywords);
}

// ------- options - String args

/** @return ArrayList of String options */
public ArrayList getOptionsList() {
return makeList(options);
}
/** @return String[] of options */
public String[] getOptionsArray() {
return (String[]) options.toArray(new String[0]);
}
public void setOption(String option) {
addOption(option);
}
public void addOption(String option) {
if ((null != option) && (0 < option.length())) {
options.add(option);
}
}
/** add options (from XML/bean) - removes any existing options */
public void setOptions(String items) {
this.options.clear();
addOptions(items);
}
/**
* Set options, removing any existing options.
* @param options String[] options to use - may be null or empty
*/
public void setOptionsArray(String[] options) {
this.options.clear();
if (!LangUtil.isEmpty(options)) {
this.options.addAll(Arrays.asList(options));
}
}
public void addOptions(String items) {
if (null != items) {
addOptions(XMLWriter.unflattenList(items));
}
}
public void addOptions(String[] ra) {
if (null != ra) {
for (int i = 0; i < ra.length; i++) {
addOption(ra[i]);
}
}
}

// -------------- source locations
// /** @return ArrayList of ISourceLocation sourceLocs */
// public ArrayList getSourceLocationsList() {
// return makeList(sourceLocations);
// }
//
// /** @return ISourceLocation[] sourceLocs */
// public ISourceLocation[] getSourceLocationsArray() {
// return (ISourceLocation[]) sourceLocations.toArray(new ISourceLocation[0]);
// }
//
//
// public void setSourceLocation(String input) {
// if (null != input) {
// ISourceLocation sl = BridgeUtil.makeSourceLocation(input);
// if (null != sl) {
// addSourceLocation(sl);
// }
// // XXX need error-handling for bad input
// }
// }
//
// public void addSourceLocation(ISourceLocation sourceLoc) {
// if (null != sourceLoc) {
// sourceLocations.add(sourceLoc);
// }
// }

// --------------- (String) paths
/** @return ArrayList of String paths */
public ArrayList getPathsList() {
return makeList(paths);
}
/** @return String[] of paths */
public String[] getPathsArray() {
return (String[]) paths.toArray(new String[0]);
}

// /** @return String[] of paths, removing any matching stripPrefix prefix */
// public String[] getPathsArray(String stripPrefix) {
// String[] result = getPathsArray();
// if (!LangUtil.isEmpty(stripPrefix)) {
// final int LEN = stripPrefix.length();
// for (int i = 0; i < result.length; i++) {
// if ((null != result[i]) && result[i].startsWith(stripPrefix)) {
// result[i] = result[i].substring(LEN);
// }
// }
// }
// return result;
// }
//
// /** @return ArrayList of File baseDir/{path} */
// public ArrayList getPathsAsFile(File baseDir) {
// if (null == baseDir) {
// baseDir = new File(".");
// }
// ArrayList result = makeList(null);
// for (Iterator iter = paths.iterator(); iter.hasNext();) {
// String path = (String) iter.next();
// result.add(new File(baseDir, path));
// }
// return result;
// }
public void setPath(String path) {
addPath(path);
}

public void setPaths(String paths) {
addPaths(paths);
}
public void addPath(String path) {
if (null != path) {
paths.add(path);
}
}

public void addPaths(String items) {
if (null != items) {
addPaths(XMLWriter.unflattenList(items));
}
}

public void addPaths(String[] ra) {
if (null != ra) {
for (int i = 0; i < ra.length; i++) {
addPath(ra[i]);
}
}
}

public void addWrapFile(WrapFile file) {
if (null != file) {
paths.add(file.path);
}
}
// --------------------- dir changes
public void addDirChanges(DirChanges.Spec dirChangesSpec) {
if (null != dirChangesSpec) {
dirChanges.add(dirChangesSpec);
}
}
// --------------------- messages
public void setMessage(String message) {
addMessage(message);
}

public void addMessage(IMessage message) {
if (null != message) {
expectedMessages.add(message);
}
}
public void addMessage(String message) {
if (null != message) {
IMessage m = BridgeUtil.readMessage(message);
expectedMessages.add(m);
}
}
/** this can ONLY work if each item has no internal comma
*/
public void addMessages(String items) {
if (null != items) {
String[] ra = XMLWriter.unflattenList(items);
for (int i = 0; i < ra.length; i++) {
addMessage(ra[i]);
}
}
}
public void addMessages(List messages) {
if (null != messages) {
for (Iterator iter = messages.iterator(); iter.hasNext();) {
Object o = iter.next();
if (o instanceof IMessage) {
addMessage((IMessage) o);
} else {
// XXX warning?
}
}
}
}

public ArrayList getMessages(IMessage.Kind kind) {
return makeList(MessageUtil.getMessages(expectedMessages, kind));
}

/** @return int number of message of this kind (optionally or greater */
public int numMessages(IMessage.Kind kind, boolean orGreater) {
return MessageUtil.numMessages(expectedMessages, kind, orGreater);
}
public ArrayList getMessages() {
return getMessages(null); // XXX special meaning of null
}
public void addChild(IRunSpec child) {
// fyi, child is added when complete (depth-first), not when initialized,
// so cannot affect initialization of child here
if (null != child) {
children.add(child);
}
}
/** @return copy of children list */
public ArrayList getChildren() {
return makeList(children);
}

/** @return copy of children list without children to skip */
public ArrayList getWorkingChildren() {
if (skipAll) {
return new ArrayList();
}
if (null == skipSet) {
return getChildren();
}
ArrayList result = new ArrayList();
int i = 0;
for (Iterator iter = children.listIterator();
iter.hasNext(); i++) {
Object child = iter.next();
if (!skipSet.get(i)) {
result.add(child);
}
}
return result;
}

/**
* Recursively absorb parent values if different.
* This implementation calls doAdoptParentValues(..)
* and then calls this for any children.
* This is when skipped children are determined.
* Children may elect to balk at this point, reducing the
* number of children or causing this spec to skip if
* skipIfAnyChildrenSkipped. For each test skipped, either
* this doAdoptParentValues(..) or the child's adoptParentValues(..)
* should add one info message with the reason this is being skipped.
* The only reason to override this would be to NOT
* invoke the same for children, or to do something similar
* for children which are not AbstractRunSpec.
* @param parentRuntime the RT values to adopt - ignored if null
* @param handler the IMessageHandler for info messages when skipping
* @return false if this wants to be skipped, true otherwise
*/
public boolean adoptParentValues(RT parentRuntime, IMessageHandler handler) {
boolean skipped = false;
skipAll = false;
skipSet = new BitSet();
if (null != parentRuntime) {
skipped = !doAdoptParentValues(parentRuntime, handler);
if (skipped && skipIfAnyChildSkipped) { // no need to continue checking
skipAll = true;
return false;
}
int i = 0;
for (ListIterator iter = children.listIterator(); iter.hasNext(); i++) {
IRunSpec child = (IRunSpec) iter.next();
if (child instanceof AbstractRunSpec) { // XXX ugly instanceof
AbstractRunSpec arsChild = (AbstractRunSpec) child;
if (!arsChild.adoptParentValues(runtime, handler)) {
skipSet.set(i);
//iter.remove();
if (!skipped) {
skipped = true;
if (skipIfAnyChildSkipped) { // no need to continue checking
// String m = "skipping " + toString() + " because child "
// + arsChild + " skipped";
// MessageUtil.info(handler, m);
skipAll = true;
//children.clear();
return false;
}
}
}
}
}
}
return true;
}
/**
* Adopt parent values.
* This implementation makes a local copy.
* If we interpret (and absorb) any options, they should be removed
* from parentRuntime.
* This sets verbose if different (override)
* and directly adopts parentOptions if ours is null
* and otherwise adds any non-null options we don't already have.
* setting verbose and adding to parent options.
* Implementors override this to affect how parent values are adopted.
* Implementors should not recurse into children.
* This method may be called multiple times, so implementors
* should not destroy any spec information.
* Always add an info message when returning false to skip
* @param parentRuntime the RT values to adopt - never null
* @return false if this wants to be skipped, true otherwise
*/
protected boolean doAdoptParentValues(RT parentRuntime, IMessageHandler handler) {
if (runtime.verbose != parentRuntime.verbose) {
runtime.verbose = parentRuntime.verbose;
}
if (!LangUtil.isEmpty(runtime.parentOptions)) {
runtime.parentOptions.clear();
}
if (!LangUtil.isEmpty(parentRuntime.parentOptions)) {
runtime.parentOptions.addAll(parentRuntime.parentOptions);
}
return true;
}
/**
* Implementations call this when signalling skips to ensure consistency
* in message formatting
* @param handler the IMessageHandler sink - not null
* @param reason the String reason to skip - not null
*/
protected void skipMessage(IMessageHandler handler, String reason) {
LangUtil.throwIaxIfNull(handler, "handler");
LangUtil.throwIaxIfNull(handler, "reason");
// XXX for Runs, label does not identify the test
String label = toString();
MessageUtil.info(handler, "skipping \"" + label + "\" because " + reason);
}
// --------------------------- writing xml - would prefer castor..
/**
* Control XML output by renaming or suppressing output for
* attributes and subelements.
* Subelements are skipped by setting the XMLNames booleans to false.
* Attributes are skipped by setting their name to null.
* @param names XMLNames with new names and/or suppress flags.
*/
protected void setXMLNames(XMLNames names) {
if (null != names) {
xmlNames = names;
}
}
/** @return null if value is null or name="{value}" otherwise */
private String makeAttr(XMLWriter out, String name, String value) {
if (null == value) {
return null;
}
return out.makeAttribute(name, value);
}
/** @return null if list is null or empty or name="{flattenedList}" otherwise */
private String makeAttr(XMLWriter out, String name, List list) {
if (LangUtil.isEmpty(list)) {
return null;
}
String flat = out.flattenList(list);
return out.makeAttribute(name, flat);
}
/** @return true if writeAttributes(..) will produce any output */
protected boolean haveAttributes() {
return ((!LangUtil.isEmpty(xmlNames.descriptionName)
&& !LangUtil.isEmpty(description))
|| (!LangUtil.isEmpty(xmlNames.keywordsName)
&& !LangUtil.isEmpty(keywords))
|| (!LangUtil.isEmpty(xmlNames.optionsName)
&& !LangUtil.isEmpty(options))
|| (!LangUtil.isEmpty(xmlNames.pathsName)
&& !LangUtil.isEmpty(paths)));
}
/**
* Write attributes without opening or closing elements/attributes.
* An attribute is written only if the value is not empty
* and the name in xmlNames is not empty
*/
protected void writeAttributes(XMLWriter out) {
if (!LangUtil.isEmpty(xmlNames.descriptionName)
&& !LangUtil.isEmpty(description)) {
out.printAttribute(xmlNames.descriptionName, description);
}
if (!LangUtil.isEmpty(xmlNames.keywordsName)
&& !LangUtil.isEmpty(keywords)) {
out.printAttribute(xmlNames.keywordsName, out.flattenList(keywords));
}
if (!LangUtil.isEmpty(xmlNames.optionsName)
&& !LangUtil.isEmpty(options)) {
out.printAttribute(xmlNames.optionsName, out.flattenList(options));
}
if (!LangUtil.isEmpty(xmlNames.pathsName)
&& !LangUtil.isEmpty(paths)) {
out.printAttribute(xmlNames.pathsName, out.flattenList(paths));
}
if (!LangUtil.isEmpty(xmlNames.commentName)
&& !LangUtil.isEmpty(comment)) {
out.printAttribute(xmlNames.commentName, comment);
}
}

/**
* The default implementation writes everything as attributes,
* then subelements for dirChanges, messages, then subelements for children.
* Subclasses that override may delegate back for any of these.
* Subclasses may also set XMLNames to name or suppress any attribute
* or subelement.
* @see writeMessages(XMLWriter)
* @see writeChildren(XMLWriter)
* @see IXmlWritable#writeXml(XMLWriter)
*/
public void writeXml(XMLWriter out) {
out.startElement(xmlElementName,false);
writeAttributes(out);
out.endAttributes();
if (!xmlNames.skipMessages) {
writeMessages(out);
}
if (!xmlNames.skipChildren) {
writeChildren(out);
}
out.endElement(xmlElementName);
}

/**
* Write messages. Assumes attributes are closed,
* can write child elements of current element.
*/
protected void writeMessages(XMLWriter out) {
if (0 < expectedMessages.size()) {
SoftMessage.writeXml(out, expectedMessages);
}

}

/**
* Write children. Assumes attributes are closed,
* can write child elements of current element.
*/
protected void writeChildren(XMLWriter out) {
if (0 < children.size()) {
for (Iterator iter = children.iterator(); iter.hasNext();) {
IXmlWritable self = (IXmlWritable) iter.next();
self.writeXml(out);
}
}
}

// --------------------------- logging
public void printAll(PrintStream out, String prefix) {
out.println(prefix + toString());
for (Iterator iter = children.iterator(); iter.hasNext();) {
AbstractRunSpec child = (AbstractRunSpec) iter.next(); // IRunSpec
child.printAll(out, prefix + " ");
}
}
/**
* default implementation returns the description if not empty
* or the unqualified class name otherwise.
* Subclasses should not call toString from here unless they reimplement it.
* @return name of this thing or type
*/
protected String getPrintName() {
if (!LangUtil.isEmpty(description)) {
return description;
} else {
return LangUtil.unqualifiedClassName(this);
}
}
/** @return summary count of spec elements */
public String toString() {
return getPrintName() + "(" + containedSummary() + ")";
}
/** @return String of the form (# [options|paths|locations|messages]).. */
protected String containedSummary() {
int nOptions = options.size();
int nPaths = paths.size();
int nLoc = sourceLocations.size();
int nMssg = expectedMessages.size();
return
( (nOptions == 0 ? "" : nOptions + " options " )
+ (nPaths == 0 ? "" : nPaths + " paths " )
+ (nLoc == 0 ? "" : nLoc + " locations " )
+ (nMssg == 0 ? "" : nMssg + " messages" )).trim();
}
public String toLongString() {
String mssg = "";
if (expectedMessages.size() > 0) {
// XXX add MessageHandler.renderCounts(IMessage[] messages)
MessageHandler h = new MessageHandler();
for (Iterator iter = expectedMessages.iterator(); iter.hasNext();) {
h.handleMessage((IMessage) iter.next());
}
mssg = " expected messages (" + MessageUtil.renderCounts(h) + ")";
}
return getPrintName() + containedSummary() + mssg.trim();
}
private ArrayList makeList(List list) {
ArrayList result = new ArrayList();
if (null != list) {
result.addAll(list);
}
return result;
}
/**
* Subclasses use this to rename attributes or omit attributes or subelements.
* To suppress output of an attribute, * pass "" as the name of the attribute.
* To use default entries, pass null for that entry.
*/
public static class XMLNames {
public static final XMLNames DEFAULT =
new XMLNames(null, "description", "sourceLocation",
"keywords", "options", "paths", "comment", false, false, false);
final String descriptionName;
final String sourceLocationName;
final String keywordsName;
final String optionsName;
final String pathsName;
final String commentName;
final boolean skipDirChanges;
final boolean skipMessages;
final boolean skipChildren;
/** reset all names/behavior or pass defaultNames
* as the defaults for any null elements
*/
XMLNames(XMLNames defaultNames,
String descriptionName,
String sourceLocationName,
String keywordsName,
String optionsName,
String pathsName,
String commentName,
boolean skipDirChanges,
boolean skipMessages,
boolean skipChildren) {
this.skipDirChanges = skipDirChanges;
this.skipMessages = skipMessages;
this.skipChildren = skipChildren;
if (null != defaultNames) {
this.descriptionName = (null != descriptionName? descriptionName : defaultNames.descriptionName);
this.sourceLocationName = (null != sourceLocationName ? sourceLocationName : defaultNames.sourceLocationName);
this.keywordsName = (null != keywordsName ? keywordsName : defaultNames.keywordsName);
this.optionsName = (null != optionsName ? optionsName : defaultNames.optionsName);
this.pathsName = (null != pathsName ? pathsName : defaultNames.pathsName);
this.commentName = (null != commentName ? commentName : defaultNames.commentName);
} else {
this.descriptionName = descriptionName;
this.sourceLocationName = sourceLocationName;
this.keywordsName = keywordsName;
this.optionsName = optionsName;
this.pathsName = pathsName;
this.commentName = commentName;
}
}
}

/** subclasses implement this to create and set up a run */
abstract public IRunIterator makeRunIterator(Sandbox sandbox, Validator validator);

/** This is for separate file (sub-) elements with path attributes */
public static class WrapFile {
public String path;
public void setPath(String path) {
this.path = path;
}
}
/** segregate runtime-only state in spec */
public static class RT {
/** true if we should emit verbose messages */
private boolean verbose;
/** null unless parent set options for children to consider */
final private ArrayList /*String*/ parentOptions;
public RT() {
parentOptions = new ArrayList();
}
public boolean isVerbose() {
return verbose;
}
/**
* Set parent options - old options destroyed.
* Will result in duplicates if duplicates added.
* Null or empty entries are ignored
* @param options ignored if null or empty
*/
public void setOptions(String[] options) {
parentOptions.clear();
if (!LangUtil.isEmpty(options)) {
for (int i = 0; i < options.length; i++) {
if (!LangUtil.isEmpty(options[i])) {
parentOptions.add(options[i]);
}
}
}
}
/**
* Copy values from another RT
* @param toCopy the RT to copy from
* @throws IllegalArgumentException if toCopy is null
*/
public void copy(RT toCopy) {
LangUtil.throwIaxIfNull(toCopy, "parent");
parentOptions.clear();
parentOptions.addAll(toCopy.parentOptions);
verbose = toCopy.verbose;
}
/**
* Return any parent option which has one of validOptions as a prefix,
* optionally absorbing (removing) the parent option.
* @param validOptions String[] of options to extract
* @param absorb if true, then remove any parent option matched
* @return String[] containing any validOptions[i] in parentOptions
* (at most once)
*/
public String[] extractOptions(String[] validOptions, boolean absorb) {
if (LangUtil.isEmpty(validOptions) || LangUtil.isEmpty(parentOptions)) {
return new String[0];
}
ArrayList result = new ArrayList();
boolean haveOption = false;
for (int i = 0; i < validOptions.length; i++) {
String option = validOptions[i];
if (LangUtil.isEmpty(option)) {
continue;
}
for (ListIterator iter = parentOptions.listIterator(); iter.hasNext();) {
String parentOption = (String) iter.next();
if (parentOption.startsWith(option)) {
result.add(parentOption);
if (absorb) {
iter.remove();
}
}
}
}
return (String[]) result.toArray(new String[0]);
}
/** Get ListIterator that permits removals */
ListIterator getListIterator() {
return parentOptions.listIterator();
}
/**
* Enable verbose logging
* @param verbose if true, do verbose logging
*/
public void setVerbose(boolean verbose) {
if (this.verbose != verbose) {
this.verbose = verbose;
}
}
} // class RT
}

+ 282
- 0
testing/src/org/aspectj/testing/harness/bridge/AjcMessageHandler.java View File

@@ -0,0 +1,282 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.harness.bridge;

import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.IMessageHolder;
import org.aspectj.bridge.MessageHandler;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.testing.util.BridgeUtil;
import org.aspectj.testing.util.Diffs;
import org.aspectj.util.LangUtil;

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;


/**
* Adapter to pass messages through to handler,
* suppress output of expected errors and warnings,
* and calculate differences between expected and
* actual messages.
*/
public class AjcMessageHandler extends MessageHandler {

/** Comparator for enclosed IMessage diffs, etc. */
public static final Comparator COMP_IMessage
= BridgeUtil.Comparators.WEAK_IMessage;
/** Comparator for enclosed File diffs, etc. */
public static final Comparator COMP_File
= BridgeUtil.Comparators.WEAK_File;


/** unmodifiable list of IMessage messages of type IMesssage.ERROR */
final List expectedErrors; // revert to IMessageHolder for generality?
/** unmodifiable list of IMessage messages of type IMesssage.WARNING */
final List expectedWarnings;

/** unmodifiable list of File expected to be recompiled */
final List expectedRecompiled;
/** list of File actually recompiled */
List actualRecompiled;
/** if true, ignore warnings when calculating diffs and passed() */
boolean ignoreWarnings;
/** cache expected/actual diffs, nullify if any new message */
transient CompilerDiffs diffs;

private boolean expectingCommandTrue;
/**
* @param messages List of IMessage to extract ERROR and WARNING from
*/
AjcMessageHandler(List messages) {
this(MessageUtil.getMessages(messages, IMessage.ERROR),
MessageUtil.getMessages(messages, IMessage.WARNING));
}

/**
* @param errors unmodifiable List of IMessage of kind ERROR to adopt
* @param warnings unmodifiable List of IMessage of kind WARNING to adopt
*/
AjcMessageHandler(List errors, List warnings) {
this(errors, warnings, null);
}

AjcMessageHandler(List errors, List warnings, List recompiled) {
this.expectedErrors = LangUtil.safeList(errors);
this.expectedWarnings = LangUtil.safeList(warnings);
this.expectedRecompiled = LangUtil.safeList(recompiled);
expectingCommandTrue = (0 == expectedErrors.size());
}
public void setIgnoreWarnings(boolean ignoreWarnings) {
this.ignoreWarnings = ignoreWarnings;
}
/** clear out any actual values to be re-run */
public void init() {
super.init();
actualRecompiled = null;
diffs = null;
}

/**
* Return true if we have this kind of
* message for the same line and store all messages.
* @see bridge.tools.impl.ErrorHandlerAdapter#doShowMessage(IMessage)
* @return true if message handled (processing should abort)
*/
public boolean handleMessage(IMessage message) {
if (null == message) {
throw new IllegalArgumentException("null message");
}
messages.add(message);
IMessage.Kind kind = message.getKind();
return expecting(message, getExpected(kind));
}
/**
* Set the actual files recompiled.
* @param List of File recompiled - may be null; adopted but not modified
* @throws IllegalStateException if they have been set already.
*/
public void setRecompiled(List list) {
if (null != actualRecompiled) {
throw new IllegalStateException("actual recompiled already set");
}
this.actualRecompiled = LangUtil.safeList(list);
}
/** @return immutable List of IMessage expected of this kind */
public List getExpected(IMessage.Kind kind) {
if (IMessage.ERROR == kind) {
return expectedErrors;
} else if (IMessage.WARNING == kind) {
return expectedWarnings;
} else {
return Collections.EMPTY_LIST;
}
}
/**
* Check if the message was expected, and clear diffs if not.
* @return true if we expect a message of this kind with this line number
*/
protected boolean expecting(IMessage message, List sink) { // XXX ignores File
if ((null != message) && (0 < sink.size())) {
// XXX need to cache list: int[] errLines, warningLines;
for (Iterator iter = sink.iterator(); iter.hasNext();) {
IMessage m = (IMessage) iter.next();
if (0 == COMP_IMessage.compare(message, m)) {
return true;
}
}
}
if (null != diffs) {
diffs = null;
}
return false;
}

/** Generate differences between expected and actual errors and warnings */
public CompilerDiffs getCompilerDiffs() {
if (null == diffs) {
final List actualErrors = Arrays.asList(getMessages(IMessage.ERROR, IMessageHolder.EQUAL));
final List actualWarnings = Arrays.asList(getMessages(IMessage.WARNING, IMessageHolder.EQUAL));
Diffs errors = new Diffs("error", expectedErrors, actualErrors, COMP_IMessage);
Diffs warnings = new Diffs("warning", expectedWarnings, actualWarnings, COMP_IMessage);
Diffs recompiled = new Diffs("recompiled", expectedRecompiled, actualRecompiled, COMP_File);
diffs = new CompilerDiffs(errors, warnings, recompiled);
}
return diffs;
}
/** calculate passed based on default or set value for ignoreWarnings */
public boolean passed() {
return passed(ignoreWarnings);
}
/** @return true if we are expecting the command to fail - i.e., any expected errors */
public boolean expectingCommandTrue() {
return expectingCommandTrue;
}
/**
* Get the (current) result of this run,
* ignoring differences in warnings on request.
* Note it may return passed (true) when there are expected error messages.
* @return false
* if there are any fail or abort messages,
* or if the expected errors, warnings, or recompiled do not match actual.
*
*/
public boolean passed(boolean ignoreWarnings) {
if (hasAnyMessage(IMessage.FAIL, IMessageHolder.EQUAL)) {
return false;
}
CompilerDiffs diffs = getCompilerDiffs();
if (!ignoreWarnings) {
return (!diffs.different);
} else {
return ((!diffs.errors.different) && (!diffs.recompiled.different));
}
}
/**
* Report results to a handler,
* adding all messages
* and creating fail messages for each diff.
*/
public void report(IMessageHandler handler) {
if (null == handler) {
MessageUtil.debug(this, "report got null handler");
}
IMessage[] messages = getMessages(null, IMessageHolder.EQUAL);
for (int i = 0; i < messages.length; i++) {
handler.handleMessage(messages[i]);
}
CompilerDiffs diffs = getCompilerDiffs();
if (diffs.different) {
diffs.errors.report(handler, IMessage.FAIL);
diffs.warnings.report(handler, IMessage.FAIL);
diffs.recompiled.report(handler, IMessage.FAIL);
}
}
/** @return String consisting of differences and any other messages */
public String toString() {
CompilerDiffs diffs = getCompilerDiffs();
StringBuffer sb = new StringBuffer(super.toString());
final String EOL = "\n";
sb.append(EOL);
render(sb, " unexpected error ", EOL, diffs.errors.unexpected);
render(sb, "unexpected warning ", EOL, diffs.warnings.unexpected);
render(sb, " missing error ", EOL, diffs.errors.missing);
render(sb, " missing warning ", EOL, diffs.warnings.missing);
render(sb, " fail ", EOL, getList(IMessage.FAIL));
render(sb, " abort ", EOL, getList(IMessage.ABORT));
render(sb, " info ", EOL, getList(IMessage.INFO));
return sb.toString(); // XXX cache toString
}
/** @return immutable list of a given kind - use null for all kinds */
private List getList(IMessage.Kind kind) {
if ((null == kind) || (0 == numMessages(kind, IMessageHolder.EQUAL))) {
return Collections.EMPTY_LIST;
}
return Arrays.asList(getMessages(kind, IMessageHolder.EQUAL));
}
/** @return "" if no items or {prefix}{item}{suffix}... otherwise */
private void render( // LangUtil instead?
StringBuffer result,
String prefix,
String suffix,
List items) {
if ((null != items)) {
for (Iterator iter = items.iterator(); iter.hasNext();) {
result.append(prefix + iter.next() + suffix);
}
}
}
/** compiler results for errors, warnings, and recompiled files */
public static class CompilerDiffs {
public final Diffs errors;
public final Diffs warnings;
public final Diffs recompiled;
public final boolean different;
public CompilerDiffs(Diffs errors, Diffs warnings) {
this(errors, warnings, Diffs.NONE);
}
public CompilerDiffs(Diffs errors, Diffs warnings, Diffs recompiled) {
this.errors = errors;
this.warnings = warnings;
this.recompiled = recompiled;
different = (warnings.different || errors.different
|| recompiled.different);
}
}
}

+ 371
- 0
testing/src/org/aspectj/testing/harness/bridge/AjcTest.java View File

@@ -0,0 +1,371 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.harness.bridge;

import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.testing.run.IRunIterator;
import org.aspectj.testing.run.IRunStatus;
import org.aspectj.testing.run.Runner;
import org.aspectj.testing.xml.XMLWriter;
import org.aspectj.util.LangUtil;

import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
* An AjcTest has child subruns (compile, [inc-compile|run]*).
*/
public class AjcTest extends RunSpecIterator {
/** Unwrap an AjcTest.Spec from an IRunStatus around an AjcTest */
public static Spec unwrapSpec(IRunStatus status) {
if (null != status) {
Object id = status.getIdentifier();
if (id instanceof Runner.IteratorWrapper) {
IRunIterator iter = ((Runner.IteratorWrapper) id).iterator;
if (iter instanceof AjcTest) {
return (Spec) ((AjcTest) iter).spec;
}
}
}
return null;
}
/** Unwrap initial CompilerRun.Spec from an AjcTest.Spec */
public static CompilerRun.Spec unwrapCompilerRunSpec(Spec spec) {
if (null != spec) {
List kids = spec.getChildren();
if (0 < kids.size()) {
Object o = kids.get(0);
if (o instanceof CompilerRun.Spec) {
return (CompilerRun.Spec) o;
}
}
}
return null;
}

/** The spec creates the sandbox, so we use it throughout */
public AjcTest(Spec spec, Sandbox sandbox, Validator validator) {
super(spec, sandbox, validator, true);
}
/**
* Clear the command from the sandbox, to avoid memory leaks.
* @see org.aspectj.testing.harness.bridge.RunSpecIterator#iterationCompleted()
*/
public void iterationCompleted() {
super.iterationCompleted();
sandbox.clearCommand(this);
}
/**
* Specification for an ajc test.
* Keyword directives are global/parent options passed as
* <pre>-ajctest[Require|Skip]Keywords=keyword{,keyword}..</pre>.
*/
public static class Spec extends AbstractRunSpec {
public static final String XMLNAME = "ajc-test";
/**
* do description as title, do sourceLocation,
* do keywords, do options, skip paths, do comment,
* skip dirChanges, do messages and do children
* (though we do children directly).
*/
private static final XMLNames NAMES = new XMLNames(XMLNames.DEFAULT,
"title", null, null, null, "", null, true, false, false);
private static final String OPTION_PREFIX = "-ajctest";
private static final String[] VALID_OPTIONS = new String[] { OPTION_PREFIX };

private static final String REQUIRE_KEYWORDS = "RequireKeywords=";
private static final String SKIP_KEYWORDS = "SkipKeywords=";
private static final String PICK_PR = "PR=";
private static final List VALID_SUFFIXES
= Collections.unmodifiableList(Arrays.asList(new String[]
{ REQUIRE_KEYWORDS, SKIP_KEYWORDS, PICK_PR }));
/** base directory of the test suite - set before making run */
private File suiteDir;
/** path offset from suite directory to base of test directory */
String testDirOffset; // XXX revert to private after fixes
/** id of bug - if 0, then no bug associated with this test */
private int bugId;

public Spec() {
super(XMLNAME);
setXMLNames(NAMES);
}
public void setSuiteDir(File suiteDir) {
this.suiteDir = suiteDir;
}
public File getSuiteDir() {
return suiteDir;
}
/** @param bugId 100..9999 */
public void setBugId(int bugId) {
LangUtil.throwIaxIfFalse((bugId > 10) && (bugId < 10000), "bad bug id: " + bugId);
this.bugId = bugId;
}
public int getBugId() {
return bugId;
}
public void setTestDirOffset(String testDirOffset) {
if (!LangUtil.isEmpty(testDirOffset)) {
this.testDirOffset = testDirOffset;
}
}
public String getTestDirOffset() {
return (null == testDirOffset ? "" : testDirOffset);
}
/**
* @param sandbox ignored
* @see org.aspectj.testing.harness.bridge.AbstractRunSpec#makeAjcRun(Sandbox, Validator)
*/
public IRunIterator makeRunIterator(Sandbox sandbox, Validator validator) {
LangUtil.throwIaxIfNull(validator, "validator");

// if no one set suiteDir, see if we have a source location
if (null == suiteDir) {
ISourceLocation loc = getSourceLocation();
if (!validator.nullcheck(loc, "suite file location")
|| !validator.nullcheck(loc.getSourceFile(), "suite file")) {
return null;
}
File locDir = loc.getSourceFile().getParentFile();
if (!validator.canReadDir(locDir, "source location dir")) {
return null;
}
suiteDir = locDir;
}

// we make a new sandbox with more state for our subruns, keep that,
// in order to defer initialization to nextRun()
File testBaseDir;
String testDirOffset = getTestDirOffset();
if (LangUtil.isEmpty(testDirOffset)) {
testBaseDir = suiteDir;
} else {
testBaseDir = new File(suiteDir, testDirOffset);
if (!validator.canReadDir(testBaseDir, "testBaseDir")) {
return null;
}
}
Sandbox childSandbox = null;
try {
childSandbox = new Sandbox(testBaseDir, validator);
validator.registerSandbox(childSandbox);
} catch (IllegalArgumentException e) {
validator.fail(e.getMessage());
return null;
}
return new AjcTest(this, childSandbox, validator);
}

/** @see IXmlWritable#writeXml(XMLWriter) */
public void writeXml(XMLWriter out) {
out.println("");
String value = (null == testDirOffset? "" : testDirOffset);
String attr = out.makeAttribute("dir", value);
if (0 != bugId) {
attr += " " + out.makeAttribute("pr", ""+bugId);
}
out.startElement(xmlElementName, attr, false);
super.writeAttributes(out);
out.endAttributes();
super.writeChildren(out);
out.endElement(xmlElementName);
}
/**
* AjcTest overrides this to skip if
* <ul>
* <li>the spec has a keyword the parent wants to skip</li>
* <li>the spec does not have keyword the parent requires</li>
* <li>the spec does not have the bugId required</li>
* </ul>
* @return false if this wants to be skipped, true otherwise
* @throws Error if selected option is not of the form
* <pre>-ajctest[Require|Skip]Keywords=keyword{,keyword}..</pre>.
*/
protected boolean doAdoptParentValues(RT parentRuntime, IMessageHandler handler) {
if (!super.doAdoptParentValues(parentRuntime, handler)) {
return false;
}
runtime.copy(parentRuntime);
String[] globalOptions = runtime.extractOptions(VALID_OPTIONS, true);
for (int i = 0; i < globalOptions.length; i++) {
String option = globalOptions[i];
if (!option.startsWith(OPTION_PREFIX)) {
throw new Error("only expecting " + OPTION_PREFIX + "..: " + option);
}
option = option.substring(OPTION_PREFIX.length());
boolean keywordMustExist = false;
String havePr = null;
if (option.startsWith(REQUIRE_KEYWORDS)) {
option = option.substring(REQUIRE_KEYWORDS.length());
keywordMustExist = true;
} else if (option.startsWith(SKIP_KEYWORDS)) {
option = option.substring(SKIP_KEYWORDS.length());
} else if (option.startsWith(PICK_PR)) {
if (0 == bugId) {
skipMessage(handler, "bugId required, but no bugId for this test");
return false;
} else {
havePr = "" + bugId;
}
option = option.substring(PICK_PR.length());
} else {
throw new Error("unrecognized suffix: " + globalOptions[i]
+ " (expecting: " + OPTION_PREFIX + VALID_SUFFIXES + "...)");
}
List specs = LangUtil.commaSplit(option);
// XXX also throw Error on empty specs...
for (Iterator iter = specs.iterator(); iter.hasNext();) {
String spec = (String) iter.next();
if (null != havePr) {
if (havePr.equals(spec)) { // String.equals()
return true;
}
} else if (keywordMustExist != keywords.contains(spec)) {
String reason = "keyword " + spec
+ " was " + (keywordMustExist ? "not found" : "found");
skipMessage(handler, reason);
return false;
}
}
if (null != havePr) {
skipMessage(handler, "bugId required, but not matched for this test");
return false;
}
}
return true;
}

} // AjcTest.Spec
/**
* A suite of AjcTest has children for each AjcTest
* and flows all options down as globals
*/
public static class Suite extends RunSpecIterator {
final Spec spec;
public Suite(Spec spec, Sandbox sandbox, Validator validator) {
super(spec, sandbox, validator, false);
this.spec = spec;
}
/**
* While being called to make the sandbox for the child,
* set up the child's suite dir based on ours.
* @param child must be instanceof AjcTest.Spec
* @see org.aspectj.testing.harness.bridge.RunSpecIterator#makeSandbox(IRunSpec, Validator)
* @return super.makeSandbox(child, validator)
*/
protected Sandbox makeSandbox(
IRunSpec child,
Validator validator) {
if (!(child instanceof AjcTest.Spec)) {
validator.fail("only expecting AjcTest children");
return null;
}
if (!validator.canReadDir(spec.suiteDir, "spec.suiteDir")) {
return null;
}
((AjcTest.Spec) child).setSuiteDir(spec.suiteDir);
return super.makeSandbox(child, validator);
}

/**
* A suite spec contains AjcTest children.
* The suite dir or source location should be set
* if the tests do not each have a source location
* with a source file in the suite dir.
* XXX whether to write out suiteDir in XML?
*/
public static class Spec extends AbstractRunSpec {
public static final String XMLNAME = "suite";
/**
* do description, do sourceLocation,
* do keywords, do options, skip paths, do comment
* skip dirChanges, skip messages and do children
* (though we do children directly).
*/
private static final XMLNames NAMES = new XMLNames(XMLNames.DEFAULT,
null, null, null, null, "", null, true, true, false);
File suiteDir;
public Spec() {
super(XMLNAME, false); // do not skip this even if children skip
}
/** @param suiteDirPath the String path to the base suite dir */
public void setSuiteDir(String suiteDirPath) {
if (!LangUtil.isEmpty(suiteDirPath)) {
this.suiteDir = new File(suiteDirPath);
}
}
/** @param suiteDirFile the File for the base suite dir */
public void setSuiteDirFile(File suiteDir) {
this.suiteDir = suiteDir;
}
/** @get suiteDir from any set or source location if set */
public File getSuiteDirFile() {
if (null == suiteDir) {
ISourceLocation loc = getSourceLocation();
if (null != loc) {
File sourceFile = loc.getSourceFile();
if (null != sourceFile) {
suiteDir = sourceFile.getParentFile();
}
}
}
return suiteDir;
}
/**
* @return
* @see org.aspectj.testing.harness.bridge.AbstractRunSpec#makeRunIterator(Sandbox, Validator)
*/
public IRunIterator makeRunIterator(
Sandbox sandbox,
Validator validator) {
return new Suite(this, sandbox, validator);
}

public String toString() {
// removed nKids as misleading, since children.size() may change
//int nKids = children.size();
//return "Suite.Spec(" + suiteDir + ", " + nKids + " tests)";
return "Suite.Spec(" + suiteDir + ")";
}
}
}
}

+ 966
- 0
testing/src/org/aspectj/testing/harness/bridge/CompilerRun.java View File

@@ -0,0 +1,966 @@
/* *******************************************************************
* Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.harness.bridge;

import org.aspectj.bridge.ICommand;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.bridge.ReflectionFactory;
import org.aspectj.testing.run.IRunIterator;
import org.aspectj.testing.run.IRunStatus;
import org.aspectj.testing.run.WrappedRunIterator;
import org.aspectj.testing.xml.SoftMessage;
import org.aspectj.testing.xml.XMLWriter;
import org.aspectj.util.FileUtil;
import org.aspectj.util.LangUtil;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

/**
* Run the compiler once in non-incremental mode.
*/
public class CompilerRun implements IAjcRun {
static final String[] RA_String = new String[0];
static final String[] JAR_SUFFIXES = new String[] { ".jar", ".zip" };

static final String[] SOURCE_SUFFIXES
= (String[]) FileUtil.SOURCE_SUFFIXES.toArray(new String[0]);
/** specifications, set on construction */
Spec spec;

//------------ calculated during setup
/** get shared stuff during setup */
Sandbox sandbox;
/**
* During run, these String are passed as the source and arg files to compile.
* The list is set up in setupAjcRun(..), when arg files are prefixed with "@".
*/
final List /*String*/ arguments;
/**
* During run, these String are collapsed and passed as the injar option.
* The list is set up in setupAjcRun(..).
*/
final List /*String*/ injars;
/**
* During run, these String are collapsed and passed as the aspectpath option.
* The list is set up in setupAjcRun(..).
*/
final List /*String*/ aspectpath;

private CompilerRun(Spec spec) {
if (null == spec) {
throw new IllegalArgumentException("null spec");
}
this.spec = spec;
arguments = new ArrayList();
injars = new ArrayList();
aspectpath = new ArrayList();
}
/**
* This checks that the spec is reasonable and does setup:
* <li>calculate and set sandbox testBaseSrcDir as
* {Sandbox.testBaseDir}/{Spec.testSrcDirOffset}/<li>
* <li>the list of source File to compile as
* {Sandbox.testBaseSrcDir}/{Spec.getPaths..}</li>
* All sources must be readable at this time.
* @param classesDir the File
* @see org.aspectj.testing.harness.bridge.AjcTest.IAjcRun#setup(File, File)
* @throws AbortException containing IOException or IllegalArgumentException
* if the staging operations fail
*/
public boolean setupAjcRun(Sandbox sandbox, Validator validator) {

if (!validator.nullcheck(spec.getOptionsArray(), "localOptions")
|| !validator.nullcheck(sandbox, "sandbox")
|| !validator.nullcheck(spec.compiler, "compilerName")
|| !validator.canRead(Globals.F_aspectjrt_jar, "aspectjrt.jar")
|| !validator.canRead(Globals.F_testingclient_jar, "testing-client.jar")
//|| !validator.canRead(Main.F_bridge_jar, "bridge.jar")
) {
return false;
}
this.sandbox = sandbox;
File[] cp = new File[]
{ sandbox.classesDir, Globals.F_aspectjrt_jar, Globals.F_testingclient_jar };
sandbox.setClasspath(cp, true, this);
String rdir = spec.testSrcDirOffset;
File testBaseSrcDir;
if ((null == rdir) || (0 == rdir.length())) {
testBaseSrcDir = sandbox.testBaseDir;
} else {
testBaseSrcDir = new File(sandbox.testBaseDir, rdir);
if (!validator.canReadDir(testBaseSrcDir, "sandbox.testBaseSrcDir")) {
return false;
}
}
sandbox.setTestBaseSrcDir(testBaseSrcDir, this);
arguments.clear();
// sources come as relative paths - check read, copy if staging

// this renders paths absolute before run(RunStatusI) is called
// for a compile run to support relative paths + source base
// change so the run calculates the paths (differently when staging)

final String[] injarPaths;
final String[] srcPaths;
{
final String[] paths = spec.getPathsArray();
srcPaths = LangUtil.endsWith(paths, CompilerRun.SOURCE_SUFFIXES, true);
injarPaths = LangUtil.endsWith(paths, CompilerRun.JAR_SUFFIXES, true);
}
// validate readable for sources
if (!validator.canRead(testBaseSrcDir, srcPaths, "sources")
|| !validator.canRead(testBaseSrcDir, injarPaths, "injars")
|| !validator.canRead(testBaseSrcDir, spec.argfiles, "argfiles")) {
return false;
}
int numSources = srcPaths.length + injarPaths.length + spec.argfiles.length;
if (numSources < 1) {
validator.fail("no source files, input jars, or arg files");
return false;
}
File[] srcFiles;
File[] argFiles;
File[] injarFiles;
File[] aspectFiles;
if (!spec.isStaging()) { // XXX why this? was always? || (testBaseSrcDir != sandbox.stagingDir))) {
srcFiles = FileUtil.getBaseDirFiles(testBaseSrcDir, srcPaths, CompilerRun.SOURCE_SUFFIXES);
argFiles = FileUtil.getBaseDirFiles(testBaseSrcDir, spec.argfiles);
injarFiles = FileUtil.getBaseDirFiles(testBaseSrcDir, injarPaths);
aspectFiles = FileUtil.getBaseDirFiles(testBaseSrcDir, spec.aspectpath);
} else { // staging - copy files
try {
srcFiles = FileUtil.copyFiles(testBaseSrcDir, srcPaths, sandbox.stagingDir);
argFiles = FileUtil.copyFiles(testBaseSrcDir, spec.argfiles, sandbox.stagingDir);
injarFiles = FileUtil.copyFiles(testBaseSrcDir, injarPaths, sandbox.stagingDir);
aspectFiles = FileUtil.copyFiles(testBaseSrcDir, spec.aspectpath, sandbox.stagingDir);
} catch (IllegalArgumentException e) {
validator.fail("staging - bad input", e);
return false;
} catch (IOException e) {
validator.fail("staging - operations", e);
return false;
}
// validate readable for copied sources
if (!validator.canRead(srcFiles, "copied paths")
|| !validator.canRead(argFiles, "copied argfiles")) {
return false;
}
}
arguments.clear();
injars.clear();
aspectpath.clear();
if (!LangUtil.isEmpty(srcFiles)) {
arguments.addAll(Arrays.asList(FileUtil.getPaths(srcFiles)));
}
if (!LangUtil.isEmpty(injarFiles)) {
injars.addAll(Arrays.asList(FileUtil.getPaths(injarFiles)));
}
if (!LangUtil.isEmpty(aspectFiles)) {
aspectpath.addAll(Arrays.asList(FileUtil.getPaths(aspectFiles)));
}
if (!LangUtil.isEmpty(argFiles)) {
String[] ra = FileUtil.getPaths(argFiles);
for (int j = 0; j < ra.length; j++) {
arguments.add("@" + ra[j]);
}
if (spec.isStaging) {
validator.info("warning: files listed in argfiles not staged");
}
}

return true;
}
// if ((null != spec.dirChanges) && (0 < spec.numMessages(IMessage.ERROR, false))) {
// validator.info("CompilerRun warning: when expecting errors, cannot expect dir changes");
// }

/**
* Setup result evaluation and command line, run, and evaluate result.
* <li>setup an AjcMessageHandler using the expected messages from
* {@link Spec#getMessages()}.<li>
* <li>heed any globals interpreted into a TestSetup by reading
* {@link Spec@getOptions()}. For a list of supported globals, see
* {@link setupArgs(ArrayList, IMessageHandler}.</li>
* <li>construct a command line, using as classpath
* {@link Sandbox.classpathToString()}<li>
* <li>construct a compiler using {@link Spec#compilerName}
* or any overriding value set in TestSetup.<li>
* <li>Just before running, set the compiler in the sandbox using
* {@link Sandbox.setCompiler(ICommand)}.<li>
* <li>After running, report AjcMessageHandler results to the status parameter.
* If the AjcMessageHandler reports a failure, then send info messages
* for the Spec, TestSetup, and command line.<li>
* XXX better to upgrade AjcMessageHandler to adopt status
* so the caller can control fast-fail, etc.
* @see org.aspectj.testing.run.IRun#run(IRunStatus)
*/
public boolean run(IRunStatus status) {
if (null == spec.testSetup) {
MessageUtil.abort(status, "no test setup - adoptParentValues not called");
return false;
} else if (!spec.testSetup.result) {
MessageUtil.abort(status, spec.testSetup.failureReason);
return false;
}
AjcMessageHandler handler = new AjcMessageHandler(spec.getMessages());
handler.init();
boolean handlerResult = false;
boolean result = false;
boolean commandResult = false;
ArrayList argList = new ArrayList();
final Spec.TestSetup setupResult = spec.testSetup;
try {
argList.addAll(setupResult.commandOptions);
argList.add("-d");
String outputDirPath = sandbox.classesDir.getAbsolutePath();
try { // worth it to try for canonical?
outputDirPath = sandbox.classesDir.getCanonicalPath();
} catch (IOException e) {
MessageUtil.abort(status, "canonical " + sandbox.classesDir, e);
}
argList.add(outputDirPath);
argList.add("-classpath");
argList.add(sandbox.classpathToString(this));
// XXX classpath additions here?

if (0 < injars.size()) {
argList.add("-injars");
argList.add(FileUtil.flatten((String[]) injars.toArray(new String[0]), null));
}

if (0 < aspectpath.size()) {
argList.add("-aspectpath");
argList.add(FileUtil.flatten((String[]) aspectpath.toArray(new String[0]), null));
}

// add both java/aspectj and argfiles
argList.addAll(arguments);
// hack - do seeking on request as a side effect. reimplement as listener
if (null != setupResult.seek) {
String slopPrefix = Spec.SEEK_MESSAGE_PREFIX + " slop - ";
PrintStream slop = MessageUtil.handlerPrintStream(
status,
IMessage.INFO,
System.err,
slopPrefix);
List found = FileUtil.lineSeek(setupResult.seek, arguments, false, slop);
if (!LangUtil.isEmpty(found)) {
for (Iterator iter = found.iterator(); iter.hasNext();) {
MessageUtil.info(status, Spec.SEEK_MESSAGE_PREFIX + iter.next());
}
}
}
ICommand compiler = ReflectionFactory.makeCommand(setupResult.compilerName, status);
DirChanges dirChanges = null;
if (null == compiler) {
MessageUtil.fail(status, "unable to make compiler " + setupResult.compilerName);
return false;
} else {
if (status.aborted()) {
MessageUtil.debug(status, "aborted, but compiler valid?: " + compiler);
} else {
// same DirChanges handling for JavaRun, CompilerRun, IncCompilerRun
// XXX around advice or template method/class
if (!LangUtil.isEmpty(spec.dirChanges)) {
LangUtil.throwIaxIfFalse(1 == spec.dirChanges.size(), "expecting 0..1 dirChanges");
dirChanges = new DirChanges((DirChanges.Spec) spec.dirChanges.get(0));
if (!dirChanges.start(status, sandbox.classesDir)) {
return false; // setup failed
}
}
MessageUtil.info(status, compiler + "(" + argList + ")");
sandbox.setCommand(compiler, this);
String[] args = (String[]) argList.toArray(RA_String);
commandResult = compiler.runCommand(args, handler);
}
}
if (!setupResult.ignoreWarningsSet) {
handlerResult = handler.passed();
} else {
handlerResult = handler.passed(setupResult.ignoreWarnings);
}
if (!handlerResult) {
return false;
} else {
result = (commandResult == handler.expectingCommandTrue());
if (! result) {
String m = commandResult
? "compile did not fail as expected"
: "compile failed unexpectedly";
MessageUtil.fail(status, m);
} else if (null != dirChanges) {
result = dirChanges.end(status, sandbox.testBaseDir);
}
}
return result;
} finally {
if (!handlerResult) { // more debugging context in case of failure
MessageUtil.info(handler, spec.toLongString());
MessageUtil.info(handler, "" + argList);
if (null != setupResult) {
MessageUtil.info(handler, "" + setupResult);
}
}
handler.report(status); // XXX weak - actual messages not reported in real-time, no fast-fail
}
}

public String toString() {
return "CompilerRun(" + spec + ")";
}
// String[] sourcePaths = (null == this.sourcePaths ? new String[0] : this.sourcePaths);
// List sources = (null == this.sources ? Collections.EMPTY_LIST : this.sources);
// return "CompilerRun-" + compilerName
// + "(" + Arrays.asList(sourcePaths)
// + ", " + sources
// + ", " + Arrays.asList(globalOptions)
// + ", " + Arrays.asList(localOptions)
// + ")";
/**
* Initializer/factory for CompilerRun
* any path or file is relative to this test base dir
*/
public static class Spec extends AbstractRunSpec {
public static final String XMLNAME = "compile";
static final String SEEK_PREFIX = "-seek:";
static final String SEEK_MESSAGE_PREFIX = "found: ";
/** no support in the harness for these otherwise-valid options */
private static final String[] INVALID_OPTIONS = new String[]
{ "-workingdir", "-argfile", "-sourceroot", "-outjar"};
// when updating these, update tests/harness/selectionTest.xml

/** no support in the eclipse-based compiler for these otherwise-valid options */
private static final String[] INVALID_ECLIPSE_OPTIONS = new String[]
{ "-lenient", "-strict", "-usejavac", "-preprocess",
"-XOcodeSize", "-XSerializable", "-XaddSafePrefix",
"-XserializableAspects", "-XtargetNearSource" };

/** options supported by the harness */
private static final String[] VALID_OPTIONS = new String[]
{
SEEK_PREFIX,
// eajc does not support -usejavac, -preprocess
// testFlag() handles -ajc, -eclipse, -ignoreWarnings
"-usejavac", "-preprocess",
"-Xlint", "-lenient", "-strict",
"-source14", "-verbose", "-emacssym",
"-ajc", "-eclipse", "-ignoreWarnings",
"!usejavac", "!preprocess",
"!Xlint", "!lenient", "!strict",
"!source14", "!verbose", "!emacssym",
"!ajc", "!eclipse",

"^usejavac", "^preprocess",
"^Xlint", "^lenient", "^strict",
"^source14", "^verbose", "^emacssym",
"^ajc", "^eclipse"
};
public static final String DEFAULT_COMPILER
= ReflectionFactory.ECLIPSE;
// = ReflectionFactory.OLD_AJC;
/**
* Retitle description to title, paths to files, do comment
* do dirChanges, and print no chidren.
*/
private static final XMLNames NAMES = new XMLNames(XMLNames.DEFAULT,
"title", null, null, null, "files", null, false, false, true);
protected String compiler;
protected TestSetup testSetup;
protected String[] argfiles = new String[0];
protected String[] aspectpath = new String[0];
/** src path = {suiteParentDir}/{testBaseDirOffset}/{testSrcDirOffset}/{path} */
protected String testSrcDirOffset;
public Spec() {
super(XMLNAME);
setXMLNames(NAMES);
compiler = DEFAULT_COMPILER;
}
public void setCompiler(String compilerName) {
this.compiler = compilerName;
}

public void setTestSrcDirOffset(String s) {
if (null != s) {
testSrcDirOffset = s;
}
}
/** override to set dirToken to Sandbox.CLASSES and default suffix to ".class" */
public void addDirChanges(DirChanges.Spec spec) {
if (null == spec) {
return;
}
spec.setDirToken(Sandbox.CLASSES_DIR);
spec.setDefaultSuffix(".class");
super.addDirChanges(spec);
}
protected String getPrintName() {
return "CompilerRun.Spec " + getShortCompilerName();
}
public String toLongString() {
return getPrintName() + "(" + super.containedSummary() + ")";
}

public String toString() {
return getPrintName() + "(" + super.containedSummary() + ")";
}

/** bean mapping for writers */
public void setFiles(String paths) {
addPaths(paths);
}
/**
* Set aspectpath, deleting any old ones
* @param files comma-delimited list of argfiles - ignored if null or empty
*/
public void setAspectpath(String files) {
if (!LangUtil.isEmpty(files)) {
aspectpath = XMLWriter.unflattenList(files);
}
}

/**
* Set argfiles, deleting any old ones
* @param files comma-delimited list of argfiles - ignored if null or empty
*/
public void setArgfiles(String files) {
if (!LangUtil.isEmpty(files)) {
argfiles = XMLWriter.unflattenList(files);
}
}

/** @return String[] copy of argfiles array */
public String[] getArgfilesArray() {
String[] argfiles = this.argfiles;
if (LangUtil.isEmpty(argfiles)) {
return new String[0];
}
return (String[]) LangUtil.copy(argfiles);
}
/**
* This implementation skips if:
* <ul>
* <li>incremental test, but using ajc (not eclipse)</li>
* <li>usejavac, but javac is not available on the classpath</li>
* <li>eclipse, but -usejavac or -preprocess test</li>
* <li>-source14, but running under 1.2 (XXX design)</li>
* <li>local/global option conflicts (-lenient/-strict)</li>
* <li>semantic conflicts (e.g., -lenient/-strict)</li>
* </ul>
* @return false if this wants to be skipped, true otherwise
*/
protected boolean doAdoptParentValues(RT parentRuntime, IMessageHandler handler) {
if (!super.doAdoptParentValues(parentRuntime, handler)) {
return false;
}
testSetup = setupArgs(handler);
if (!testSetup.result) {
skipMessage(handler, testSetup.failureReason);
}
return testSetup.result;
}

private String getShortCompilerName() {
String cname = compiler;
if (null != testSetup) {
cname = testSetup.compilerName;
}
if (null != cname) {
int loc = cname.lastIndexOf(".");
if (-1 != loc) {
cname = cname.substring(loc+1);
}
}
return cname;
}
/** @return a CompilerRun with this as spec if setup completes successfully. */
public IRunIterator makeRunIterator(Sandbox sandbox, Validator validator) {
CompilerRun run = new CompilerRun(this);
if (run.setupAjcRun(sandbox, validator)) {
// XXX need name for compilerRun
return new WrappedRunIterator(this, run);
}
return null;
}
/**
* Each non-incremental run, fold the global flags in with
* the run flags, which may involve adding or removing from
* either list, depending on the flag prefix:
* <ul>
* <li>-foo: use -foo unless forced off.<li>
* <li>^foo: (force off) remove any -foo option from the run flags</li>
* <li>!foo: (force on) require the -foo flag </li>
* </ul>
* If there is a force conflict, then the test is skipped
* ("skipping" info message, TestSetup.result is false).
* This means an option local to the test which was specified
* without forcing may be overridden by a globally-forced option.
* <p>
* There are some flags which are interpreted by the test
* and removed from the list of flags passed to the command
* (see testFlag()):
* <ul>
* <li>eclipse: use the new eclipse compiler (can force)</li>
* <li>ajc: use the old ajc compiler (can force)</li>
* <li>ignoreWarnings: ignore warnings in result evaluations (no force)</li>
* </ul>
* <p>
* There are some flags which are inconsistent with each other.
* These are treated as conflicts and the test is skipped:
* <ul>
* <li>lenient, strict</li>
* </ul>
* <p>
* The -source 1.4 flag should always be specified as -source14,
* as this will otherwise fail to process it correctly.
* This converts it back to -source 1.4.
* <p>
* Finally, compiler limitations are enforced here by skipping
* tests which the compiler cannot do:
* <ul>
* <li>eclipse does not do -lenient, -strict, -usejavac, -preprocess,
* -XOcodeSize, -XSerializable, XaddSafePrefix,
* -XserializableAspects,-XtargetNearSource</li>
* <li>ajc does not run in incremental (staging) mode,
* nor with -usejavac if javac is not on the classpath</li>
* </ul>
* <u>Errors</u>:This will remove an arg not prefixed by [-|!|^] after
* providing an info message.
* <u>TestSetup Result</u>:
* If this completes successfully, then TestSetup.result is true,
* and commandOptions is not null, and any test flags (ignore warning,
* compiler) are reflected in the TestSetup.
* If this fails, then TestSetup.result is false,
* and a TestSetup.failreason is set.
* @return TestSetup with results
* (TestSetup result=false if the run should not continue)
*/
protected TestSetup setupArgs(IMessageHandler handler) {
// warning: HarnessSelectionTest checks for specific error wording
ArrayList argList = new ArrayList();
argList.addAll(getOptionsList());
final Spec spec = this;
TestSetup result = new TestSetup();
if (argList.contains("-source")) {
result.failureReason = "use -source14 for -source 1.4: " + argList;
return result;
}
result.compilerName = spec.compiler;
String[] globalOptions = spec.runtime.extractOptions(Spec.VALID_OPTIONS, true);
if ((null != globalOptions) && (globalOptions.length > 0)) {
// --- fold in globals, removing conflicts, etc.
for (int i = 0; i < globalOptions.length; i++) {
String globalArg = globalOptions[i];
if ((null == globalArg) || (2 > globalArg.length())) {
continue;
} else if (globalArg.startsWith(SEEK_PREFIX)) {
result.seek = globalArg.substring(SEEK_PREFIX.length());
continue;
} else if ("-source".equals(globalArg)) {
result.failureReason = "use -source14 for -source 1.4 [" + i + "]";
return result;
}
char first = globalArg.charAt(0);
globalArg = globalArg.substring(1);
boolean globalForceOn = (first == '!');
boolean globalForceOff = (first == '^');
boolean globalSet = (first == '-');
if (!globalSet && !globalForceOn && !globalForceOff) {
MessageUtil.info(handler, "ignoring bad global: " + globalOptions[i]);
continue;
}
int argIndex = indexOf(globalArg, argList);
if (-1 == argIndex) { // no apparent conflict - look for eclipse/ajc conflicts XXX unresolved
boolean ajcGlobal = true;
if ("ajc".equals(globalArg)) {
argIndex = indexOf("eclipse", argList);
} else if ("eclipse".equals(globalArg)) {
argIndex = indexOf("ajc", argList);
ajcGlobal = false;
}
if (-1 != argIndex) { // resolve eclipse/ajc conflict
String arg = ((String) argList.get(argIndex));
char argFirst = arg.charAt(0);
argList.remove(arg); // replace with resolved variant...
char ajcFirst;
char eclipseFirst;
if (ajcGlobal) {
ajcFirst = first;
eclipseFirst = argFirst;
} else {
ajcFirst = argFirst;
eclipseFirst = first;
}
if ('!' == eclipseFirst) {
if ('!' == ajcFirst) {
result.failureReason = "conflict between !eclipse and !ajc";
return result;
} else {
argList.add("-eclipse");
}
} else if (('!' == ajcFirst) || ('^' == eclipseFirst)) {
argList.add("-ajc");
} else if ('^' == ajcFirst) {
argList.add("-eclipse");
} else if (('-' != ajcFirst) || ('-' != eclipseFirst)) {
result.failureReason = "harness logic error resolving "
+ arg + " and global " + globalArg;
return result;
} else if (ajcGlobal) {
argList.add("-ajc");
} else {
argList.add("-eclipse");
}
continue; // resolved
}
}
if (-1 == argIndex) { // no dup, so no conflict
if (!globalForceOff) {
argList.add("-" + globalArg);
}
} else { // have conflict - resolve
String arg = (String) argList.get(argIndex);
first = arg.charAt(0);
boolean localForceOn = (first == '!');
boolean localForceOff = (first == '^');
boolean localSet = (first == '-');
if (!localSet && !localForceOn && !localForceOff) {
result.failureReason = "only handling [-^!]{arg}: " + arg;
return result;
}
if ((localForceOn && globalForceOff)
|| (localForceOff && globalForceOn)) {
result.failureReason = "force conflict between arg="
+ arg + " and global=" + globalOptions[i];
return result;
}
if (globalForceOn) {
if (localForceOn) { // localSet is already correct, localForceOff was conflict
argList.remove(arg); // no !funkiness
argList.add("-" + globalArg);
}
} else if (globalSet) {
if (localSet) {
// do nothing - already correct
} else if (localForceOn) {
argList.remove(arg); // no !funkiness
argList.add("-" + globalArg);
}
} else if (globalForceOff) {
argList.remove(arg);
} else {
throw new Error("illegal arg state?? : " + arg);
//MessageUtil.info(handler, "illegal arg state??: " + arg);
}
}
}
}
// remove funky prefixes from remainder, fixup two-part flags
// and interpret special flags
boolean source14 = false;
ArrayList toAdd = new ArrayList();
for (ListIterator iter = argList.listIterator(); iter.hasNext();) {
String arg = (String) iter.next();
if (testFlag(arg, result)) {
iter.remove();
continue;
}
char c = arg.charAt(0);
String rest = arg.substring(1);
if (c == '^') {
iter.remove();
continue;
}
if (c == '!') {
iter.remove();
if (!("source14".equals(rest))) {
toAdd.add("-" + rest);
} else {
source14 = true;
}
rest = null;
} else if ("source14".equals(rest)) {
iter.remove();
source14 = true;
}
}
if (source14) {
// must run under 1.4 VM or (if ajc) set up bootclasspath
if (!LangUtil.supportsJava("1.4")) {
if (ReflectionFactory.ECLIPSE.equals(result.compilerName)) {
result.failureReason
= "eclipse must run under 1.4 to implement -source 1.4";
return result;
}
if (!FileUtil.canReadFile(Globals.J2SE14_RTJAR)) {
result.failureReason
= "unable to get 1.4 libraries to implement -source 1.4";
return result;
}
toAdd.add("-bootclasspath");
toAdd.add(Globals.J2SE14_RTJAR.getAbsolutePath());
}
toAdd.add("-source");
toAdd.add("1.4");
}
argList.addAll(toAdd);
// finally, check for semantic conflicts
String[] badOptions = LangUtil.selectOptions(argList, Spec.INVALID_OPTIONS);
if (!LangUtil.isEmpty(badOptions)) {
result.failureReason = "no support for (normally-valid) options "
+ Arrays.asList(badOptions);
} else if (argList.contains("-lenient") && argList.contains("-strict")) {
result.failureReason = "semantic conflict -lenient | -strict";
} else if (ReflectionFactory.OLD_AJC.equals(result.compilerName)) {
if (spec.isStaging) {
result.failureReason = "OLD_AJC does not do incremental compiles";
} else if (argList.contains("-usejavac") && !haveJavac()) {
result.failureReason = "-usejavac but no javac on classpath";
} else {
result.result = true;
}
} else if (!ReflectionFactory.ECLIPSE.equals(result.compilerName)) {
result.failureReason = "unrecognized compiler: " + result.compilerName;
} else {
badOptions = LangUtil.selectOptions(argList, Spec.INVALID_ECLIPSE_OPTIONS);
if (!LangUtil.isEmpty(badOptions)) {
result.failureReason = "no support in eclipse-based compiler"
+ " for (normally-valid) options "+ Arrays.asList(badOptions);
} else {
result.result = true;
}
}
if (result.result) {
result.commandOptions = argList;
}
return result;
}

/** @return true if javac is available on the classpath */
private boolean haveJavac() { // XXX copy/paste from JavaCWrapper.java
Class compilerClass = null;
try {
compilerClass = Class.forName("com.sun.tools.javac.Main");
} catch (ClassNotFoundException ce1) {
try {
compilerClass = Class.forName("sun.tools.javac.Main");
} catch (ClassNotFoundException ce2) {
}
}
return (null != compilerClass);
}

/**
* Handle flags that are interpreted by the test rather than the
* underlying command. These flags are to be removed from the
* arg list.
* @return true if this is a flag to remove from the arg list
*/
protected boolean testFlag(String arg, TestSetup result) {
if ("-eclipse".equals(arg) || "!eclipse".equals(arg) || "^ajc".equals(arg)) {
result.compilerName = ReflectionFactory.ECLIPSE;
return true;
} else if ("-ajc".equals(arg) || "!ajc".equals(arg) || "^eclipse".equals(arg)) {
result.compilerName = ReflectionFactory.OLD_AJC;
return true;
} else if ("-ignoreWarnings".equals(arg)) {
result.ignoreWarnings = true;
result.ignoreWarningsSet = true;
return true;
}
return false;
}
// XXX need keys, cache...
/** @return index of global in argList, ignoring first char */
protected int indexOf(String global, ArrayList argList) {
int max = argList.size();
for (int i = 0; i < max; i++) {
if (global.equals(((String) argList.get(i)).substring(1))) {
return i;
}
}
return -1;
}
/**
* Write this out as a compile element as defined in
* AjcSpecXmlReader.DOCTYPE.
* @see AjcSpecXmlReader#DOCTYPE
* @see IXmlWritable#writeXml(XMLWriter)
*/
public void writeXml(XMLWriter out) {
// If our state is empty, we just delegate to super.
if (DEFAULT_COMPILER.equals(compiler)
&& LangUtil.isEmpty(testSrcDirOffset)
&& LangUtil.isEmpty(argfiles)) {
super.writeXml(out);
return;
}
String attr = "";
out.startElement(xmlElementName, false);
if (!DEFAULT_COMPILER.equals(compiler)) {
out.printAttribute("compiler", compiler);
}
if (!LangUtil.isEmpty(testSrcDirOffset)) {
out.printAttribute("dir", testSrcDirOffset);
}
if (!LangUtil.isEmpty(argfiles)) {
out.printAttribute("argfiles", out.flattenFiles(argfiles));
}
super.writeAttributes(out);
out.endAttributes();
if (!LangUtil.isEmpty(dirChanges)) {
DirChanges.Spec.writeXml(out, dirChanges);
}
List messages = getMessages();
if (!LangUtil.isEmpty(messages)) {
SoftMessage.writeXml(out, messages);
}
out.endElement(xmlElementName);
}
/**
* Encapsulate the directives that can be set using
* global arguments supplied in {@link Spec.getOptions()}.
* This supports changing the compiler and ignoring warnings.
*/
class TestSetup {
/** null unless overriding the compiler to be used */
String compilerName;
/**
* true if we should tell AjcMessageHandler whether
* to ignore warnings in its result evaluation
*/
boolean ignoreWarningsSet;
/** if telling AjcMessageHandler, what we tell it */
boolean ignoreWarnings;
/** false if setup failed */
boolean result;
/** if setup failed, this has the reason why */
String failureReason;
/** beyond running test, also seek text in sources */
String seek;
/** if setup completed, this has the combined global/local options */
ArrayList commandOptions;
public String toString() {
return "TestSetup("
+ (null == compilerName ? "" : compilerName + " ")
+ (!ignoreWarningsSet ? ""
: (ignoreWarnings ? "" : "do not ")
+ "ignore warnings ")
+ (result ? "" : "setup failed")
+ ")";
}
}
} // CompilerRun.Spec
} // CompilerRun
// /**
// * Write this out as a compile element as defined in
// * AjcSpecXmlReader.DOCTYPE.
// * @see AjcSpecXmlReader#DOCTYPE
// * @see IXmlWritable#writeXml(XMLWriter)
// */
// public void writeXml(XMLWriter out) {
// StringBuffer sb = new StringBuffer();
// Spec spec = this;
// final String elementName = "compile";
// List list = spec.getOptionsList();
// String args = XMLWriter.flattenList(spec.getOptionsList()).trim();
// String argsAttr = out.makeAttribute("options", args).trim();
// String files = XMLWriter.flattenFiles(spec.getPathsArray()).trim();
// String filesAttr = out.makeAttribute("files", files).trim();
// List messages = spec.getMessages();
// int nMessages = messages.size();
// int both = argsAttr.length() + filesAttr.length();
// final int MAX = 55;
//
// // tortured logic to make more readable XML...
// if ((both < MAX) || (0 == args.length())) {
// // if short enough, print entire or just start
// if (0 != args.length()) {
// filesAttr = argsAttr + " " + filesAttr;
// }
// if (0 == nMessages) {
// out.printElement(elementName, filesAttr);
// return;
// } else {
// out.startElement(elementName, filesAttr, true);
// }
// } else if (argsAttr.length() < filesAttr.length()) {
// out.startElement(elementName, argsAttr, false);
// out.printAttribute("files", files);
// out.endAttributes();
// } else {
// out.startElement(elementName, filesAttr, false);
// out.printAttribute("options", args);
// out.endAttributes();
// }
// if (0 < nMessages) {
// SoftMessage.writeXml(out, messages);
// }
// out.endElement(elementName);
// }


+ 522
- 0
testing/src/org/aspectj/testing/harness/bridge/DirChanges.java View File

@@ -0,0 +1,522 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.harness.bridge;

import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.testing.util.TestUtil;
import org.aspectj.testing.xml.IXmlWritable;
import org.aspectj.testing.xml.XMLWriter;
import org.aspectj.util.FileUtil;
import org.aspectj.util.LangUtil;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
* Calculate changes in a directory tree.
* Usage:
* <ul>
* <li>Set up with any expected changes and/or an expected directory</li>
* <li>Set up with any file checker</li>
* <li>start(..) before changes.
* This issues messages for any removed files not found,
* which represent an error in the expected changes.</li>
* <li>Do whatever operations will change the directory</li>
* <li>end(..).
* This issues messages for any files not removed, added, or updated,
* and, if any checker was set, any checker messages for matching
* added or updated files</li>
* </ul>
*/
public class DirChanges {
private static final boolean EXISTS = true;

final Spec spec;
/** start time, in milliseconds - valid only from start(..)..end(..) */
long startTime;
/** base directory of actual files - valid only from start(..)..end(..) */
File baseDir;
/** if set, this is run against any resulting existing files */
IFileChecker fileChecker;
/** handler valid from start..end of start(..) and end(..) methods */
IMessageHandler handler;
/**
* Constructor for DirChanges.
*/
public DirChanges(Spec spec) {
LangUtil.throwIaxIfNull(spec, "spec");
this.spec = spec;
}

/**
* Inspect the base dir, and issue any messages for
* removed files not present.
* @param baseDir File for a readable directory
* @return true if this started without sending messages
* for removed files not present.
*/
public boolean start(IMessageHandler handler, File baseDir) {
FileUtil.throwIaxUnlessCanReadDir(baseDir, "baseDir");
final IMessageHandler oldHandler = this.handler;
this.handler = handler;
this.baseDir = baseDir;
startTime = 0l;
final boolean doCompare = false;
boolean result
= exists("at start, did not expect file to be added", !EXISTS, spec.added, doCompare);
result &= exists("at start, expected file to be unchanged", EXISTS, spec.unchanged, doCompare);
result &= exists("at start, expected file to be updated", EXISTS, spec.updated, doCompare);
result &= exists("at start, expected file to be removed", EXISTS, spec.removed, doCompare);
startTime = System.currentTimeMillis();
this.handler = oldHandler;
return result;
}

/**
* Inspect the base dir, issue any messages for
* files not added, files not updated, and files not removed,
* and compare expected/actual files added or updated.
* @throws IllegalStateException if called before start(..)
*/
public boolean end(IMessageHandler handler, File srcBaseDir) {
FileUtil.throwIaxUnlessCanReadDir(baseDir, "baseDir");
if (0l == startTime) {
throw new IllegalStateException("called before start");
}
final IMessageHandler oldHandler = this.handler;
this.handler = handler;
final boolean doCompare = (null != fileChecker);
final boolean fastFail = spec.fastFail;
boolean result
= exists("at end, expected file was not added", EXISTS, spec.added, doCompare);
if (result || !fastFail) {
result &= exists("at end, expected file was not unchanged", EXISTS, spec.unchanged, doCompare, false);
}
if (result || !fastFail) {
result &= exists("at end, expected file was not updated", EXISTS, spec.updated, doCompare);
}
if (result || !fastFail) {
result &= exists("at end, file exists, was not removed", !EXISTS, spec.removed, doCompare);
}
if (result || !fastFail) {
result &= compareDir(srcBaseDir);
}
// XXX validate that unchanged mod-time did not change

this.handler = oldHandler;
baseDir = null;
startTime = 0l;
return result;
}

/**
* Verify that all files in any specified expected directory
* have matching files in the base directory, putting any messages
* in the handler (only one if the spec indicates fast-fail).
* @param srcBaseDir the File for the base directory of the test sources
* (any expected dir is specified relative to this directory)
* @return true if the same, false otherwise
*/
private boolean compareDir(File srcBaseDir) {
if (null == spec.dirExpected) {
return true;
}
File expDir = new File(srcBaseDir, spec.dirExpected);
File actDir = baseDir;
return TestUtil.sameDirectoryContents(handler, expDir, actDir, spec.fastFail);
}


/** @param comp FileMessageComparer (if any) given matching messages to compare */
protected void setFileComparer(IFileChecker comp) {
this.fileChecker = comp;
}


/**
* Signal fail if any files do {not} exist or do {not} have last-mod-time after startTime
* @param handler the IMessageHandler sink for messages
* @param label the String infix for the fail message
* @param exists if true, then file must exist and be modified after start time;
* if false, then the file must not exist or must be modified before start time.
* @param pathList the List of path (without any Spec.defaultSuffix) of File
* in Spec.baseDir to find (or not, if !exists)
*/
protected boolean exists(
String label,
boolean exists,
List pathList,
boolean doCompare) {
boolean expectStartEarlier = true;
return exists(label, exists, pathList,doCompare, true);
}
protected boolean exists(
String label,
boolean exists,
List pathList,
boolean doCompare,
boolean expectStartEarlier) {
boolean result = true;
if (!LangUtil.isEmpty(pathList)) {
final File expDir = ((!doCompare || (null == spec.dirExpected))
? null
: new File(baseDir, spec.dirExpected));
for (Iterator iter = pathList.iterator(); iter.hasNext();) {
final String entry = (String) iter.next() ;
String path = entry ;
if (null != spec.defaultSuffix) {
if (".class".equals(spec.defaultSuffix)) {
path = path.replace('.', '/');
}
path = path + spec.defaultSuffix;
}
File actualFile = new File(baseDir, path);
if (exists != (actualFile.canRead() && actualFile.isFile()
&& (expectStartEarlier
? startTime <= actualFile.lastModified()
: startTime > actualFile.lastModified()
))) {
failMessage(handler, exists, label, path, actualFile);
if (result) {
result = false;
}
} else if (exists && doCompare && (null != fileChecker)) {
if (!fileChecker.checkFile(handler, path, actualFile) && result) {
result = false;
}
}
}
}
return result;
}

/**
* Generate fail message "{un}expected {label} file {path} in {baseDir}".
* @param handler the IMessageHandler sink
* @param label String message infix
* @param path the path to the file
*/
protected void failMessage(
IMessageHandler handler,
boolean exists,
String label,
String path,
File file) {
MessageUtil.fail(handler, label + " \"" + path + "\" in " + baseDir);
}


/** Check actual File found at a path, usually to diff expected/actual contents */
public static interface IFileChecker {
/**
* Check file found at path.
* Implementations should return false when adding fail (or worse)
* message to the handler, and true otherwise.
* @param handler IMessageHandler sink for messages, esp. fail.
* @param path String for normalized path portion of actualFile.getPath()
* @param actualFile File to file found
* @return false if check failed and messages added to handler
*/
boolean checkFile(IMessageHandler handler, String path, File actualFile);
}
// /**
// * Default FileChecker compares files literally, transforming any
// * with registered normalizers.
// */
// public static class FileChecker implements IFileChecker {
// final File baseExpectedDir;
// NormalizedCompareFiles fileComparer;
//
// public FileChecker(File baseExpectedDir) {
// this.baseExpectedDir = baseExpectedDir;
// fileComparer = new NormalizedCompareFiles();
// }
// public boolean checkFile(IMessageHandler handler, String path, File actualFile) {
// if (null == baseExpectedDir) {
// MessageUtil.error(handler, "null baseExpectedDir set on construction");
// } else if (!baseExpectedDir.canRead() || !baseExpectedDir.isDirectory()) {
// MessageUtil.error(handler, "bad baseExpectedDir: " + baseExpectedDir);
// } else {
// File expectedFile = new File(baseExpectedDir, path);
// if (!expectedFile.canRead()) {
// MessageUtil.fail(handler, "cannot read expected file: " + expectedFile);
// } else {
// return doCheckFile(handler, expectedFile, actualFile, path);
// }
// }
// return false;
// }
//
// protected boolean doCheckFile(
// IMessageHandler handler,
// File expectedFile,
// File actualFile,
// String path) {
// fileComparer.setHandler(handler);
// FileLine[] expected = fileComparer.diff();
// return false;
// }
// }
// /**
// * CompareFiles implementation that pre-processes input
// * to normalize it. Currently it reads all files except
// * .class files, which it disassembles first.
// */
// public static class NormalizedCompareFiles extends CompareFiles {
// private final static String[] NO_PATHS = new String[0];
// private static String normalPath(File file) { // XXX util
// if (null == file) {
// return "";
// }
// return file.getAbsolutePath().replace('\\', '/');
// }
//
// private String[] baseDirs;
// private IMessageHandler handler;
//
// public NormalizedCompareFiles() {
// }
//
// void init(IMessageHandler handler, File[] baseDirs) {
// this.handler = handler;
// if (null == baseDirs) {
// this.baseDirs = NO_PATHS;
// } else {
// this.baseDirs = new String[baseDirs.length];
// for (int i = 0; i < baseDirs.length; i++) {
// this.baseDirs[i] = normalPath(baseDirs[i]) + "/";
// }
// }
// }
//
// private String getClassName(File file) {
// String result = null;
// String path = normalPath(file);
// if (!path.endsWith(".class")) {
// MessageUtil.error(handler,
// "NormalizedCompareFiles expected "
// + file
// + " to end with .class");
// } else {
// path = path.substring(0, path.length()-6);
// for (int i = 0; i < baseDirs.length; i++) {
// if (path.startsWith(baseDirs[i])) {
// return path.substring(baseDirs[i].length()).replace('/', '.');
// }
// }
// MessageUtil.error(handler,
// "NormalizedCompareFiles expected "
// + file
// + " to start with one of "
// + LangUtil.arrayAsList(baseDirs));
// }
//
// return result;
// }
//
// /**
// * Read file as normalized lines, sending handler any messages
// * ERROR for input failures and FAIL for processing failures.
// * @return NOLINES on error or normalized lines from file otherwise
// */
// public FileLine[] getFileLines(File file) {
// FileLineator capture = new FileLineator();
// InputStream in = null;
// try {
// if (!file.getPath().endsWith(".class")) {
// in = new FileInputStream(file);
// FileUtil.copyStream(
// new BufferedReader(new InputStreamReader(in)),
// new PrintWriter(capture));
// } else {
// String name = getClassName(file);
// if (null == name) {
// return new FileLine[0];
// }
// String path = normalPath(file);
// path = path.substring(0, path.length()-name.length());
// // XXX sole dependency on bcweaver/bcel
// LazyClassGen.disassemble(path, name, capture);
// }
// } catch (IOException e) {
// MessageUtil.fail(handler,
// "NormalizedCompareFiles IOException reading " + file, e);
// return null;
// } finally {
// if (null != in) {
// try { in.close(); }
// catch (IOException e) {} // ignore
// }
// capture.flush();
// capture.close();
// }
// String missed = capture.getMissed();
// if (!LangUtil.isEmpty(missed)) {
// MessageUtil.fail(handler,
// "NormalizedCompareFiles missed input: "
// + missed);
// return null;
// } else {
// return capture.getFileLines();
// }
// }
//
//
// }
/**
* Specification for a set of File added, removed, or updated
* in a given directory, or for a directory base for a tree of expected files.
* If defaultSuffix is specified, entries may be added without it.
*/
public static class Spec implements IXmlWritable {
public static final String XMLNAME = "dir-changes";
String dirToken;
String defaultSuffix;
String dirExpected;
/** if true, ok to fail on first mis-match */
boolean fastFail;
final ArrayList added;
final ArrayList removed;
final ArrayList updated;
final ArrayList unchanged;
/**
* @param dirToken the symbol name of the base directory
* @param clipSuffix the String suffix, if any, to clip automatically
*/
public Spec() {
added = new ArrayList();
removed = new ArrayList();
updated = new ArrayList();
unchanged = new ArrayList();
}
/**
* @param dirToken the symbol name of the base directory (classes, run)
*/
public void setDirToken(String dirToken) {
this.dirToken = dirToken;
}
/**
* Set the directory containing the expected files.
* Currently this only is used to verify files that are expected
* and found after the process completes; it does not stand on its
* own as a specification of files.
* @param expectedDirRelativePath path relative to the test base
* of the directory containing expected results for the output dir.
*/
public void setExpDir(String expectedDirRelativePath) {
}
/**
* @param clipSuffix the String suffix, if any, to clip automatically
*/
public void setDefaultSuffix(String defaultSuffix) {
this.defaultSuffix = defaultSuffix;
}
public void setAdded(String items) {
XMLWriter.addFlattenedItems(added, items);
}
public void setRemoved(String items) {
XMLWriter.addFlattenedItems(removed, items);
}
public void setUpdated(String items) {
XMLWriter.addFlattenedItems(updated, items);
}
public void setUnchanged(String items) {
XMLWriter.addFlattenedItems(unchanged, items);
}
public void setFastfail(boolean fastFail) {
this.fastFail = fastFail; // XXX support in XML
}
/** @return true if some list was specified */
private boolean hasFileList() {
return (!LangUtil.isEmpty(added)
|| !LangUtil.isEmpty(removed)
|| !LangUtil.isEmpty(updated)
|| !LangUtil.isEmpty(unchanged)
);
}
/** this writes nothing if there are no added, removed, or changed */
public void writeXml(XMLWriter out) {
if (!hasFileList()) {
return;
}
String attr = "";
// XXX need to permit defaults here...
if (null != dirToken) {
attr = out.makeAttribute("dirToken", dirToken) + " ";
}
if (null != defaultSuffix) {
attr += out.makeAttribute("defaultSuffix", defaultSuffix);
}
out.startElement(XMLNAME, attr, false);
if (!LangUtil.isEmpty(added)) {
out.printAttribute("added", XMLWriter.flattenList(added));
}
if (!LangUtil.isEmpty(removed)) {
out.printAttribute("removed", XMLWriter.flattenList(removed));
}
if (!LangUtil.isEmpty(updated)) {
out.printAttribute("updated", XMLWriter.flattenList(updated));
}
if (!LangUtil.isEmpty(unchanged)) {
out.printAttribute("unchanged", XMLWriter.flattenList(unchanged));
}
out.endElement(XMLNAME);
}
/**
* Write list as elements to XMLWriter.
* @param out XMLWriter output sink
* @param dirChanges List of DirChanges.Spec to write
*/
public static void writeXml(XMLWriter out, List dirChanges) {
if (LangUtil.isEmpty(dirChanges)) {
return;
}
LangUtil.throwIaxIfNull(out, "out");
for (Iterator iter = dirChanges.iterator(); iter.hasNext();) {
DirChanges.Spec spec = (DirChanges.Spec) iter.next();
if (null == spec) {
continue;
}
spec.writeXml(out);
}
}

} // class Spec

}

+ 377
- 0
testing/src/org/aspectj/testing/harness/bridge/FlatSuiteReader.java View File

@@ -0,0 +1,377 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.harness.bridge;

import org.aspectj.bridge.AbortException;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.bridge.Message;
import org.aspectj.bridge.SourceLocation;
import org.aspectj.bridge.IMessage.Kind;
import org.aspectj.testing.util.BridgeUtil;
import org.aspectj.testing.util.ObjectChecker;
import org.aspectj.testing.util.SFileReader;
import org.aspectj.testing.util.StandardObjectChecker;
import org.aspectj.util.FileUtil;
import org.aspectj.util.LangUtil;
import org.aspectj.util.LineReader;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
* SFileReader.Maker implementation to read tests
* XXX supports iterative but not yet incremental compiles
*/
public class FlatSuiteReader implements SFileReader.Maker {
public static final String[] RA_String = new String[0];
public static final FlatSuiteReader ME = new FlatSuiteReader();
private static final SFileReader READER = new SFileReader(ME);

static boolean isNumber(String s) { // XXX costly
if ((null == s) || (0 == s.length())) {
return false;
}
try {
Integer.valueOf(s);
return true;
} catch (NumberFormatException e) {
return false;
}
}

/** if true, clean up records before returning from make */
public boolean clean;
private FlatSuiteReader() {
}

/**
* @see org.aspectj.testing.harness.bridge.SFileReader.Maker#getType()
*/
public Class getType() {
return AjcTest.Spec.class;
}

/**
* This constructs an AjcTest.Spec assuming we are at the start of a
* test definition in reader and taking the parent directory of
* the reader as the base directory for the test suite root.
* @return the next AjcTest in reader, or null
* @see org.aspectj.testing.harness.bridge.SFileReader.Maker#make(LineReader)
*/
public Object make(final LineReader reader)
throws AbortException, IOException {
final AjcTest.Spec result = new AjcTest.Spec();
boolean usingEclipse = false; // XXX
/** handle read errors by throwing AbortException with context info */
class R {
public String read(String context) throws IOException {
return read(context, true);
}
public String read(String context, boolean required)
throws IOException {
final boolean skipEmpties = false;
String result = reader.nextLine(skipEmpties);
if ((null != result) && (0 == result.length())) {
result = null;
}
if ((null == result) && required) {
String s = "expecting " + context + " at " + reader;
throw new AbortException(s);
}
return result;
}
}

final R r = new R();
//final String baseDir = reader.getFile().getParent();
String line;
String[] words;
boolean isRequired = true;

final int startLine = reader.getLineNumber() - 1;

// description first - get from last line read
// XXX permits exactly one blank line between test records?
result.description = reader.lastLine();
if (null == result.description) {
throw new AbortException("expecting description at " + reader);
}

// next line is baseDir {option..}
line = r.read("baseDir {option..}");
words = LangUtil.split(line);
if ((null == words) || (0 == words.length)) {
throw new AbortException(
"expecting dir {option..} at " + reader);
}
// XXX per-test (shared) root
//final File sourceRoot = new File(baseDir, words[0]);
result.setTestDirOffset(words[0]);

String[] compileOptions = new String[words.length - 1];
System.arraycopy(words, 1, compileOptions, 0, words.length - 1);

// next are 1..n source lines: source...
CompilerRun.Spec lastCompileSpec = null;
// save last source file as default for error/warning line
File lastFile = null; // XXX per-compiler-run errors
while (null != (line = r.read("source.."))) {
words = LangUtil.split(line);
if (0 == FileUtil.sourceSuffixLength(words[0])) { // XXX
break;
} else {
lastCompileSpec = new CompilerRun.Spec();
lastCompileSpec.testSrcDirOffset = null;
// srcs are in test base for old
lastCompileSpec.addOptions(compileOptions);
lastCompileSpec.addPaths(words);
lastFile = new File(words[words.length - 1]);
result.addChild(lastCompileSpec);
}
}
if (null == lastCompileSpec) {
throw new AbortException("expected sources at " + reader);
}

ArrayList exp = new ArrayList();
// !compile || noerrors || className {runOption..}
String first = words[0];
if ("!compile".equals(first)) {
//result.className = words[0];
//result.runOptions = new String[words.length-1];
//System.arraycopy(words, 0, result.runOptions, 0, words.length-1);
} else if ("noerrors".equals(first)) {
// className is null, but no errors expected
// so compile succeeds but run not attempted
//result.errors = Main.RA_ErrorLine;
// result.runOptions = Main.RA_String;
} else if (isNumber(first) || (-1 != first.indexOf(":"))) {
exp.addAll(makeMessages(IMessage.ERROR, words, 0, lastFile));
} else {
String[] args = new String[words.length - 1];
System.arraycopy(words, 0, args, 0, args.length);
JavaRun.Spec spec = new JavaRun.Spec();
spec.className = first;
spec.addOptions(args);
//XXXrun.runDir = sourceRoot;
result.addChild(spec);
}

// optional: warnings, eclipse.warnings, eclipse.errors
// XXX unable to specify error in eclipse but not ajc
boolean gotErrors = false;
while (null
!= (line =
r.read(
" errors, warnings, eclipse.warnings, eclipse.error",
false))) {
words = LangUtil.split(line);
first = words[0];
if ("eclipse.warnings:".equals(first)) {
if (usingEclipse) {
exp.addAll(
makeMessages(
IMessage.WARNING,
words,
0,
lastFile));
}
} else if ("eclipse.errors:".equals(first)) {
if (usingEclipse) {
exp.addAll(
makeMessages(IMessage.ERROR, words, 0, lastFile));
}
} else if ("warnings:".equals(first)) {
exp.addAll(
makeMessages(IMessage.WARNING, words, 0, lastFile));
} else if (gotErrors) {
exp.addAll(
makeMessages(IMessage.WARNING, words, 0, lastFile));
} else {
exp.addAll(
makeMessages(IMessage.ERROR, words, 0, lastFile));
gotErrors = true;
}
}
lastCompileSpec.addMessages(exp);

int endLine = reader.getLineNumber();
File sourceFile = reader.getFile();
ISourceLocation sl =
new SourceLocation(sourceFile, startLine, endLine, 0);
result.setSourceLocation(sl);

if (clean) {
cleanup(result, reader);
}
return result;
}
/** post-process result
* - use file name as keyword
* - clip / for dir offsets
* - extract purejava keyword variants
* - extract bugID
* - convert test options to force-options
* - detect illegal xml characters
*/
private void cleanup(AjcTest.Spec result, LineReader lineReader) {
LangUtil.throwIaxIfNull(result, "result");
LangUtil.throwIaxIfNull(lineReader, "lineReader");

File suiteFile = lineReader.getFile();
String name = suiteFile.getName();
if (!name.endsWith(".txt")) {
throw new Error("unexpected name: " + name);
}
result.addKeyword("from-" + name.substring(0,name.length()-4));

final String dir = result.testDirOffset;
if (dir.endsWith("/")) {
result.testDirOffset = dir.substring(0,dir.length()-1);
}

StringBuffer description = new StringBuffer(result.description);
if (strip(description, "PUREJAVA")) {
result.addKeyword("purejava");
}
if (strip(description, "PUREJAVE")) {
result.addKeyword("purejava");
}
if (strip(description, "[purejava]")) {
result.addKeyword("purejava");
}
String input = description.toString();
int loc = input.indexOf("PR#");
if (-1 != loc) {
String prefix = input.substring(0, loc).trim();
String pr = input.substring(loc+3, loc+6).trim();
String suffix = input.substring(loc+6).trim();
description.setLength(0);
description.append((prefix + " " + suffix).trim());
try {
result.setBugId(Integer.valueOf(pr).intValue());
} catch (NumberFormatException e) {
throw new Error("unable to convert " + pr + " for " + result
+ " at " + lineReader);
}
}
input = description.toString();
String error = null;
if (-1 != input.indexOf("&")) {
error = "char &";
} else if (-1 != input.indexOf("<")) {
error = "char <";
} else if (-1 != input.indexOf(">")) {
error = "char >";
} else if (-1 != input.indexOf("\"")) {
error = "char \"";
}
if (null != error) {
throw new Error(error + " in " + input + " at " + lineReader);
}
result.description = input;
ArrayList newOptions = new ArrayList();
ArrayList optionsCopy = result.getOptionsList();
for (Iterator iter = optionsCopy.iterator(); iter.hasNext();) {
String option = (String) iter.next();
if (option.startsWith("-")) {
newOptions.add("!" + option.substring(1));
} else {
throw new Error("non-flag option? " + option);
}
}
result.setOptionsArray((String[]) newOptions.toArray(new String[0]));
}
private boolean strip(StringBuffer sb, String infix) {
String input = sb.toString();
int loc = input.indexOf(infix);
if (-1 != loc) {
String prefix = input.substring(0, loc);
String suffix = input.substring(loc+infix.length());
input = (prefix.trim() + " " + suffix.trim()).trim();
sb.setLength(0);
sb.append(input);
return true;
}
return false;
}

/**
* Generate list of expected messages of this kind.
* @param kind any non-null kind, but s.b. IMessage.WARNING or ERROR
* @param words
* @param start index in words where to start
* @param lastFile default file for source location if the input does not specify
* @return List
*/
private List makeMessages(// XXX weak - also support expected exceptions, etc.
Kind kind, String[] words, int start, File lastFile) {
ArrayList result = new ArrayList();
for (int i = start; i < words.length; i++) {
ISourceLocation sl =
BridgeUtil.makeSourceLocation(words[i], lastFile);
if (null == sl) { // XXX signalling during make
// System.err.println(...);
//MessageUtil.debug(handler, "not a source location: " + words[i]);
} else {
String text =
(("" + sl.getLine()).equals(words[i]) ? "" : words[i]);
result.add(new Message(text, kind, null, sl));
}
}
return (0 == result.size() ? Collections.EMPTY_LIST : result);
}

/**
* Read suite spec from a flat .txt file.
* @throws AbortException on failure
* @return AjcTest.Suite.Spec with any AjcTest.Spec as children
*/
public AjcTest.Suite.Spec readSuite(File suiteFile) {
LangUtil.throwIaxIfNull(suiteFile, "suiteFile");
if (!suiteFile.isAbsolute()) {
suiteFile = suiteFile.getAbsoluteFile();
}
final AjcTest.Suite.Spec result = new AjcTest.Suite.Spec();
result.setSuiteDirFile(suiteFile.getParentFile());
ObjectChecker collector = new StandardObjectChecker(IRunSpec.class) {
public boolean doIsValid(Object o) {
result.addChild((IRunSpec) o);
return true;
}
};
boolean abortOnError = true;
try {
READER.readNodes(
suiteFile,
collector,
abortOnError,
System.err);
} catch (IOException e) {
IMessage m = Message.fail("reading " + suiteFile, e);
throw new AbortException(m);
}
return result;
}
}

+ 94
- 0
testing/src/org/aspectj/testing/harness/bridge/Globals.java View File

@@ -0,0 +1,94 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.harness.bridge;

import org.aspectj.util.FileUtil;
import org.aspectj.util.LangUtil;

import java.io.File;

/**
*/
public class Globals {
/** name/key of the System property to set to define library dir */
public static final String LIBDIR_NAME = "harness.libdir";

/** name/key of the System property to set to define J2SE_HOME dir */
public static final String J2SE14_RTJAR_NAME = "j2se14.rtjar";

/** assumed relative location of a library with required jars */
public static final String LIBDIR = getSystemProperty(LIBDIR_NAME, "../lib/test");

/** Path to J2SE_HOME */
public static final File J2SE14_RTJAR;

/** array of parameter types for main(String[]) */
public static final Class[] MAIN_PARM_TYPES = new Class[] {String[].class};
public static final String S_testingclient_jar = LIBDIR + "/testing-client.jar";
public static final String S_aspectjrt_jar = LIBDIR + "/aspectjrt.jar";
public static final String S_bridge_jar = LIBDIR + "/bridge.jar";
public static final File F_testingclient_jar = new File(S_testingclient_jar);
public static final File F_aspectjrt_jar = new File(S_aspectjrt_jar);
public static final File F_bridge_jar = new File(S_bridge_jar);
public static final boolean globalsValid;
static {
File j2seJar = null;
String path = getSystemProperty(J2SE14_RTJAR_NAME, "c:/home/apps/jdk14");
File j2seHome = new File(path);
if (j2seHome.exists() && j2seHome.isDirectory()) {
File rtjar = new File(j2seHome.getAbsolutePath() + "/jre/lib/rt.jar");
if (rtjar.canRead() && rtjar.isFile()) {
j2seJar = rtjar;
}
}
J2SE14_RTJAR = j2seJar;
globalsValid =
(FileUtil.canReadFile(F_testingclient_jar)
&& FileUtil.canReadFile(F_aspectjrt_jar)
&& FileUtil.canReadFile(F_bridge_jar)
&& FileUtil.canReadFile(J2SE14_RTJAR)
);
}

/**
*
* @return null if not found, or
* String with class path for compiler to load J2SE 1.4 classes from.
*/
public static String get14Bootclasspath() {
return null;
}

/**
* Get System property completely safely.
* @param propertyName the String name of the property to get
* @param defaultValue the String default value to return value is null or empty
* @return String value or defaultValue if not available.
*/
private static String getSystemProperty(
String propertyName,
String defaultValue) {
String result = defaultValue;
try {
String value = System.getProperty(propertyName);
if (!LangUtil.isEmpty(value)) {
result = value;
}
} catch (Throwable t) {}
return result;
}

}

+ 44
- 0
testing/src/org/aspectj/testing/harness/bridge/IAjcRun.java View File

@@ -0,0 +1,44 @@
/* *******************************************************************
* Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.harness.bridge;

import org.aspectj.testing.run.IRun;
import org.aspectj.testing.run.IRunStatus;
import org.aspectj.testing.xml.XMLWriter;

// XXX candidate to be subsumed in class/constructors, since inner spec does setup
// at the same time it constructs the run.
public interface IAjcRun extends IRun {
boolean setupAjcRun(Sandbox sandbox, Validator validator);
// XXX add for result eval? ArrayList getExpectedMessages();
/** this IAjcRun does nothing, returning true always */
public static final IAjcRun NULLRUN = new IAjcRun() {
public boolean setupAjcRun(Sandbox sandbox, Validator validator) {
return true;
}
public boolean run(IRunStatus status) {
if (!status.started()) {
status.start();
}
status.finish(IRunStatus.PASS);
return true;
}

public void writeXml(XMLWriter out) {
throw new UnsupportedOperationException("unimplemented");
}
public String toString() { return "IAjcRun.NULLRUN"; }
};
}

+ 24
- 0
testing/src/org/aspectj/testing/harness/bridge/IRunSpec.java View File

@@ -0,0 +1,24 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.harness.bridge;

import org.aspectj.testing.run.IRunIterator;
import org.aspectj.testing.xml.IXmlWritable;

/**
* A run spec can make a run iterator and write itself as XML.
*/
public interface IRunSpec extends IXmlWritable {
IRunIterator makeRunIterator(Sandbox sandbox, Validator validator);
}

+ 444
- 0
testing/src/org/aspectj/testing/harness/bridge/IncCompilerRun.java View File

@@ -0,0 +1,444 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.harness.bridge;

import org.aspectj.bridge.ICommand;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.testing.run.IRunIterator;
import org.aspectj.testing.run.IRunStatus;
import org.aspectj.testing.run.WrappedRunIterator;
import org.aspectj.testing.xml.SoftMessage;
import org.aspectj.testing.xml.XMLWriter;
import org.aspectj.util.FileUtil;
import org.aspectj.util.LangUtil;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
* An incremental compiler run takes an existing compiler commmand
* from the sandbox, updates the staging directory, and recompiles.
* The staging directory is updated by prefix/suffix rules applied
* to files found below Sandbox.testBaseSrcDir.
* Files with suffix .{tag}.java are owned by this run
* and are copied to the staging directory
* unless they are prefixed "delete.", in which case the
* corresponding file is deleted. Any "owned" file is passed to
* the compiler as the list of changed files.
* The files entry contains the expected files recompiled. XXX underinclusive
* XXX prefer messages for expected files?
* XXX later: also support specified paths, etc.
*/
public class IncCompilerRun implements IAjcRun {

final Spec spec; // nonfinal later to make re-runnable
Sandbox sandbox;
/**
* @param handler must not be null, but may be reused in the same thread
*/
public IncCompilerRun(Spec spec) {
LangUtil.throwIaxIfNull(spec, "spec");
this.spec = spec;
}

/**
* Initialize this from the sandbox, using compiler and changedFiles.
* @param sandbox the Sandbox setup for this test, including copying
* any changed files, etc.
* @see org.aspectj.testing.harness.bridge.AjcTest.IAjcRun#setup(File, File)
* @throws AbortException containing IOException or IllegalArgumentException
* if the staging operations fail
*/
public boolean setupAjcRun(Sandbox sandbox, Validator validator) {
LangUtil.throwIaxIfNull(validator, "validator");
if (!validator.nullcheck(sandbox, "sandbox")
|| !validator.nullcheck(spec, "spec")
|| !validator.nullcheck(spec.tag, "fileSuffix")) {
return false;
}
File srcDir = sandbox.getTestBaseSrcDir(this);
File destDir = sandbox.stagingDir;
if (!validator.canReadDir(srcDir, "testBaseSrcDir")
|| !validator.canReadDir(destDir, "stagingDir")) {
return false;
}

this.sandbox = sandbox;
return doStaging(validator);
}
/**
* Handle copying and deleting of files per tag.
* This returns false unless some file was copied or deleted successfully
* and there were no failures copying or deleting files.
* @return true if staging completed successfully
*/
boolean doStaging(final Validator validator) {
boolean result = false;
try {
//ArrayList changed = new ArrayList();
final String toSuffix = ".java";
final String fromSuffix = "." + spec.tag + toSuffix;
// copy our tagged generation of files to the staging directory,
// deleting any with ChangedFilesCollector.DELETE_SUFFIX
class intHolder {
int numCopies;
int numDeletes;
int numFails;
}
final intHolder holder = new intHolder();
FileFilter deleteOrCount = new FileFilter() {
final String clip = ".delete" + toSuffix;
/** do copy unless file should be deleted */
public boolean accept(File file) {
boolean doCopy = true;
String path = file.getAbsolutePath();
if (!path.endsWith(clip)) {
holder.numCopies++;
} else {
doCopy = false;
path = path.substring(0, path.length()-clip.length()) + toSuffix;
File toDelete = new File(path);
if (toDelete.delete()) {
holder.numDeletes++;
} else {
validator.fail("unable to delete file: " + path);
holder.numFails++;
}
}
return doCopy;
}

};
File srcDir = sandbox.getTestBaseSrcDir(this);
File destDir = sandbox.stagingDir;
FileUtil.copyDir(srcDir, destDir, fromSuffix, toSuffix, deleteOrCount);
if ((0 == holder.numCopies) && (0 == holder.numDeletes)) {
validator.fail("no files changed??");
} else {
result = (0 == holder.numFails);
}
} catch (NullPointerException npe) {
validator.fail("staging - input", npe);
} catch (IOException e) {
validator.fail("staging - operations", e);
}
return result;
}
/**
* @see org.aspectj.testing.run.IRun#run(IRunStatus)
*/
public boolean run(IRunStatus status) {

ICommand compiler = sandbox.getCommand(this);
if (null == compiler) {
MessageUtil.abort(status, "null compiler");
}
// // This is a list of expected classes (in File-normal form
// // relative to base class/src dir, without .class suffix
// // -- like "org/aspectj/tools/ajc/Main")
// // A preliminary list is generated in doStaging.
// ArrayList expectedClasses = doStaging(status);
// if (null == expectedClasses) {
// return false;
// }
//
// // now add any (additional) expected-class entries listed in the spec
// // normalize to a similar file path (and do info messages for redundancies).
//
// List alsoChanged = spec.getPathsAsFile(sandbox.stagingDir);
// for (Iterator iter = alsoChanged.iterator(); iter.hasNext();) {
// File f = (File) iter.next();
//
// if (expectedClasses.contains(f)) {
// // XXX remove old comment changed.contains() works b/c getPathsAsFile producing both File
// // normalizes the paths, and File.equals(..) compares these lexically
// String s = "specification of changed file redundant with tagged file: ";
// MessageUtil.info(status, s + f);
// } else {
// expectedClasses.add(f);
// }
// }
//
// // now can create handler, use it for reporting
// List errors = spec.getMessages(IMessage.ERROR);
// List warnings = spec.getMessages(IMessage.WARNING);
// AjcMessageHandler handler = new AjcMessageHandler(errors, warnings, expectedClasses);
// same DirChanges handling for JavaRun, CompilerRun, IncCompilerRun
// XXX around advice or template method/class
DirChanges dirChanges = null;
if (!LangUtil.isEmpty(spec.dirChanges)) {
LangUtil.throwIaxIfFalse(1 == spec.dirChanges.size(), "expecting only 1 dirChanges");
dirChanges = new DirChanges((DirChanges.Spec) spec.dirChanges.get(0));
if (!dirChanges.start(status, sandbox.classesDir)) {
return false; // setup failed
}
}
List errors = spec.getMessages(IMessage.ERROR);
List warnings = spec.getMessages(IMessage.WARNING);
List expectRecompiled = Collections.EMPTY_LIST;
AjcMessageHandler handler = new AjcMessageHandler(errors, warnings, expectRecompiled);
boolean handlerResult = false;
boolean commandResult = false;
boolean result = false;
boolean report = false;
try {
handler.init();
final long startTime = System.currentTimeMillis();
commandResult = compiler.repeatCommand(handler);
// XXX disabled LangUtil.throwIaxIfNotAllAssignable(actualRecompiled, File.class, "recompiled");
report = true;
// handler does not verify sandbox...
handlerResult = handler.passed();
if (!handlerResult) {
result = false;
} else {
result = (commandResult == handler.expectingCommandTrue());
if (! result) {
String m = commandResult
? "incremental compile command did not return false as expected"
: "incremental compile command returned false unexpectedly";
MessageUtil.fail(status, m);
} else if (null != dirChanges) {
result = dirChanges.end(status, sandbox.testBaseDir);
}
}
} finally {
if (!result || spec.runtime.isVerbose()) { // more debugging context in case of failure
MessageUtil.info(handler, "spec: " + spec.toLongString());
MessageUtil.info(handler, "sandbox: " + sandbox);
String[] classes = FileUtil.listFiles(sandbox.classesDir);
MessageUtil.info(handler, "sandbox.classes: " + Arrays.asList(classes));
}
// XXX weak - actual messages not reported in real-time, no fast-fail
if (report) {
handler.report(status);
}
}
return result;
}

private boolean hasFile(ArrayList changed, File f) {
return changed.contains(f); // d
}


public String toString() {
return "" + spec;
// return "IncCompilerRun(" + spec + ")"; // XXX
}

// /** @see IXmlWritable#writeXml(XMLWriter) */
// public void writeXml(XMLWriter out) {
// String elementName = "inc-compile";
// String tagAttr = out.makeAttribute("tag", spec.tag);
// List messages = spec.getMessages();
// int nMessages = messages.size();
// if (0 == nMessages) {
// out.printElement(elementName, tagAttr);
// } else {
// out.startElement(elementName, tagAttr, true);
// SoftMessage.writeXml(out, messages);
// out.endElement(elementName);
// }
// }
//
/**
* initializer/factory for IncCompilerRun.
*/
public static class Spec extends AbstractRunSpec {
public static final String XMLNAME = "inc-compile";

protected ArrayList classesAdded;
protected ArrayList classesRemoved;
protected ArrayList classesUpdated;

/**
* skip description, skip sourceLocation,
* do keywords, skip options, do paths as classes, do comment,
* do dirChanges, do messages but skip children.
*/
private static final XMLNames NAMES = new XMLNames(XMLNames.DEFAULT,
"", "", null, "", "classes", null, false, false, true);
/** identifies files this run owns, so {name}.{tag}.java maps to {name}.java */
String tag;

public Spec() {
super(XMLNAME);
setStaging(true);
classesAdded = new ArrayList();
classesRemoved = new ArrayList();
classesUpdated = new ArrayList();
}
public void setTag(String input) {
tag = input;
}
public String toString() {
return "IncCompile.Spec(" + tag + ", " + super.toString() + ")";
}
/** override to set dirToken to Sandbox.CLASSES and default suffix to ".class" */
public void addDirChanges(DirChanges.Spec spec) { // XXX copy/paste of CompilerRun.Spec...
if (null == spec) {
return;
}
spec.setDirToken(Sandbox.CLASSES_DIR);
spec.setDefaultSuffix(".class");
super.addDirChanges(spec);
}
/** @return a IncCompilerRun with this as spec if setup completes successfully. */
public IRunIterator makeRunIterator(Sandbox sandbox, Validator validator) {
IncCompilerRun run = new IncCompilerRun(this);
if (run.setupAjcRun(sandbox, validator)) {
// XXX need name
return new WrappedRunIterator(this, run);
}
return null;
}
/**
* Write this out as a compile element as defined in
* AjcSpecXmlReader.DOCTYPE.
* @see AjcSpecXmlReader#DOCTYPE
* @see IXmlWritable#writeXml(XMLWriter)
*/
public void writeXml(XMLWriter out) {
String attr = out.makeAttribute("tag", tag);
out.startElement(xmlElementName, attr, false);
super.writeAttributes(out);
out.endAttributes();
if (!LangUtil.isEmpty(dirChanges)) {
DirChanges.Spec.writeXml(out, dirChanges);
}
List messages = getMessages();
if (0 < messages.size()) {
SoftMessage.writeXml(out, messages);
}
out.endElement(xmlElementName);
}
public void setClassesAdded(String items) {
addItems(classesAdded, items);
}
public void setClassesUpdated(String items) {
addItems(classesUpdated, items);
}
public void setClassesRemoved(String items) {
addItems(classesRemoved, items);
}
private void addItems(ArrayList list, String items) {
if (null != items) {
String[] classes = XMLWriter.unflattenList(items);
if (!LangUtil.isEmpty(classes)) {
for (int i = 0; i < classes.length; i++) {
if (!LangUtil.isEmpty(classes[i])) {
list.add(classes[i]);
}
}
}
}
}
} // class IncCompilerRun.Spec
}
// // XXX replaced with method-local class - revisit if useful
//
// /**
// * This class collects the list of all changed files and
// * deletes the corresponding file for those prefixed "delete."
// */
// static class ChangedFilesCollector implements FileFilter {
// static final String DELETE_SUFFIX = ".delete.java";
// static final String REPLACE_SUFFIX = ".java";
// final ArrayList changed;
// final Validator validator;
// /** need this to generate paths by clipping */
// final File destDir;
//
// /** @param changed the sink for all files changed (full paths) */
// public ChangedFilesCollector(ArrayList changed, File destDir, Validator validator) {
// LangUtil.throwIaxIfNull(validator, "ChangedFilesCollector - handler");
// this.changed = changed;
// this.validator = validator;
// this.destDir = destDir;
// }
//
// /**
// * This converts the input File to normal String path form
// * (without any source suffix) and adds it to the list changed.
// * If the name of the file is suffixed ".delete..", then
// * delete the corresponding file, and return false (no copy).
// * Return true otherwise (copy file).
// * @see java.io.FileFilter#accept(File)
// */
// public boolean accept(File file) {
// final String aname = file.getAbsolutePath();
// String name = file.getName();
// boolean doCopy = true;
// boolean failed = false;
// if (name.endsWith(DELETE_SUFFIX)) {
// name = name.substring(0,name.length()-DELETE_SUFFIX.length());
// file = file.getParentFile();
// file = new File(file, name + REPLACE_SUFFIX);
// if (!file.canWrite()) {
// validator.fail("file to delete is not writable: " + file);
// failed = true;
// } else if (!file.delete()) {
// validator.fail("unable to delete file: " + file);
// failed = true;
// }
// doCopy = false;
// }
// if (!failed && doCopy) {
// int clip = FileUtil.sourceSuffixLength(file);
// if (-1 != clip) {
// name.substring(0, name.length()-clip);
// }
// if (null != destDir) {
// String path = destDir.getPath();
// if (!LangUtil.isEmpty(path)) {
// // XXX incomplete
// if (name.startsWith(path)) {
// } else {
// int loc = name.lastIndexOf(path);
// if (-1 == loc) { // sigh
//
// } else {
//
// }
// }
// }
// }
// name = FileUtil.weakNormalize(name);
// changed.add(file);
// }
// return doCopy;
// }
// };


+ 298
- 0
testing/src/org/aspectj/testing/harness/bridge/JavaRun.java View File

@@ -0,0 +1,298 @@
/* *******************************************************************
* Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.harness.bridge;

import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.testing.Tester;
import org.aspectj.testing.run.IRunIterator;
import org.aspectj.testing.run.IRunStatus;
import org.aspectj.testing.run.WrappedRunIterator;
import org.aspectj.testing.util.TestClassLoader;
import org.aspectj.testing.xml.SoftMessage;
import org.aspectj.testing.xml.XMLWriter;
import org.aspectj.util.FileUtil;
import org.aspectj.util.LangUtil;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Arrays;
import java.util.List;

/**
* Run a class in this VM using reflection.
*/
public class JavaRun implements IAjcRun {
Spec spec;
private Sandbox sandbox;

/** programmatic initialization per spec */
public JavaRun(Spec spec) {
this.spec = spec;
}
// XXX init(Spec)
/**
* This checks the spec for a class name
* and checks the sandbox for a readable test source directory,
* a writable run dir, and (non-null, possibly-empty) lists
* of readable classpath dirs and jars.
* @return true if all checks pass
* @see org.aspectj.testing.harness.bridge.AjcTest.IAjcRun#setup(File, File)
*/
public boolean setupAjcRun(Sandbox sandbox, Validator validator) {
this.sandbox = sandbox;
return (validator.nullcheck(spec.className, "class name")
&& validator.nullcheck(sandbox, "sandbox")
&& validator.canReadDir(sandbox.getTestBaseSrcDir(this), "testBaseSrc dir")
&& validator.canWriteDir(sandbox.runDir, "run dir")
&& validator.canReadFiles(sandbox.getClasspathJars(true, this), "classpath jars")
&& validator.canReadDirs(sandbox.getClasspathDirectories(true, this), "classpath dirs")
);
}
/** caller must record any exceptions */
public boolean run(IRunStatus status)
throws IllegalAccessException,
InvocationTargetException,
ClassNotFoundException,
NoSuchMethodException {

boolean completedNormally = false;
Class targetClass = null;
if (!LangUtil.isEmpty(spec.dirChanges)) {
MessageUtil.info(status, "XXX dirChanges not implemented in JavaRun");
}
try {
final boolean readable = true;
File[] libs = sandbox.getClasspathJars(readable, this);
URL[] urls = FileUtil.getFileURLs(libs);
File[] dirs = sandbox.getClasspathDirectories(readable, this);
ClassLoader loader = new TestClassLoader(urls, dirs);
// make the following load test optional
// Class testAspect = loader.loadClass("org.aspectj.lang.JoinPoint");
targetClass = loader.loadClass(spec.className);
Method main = targetClass.getMethod("main", Globals.MAIN_PARM_TYPES);
setupTester(sandbox.getTestBaseSrcDir(this), loader, status);
main.invoke(null, new Object[] { spec.getOptionsArray() });
completedNormally = true;
} catch (ClassNotFoundException e) {
String[] classes = FileUtil.listFiles(sandbox.classesDir);
MessageUtil.info(status, "sandbox.classes: " + Arrays.asList(classes));
MessageUtil.fail(status, null, e);
} finally {
if (!completedNormally) {
MessageUtil.info(status, spec.toLongString());
MessageUtil.info(status, "targetClass: " + targetClass);
MessageUtil.info(status, "" + sandbox);
}
}
return completedNormally;
}

/**
* Clear (static) testing state and setup base directory,
* unless spec.skipTesting.
* @return null if successful, error message otherwise
*/
protected void setupTester(File baseDir, ClassLoader loader, IMessageHandler handler) {
if (null == loader) {
setupTester(baseDir, handler);
return;
}
File baseDirSet = null;
try {
if (!spec.skipTester) {
Class tc = loader.loadClass("org.aspectj.testing.Tester");
// Tester.clear();
Method m = tc.getMethod("clear", new Class[0]);
m.invoke(null, new Object[0]);
// Tester.setMessageHandler(handler);
m = tc.getMethod("setMessageHandler", new Class[] {IMessageHandler.class});
m.invoke(null, new Object[] { handler});
//Tester.setBASEDIR(baseDir);
m = tc.getMethod("setBASEDIR", new Class[] {File.class});
m.invoke(null, new Object[] { baseDir});

//baseDirSet = Tester.getBASEDIR();
m = tc.getMethod("getBASEDIR", new Class[0]);
baseDirSet = (File) m.invoke(null, new Object[0]);
if (!baseDirSet.equals(baseDir)) {
String l = "AjcScript.setupTester() setting "
+ baseDir + " returned " + baseDirSet;
MessageUtil.debug(handler, l);
}
}
} catch (Throwable t) {
MessageUtil.abort(handler, "baseDir=" + baseDir, t);
}
}

/**
* Clear (static) testing state and setup base directory,
* unless spec.skipTesting.
* This implementation assumes that Tester is defined for the
* same class loader as this class.
* @return null if successful, error message otherwise
*/
protected void setupTester(File baseDir, IMessageHandler handler) {
File baseDirSet = null;
try {
if (!spec.skipTester) {
Tester.clear();
Tester.setMessageHandler(handler);
Tester.setBASEDIR(baseDir);
baseDirSet = Tester.getBASEDIR();
if (!baseDirSet.equals(baseDir)) {
String l = "AjcScript.setupTester() setting "
+ baseDir + " returned " + baseDirSet;
MessageUtil.debug(handler, l);
}
}
} catch (Throwable t) {
MessageUtil.abort(handler, "baseDir=" + baseDir, t);
}
}
public String toString() {
return "JavaRun(" + spec + ")";
}
/**
* Initializer/factory for JavaRun.
* The classpath is not here but precalculated in the Sandbox. XXX libs?
*/
public static class Spec extends AbstractRunSpec {
public static final String XMLNAME = "run";
/**
* skip description, skip sourceLocation,
* do keywords, do options, skip paths, do comment,
* do dirChanges, do messages but skip children.
*/
private static final XMLNames NAMES = new XMLNames(XMLNames.DEFAULT,
"", "", null, null, "", null, false, false, true);
/** fully-qualified name of the class to run */
protected String className;
/** minimum required version of Java, if any */
protected String javaVersion;
/** if true, skip Tester setup (e.g., if Tester n/a) */
protected boolean skipTester;
public Spec() {
super(XMLNAME);
setXMLNames(NAMES);
}
/**
* @param version "1.1", "1.2", "1.3", "1.4"
* @throws IllegalArgumentException if version is not recognized
*/
public void setJavaVersion(String version) {
LangUtil.supportsJava(version);
this.javaVersion = version;
}
/** @className fully-qualified name of the class to run */
public void setClassName(String className) {
this.className = className;
}
/** @param skip if true, then do not set up Tester */
public void setSkipTester(boolean skip) {
skipTester = skip;
}
/** override to set dirToken to Sandbox.RUN_DIR */
public void addDirChanges(DirChanges.Spec spec) {
if (null == spec) {
return;
}
spec.setDirToken(Sandbox.RUN_DIR);
super.addDirChanges(spec);
}

/** @return a JavaRun with this as spec if setup completes successfully. */
public IRunIterator makeRunIterator(Sandbox sandbox, Validator validator) {
JavaRun run = new JavaRun(this);
if (run.setupAjcRun(sandbox, validator)) {
// XXX need name for JavaRun
return new WrappedRunIterator(this, run);
}
return null;
}

/**
* Write this out as a run element as defined in
* AjcSpecXmlReader.DOCTYPE.
* @see AjcSpecXmlReader#DOCTYPE
* @see IXmlWritable#writeXml(XMLWriter)
*/
public void writeXml(XMLWriter out) {
String attr = out.makeAttribute("class", className);
out.startElement(xmlElementName, attr, false);
if (skipTester) {
out.printAttribute("skipTester", "true");
}
if (null != javaVersion) {
out.printAttribute("vm", javaVersion);
}
super.writeAttributes(out);
out.endAttributes();
if (!LangUtil.isEmpty(dirChanges)) {
DirChanges.Spec.writeXml(out, dirChanges);
}
List messages = getMessages();
if (0 < messages.size()) {
SoftMessage.writeXml(out, messages);
}
out.endElement(xmlElementName);
}
public String toLongString() {
return toString() + "[" + super.toLongString() + "]";
}
public String toString() {
if (skipTester) {
return "JavaRun(" + className + ", skipTester)";
} else {
return "JavaRun(" + className + ")";
}
}
/**
* This implementation skips if:
* <ul>
* <li>current VM is not at least any specified javaVersion </li>
* </ul>
* @return false if this wants to be skipped, true otherwise
*/
protected boolean doAdoptParentValues(RT parentRuntime, IMessageHandler handler) {
if (!super.doAdoptParentValues(parentRuntime, handler)) {
return false;
}
if ((null != javaVersion) && (!LangUtil.supportsJava(javaVersion))) {
skipMessage(handler, "requires Java version " + javaVersion);
return false;
}
return true;
}
}
}

+ 256
- 0
testing/src/org/aspectj/testing/harness/bridge/RunSpecIterator.java View File

@@ -0,0 +1,256 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.harness.bridge;

import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.Message;
import org.aspectj.testing.run.IRun;
import org.aspectj.testing.run.IRunIterator;
import org.aspectj.testing.run.Runner;
import org.aspectj.testing.run.WrappedRunIterator;
import org.aspectj.util.LangUtil;

import java.util.ArrayList;


/**
* This wraps an AbstractRunSpec, which has children that
* return IRunIterator, the results of which we return
* from nextRun(..)
* We extract global options from the AbstractRunSpec options
* and set the global options in the AbstractRunSpec,
* which is responsible for setting them in any children
* during makeRun(..).
*/
public class RunSpecIterator implements IRunIterator {
/*
* When constructed, this gets its own spec
* and a sandbox to be used for making all children.
* In nextRun() this uses the spec's child specs
* and the sandbox to create the next child run iterator.
* This returns all the run provided by that child iterator
* before going to the next child.
*/
/** spec for this test */
public final AbstractRunSpec spec; // XXX reconsider public after debugging
/** current sandbox by default shared by all children */
Sandbox sandbox;
/** keep our copy to avoid recopying */
ArrayList childSpecs;
/** index into child specs of next run */
int specIndex;
/** child creation until the start of each run */
final Validator validator;
/** current child iterator */
IRunIterator childIterator;
final boolean haltOnFailure;

private int numIncomplete;

private final IMessage.Kind failureKind;

private boolean didCleanup;
/**
* Create a RunSpecIterator.
* Failure messages are of type IMessage.ABORT if abortOnFailure is true,
* or IMessage.ERROR otherwise.
* @param spec the AbstractRunSpec whose children we iterate - not null
* @param sandbox the default Sandbox to use for children to make runs - may be null
* @param haltOnFailure if true, stop after any failure in providing runs
*/
public RunSpecIterator(
AbstractRunSpec spec,
Sandbox sandbox,
Validator validator,
boolean haltOnFailure) {
this(spec, sandbox, validator, haltOnFailure,
(haltOnFailure ? IMessage.ABORT : IMessage.ERROR));
}
/**
* Create a RunSpecIterator, specifying any failure message kind.
* @param spec the AbstractRunSpec whose children we iterate - not null
* @param sandbox the default Sandbox to use for children to make runs - may be null
* @param haltOnFailure if true, stop after any failure in providing runs
* @param failureKind the IMessage.Kind for any failure messages - if null, no messages sent
*/
public RunSpecIterator(
AbstractRunSpec spec,
Sandbox sandbox,
Validator validator,
boolean haltOnFailure,
IMessage.Kind failureKind) {
LangUtil.throwIaxIfNull(spec, "spec");
LangUtil.throwIaxIfNull(sandbox, "sandbox");
LangUtil.throwIaxIfNull(validator, "validator");
this.sandbox = sandbox;
this.spec = spec;
this.validator = validator;
this.haltOnFailure = haltOnFailure;
this.failureKind = failureKind;
reset();
}
/**
* @return value set on construction for abortOnError
* @see org.aspectj.testing.run.IRunIterator#abortOnFailure()
*/
public boolean abortOnFailure() {
return haltOnFailure;
}

/** reset to start at the beginning of the child specs. */
public void reset() {
specIndex = 0;
childSpecs = spec.getWorkingChildren();
childIterator = null;
numIncomplete = 0;
}
/** @return int number of child run attempts that did not produce IRun */
public int getNumIncomplete() {
return numIncomplete;
}
/**
* @see org.aspectj.testing.run.IRunIterator#hasNextRun()
*/
public boolean hasNextRun() {
return ((specIndex < childSpecs.size())
|| ((null != childIterator)
&& childIterator.hasNextRun()));
}

/**
* Get the next child IRunIterator as an IRun.
* In case of failure to get the next child,
* numIncomplete is incremented, and
* a message of type failureKind is passed to the handler
* (if failureKind was defined in the contructor).
* @return next child IRunIterator wrapped as an IRun
*/
public IRun nextRun(final IMessageHandler handler, Runner runner) {
validator.pushHandler(handler);
try {
IRun result = null;
IRunSpec childSpec = null;
String error = null;
String specLabel = "getting run for child of \"" + spec + "\" ";
while ((null == result) && hasNextRun() && (null == error)) {
if (null == childIterator) {
childSpec = (IRunSpec) childSpecs.get(specIndex++);
if (null == childSpec) {
error = "unexpected - no more child specs at " + --specIndex;
} else {
Sandbox sandbox = makeSandbox(childSpec, validator);
if (null == sandbox) {
error = "unable to make sandbox for \"" + childSpec + "\"";
childIterator = null;
} else {
IRunIterator iter = childSpec.makeRunIterator(sandbox, validator);
if (null == iter) {
// client should read reason why from validator
error = "child \"" + childSpec + "\".makeRunIterator(..) returned null";
} else {
// hoist: result not wrapped but single IRun
if ((iter instanceof WrappedRunIterator)) {
if (!iter.hasNextRun()) {
error = "child \"" + childSpec + "\".hasNextRun()"
+ " is not true - should be exactly one run";
} else {
result = iter.nextRun(handler, runner);
if (null == result) {
error = "child \"" + childSpec + "\".nextRun()"
+ " returned null - should be exactly one run";
} else {
childIterator = null;
return result;
}
}
} else {
childIterator = iter;
}
}
}
}
}
if (null != childIterator) {
result = runner.wrap(childIterator, null);
childIterator = null;
} else if (null != error) {
numIncomplete++;
if (null != failureKind) {
handler.handleMessage(new Message(specLabel + error, failureKind, null, null));
}
if (!haltOnFailure) {
error = null;
} else if (result != null) {
result = null; // do not return result if halting due to failure
}
}
}
return result;
} finally {
validator.popHandler(handler);
}
}

/**
* @see org.aspectj.testing.run.IRunIterator#iterationCompleted()
*/
public void iterationCompleted() {
}
public String toString() {
return "" + spec;
//return "RunSpecIterator(" + specIndex + ", " + spec + ")" ;
}
/*
* Subclasses may:
* - set the sandbox on construction
* - lazily set it on first use
* - set it for each child
*/

/**
* Create the sandbox used for each child.
* This implementation always uses the sandbox set on construction.
* Subclasses may decide to create one sandbox per child iterator.
*/
protected Sandbox makeSandbox(IRunSpec child, Validator validator) {
return getSandbox();
}

/** Get the sandbox currently in use */
protected Sandbox getSandbox() {
return sandbox;
}

/** Set the sandbox currently in use */
protected void setSandbox(Sandbox sandbox) {
LangUtil.throwIaxIfNull(sandbox, "sandbox");
this.sandbox = sandbox;
}
}

+ 386
- 0
testing/src/org/aspectj/testing/harness/bridge/Sandbox.java View File

@@ -0,0 +1,386 @@
/* *******************************************************************
* Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.harness.bridge;

import org.aspectj.bridge.ICommand;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.testing.util.Diffs;
import org.aspectj.util.FileUtil;
import org.aspectj.util.LangUtil;

import java.io.File;
import java.util.ArrayList;

/**
* A sandbox holds state shared by AjcTest sub-runs,
* mostly directories relevant to testing.
* It permits a limited amount of coordination and
* setup/cleanup operations (todo XXX).
* <p>
* AjcTest creates the Sandbox and initializes the final fields.
* To coordinate with each other, run components may set and get values,
* with the sources running first and the sinks second.
* To make the interactions clear
* (and to avoid accidentally violating these semantics),
* setters/getters for a coordinated property are constrained two ways:
* <li>Both have an extra (typed) "caller" parameter which must not
* be null, authenticating that the caller is known & valid.</li>
* <li>A getter throws IllegalStateException if called before the setter</li>
* <li>A setter throws IllegalStateException if called after the getter<li>
* XXX subclass more general sandbox?
*/
public class Sandbox {
/** classes directory token for DirChanges.Spec */
public static final String RUN_DIR = "run";
/** run directory token for DirChanges.Spec */
public static final String CLASSES_DIR = "classes";
private static boolean canRead(File dir) {
return ((null != dir) && dir.isDirectory() && dir.canRead());
}

private static boolean canWrite(File dir) {
return ((null != dir) && dir.isDirectory() && dir.canWrite());
}

private static void iaxWrite(File dir, String label) {
if (!canWrite(dir)) {
throw new IllegalArgumentException(label + " - " + dir);
}
}

private static void iaxRead(File dir, String label) {
if (!canRead(dir)) {
throw new IllegalArgumentException(label + " - " + dir);
}
}
/** @throws IllegalStateException(message) if test */
private static void assertState(boolean test, String message) {
if (!test) {
throw new IllegalStateException(message);
}
}
/**
* The (read-only) base of the test sources (which may or may not
* be the base of the java sources)
*/
public final File testBaseDir;
/** the parent of a temporary workspace, probably includes some others */
public final File sandboxDir;

/** a shared working dir */
public final File workingDir;

/** a shared classes dir */
public final File classesDir;

/** a run dir (which will be ignored in non-forking runs) */
public final File runDir;
/** staging directory for IAjcRun requiring files be copied, deleted, etc. */
public final File stagingDir;
/**
* This manages creation and deletion of temporary directories.
* We hold a reference so that our clients can signal whether
* this should be deleted.
*/
private final Validator validator; // XXX required after completing tests?
/** original base of the original java sources, set by CompileRun.setup(..) */
private File testBaseSrcDir;

/** directories and libraries on the classpath, set by CompileRun.setup(..) */
private File[] classpath;

/** track whether classpath getter ran */
private boolean gotClasspath;
/** command shared between runs using sandbox - i.e., compiler */
private ICommand command;

/** track whether command getter ran */
private boolean gotCommand;
/** cache results of rendering final fields */
private transient String toStringLeader;
/** @throws IllegalArgumentException unless validator validates
* testBaseDir as readable
*/
public Sandbox(File testBaseDir, Validator validator) {
LangUtil.throwIaxIfNull(validator, "validator");
this.validator = validator;
Sandbox.iaxRead(testBaseDir, "testBaseDir");
this.testBaseDir = testBaseDir;
sandboxDir = FileUtil.getTempDir("Sandbox");
Sandbox.iaxWrite(sandboxDir, "sandboxDir"); // XXX not really iax

workingDir = FileUtil.makeNewChildDir(sandboxDir, "workingDir");
Sandbox.iaxWrite(workingDir, "workingDir");

classesDir = FileUtil.makeNewChildDir(sandboxDir, "classes");
Sandbox.iaxWrite(classesDir, "classesDir");

runDir = FileUtil.makeNewChildDir(sandboxDir, "run");
Sandbox.iaxWrite(runDir, "runDir");

stagingDir = FileUtil.makeNewChildDir(sandboxDir, "staging");
Sandbox.iaxWrite(stagingDir, "stagingDir");

validator.registerSandbox(this);
}

private String getToStringLeader() {
if (null == toStringLeader) {
toStringLeader = "Sandbox(" + sandboxDir.getName()
+ ", " + testBaseSrcDir.getName();
}
return toStringLeader;
}
/** @return "Sandbox(sandbox, src, classes)" with names only */
public String toString() {
return getToStringLeader() + ", " + classesDir.getName() + ")";
}
/** @return "Sandbox(sandbox, src, classes)" with paths */
public String toLongString() {
return getToStringLeader() + ", " + classesDir.getPath()
+ (null == command ? ", (null command)" : ", " + command) + ")";
}
void setCommand(ICommand command, CompilerRun caller) {
LangUtil.throwIaxIfNull(caller, "caller");
LangUtil.throwIaxIfNull(command, "command");
LangUtil.throwIaxIfFalse(!gotCommand, "no command");
this.command = command;
}
/** When test is completed, clear the compiler to avoid memory leaks */
void clearCommand(AjcTest caller) {
LangUtil.throwIaxIfNull(caller, "caller");
if (null != command) {
command = null;
}
}
// /**
// * Populate the staging directory by copying any files in the
// * source directory ending with fromSuffix
// * to the staging directory, after renaming them with toSuffix.
// * If the source file name starts with "delete", then the
// * corresponding file in the staging directory is deleting.
// * @return a String[] of the files copied or deleted
// * (path after suffix changes and relative to staging dir)
// * @throws Error if no File using fromSuffix are found
// */
// String[] populateStagingDir(String fromSuffix, String toSuffix, IAjcRun caller) {
// LangUtil.throwIaxIfNull(fromSuffix, "fromSuffix");
// LangUtil.throwIaxIfNull(toSuffix, "toSuffix");
// LangUtil.throwIaxIfNull(caller, "caller");
//
// ArrayList result = new ArrayList();
// FileUtil.copyDir(
// srcBase,
// targetSrc,
// fromSuffix,
// toSuffix,
// collector);
//
// final String canonicalFrom = srcBase.getCanonicalPath();
// final Definition[] defs = getDefinitions(srcBase);
// if ((null == defs) || (defs.length < 9)) {
// throw new Error("did not get definitions");
// }
// MessageHandler compilerMessages = new MessageHandler();
// StringBuffer commandLine = new StringBuffer();
// for (int i = 1; result && (i < 10); i++) {
// String fromSuffix = "." + i + "0.java";
// // copy files, collecting as we go...
// files.clear();
// if (0 == files.size()) { // XXX detect incomplete?
// break;
// }
//
//
// return (String[]) result.toArray(new String[0]);
// }
// XXX move to more general in FileUtil
void reportClassDiffs(
final IMessageHandler handler,
IncCompilerRun caller,
long classesDirStartTime,
String[] expectedSources) {
LangUtil.throwIaxIfFalse(0 < classesDirStartTime, "0 >= " + classesDirStartTime);
boolean acceptPrefixes = true;
Diffs diffs = org.aspectj.testing.util.FileUtil.dirDiffs(
"classes",
classesDir,
classesDirStartTime,
".class",
expectedSources,
acceptPrefixes);
diffs.report(handler, IMessage.ERROR);
}

// // XXX replace with IMessage-based implementation
// // XXX move to more general in FileUtil
// void reportClassesDirDiffs(final IMessageHandler handler, IncCompilerRun caller,
// String[] expectedSources) {
// // normalize sources to ignore
// final ArrayList sources = new ArrayList();
// if (!LangUtil.isEmpty(expectedSources)) {
// for (int i = 0; i < expectedSources.length; i++) {
// String srcPath = expectedSources[i];
// int clip = FileUtil.sourceSuffixLength(srcPath);
// if (0 != clip) {
// srcPath = srcPath.substring(0, srcPath.length() - clip);
// sources.add(FileUtil.weakNormalize(srcPath));
// } else if (srcPath.endsWith(".class")) {
// srcPath = srcPath.substring(0, srcPath.length() - 6);
// sources.add(FileUtil.weakNormalize(srcPath));
// } else {
// MessageUtil.info(handler, "not source file: " + srcPath);
// }
// }
// }
//
// // gather, normalize paths changed
// final ArrayList changed = new ArrayList();
// FileFilter touchedCollector = new FileFilter() {
// public boolean accept(File file) {
// if (file.lastModified() > classesDirTime) {
// String path = file.getPath();
// if (!path.endsWith(".class")) {
// MessageUtil.info(handler, "changed file not a class: " + file);
// } else {
// String classPath = path.substring(0, path.length() - 6);
// classPath = FileUtil.weakNormalize(classPath);
// if (sources.contains(classPath)) {
// sources.remove(classPath);
// } else {
// changed.add(classPath);
// }
// }
// }
// return false;
// }
// };
// classesDir.listFiles(touchedCollector);
//
// // report any unexpected changes
// Diffs diffs = new Diffs("classes", sources, changed, String.CASE_INSENSITIVE_ORDER);
// diffs.report(handler, IMessage.ERROR);
// }
ICommand getCommand(IncCompilerRun caller) {
LangUtil.throwIaxIfNull(caller, "caller");
assertState(null != command, "command never set");
return command;
}

File getTestBaseSrcDir(IncCompilerRun caller) {
LangUtil.throwIaxIfNull(caller, "caller");
return testBaseSrcDir;
}
File getTestBaseSrcDir(JavaRun caller) {
LangUtil.throwIaxIfNull(caller, "caller");
return testBaseSrcDir;
}
/** @throws IllegalArgumentException unless a readable directory */
void setTestBaseSrcDir(File dir, CompilerRun caller) {
LangUtil.throwIaxIfNull(caller, "caller");
if ((null == dir) || !dir.isDirectory() || !dir.canRead()) {
throw new IllegalArgumentException("bad test base src dir: " + dir);
}
testBaseSrcDir = dir;
}
/** @param readable if true, then throw IllegalArgumentException if not readable */
void setClasspath(File[] files, boolean readable, CompilerRun caller) {
LangUtil.throwIaxIfNull(files, "files");
LangUtil.throwIaxIfNull(caller, "caller");
assertState(!gotClasspath, "classpath already read");
classpath = new File[files.length];
for (int i = 0; i < files.length; i++) {
LangUtil.throwIaxIfNull(files[i], "files[i]");
if (readable && !files[i].canRead()) {
throw new IllegalArgumentException("bad classpath entry: " + files[i]);
}
classpath[i] = files[i];
}
}

File[] getClasspath(JavaRun caller) {
LangUtil.throwIaxIfNull(caller, "caller");
assertState(null != classpath, "classpath not set");
File[] result = new File[classpath.length];
System.arraycopy(classpath, 0, result, 0, result.length);
return result;
}
/** @param readable if true, omit non-readable directories */
File[] getClasspathDirectories(boolean readable, JavaRun caller) {
LangUtil.throwIaxIfNull(caller, "caller");
assertState(null != classpath, "classpath not set");
ArrayList result = new ArrayList();
File[] src = classpath;
for (int i = 0; i < src.length; i++) {
File f = src[i];
if ((null != f) && (f.isDirectory()) && (!readable || f.canRead())) {
result.add(f);
}
}
return (File[]) result.toArray(new File[0]);
}

/** @param readable if true, omit non-readable directories */
File[] getClasspathJars(boolean readable, JavaRun caller) {
LangUtil.throwIaxIfNull(caller, "caller");
assertState(null != classpath, "classpath not set");
ArrayList result = new ArrayList();
File[] src = classpath;
for (int i = 0; i < src.length; i++) {
File f = src[i];
if (FileUtil.hasZipSuffix(f) && (!readable || f.canRead())) {
result.add(f);
}
}
return (File[]) result.toArray(new File[0]);
}
/** @return String of classpath entries delimited internally by File.pathSeparator */
String classpathToString(CompilerRun caller) {
LangUtil.throwIaxIfNull(caller, "caller");
assertState(null != classpath, "classpath not set");
StringBuffer sb = new StringBuffer();
for (int i = 0; i < classpath.length; i++) {
if (i > 0) {
sb.append(File.pathSeparator);
}
sb.append(classpath[i].getAbsolutePath());
}
return sb.toString();
}
}

+ 557
- 0
testing/src/org/aspectj/testing/harness/bridge/Validator.java View File

@@ -0,0 +1,557 @@
/* *******************************************************************
* Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.harness.bridge;

import org.aspectj.bridge.AbortException;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.util.FileUtil;
import org.aspectj.util.LangUtil;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Stack;

/**
* Check input and implement defaults.
* This handles failure messaging and collecting temp directories
* for later cleanup.
* The default behavior is to send a fail message to the message
* handler. Clients may instead:
* <li>Toggle abortOnException to throw AbortException on failure</li>
* <li>push their own message handler to redirect messages
* (pop to remove if any pushed)</li>
* <li>subclass this to reimplement <code>fail(String)</code>
* <p>
* A component given this to do validation should
* not change the reporting scheme established by the caller,
* so the caller may lock and unlock the error handling policy.
* When the policy is locked, this silently ignores attempts
* to toggle exceptions, or delete temporary files.
* XXX callers cannot prevent others from pushing other error handlers.
*/
public class Validator {
/** stack of handlers */
private final Stack handlers;
/** list of File registered for deletion on demand */
private final ArrayList tempFiles; // deleteTempFiles requires ListIterator.remove()
/** list of Sandboxes registered for cleanup on demand */
private final ArrayList sandboxes;
/** if true, throw AbortException on failure */
boolean abortOnFailure;
/** this object prevents any changes to error-handling policy */
private Object locker;
public Validator(IMessageHandler handler) {
tempFiles = new ArrayList();
sandboxes = new ArrayList();
handlers = new Stack();
pushHandler(handler);
}
/**
* Push IMessageHandler onto stack,
* so it will be used until the next push or pop
* @param handler not null
*
*/
public void pushHandler(IMessageHandler handler) {
LangUtil.throwIaxIfNull(handler, "handler");
handlers.push(handler);
}
/** @throws IllegalStateException if handler is not on top */
public void popHandler(IMessageHandler handler) {
LangUtil.throwIaxIfNull(handler, "handler");
if (handler != handlers.peek()) {
throw new IllegalStateException("not current handler");
}
handlers.pop();
}
/** @return true if this requestor now has locked the error handling policy */
public boolean lock(Object requestor) {
if (null == locker) {
locker = requestor;
}
return (locker == requestor);
}

/** @return true if the error handling policy is now unlocked */
public boolean unlock(Object requestor) {
if (requestor == locker) {
locker = null;
}
return (locker == null);
}
public void setAbortOnFailure(boolean abortOnFailure) {
if (null == locker) {
if (this.abortOnFailure != abortOnFailure) {
this.abortOnFailure = abortOnFailure;
}
}
}
/**
* May fail with any of the messages
* <li>{null check} array<li>
* <li>{null check} {message}[#}<li>
*/
public boolean nullcheck(Object[] ra, String message) {
return ((nullcheck((Object) ra, message + " array"))
&& nullcheck(Arrays.asList(ra), message));
}
/**
* Like nullcheck(Collection, message), except adding lower and upper bound
* @param atLeast fail if list size is smaller than this
* @param atMost fail if list size is greater than this
*/
public boolean nullcheck(Collection list, int atLeast, int atMost, String message) {
if (nullcheck(list, message)) {
int size = list.size();
if (size < atLeast) {
fail(message + ": " + size + "<" + atLeast);
} else if (size > atMost) {
fail(message + ": " + size + ">" + atMost);
} else {
return true;
}
}
return false;
}
/**
* May fail with any of the messages
* <li>{null check} list<li>
* <li>{null check} {message}[#}<li>
*/
public boolean nullcheck(Collection list, String message) {
if (nullcheck((Object) list, message + " list")) {
int i = 0;
for (Iterator iter = list.iterator(); iter.hasNext();) {
if (!nullcheck(iter.next(), message + "[" + i++ + "]")) {
return false;
}
}
return true;
}
return false;
}
/**
* May fail with the message "null {message}"
* if o and the def{ault} are null
* @return o if not null or default otherwise
*/
public Object nulldefault(Object o, String message, Object def) {
if (null == o) {
o = def;
}
nullcheck(o, message);
return o;
}

/** may fail with the message "null {message}" */
public boolean nullcheck(Object o, String message) {
if (null == o) {
if (null == message) message = "object";
fail("null " + message);
return false;
}
return true;
}
/**
* Verify that all paths are readable relative to baseDir.
* may fail with the message "cannot read {file}"
*/
public boolean canRead(File baseDir, String[] paths, String message) {
if (!canRead(baseDir, "baseDir - " + message)
|| !nullcheck(paths, "paths - " + message)) {
return false;
}
final String dirPath = baseDir.getPath();
File[] files = FileUtil.getBaseDirFiles(baseDir, paths);
for (int j = 0; j < files.length; j++) {
if (!canRead(files[j], "{" + dirPath + "} " + files[j])) {
return false;
}
}
return true;
}
/**
* Verify that all paths are readable relative to baseDir.
* may fail with the message "cannot read {file}"
*/
public boolean canRead(File[] files, String message) {
if (!nullcheck(files, message)) {
return false;
}
for (int j = 0; j < files.length; j++) {
if (!canRead(files[j], files[j].getPath())) {
return false;
}
}
return true;
}

/** may fail with the message "cannot read {file}" */
public boolean canRead(File file, String message) {
if (nullcheck(file, message)) {
if (file.canRead()) {
return true;
} else {
fail("cannot read " + file);
}
}
return false;
}
/** may fail with the message "cannot write {file}" */
public boolean canWrite(File file, String message) {
if (nullcheck(file, message)) {
if (file.canRead()) {
return true;
} else {
fail("cannot write " + file);
}
}
return false;
}
/** may fail with the message "not a directory {file}" */
public boolean canReadDir(File file, String message) {
if (canRead(file, message)) {
if (file.isDirectory()) {
return true;
} else {
fail("not a directory " + file);
}
}
return false;
}
/** may fail with the message "not a directory {file}" */
public boolean canWriteDir(File file, String message) {
if (canWrite(file, message)) {
if (file.isDirectory()) {
return true;
} else {
fail("not a directory " + file);
}
}
return false;
}
/**
* May fail with any of the messages
* <li>{null check} dir array<li>
* <li>"#: not a File {file}"<li>
* <li>{canRead} {message}[#}<li>
*/
public boolean canReadFiles(Object[] dirs, String message) {
return ((nullcheck((Object) dirs, message + " dir array"))
&& canReadFiles(Arrays.asList(dirs), message));
}
/**
* May fail with any of the messages
* <li>{null check} files<li>
* <li>"#: not a File {file}"<li>
* <li>{canRead} {message}[#}<li>
*/
public boolean canReadFiles(Collection dirs, String message) {
if (nullcheck((Object) dirs, message + " files")) {
int i = 0;
for (Iterator iter = dirs.iterator(); iter.hasNext();) {
Object o = iter.next();
if (! (o instanceof File)) {
fail(i + ": not a file " + o);
}
if (!canRead((File) o, message + "[" + i++ + "]")) {
return false;
}
}
return true;
}
return false;
}
/**
* May fail with any of the messages
* <li>{null check} dir array<li>
* <li>"#: not a File {file}"<li>
* <li>{canReadDir} {message}[#}<li>
*/
public boolean canReadDirs(Object[] dirs, String message) {
return ((nullcheck((Object) dirs, message + " dir array"))
&& canReadDirs(Arrays.asList(dirs), message));
}
/**
* May fail with any of the messages
* <li>{null check} dirs<li>
* <li>"#: not a File {file}"<li>
* <li>{canReadDir} {message}[#}<li>
*/
public boolean canReadDirs(Collection dirs, String message) {
if (nullcheck((Object) dirs, message + " dirs")) {
int i = 0;
for (Iterator iter = dirs.iterator(); iter.hasNext();) {
Object o = iter.next();
if (! (o instanceof File)) {
fail(i + ": not a file " + o);
}
if (!canReadDir((File) o, message + "[" + i++ + "]")) {
return false;
}
}
return true;
}
return false;
}

/**
* May fail with any of the messages
* <li>{null check} dir array<li>
* <li>"#: not a File {file}"<li>
* <li>{canWrite} {message}[#}<li>
*/
public boolean canWriteFiles(Object[] dirs, String message) {
return ((nullcheck((Object) dirs, message + " dir array"))
&& canWriteFiles(Arrays.asList(dirs), message));
}
/**
* May fail with any of the messages
* <li>{null check} files<li>
* <li>"#: not a File {file}"<li>
* <li>{canWrite} {message}[#}<li>
*/
public boolean canWriteFiles(Collection dirs, String message) {
if (nullcheck((Object) dirs, message + " files")) {
int i = 0;
for (Iterator iter = dirs.iterator(); iter.hasNext();) {
Object o = iter.next();
if (! (o instanceof File)) {
fail(i + ": not a file " + o);
}
if (!canWrite((File) o, message + "[" + i++ + "]")) {
return false;
}
}
return true;
}
return false;
}
/**
* May fail with any of the messages
* <li>{null check} dir array<li>
* <li>"#: not a File {file}"<li>
* <li>{canWriteDir} {message}[#}<li>
*/
public boolean canWriteDirs(Object[] dirs, String message) {
return ((nullcheck((Object) dirs, message + " dir array"))
&& canWriteDirs(Arrays.asList(dirs), message));
}
/**
* May fail with any of the messages
* <li>{null check} dirs<li>
* <li>"#: not a File {file}"<li>
* <li>{canWriteDir} {message}[#}<li>
*/
public boolean canWriteDirs(Collection dirs, String message) {
if (nullcheck((Object) dirs, message + " dirs")) {
int i = 0;
for (Iterator iter = dirs.iterator(); iter.hasNext();) {
Object o = iter.next();
if (! (o instanceof File)) {
fail(i + ": not a file " + o);
}
if (!canWriteDir((File) o, message + "[" + i++ + "]")) {
return false;
}
}
return true;
}
return false;
}

/**
* Send an info message to any underlying handler
* @param message ignored if null
*/
public void info(String message) {
if (null != message) {
IMessageHandler handler = getHandler();
MessageUtil.info(handler, message);
}
}
/** Fail via message or AbortException */
public void fail(String message) {
fail(message, (Throwable) null);
}
/**
* Fail via message or AbortException.
* All failure messages go through here,
* so subclasses may override to control
* failure-handling.
*/
public void fail(String message, Throwable thrown) {
if ((null == message) && (null == thrown)) {
message = "<Validator:no message>";
}
IMessage m = MessageUtil.fail(message, thrown);
if (abortOnFailure) {
throw new AbortException(m);
} else {
IMessageHandler handler = getHandler();
handler.handleMessage(m);
}
}
/**
* Register a file temporary, i.e., to be
* deleted on completion. The file need not
* exist (yet or ever) and may be a duplicate
* of existing files registered.
*/
public void registerTempFile(File file) {
if (null != file) {
tempFiles.add(file);
}
}
/**
* Get a writable {possibly-empty} directory.
* If the input dir is null, then try to create a temporary
* directory using name.
* If the input dir is not null, this tries to
* create it if it does not exist.
* Then if name is not null,
* it tries to get a temporary directory
* under this using name.
* If name is null, "Validator" is used.
* If deleteContents is true, this will try to delete
* any existing contents. If this is unable to delete
* the contents of the input directory, this may return
* a new, empty temporary directory.
* If register is true, then any directory returned is
* saved for later deletion using <code>deleteTempDirs()</code>,
* including the input directory.
* When this is unable to create a result, if failMessage
* is not null then this will fail; otherwise it returns false;
*/
public File getWritableDir(File dir, String name,
boolean deleteContents, boolean register, String failMessage) {
// check dir
if (null == dir) {
if (null == name) {
name = "Validator";
}
dir = FileUtil.getTempDir(name);
} else {
if (!dir.exists()) {
dir.mkdirs();
}
}
// fail if necessary
if ((null == dir) || (!dir.exists())) {
if (null != failMessage) {
fail(failMessage + ": unable to get parent " + dir);
}
} else {
File result = FileUtil.makeNewChildDir(dir, name);
if (deleteContents) {
FileUtil.deleteContents(dir);
}
if (register) {
tempFiles.add(dir);
}
}
return dir;
}

/**
* Delete any temp sandboxes, files or directories saved,
* optionally reporting failures in the normal way.
* This will be ignored unless the failure policy
* is unlocked.
*/
public void deleteTempFiles(boolean reportFailures) {
if (null == locker) {
for (ListIterator iter = tempFiles.listIterator(); iter.hasNext();) {
deleteFile((File) iter.next(), reportFailures);
}
for (ListIterator iter = sandboxes.listIterator(); iter.hasNext();) {
Sandbox sandbox = (Sandbox) iter.next();
// XXX assumes all dirs are in sandboxDir
deleteFile(sandbox.sandboxDir, reportFailures);
}
}
}

/**
* Manage temp files and directories of registered sandboxes.
* Note that a sandbox may register before it is initialized,
* so this must do nothing other than save the reference.
* @param sandbox the uninitialized Sandbox to track
*/
public void registerSandbox(Sandbox sandbox) {
sandboxes.add(sandbox);
}
private void deleteFile(File file, boolean reportFailures) {
if (null == file) {
if (reportFailures) {
fail("unable to delete null file");
}
return;
}
FileUtil.deleteContents(file);
if (file.exists()) {
file.delete();
}
if (reportFailures && file.exists()) {
fail("unable to delete " + file);
}
}
/** @throws IllegalStateException if handler is null */
private IMessageHandler getHandler() {
IMessageHandler handler = (IMessageHandler) handlers.peek();
if (null == handler) {
throw new IllegalStateException("no handler");
}
return handler;
}

} // class Validator

+ 62
- 0
testing/src/org/aspectj/testing/run/IRun.java View File

@@ -0,0 +1,62 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


package org.aspectj.testing.run;

/**
* A run is a Runnable that may know how to set its own status.
* @author isberg
*/
public interface IRun {
public static final IRun[] RA_IRun = new IRun[0];

/** Positive wrapper for the status parameter */
public static final IRun OK
= new IRun() {
/** This returns false when the status is null
* or runResult is false */
public boolean run(IRunStatus status) {
return ((null != status) && status.runResult());
}
public IRunStatus makeStatus() { return null; }
};

/** Negative wrapper for the status parameter */
public static final IRun NOTOK
= new IRun() {
public boolean run(IRunStatus status) {
return ((null == status) || !status.runResult());
}
public IRunStatus makeStatus() { return null; }
};

/**
* Run process, setting any known status.
* Normally the caller starts the process
* and the callee completes it, so that
* status.isCompleted() returns true after
* the call completes. However, responsible
* callees ensure starting, and responsible
* callers ensure completed after the call.
* Anyone setting completion should ensure it
* is set recursively for all children,
* and anyone starting child runs should
* ensure children are registered and initialized
* appropriately.
* @param status the IRunStatus representing the
* outcome of the process (collecting parameter).
* @see Runners
*/
boolean run(IRunStatus status) throws Exception; // IMessageHandler?
}

+ 62
- 0
testing/src/org/aspectj/testing/run/IRunIterator.java View File

@@ -0,0 +1,62 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


package org.aspectj.testing.run;

import org.aspectj.bridge.IMessageHandler;

/**
* Iterator for IRun.
* IRunIterator are useful if the underlying components
* can use a generic IRunStatus and a single listener.
* It is a requirement of any component runnin the IRunIterator
* that they call iterationCompleted() when done, to permit
* the IRunIterator to clean up.
* @see Runner#runIterator(IRunIterator, IRunStatus, IRunListener)
*/
public interface IRunIterator {

/**
* @return true if nextRun() would return something non-null
* @throws IllegalStateException if called after
* <code>iterationCompleted()</code>
*/
boolean hasNextRun();
/**
* Get the next run.
* IRunIterator which contains child IRunIterator may either return
* the children IRun or wrap them using
* Runner.wrap(IRunIterator, IRunListener)
* @param handler the IMessageHandler to use for error and other messages
* @param runnere the Runner to use to wrap any nested IRunIterator as IRun.
* @return the next run, or null if there are no more.
* @throws IllegalStateException if called after
* <code>iterationCompleted()</code>
* @see Runner#wrap(IRunIterator, IRunListener)
*/
IRun nextRun(IMessageHandler handler, Runner runner);
/**
* Signal a runner that further runs should be aborted. Runners
* should check this after each failure.
* @return true if the runner should stop iterating when an IRun fails
* @throws IllegalStateException if called after
* <code>iterationCompleted()</code>
*/
boolean abortOnFailure(); // XXX supply IRun or IRunStatus?
/** called when hasNextRun() and nextRun() will no longer be called */
void iterationCompleted();
}

+ 38
- 0
testing/src/org/aspectj/testing/run/IRunListener.java View File

@@ -0,0 +1,38 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


package org.aspectj.testing.run;

/**
* Listen to events in the run lifecycle -
* birth, death, and procreation.
* @author isberg
*/
public interface IRunListener {

/**
* Called when run is about to be started.
*/
void runStarting(IRunStatus run);

/**
* Called when run is has completed.
*/
void runCompleted(IRunStatus run);

/**
* Called when adding a child to a parent run
*/
void addingChild(IRunStatus parent, IRunStatus child);
}

+ 175
- 0
testing/src/org/aspectj/testing/run/IRunStatus.java View File

@@ -0,0 +1,175 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


package org.aspectj.testing.run;

import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHolder;
import org.aspectj.bridge.MessageUtil;

/**
* Encapsulate status and results for a run.
* A run starts and then completes normally
* (finished(Object result)),
* abruptly (thrown(Throwable thrown)),
* or by user request (abort(Object request)).
* @author isberg
*/
public interface IRunStatus extends IMessageHolder {
/** clients use this when signalling completion without a specific result */
public static final Object VOID = Boolean.TRUE;

/** result object for successful (unset) boolean run result */
public static final Boolean PASS = Boolean.TRUE;
/** result object for failed (unset) boolean run result */
public static final Boolean FAIL = Boolean.FALSE;
/** clients use this when signalling abort without any specific request */
public static final Object ABORT = Boolean.FALSE;
/** clients use this when signalling abort because no object to run */
public static final Object ABORT_NORUN = MessageUtil.ABORT_NOTHING_TO_RUN;
/** returned from getChildren when there are no children */
public static final IRunStatus[] EMPTY_NEST = new IRunStatus[0];
//------------------- process controls
/**
* Set identifier associated with this run, if any
* @throws IllegalArgumentException if id is null
* @throws IllegalStateException if id has already been set
*/
void setIdentifier(Object id);

//------------------- process controls
/**
* Call before any start() or after isCompleted() would return true
* to reset this to its pre-start state
* @throws IllegalStateException if start() has been called
* and isCompleted() is not true.
*/
void reset();
/**
* Call only once to signal this run has started.
* @throws IllegalStateException if start() has been called
*/
void start();
/**
* Call this or thrown only once after start()
* to signal this run has ended.
* If this represents a void process, use VOID.
* @param result the Object returned by this run.
* @throws IllegalStateException if start() was not called first
* or if either completed(Object) or thrown(Throwable) have been called.
*/
void finish(Object result);
/**
* Call to signal this run is ending by request.
* If there is no message, use ABORT.
* @param request the Object request to abort,
* or ABORT if none is available.
* @throws IllegalStateException if start() was not called first
* or if either completed(Object) or thrown(Throwable) have been called.
*/
void abort(Object request);
/**
* Call this or completed only once after start()
* to signal this run has ended.
* @throws IllegalStateException if start() was not called first
* or if either completed(Object) or thrown(Throwable) have been called.
*/
void thrown(Throwable thrown);
/**
* Call this for the status to throw an unchecked exception
* of the type that its controller understands.
* It is an error for a IRunStatus to continue normally
* after this is invoked.
*/
void completeAbruptly();
//------------------- process messages
/**
* Call this any time to signal any messages.
* (In particular, the IRun caller may use this to register messages
* about the mishandling of the run by the ResultStatusI by the callee.)
* This is a shortcut for getMessageHandler().handleMessage(..);
*/
//boolean handleMessage(IMessage message);
//------------------- process display
/** @return true if this run has started */
boolean started();
/** @return true if one of the result, abort request, or thrown is available */
boolean isCompleted();

/** @return true if this got an abort request */
boolean aborted();
/**
* @return true if completed and not aborted and no thrown
* or messages with kind ABORT or FAIL or ERROR
*/
boolean runResult();

/** get the invoker for any subruns */
Runner getRunner();
/** @return the Object result, if any, of this run */
Object getResult();

/** @return the Object abort request, if any, of this run */
Object getAbortRequest();

/** @return the Throwable thrown, if any, by this run */
Throwable getThrown();
/** @return any Message[] signalled, or SILENCE if none */
IMessage[] getMessages();
/** @return the identifier set for this run, if any */
Object getIdentifier();
//------------------- subprocess
/**
* Add a record for a child run
* and install self as parent.
* @throws IllegalArgumentException if child is null
*/
void addChild(IRunStatus child);

/**
* Register this as the run parent.
* (Any run that does addChild(IRunStatus) should register as parent.)
* @throws IllegalArgumentException if parent is null
* @throws IllegalStateException if parent exists already
*/
void registerParent(IRunStatus parent);

/**
* @return the current children of this run, or EMPTY_NEST if none
*/
IRunStatus[] getChildren();

/**
* @return the currently-registered parent, or null if none
*/
IRunStatus getParent();
}

+ 28
- 0
testing/src/org/aspectj/testing/run/IRunValidator.java View File

@@ -0,0 +1,28 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


package org.aspectj.testing.run;

/**
* These check whether particular runs have passed.
* @author isberg
*/
public interface IRunValidator {
/**
* Evaluate whether a run has passed.
* @param run the IRunStatus to see if it passed.
* @return true if run has passed
*/
boolean runPassed(IRunStatus run);
}

+ 136
- 0
testing/src/org/aspectj/testing/run/RunIterator.java View File

@@ -0,0 +1,136 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.run;

import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.util.LangUtil;

import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;

/**
* Adapt IRun or Run[] or List or ListIterator to RunIteratorI.
*/
public class RunIterator implements IRunIterator {
protected String name;
protected ListIterator iter;
protected IRun run;
public RunIterator(String name, IRun run) {
init(name, run);
}

public RunIterator(String name, List list) {
init(name, list);
}
public RunIterator(String name, IRun[] runs) {
init(name, Arrays.asList(runs).listIterator());
}

public RunIterator(String name, ListIterator iterator) {
init(name, iterator);
}

public void init(String name, List list) {
init(name, list.listIterator());
}

public void init(String name, IRun[] runs) {
init(name, Arrays.asList(runs).listIterator());
}
/** @return true if the first IRun from nextRun can be the sole IRun */
public boolean isHoistable() {
return (null != run);
}
/**
* @param name if null, use iterator.toString();
* @param iterator not null
* @throws IllegalArgumentException if iterator is null
*/
public void init(String name, ListIterator iterator) {
LangUtil.throwIaxIfNull(iterator, "iterator");
iter = iterator;
name = (null != name? name : iterator.toString());
run = null;
}
/**
* @param name if null, use run();
* @param run not null
* @throws IllegalArgumentException if iterator is null
*/
public void init(String name, IRun run) {
LangUtil.throwIaxIfNull(run, "run");
this.run = run;
name = (null != name? name : run.toString());
iter = null;
}

/**
* @return false always
* @see org.aspectj.testing.run.IRunIterator#abortOnFailure()
*/
public boolean abortOnFailure() {
return false;
}

/**
* @see org.aspectj.testing.run.RunIteratorI#hasNextRun()
*/
public boolean hasNextRun() {
return ((null != run) || ((null != iter) && (iter.hasNext())));
}

/**
* @see org.aspectj.testing.run.IRunIterator#iterationCompleted()
*/
public void iterationCompleted() {
}

/**
* @see org.aspectj.testing.run.RunIteratorI#nextRun(IMessageHandler, Runner)
*/
public IRun nextRun(IMessageHandler handler, Runner runner) {
if (null != run) {
IRun result = run;
run = null;
return result;
}
if (null != iter) {
for (Object o = iter.next(); iter.hasNext();) {
if (o instanceof IRunIterator) {
return runner.wrap((IRunIterator) o, null);
} else if (o instanceof IRun) {
return (IRun) o;
} else {
MessageUtil.error(handler, "not IRun or IRunIterator: " + o);
}
}
}
return null;
}

/** @return name */
public String toString() {
return name;
}
}

+ 116
- 0
testing/src/org/aspectj/testing/run/RunListener.java View File

@@ -0,0 +1,116 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.run;

import org.aspectj.bridge.AbortException;
import org.aspectj.bridge.MessageUtil;

import java.io.PrintWriter;
import java.util.List;

/**
* A generic RunListener for easier partial implementations.
* It can take a RunI selector (called on completion)
* and/or a List to accumulate complete IRunStatus
* (if the selector is null or returns true).
* It can also take a PrintWriter and String to print traces of each event
* as "{prefix} [addingChild|runStarting|runCompleted]({IRunStatus})"
*/
public class RunListener implements IRunListener {
protected final List list;
protected final IRun selector;
protected final PrintWriter writer;
protected final String prefix;
protected RunListener() {
this((List) null, (IRun) null, (PrintWriter) null, (String) null);
}
/**
* @param sink the List sink for any IRunStatus if the selector is null
* or returns true for run(IRunStatus) - ignored if null.
* @param selector the IRun called on completion,
* perhaps to select those to be accumulated
* (should NOT throw Exception)
*/
public RunListener(List sink, IRun selector) {
this(sink, selector, (PrintWriter) null, (String) null);
}

/**
* @param writer the PrintWriter to print events to - may be null
* @param prefix the String prefixing any printing - if null, ""
*/
public RunListener(PrintWriter writer, String prefix) {
this((List) null, (IRun) null, writer, prefix);
}
/**
* @param sink the List sink for any IRunStatus if the selector is null
* or returns true for run(IRunStatus) - ignored if null.
* @param selector the IRun called on completion,
* perhaps to select those to be accumulated
* (should NOT throw Exception)
* @param writer the PrintWriter to print events to - may be null
* @param prefix the String prefixing any printing - if null, ""
*/
public RunListener(List sink, IRun selector, PrintWriter writer, String prefix) {
this.prefix = (null == prefix ? "" : prefix);
this.writer = writer;
this.selector = selector;
list = sink;
}
/**
* @see org.aspectj.testing.harness.run.IRunListener#addingChild(IRunStatus, IRunStatus)
*/
public void addingChild(IRunStatus parent, IRunStatus child) {
if (null != writer) {
writer.println(prefix + " addingChild(\"" + parent
+ "\", \"" + child + "\")");
}
}

/**
* @see org.aspectj.testing.harness.run.IRunListener#runStarting(IRunStatus)
*/
public void runStarting(IRunStatus run) {
if (null != writer) {
writer.println(prefix + " runStarting(\"" + run + "\")");
}
}
/**
* Print to writer (if any), run selector (if any), and add to list
* (if any and if selector is null or returns true).
* @see org.aspectj.testing.harness.run.IRunListener#runCompleted(IRunStatus)
* @throws AbortException wrapping any Exception thrown by any selector
* (error for selector to throw Exception)
*/
public void runCompleted(IRunStatus run) {
if (null != writer) {
writer.println(prefix + " runCompleted(\"" + run + "\")");
}
try {
if (((null == selector) || selector.run(run)) && (null != list)) {
list.add(run);
}
} catch (Throwable e) {
String m = "Selectors should not throw exceptions!";
throw new AbortException(MessageUtil.abort(m, e));
}
}

}

+ 89
- 0
testing/src/org/aspectj/testing/run/RunListeners.java View File

@@ -0,0 +1,89 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


package org.aspectj.testing.run;

import java.util.ArrayList;
import java.util.Iterator;

/**
* Aggregate listeners into one and run synchronously in order added.
* @author isberg
*/
public class RunListeners extends RunListener implements IRunListener {

ArrayList listeners;
public RunListeners() {
listeners = new ArrayList();
}
public void addListener(IRunListener listener) {
if (null != listener) {
listeners.add(listener);
}
}

public void removeListener(IRunListener listener) {
if (null != listener) {
listeners.remove(listener);
}
}

/**
* Run all listeners with the given status.
* @see org.aspectj.testing.harness.run.IRunListener#runStarting(IRunStatus)
*/
public final void runStarting(IRunStatus status) {
if (null == status) {
throw new IllegalArgumentException("null RunStatusI");
}
Iterator iter = listeners.iterator();
while(!status.aborted() && iter.hasNext()) {
IRunListener element = (IRunListener) iter.next();
element.runStarting(status);
}
}

/**
* Signal all listeners with the given status.
* @see org.aspectj.testing.harness.run.IRunListener#runCompleted(IRunStatus)
*/
public final void runCompleted(IRunStatus status) {
if (null == status) {
throw new IllegalArgumentException("null RunStatusI");
}
Iterator iter = listeners.iterator();
while(!status.aborted() && iter.hasNext()) {
IRunListener element = (IRunListener) iter.next();
element.runCompleted(status);
}
}
/**
* @see org.aspectj.testing.harness.run.IRunListener#addingChild(IRunStatus, IRunStatus)
*/
public final void addingChild(IRunStatus parent, IRunStatus child) {
if (null == child) {
throw new IllegalArgumentException("null child");
}
if (null == parent) {
throw new IllegalArgumentException("null parent");
}
Iterator iter = listeners.iterator();
while(!parent.aborted() && ! child.aborted() && iter.hasNext()) {
IRunListener element = (IRunListener) iter.next();
element.addingChild(parent, child);
}
}
}

+ 408
- 0
testing/src/org/aspectj/testing/run/RunStatus.java View File

@@ -0,0 +1,408 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


package org.aspectj.testing.run;

import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHolder;
import org.aspectj.bridge.MessageHandler;
import org.aspectj.testing.util.BridgeUtil;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
* Default implementation of {@link IRunStatus}.
* @author isberg
*/
public class RunStatus implements IRunStatus {
private static int INDEX;

private final String name = "RunStatus[" + INDEX++ +"]";

/** true after isCompleted() evaluated true */
private boolean evaluated;

/** true after starting() called */
private boolean started; // set only in starting()

/** true after finished(int) or thrown(Throwable) called */
private boolean completed; // set only in completed(boolean)

/** contains any id set */
private Object id;

/** after finished(Object) called, contains that parameter */
private Object result;

/** after aborted(Object) called, contains that parameter */
private Object abortRequest;

/** use to set exception thrown, if any */
private Throwable thrown;

/** list of any messages submitted */
private IMessageHolder messageHolder;

/** list of any child status */
private ArrayList children;

/** parent of this status */
private IRunStatus parent;

/** invoker for any subruns */
private Runner runner;

/** controls runResult() */
private IRunValidator validator;

// public RunStatus() {
// reset();
// validator = RunValidator.NORMAL;
// }

public RunStatus(IMessageHolder holder, Runner runner) {
reset(holder, runner);
validator = RunValidator.NORMAL;
}

//------------------- process controls

/**
* Set identifier associated with this run, if any
* @throws IllegalArgumentException if id is null
* @throws IllegalStateException if id has already been set
*/
public void setIdentifier(Object id) {
if (null == id) {
throw new IllegalArgumentException("null id");
} else if ((null != this.id) && (id != this.id)) {
throw new IllegalStateException(
"attempt to set id " + this.id + " to " + id);
}
this.id = id;
}

/**
* Set the current validator.
* @param delegate the RunValidatorI to use when calculating runStatus
* @throws IllegalArgumentException if delegate is null
*/
public void setValidator(IRunValidator delegate) {
if (null == delegate) {
throw new IllegalArgumentException("null delegate");
}
if (validator != delegate) {
validator = delegate;
}
}

/**
* Call before any start() or after isCompleted() would return true
* to reset this to its pre-start state
* @throws IllegalStateException if start() has been called
* and isCompleted() is not true.
*/
public void reset() {
reset((IMessageHolder) null, (Runner) null);
}

/**
* Call before any start() or after isCompleted() would return true
* to reset this to its pre-start state. Does not affect validator.
* @param holder the IMessageHolder to use after resetting.
* @throws IllegalStateException if start() has been called
* and isCompleted() is not true.
*/
public void reset(IMessageHolder holder, Runner runner) {
if (null == runner) {
throw new IllegalArgumentException("null runner");
}
if (started && (!isCompleted())) {
throw new IllegalStateException("no reset() until isCompleted");
}
started = false;
completed = false;
result = null;
abortRequest = null;
thrown = null;
parent = null;
id = null;
messageHolder = (null != holder ? holder : new MessageHandler());
if (null != children) {
children.clear();
}
this.runner = runner;
evaluated = false;
}

/**
* Call only once to signal this run has started.
* @throws IllegalStateException if start() has been called
*/
public void start() {
if (started) {
throw new IllegalStateException("started already");
} else if (isCompleted()) {
throw new IllegalStateException("start after completed (do reset)");
}
started = true;
}

/**
* Call this or thrown only once after start()
* to signal this run has ended.
* If this represents a void process, use VOID.
* @param result the Object returned by this run.
* @throws IllegalStateException if start() was not called first
* or if either completed(Object) or thrown(Throwable) have been called.
*/
public void finish(Object result) {
if (null == result) {
throw new IllegalArgumentException("null result");
} else if (isCompleted()) {
throw new IllegalStateException(
"completed then finish " + result);
}
this.result = result;
}

/**
* Call to signal this run is ending by request.
* If this represents a void process, use VOID.
* If there is no message, use ABORT.
* @param request the Object request to abort,
* or ABORT if none is available.
* @throws IllegalStateException if start() was not called first
* or if either completed(Object) or thrown(Throwable) have been called.
*/
public void abort(Object request) {
if (null == request) {
throw new IllegalArgumentException("null request");
} else if (isCompleted()) {
throw new IllegalStateException(
"completed then abort " + request);
}
this.abortRequest = request;
}

/**
* Call this or completed only once after start()
* to signal this run has ended.
* @throws IllegalStateException if start() was not called first
* or if either completed(Object) or thrown(Throwable) have been called.
*/
public void thrown(Throwable thrown) {
if (null == thrown) {
throw new IllegalArgumentException("null thrown");
} else if (isCompleted()) {
throw new IllegalStateException(
"completed then thrown " + thrown);
}
this.thrown = thrown;
}

public void completeAbruptly() {
throw new Error("completing abruptly"); // XXX configurable
}

/**
* @return true if completed, not aborted, no thrown, no
* messages of kind ERROR, FAIL or ABORT, and
* result object is not IRunStatus.FAIL.
* @see org.aspectj.testing.harness.newbridge.IRunStatus#runResult()
*/
public boolean runResult() {
return validator.runPassed(this);
}

//------------------- process messages
/**
* Call this any time before isCompleted() would return true
* to signal any messages.
* @throws IllegalStateException if isCompleted().
*/
public boolean handleMessage(IMessage message) {
return messageHolder.handleMessage(message);
}
public boolean isIgnoring(IMessage.Kind kind) {
return messageHolder.isIgnoring(kind);
}
/**
* @see org.aspectj.bridge.IMessageHolder#hasAnyMessage(Kind, boolean)
*/
public boolean hasAnyMessage(IMessage.Kind kind, boolean orGreater) {
return messageHolder.hasAnyMessage(kind, orGreater);
}

/**
* @see org.aspectj.bridge.IMessageHolder#getMessages(Kind)
*/
public IMessage[] getMessages(IMessage.Kind kind, boolean orGreater) {
return messageHolder.getMessages(kind, orGreater);
}
/**
* @see org.aspectj.bridge.IMessageHolder#numMessages(Kind)
*/
public int numMessages(IMessage.Kind kind, boolean orGreater) {
return messageHolder.numMessages(kind, orGreater);
}

//------------------- process display
/** @return true if this run has started */
public boolean started() {
return started;
}

/** @return true if one of the result, abort request, or thrown is available */
public boolean isCompleted() {
if (!evaluated) {
if (started
&& ((null != thrown)
|| (null != result)
|| (null != abortRequest))) {
completed = true;
evaluated = true;
}
}
return completed;
}

/** @return true if this got an abort request */
public boolean aborted() {
return (completed && (null != abortRequest));
}

/** @return the Object result, if any, of this run */
public Object getResult() {
return result;
}

/** @return the Object abort request, if any, of this run */
public Object getAbortRequest() {
return abortRequest;
}

/** @return the Throwable thrown, if any, by this run */
public Throwable getThrown() {
return thrown;
}

/**
* @see org.aspectj.bridge.IMessageHolder#getUnmodifiableListView()
*/
public List getUnmodifiableListView() {
return messageHolder.getUnmodifiableListView();
}
/** @return any Message[] signalled, or IMessage.NONE if none */
public IMessage[] getMessages() {
return messageHolder.getMessages(null, IMessageHolder.EQUAL);
}

/** @return the identifier set for this run, if any */
public Object getIdentifier() {
return id;
}

/**
* @see org.aspectj.bridge.IMessageHolder#clearMessages()
* @throws UnsupportedOperationException always
*/
public void clearMessages() throws UnsupportedOperationException {
throw new UnsupportedOperationException("use reset");
}

//------------------- subprocess

/** get the invoker for any subrunners */
public Runner getRunner() {
return runner;
}

/**
* Add a record for a child run
* and install self as parent.
* @throws IllegalArgumentException if child is null
*/
public void addChild(IRunStatus child) {
if (null == child) {
throw new IllegalArgumentException("null child");
}
if (null == children) {
children = new ArrayList();
}
children.add(child);
}

/**
* Register this as the run parent.
* (Any run that does addChild(IRunStatus) should register as parent.)
* @throws IllegalArgumentException if parent is null
* @throws IllegalStateException if parent exists already
*/
public void registerParent(IRunStatus parent) {
if (null == parent) {
throw new IllegalArgumentException("null parent");
} else if (null != this.parent) {
throw new IllegalStateException(
"adding parent " + parent + " to parent " + this.parent);
}
this.parent = parent;
}

/**
* @return the current children of this run, or EMPTY_NEST if none
*/
public IRunStatus[] getChildren() {
if ((null == children) || (0 == children.size())) {
return EMPTY_NEST;
} else {
return (IRunStatus[]) children.toArray(EMPTY_NEST);
}
}

/**
* @return the currently-registered parent, or null if none
*/
public IRunStatus getParent() {
return parent;
}

public String toString() {
return BridgeUtil.toShortString(this);
}

public String toLongString() {
StringBuffer sb = new StringBuffer();
sb.append(BridgeUtil.toShortString(this));
if ((null != children) && (0 < children.size())) {
String label = "### --------- " + name;
int index = 0;
for (Iterator iter = children.iterator(); iter.hasNext();) {
IRunStatus childStatus = (IRunStatus) iter.next();
String childLabel =
"\n" + label + " child[" + index++ +"] "
+ childStatus.getIdentifier();
sb.append(childLabel + " ---- start\n");
sb.append(childStatus.toString());
sb.append(childLabel + " ---- end\n");
}
}
return sb.toString();
}
}

+ 206
- 0
testing/src/org/aspectj/testing/run/RunValidator.java View File

@@ -0,0 +1,206 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


package org.aspectj.testing.run;

import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHolder;
import org.aspectj.testing.util.IntRange;
import org.aspectj.testing.util.ObjectChecker;

/**
* This checks if a run status passes, as follows:
* <li>state: fail unless completed and not aborted</li>
* <li>messages: fail if any of type ABORT, FAIL - permit ERROR, WARNING, DEBUG...
* (which permits expected compiler errors and warnings) </li>
* <li>thrown: if type required, fail unless type thrown;
* if type permitted, fail if wrong type thrown</li>
* <li>result: fail unless no ObjectChecker or it validates
* and the result is not IRunStatus.FAIL.</li>
* <li>otherwise delegates to any subclass doPassed()<li>
* Client setup the expected and permitted exception classes
* and the result object checker, and may also subclass to
* query the IRunStatus more carefully.
* <p>
* Note that IRunStatus states can be out of sync with messages,
* e.g., as underlying components signal ABORT without using abort(...).
*/
public class RunValidator implements IRunValidator {
/** expect normal completion with any non-null result object */
public static final IRunValidator NORMAL
= new RunValidator(ObjectChecker.ANY);

/** expect normal completion and Integer result object with value 0 */
public static final IRunValidator ZERO_STATUS
= new RunValidator(IntRange.ZERO);

/** expect finished(IRunStatus.PASS) and no thrown, fail, etc. */
public static final IRunValidator PASS
= new RunValidator(new ObjectChecker() {
public boolean isValid(Object o) {
return (o == IRunStatus.PASS);
}
});
/** expect finished(IRunStatus.FAIL) */
public static final IRunValidator FAIL
= new RunValidator(new ObjectChecker() {
public boolean isValid(Object o) {
return (o == IRunStatus.FAIL);
}
});

/** range of status values required for passing */
private ObjectChecker resultChecker;
// XXX replace two exc. classes with one, plus boolean for how to interpret?
/** if non-null, passed() permits any thrown assignable to this class */
private Class permittedExceptionsClass;

/** if non-null, passed() requires some thrown assignable to this class */
private Class requiredExceptionsClass;
/** Create result validator that expects a certain int status */
public RunValidator(ObjectChecker resultChecker) {
this(resultChecker, null, null);
}
/**
* Create result validator that passes only when completed abruptly by
* a Throwable assignable to the specified class.
* @throws illegalArgumentException if requiredExceptionsClass is not Throwable
*/
public RunValidator(Class requiredExceptionsClass) {
this(null, null, requiredExceptionsClass);
}

/**
* Create a result handler than knows how to evaluate {@link #passed()}.
* You cannot specify both permitted and required exception class,
* and any exception class specified must be assignable to throwable.
*
* @param resultChecker {@link #passed()} will return false if
* the int status is not accepted by this int validator - if null,
* any int status result is accepted.
* @param fastFailErrorClass an Error subclass with a (String) constructor to use to
* construct and throw Error from fail(String). If null, then fail(String)
* returns normally.
* @param permittedExceptionsClass if not null and any exceptions thrown are
* assignable to this class, {@link #passed()} will not return
* false as it normally does when exceptions are thrown.
* @param requiredExceptionsClass if not null, {@link #passed()} will return false
* unless some exception was thrown that is assignable to this class.
* @throws illegalArgumentException if any exception class is not Throwable
* or if fast fail class is illegal (can't make String constructor)
*/
protected RunValidator(
ObjectChecker resultChecker,
Class permittedExceptionsClass,
Class requiredExceptionsClass) {
init(resultChecker,permittedExceptionsClass, requiredExceptionsClass);
}

/** same as init with existing values */
protected void reset() {
init(resultChecker, permittedExceptionsClass,
requiredExceptionsClass);
}
/** subclasses may use this to re-initialize this for re-use */
protected void init(
ObjectChecker resultChecker,
Class permittedExceptionsClass,
Class requiredExceptionsClass) {
this.permittedExceptionsClass = permittedExceptionsClass;
this.requiredExceptionsClass = requiredExceptionsClass;
if (null != resultChecker) {
this.resultChecker = resultChecker;
} else {
this.resultChecker = IntRange.ANY;
}

if (null != permittedExceptionsClass) {
if (!Throwable.class.isAssignableFrom(permittedExceptionsClass)) {
String e = "permitted not throwable: " + permittedExceptionsClass;
throw new IllegalArgumentException(e);
}
}
if (null != requiredExceptionsClass) {
if (!Throwable.class.isAssignableFrom(requiredExceptionsClass)) {
String e = "required not throwable: " + requiredExceptionsClass;
throw new IllegalArgumentException(e);
}
}
if ((null != permittedExceptionsClass)
&& (null != requiredExceptionsClass) ) {
String e = "define at most one of required or permitted exceptions";
throw new IllegalArgumentException(e);
}
}
/** @return true if this result passes per this validator */
public final boolean runPassed(IRunStatus result) {
if (null == result) {
throw new IllegalArgumentException("null result");
}
// After the result has completed, the result is stored.
if (!result.isCompleted()) {
return false;
}
if (result.aborted()) {
return false;
}
if (null != result.getAbortRequest()) {
return false;
}
Object resultObject = result.getResult();
if (!resultChecker.isValid(resultObject)) {
return false;
}
if (resultObject == IRunStatus.FAIL) {
return false;
}
// need MessageHandler.getMessage(...)
if (result.hasAnyMessage(IMessage.FAIL, IMessageHolder.ORGREATER)) {
return false;
}
Throwable thrown = result.getThrown();
if (null == thrown) {
if (null != requiredExceptionsClass) {
return false;
}
} else {
Class c = thrown.getClass();
// at most one of the ExceptionsClass set
if (null != requiredExceptionsClass) {
if (!requiredExceptionsClass.isAssignableFrom(c)) {
return false;
}
} else if (null != permittedExceptionsClass) {
if (!permittedExceptionsClass.isAssignableFrom(c)) {
return false;
}
} else {
return false;
}
}
return dopassed();
}

/** subclasses implement subclass-specific behavior for passed() here */
protected boolean dopassed() {
return true;
}
}

+ 510
- 0
testing/src/org/aspectj/testing/run/Runner.java View File

@@ -0,0 +1,510 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


package org.aspectj.testing.run;

import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.IMessageHolder;
import org.aspectj.bridge.Message;
import org.aspectj.bridge.MessageHandler;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.util.LangUtil;

import java.util.Enumeration;
import java.util.Hashtable;

/**
* Run IRun, setting status and invoking listeners
* for simple and nested runs.
* <p>
* This manages baseline IRun status reporting:
* any throwables are caught and reported, and
* the status is started before running and
* (if not already completed) completed after.
* <p>
* This runs any IRunListeners specified directly in the
* run*(..., IRunListener) methods
* as well as any specified indirectly by registering listeners per-type
* in {@link registerListener(Class, IRunListener)}
* <p>
* For correct handling of nested runs, this sets up
* status parent/child relationships.
* It uses the child result object supplied directly in the
* runChild(..., IRunStatus childStatus,..) methods,
* or (if that is null) one obtained from the child IRun itself,
* or (if that is null) a generic IRunStatus.
* <p>
* For IRunIterator, this uses IteratorWrapper to wrap the
* iterator as an IRun. Runner and IteratorWrapper coordinate
* to handle fast-fail (aborting further iteration when an IRun fails).
* The IRunIterator itself may specify fast-fail by returning true
* from {@link IRunIterator#abortOnFailure()}, or clients can
* register IRunIterator by Object or type for fast-failure using
* {@link registerFastFailIterator(IRunIterator)} or
* {@link registerFastFailIterator(Class)}.
* This also ensures that
* {@link IRunIterator#iterationCompleted()} is
* called after the iteration process has completed.
*/
public class Runner {
// XXX need to consider wiring in a logger - sigh

private static final IMessage FAIL_NORUN
= MessageUtil.fail("Null IRun parameter to Runner.run(IRun..)");
private static final IMessage FAIL_NORUN_ITERATOR
= MessageUtil.fail("Null IRunterator parameter to Runner.run(IRunterator...)");
public Runner() {
}
/**
* Do the run, setting run status, invoking
* listener, and aborting as necessary.
* If the run is null, the status is
* updated, but the listener is never run.
* If the listener is null, then the runner does a lookup
* for the listeners of this run type.
* Any exceptions thrown by the listener(s) are added
* to the status messages and processing continues.
* unless the status is aborted.
* The status will be completed when this method completes.
* @param run the IRun to run - if null, issue a FAIL message
* to that effect in the result.
* @throws IllegalArgumentException if status is null
* @return boolean result returned from IRun
* or false if IRun did not complete normally
* or status.runResult() if aborted.
*/
/* XXX later permit null status
* If the status is null, this tries to complete
* the run without a status. It ignores exceptions
* from the listeners, but does not catch any from the run.
*/
public boolean run(IRun run, IRunStatus status,
IRunListener listener) {
return run(run, status, listener, (Class) null);
}
public boolean run(IRun run, IRunStatus status,
IRunListener listener, Class exceptionClass) {
if (!precheck(run, status)) {
return false;
}
RunListeners listeners = getListeners(run, listener);
return runPrivate(run, status, listeners, exceptionClass);
}

/**
* Run child of parent, handling interceptor registration, etc.
* @throws IllegalArgumentException if parent or child status is null
*/
public boolean runChild(IRun child,
IRunStatus parentStatus,
IRunStatus childStatus,
IRunListener listener) {
return runChild(child, parentStatus, childStatus, listener, null);
}
/**
* Run child of parent, handling interceptor registration, etc.
* If the child run is supposed to throw an exception, then pass
* the exception class.
* After this returns, the childStatus is guaranteed to be completed.
* If an unexpected exception is thrown, an ABORT message
* is passed to childStatus.
* @param parentStatus the IRunStatus for the parent - must not be null
* @param childStatus the IRunStatus for the child - default will be created if null
* @param exceptionClass the Class of any expected exception
* @throws IllegalArgumentException if parent status is null
*/
public boolean runChild(IRun child,
IRunStatus parentStatus,
IRunStatus childStatus,
IRunListener listener,
Class exceptionClass) {
if (!precheck(child, parentStatus)) {
return false;
}
if (null == childStatus) {
childStatus = new RunStatus(new MessageHandler(), this);
}
installChildStatus(child, parentStatus, childStatus);
if (!precheck(child, childStatus)) {
return false;
}
RunListeners listeners = getListeners(child, listener);
if (null != listeners) {
try {
listeners.addingChild(parentStatus, childStatus);
} catch (Throwable t) {
String m = "RunListenerI.addingChild(..) exception " + listeners;
parentStatus.handleMessage(MessageUtil.abort(m, t)); // XXX
}
}
boolean result = false;
try {
result = runPrivate(child, childStatus, listeners, exceptionClass);
} finally {
if (!childStatus.isCompleted()) {
childStatus.finish(result ? IRunStatus.PASS : IRunStatus.FAIL);
childStatus.handleMessage(MessageUtil.debug("XXX parent runner set completion"));
}
}
boolean childResult = childStatus.runResult();
if (childResult != result) {
childStatus.handleMessage(MessageUtil.info("childResult != result=" + result));
}
return childResult;
}

public IRunStatus makeChildStatus(IRun run, IRunStatus parent, IMessageHolder handler) {
return installChildStatus(run, parent, new RunStatus(handler, this));
}
/**
* Setup the child status before running
* @param run the IRun of the child process (not null)
* @param parent the IRunStatus parent of the child status (not null)
* @param child the IRunStatus to install - if null, a generic one is created
* @return the IRunStatus child status (as passed in or created if null)
*/
public IRunStatus installChildStatus(IRun run, IRunStatus parent, IRunStatus child) {
if (null == parent) {
throw new IllegalArgumentException("null parent");
}
if (null == child) {
child = new RunStatus(new MessageHandler(), this);
}
child.setIdentifier(run); // XXX leak if ditching run...
parent.addChild(child);
return child;
}
/**
* Do a run by running all the subrunners provided by the iterator,
* creating a new child status of status for each.
* If the iterator is null, the result is
* updated, but the interceptor is never run.
* @param iterator the IRunteratorI for all the IRun to run
* - if null, abort (if not completed) or message to status.
* @throws IllegalArgumentException if status is null
*/
public boolean runIterator(IRunIterator iterator, IRunStatus status,
IRunListener listener) {
LangUtil.throwIaxIfNull(status, "status");
if (status.aborted()) {
return status.runResult();
}
if (null == iterator) {
if (!status.isCompleted()) {
status.abort(IRunStatus.ABORT_NORUN);
} else {
status.handleMessage(FAIL_NORUN);
}
return false;
}
IRun wrapped = wrap(iterator, listener);
return run(wrapped, status, listener);
}

/**
* Signal whether to abort on failure for this run and iterator,
* based on the iterator and any fast-fail registrations.
* @param iterator the IRunIterator to stop running if this returns true
* @param run the IRun that failed
* @return true to halt further iterations
*/
private boolean abortOnFailure(IRunIterator iterator, IRun run) {
return ((null == iterator) || iterator.abortOnFailure()); // XxX not complete
}


/**
* Tell Runner to stop iterating over IRun for an IRunIterator
* if any IRun.run(IRunStatus) fails.
* This overrides a false result from IRunIterator.abortOnFailure().
* @param iterator the IRunIterator to fast-fail - ignored if null.
* @see IRunIterator#abortOnFailure()
*/
public void registerFastFailIterator(IRunIterator iterator) { // XXX unimplemented
throw new UnsupportedOperationException("ignoring " + iterator);
}

/**
* Tell Runner to stop iterating over IRun for an IRunIterator
* if any IRun.run(IRunStatus) fails,
* if the IRunIterator is assignable to iteratorType.
* This overrides a false result from IRunIterator.abortOnFailure().
* @param iteratorType the IRunIterator Class to fast-fail
* - ignored if null, must be assignable to IRunIterator
* @see IRunIterator#abortOnFailure()
*/
public void registerFastFailIteratorTypes(Class iteratorType) { // XXX unimplemented
throw new UnsupportedOperationException("ignoring " + iteratorType);
}
/**
* Register a run listener for any run assignable to type.
* @throws IllegalArgumentException if either is null
*/
public void registerListener(Class type, IRunListener listener) { // XXX unregister
if (null == type) {
throw new IllegalArgumentException("null type");
}
if (null == listener) {
throw new IllegalArgumentException("null listener");
}
ClassListeners.addListener(type, listener);
}
/**
* Wrap this IRunIterator.
* This wrapper takes care of calling
* <code>iterator.iterationCompleted()</code> when done, so
* after running this, clients should not invoker IRunIterator
* methods on this iterator.
* @return the iterator wrapped as a single IRun
*/
public IRun wrap(IRunIterator iterator, IRunListener listener) {
LangUtil.throwIaxIfNull(iterator, "iterator");
return new IteratorWrapper(this, iterator, listener);
}
/**
* This gets any listeners registered for the run
* based on the class of the run (or of the wrapped iterator,
* if the run is an IteratorWrapper).
* @return a listener with all registered listener and parm,
* or null if parm is null and there are no listeners
*/
protected RunListeners getListeners(IRun run, IRunListener listener) {
if (null == run) {
throw new IllegalArgumentException("null run");
}
Class runClass = run.getClass();
if (runClass == IteratorWrapper.class) {
IRunIterator it = ((IteratorWrapper) run).iterator;
if (null != it) {
runClass = it.getClass();
}
// fyi expecting: listener == ((IteratorWrapper) run).listener
}
RunListeners listeners = ClassListeners.getListeners(runClass);
if (null != listener) {
listeners.addListener(listener);
}
return listeners; // XXX implement registration
}

/** check status and run before running */
private boolean precheck(IRun run, IRunStatus status) {
if (null == status) {
throw new IllegalArgumentException("null status");
}
// check abort request coming in
if (status.aborted()) {
return status.runResult();
} else if (status.isCompleted()) {
throw new IllegalStateException("status completed before starting");
}
if (!status.started()) {
status.start();
}
return true;
}
/** This assumes precheck has happened and listeners have been obtained */
private boolean runPrivate(IRun run, IRunStatus status,
RunListeners listeners, Class exceptionClass) {
IRunListener listener = listeners;
if (null != listener) {
try {
listener.runStarting(status);
} catch (Throwable t) {
String m = listener + " RunListenerI.runStarting(..) exception";
IMessage mssg = new Message(m, IMessage.WARNING, t, null);
// XXX need IMessage.EXCEPTION - WARNING is ambiguous
status.handleMessage(mssg);
}
}
// listener can set abort request
if (status.aborted()) {
return status.runResult();
}
if (null == run) {
if (!status.isCompleted()) {
status.abort(IRunStatus.ABORT_NORUN);
} else {
status.handleMessage(MessageUtil.FAIL_INCOMPLETE);
}
return false;
}
boolean result = false;
try {
result = run.run(status);
if (!status.isCompleted()) {
status.finish(result?IRunStatus.PASS: IRunStatus.FAIL);
}
} catch (Throwable thrown) {
if (!status.isCompleted()) {
status.thrown(thrown);
} else {
String m = "run status completed but run threw exception";
status.handleMessage(MessageUtil.abort(m, thrown));
result = false;
}
} finally {
if (!status.isCompleted()) {
// XXX should never get here... - hides errors to set result
status.finish(result ? IRunStatus.PASS : IRunStatus.FAIL);
if (!status.isCompleted()) {
status.handleMessage(MessageUtil.debug("child set of status failed"));
}
}
}
try {
if ((null != listener) && !status.aborted()) {
listener.runCompleted(status);
}
} catch (Throwable t) {
String m = listener + " RunListenerI.runCompleted(..) exception";
status.handleMessage(MessageUtil.abort(m, t));
}
return result;
}

//---------------------------------- nested classes
/**
* Wrap an IRunIterator as a IRun, coordinating
* fast-fail IRunIterator and Runner
*/
public static class IteratorWrapper implements IRun {
final Runner runner;
public final IRunIterator iterator;
final IRunListener listener;
public IteratorWrapper(Runner runner, IRunIterator iterator, IRunListener listener) {
LangUtil.throwIaxIfNull(iterator, "iterator");
LangUtil.throwIaxIfNull(runner, "runner");
this.runner = runner;
this.iterator = iterator;
this.listener = listener;
}

/** @return null */
public IRunStatus makeStatus() {
return null;
}
/** @return true unless one failed */
public boolean run(IRunStatus status) {
boolean result = true;
try {
int i = 0;
int numMessages = status.numMessages(IMessage.FAIL, IMessageHolder.ORGREATER);
while (iterator.hasNextRun()) {
IRun run = iterator.nextRun((IMessageHandler) status, runner);
if (null == run) {
MessageUtil.debug(status, "null run " + i + " from " + iterator);
continue;
}
int newMessages = status.numMessages(IMessage.FAIL, IMessageHolder.ORGREATER);
if (newMessages > numMessages) {
numMessages = newMessages;
String m = "run " + i + " from " + iterator
+ " not invoked, due to fail(+) message(s) ";
MessageUtil.debug(status, m);
continue;
}
RunStatus childStatus = null; // let runChild create
if (!runner.runChild(run, status, childStatus, listener)) {
if (result) {
result = false;
}
if (iterator.abortOnFailure()
|| runner.abortOnFailure(iterator, run)) {
break;
}
}
i++;
}
return result;
} finally {
iterator.iterationCompleted();
}
}

/** @return iterator, clipped to 75 char */
public String toString() {
String s = "" + iterator;
if (s.length() > 75) {
s = s.substring(0, 72) + "...";
}
return s;
}
}
/** per-class collection of IRun */
static class ClassListeners extends RunListeners {
private static final Hashtable known = new Hashtable();
static RunListeners getListeners(Class c) { // XXX costly and stupid
Enumeration keys = known.keys();
RunListeners many = new RunListeners();
while (keys.hasMoreElements()) {
Class kc = (Class) keys.nextElement();
if (kc.isAssignableFrom(c)) {
many.addListener((IRunListener) known.get(kc));
}
}
return many;
}
private static RunListeners makeListeners(Class c) {
RunListeners result = (ClassListeners) known.get(c);
if (null == result) {
result = new ClassListeners(c);
known.put(c, result);
}
return result;
}
static void addListener(Class c, IRunListener listener) {
if (null == listener) {
throw new IllegalArgumentException("null listener");
}
if (null == c) {
c = IRun.class;
}
makeListeners(c).addListener(listener);
}
Class clazz;
ClassListeners(Class clazz) {
this.clazz = clazz;
}
public String toString() {
return clazz.getName() + " ClassListeners: " + super.toString();
}
}
}

+ 72
- 0
testing/src/org/aspectj/testing/run/WrappedRunIterator.java View File

@@ -0,0 +1,72 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.run;

import org.aspectj.bridge.IMessageHandler;
import org.aspectj.util.LangUtil;

/** Adapt IRun to IRunIterator in a way that can be detected for hoisting. */
public class WrappedRunIterator implements IRunIterator {
protected final Object id;
protected IRun run;
/**
* @param id the Object used for toString(), if set
* @param run the IRun returned from the first call to
* nextRun(IMessageHandler handler, Runner runner)
*/
public WrappedRunIterator(Object id, IRun run) {
LangUtil.throwIaxIfNull(run, "run");
this.id = id;
this.run = run;
}

/** @return false always - we run only once anyway */
public boolean abortOnFailure() {
return false;
}

/**
* @return true until nextRun() completes
* @see org.aspectj.testing.run.RunIteratorI#hasNextRun()
*/
public boolean hasNextRun() {
return (null != run);
}

/**
* @see org.aspectj.testing.run.IRunIterator#iterationCompleted()
*/
public void iterationCompleted() {
}

/**
* @return the only IRun we have, and null thereafter
* @see org.aspectj.testing.run.RunIteratorI#nextRun(IMessageHandler, Runner)
*/
public IRun nextRun(IMessageHandler handler, Runner runner) {
if (null == run) {
return null;
} else {
IRun result = run;
run = null;
return result;
}
}

/** @return name */
public String toString() {
return (null == id ? run : id).toString();
}
}

+ 53
- 0
testing/src/org/aspectj/testing/util/AccumulatingFileFilter.java View File

@@ -0,0 +1,53 @@
/* *******************************************************************
* Copyright (c) 1999-2000 Xerox Corporation.
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


package org.aspectj.testing.util;

import java.io.File;
import java.util.Vector;

/**
* A FileFilter that accumulates the results when called if they exist.
* Subclasses override accumulate to determine whether it should be
* accumulated.
*/
public class AccumulatingFileFilter extends ValidFileFilter {
Vector files = new Vector();
public final boolean accept(File f) {
if (super.accept(f) && (accumulate(f))) {
files.add(f);
}
return true;
}

/**
* This implementation accumulates everything.
* Subclasses should override to implement filter
* @param file a File guaranteed to exist
* @return true if file should be accumulated.
*/
public boolean accumulate(File f) {
return true;
}
/**
* @return list of files currently accumulated
*/
public File[] getFiles() {
int numFiles = files.size();
File[] result = new File[numFiles];
if (0 < numFiles) {
files.copyInto(result);
}
return result;
}
}

+ 439
- 0
testing/src/org/aspectj/testing/util/BridgeUtil.java View File

@@ -0,0 +1,439 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.util;

import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.bridge.Message;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.bridge.SourceLocation;
import org.aspectj.testing.run.IRunStatus;
import org.aspectj.testing.run.RunValidator;
import org.aspectj.util.FileUtil;
import org.aspectj.util.LineReader;

import java.io.File;
import java.util.Comparator;

/**
*
*/
public class BridgeUtil {

private static final String INDENT = " ";

/** result value when writeMessage is passed null */
private static final String NULL_MESSAGE_OUTPUT = "<null message output>";
/** result value when readMessage is passed null */
private static final IMessage NULL_MESSAGE_INPUT = null;
private static final String KIND_DELIM = ": \"";
private static final String MESSAGE_DELIM = "\" - ";

public static ISourceLocation makeSourceLocation(LineReader reader) {
LangUtil.throwIaxIfNull(reader, "reader");
int line = reader.getLineNumber();
return new SourceLocation(reader.getFile(), line, line, 0);
}


/**
* Method readSourceLocation.
* @param sourceLocStr
* @return ISourceLocation
*/
private static ISourceLocation readSourceLocation(String sourceLocStr) {
return BridgeUtil.makeSourceLocation(sourceLocStr);
}
public static IMessage makeMessage(String message, IMessage.Kind kind,
Throwable thrown, LineReader reader) {
ISourceLocation sl = (null == reader ? null : MessageUtil.makeSourceLocation(reader));
if (null == kind) kind = IMessage.INFO;
return new Message(message, kind, thrown, sl);
}

/**
* Read a message from a string written by writeMessage(IMessage).
* Does not handle exceptions at all or source location well. XXX
* @param message the String representation of a message
* @return IMessage
*/
public static IMessage readMessage(String message) {
if (null == message) {
return NULL_MESSAGE_INPUT;
}
if (NULL_MESSAGE_OUTPUT.equals(message)) {
return null;
}
int kindEnd = message.indexOf(KIND_DELIM);
int messageEnd = message.indexOf(MESSAGE_DELIM);
int messageStart = kindEnd+KIND_DELIM.length();
int sourceLocStart = messageEnd+MESSAGE_DELIM.length();
String kindStr = message.substring(0, kindEnd);
String text = message.substring(messageStart, messageEnd);
String sourceLocStr = message.substring(sourceLocStart);
IMessage.Kind kind = MessageUtil.getKind(kindStr);
ISourceLocation loc = readSourceLocation(sourceLocStr);
return new Message(text, kind, null, loc);
}

/**
* Write a message to a string to be read by readMessage(String)
* @param message the String representation of a message
* @return IMessage
*/
public static String writeMessage(IMessage message) {
if (null == message) {
return NULL_MESSAGE_OUTPUT;
}
return message.getKind()
+ KIND_DELIM
+ message.getMessage()
+ MESSAGE_DELIM
+ message.getISourceLocation(); // XXX implement
}

public static class Comparators {
/**
* This is a weak ordering
* so use only for sorts, not to maintain maps.
* It returns 0 if one file path is a suffix of the other
* or a case-insensitive string comparison otherwise.
*/
public static final Comparator WEAK_File = new Comparator() {
public int compare(Object o1, Object o2) {
File one = (File) o1;
File two = (File) o2;
if (null == one) {
return (null == two ? 0 : 1);
} else if (null == two) {
return -1;
} else if (one == two) {
return 0;
} else {
String s1 = FileUtil.weakNormalize(one.getPath());
String s2 = FileUtil.weakNormalize(two.getPath());
if (s1.endsWith(s2) || s2.endsWith(s1)) {
return 0;
} else {
return String.CASE_INSENSITIVE_ORDER.compare(s1, s2);
}
}
}};
/**
* Ordering ignores filename,
* so use only for sorts, not to maintain maps
*/
public static final Comparator WEAK_ISourceLocation = new Comparator() {
public int compare(Object o1, Object o2) {
ISourceLocation one = (ISourceLocation) o1;
ISourceLocation two = (ISourceLocation) o2;
if (null == one) {
return (null == two ? 0 : 1);
} else if (null == two) {
return -1;
} else if (one == two) {
return 0;
} else { // XXX start with File to make strong
int i1 = one.getLine();
int i2 = two.getLine();
return i1 - i2;
// // defer subcomparisons until messages complete
// if (i1 != i2) {
// return i1 - i2;
// } else {
// i1 = one.getColumn();
// i2 = two.getColumn();
// if (i1 != i2) {
// return i1 - i2;
// } else {
// i1 = one.getEndLine();
// i2 = two.getEndLine();
// return i1 - i2;
// }
// }
}
}};

/**
* Ordering ignores filename and message
* so use only for sorts, not to maintain maps
*/
public static final Comparator WEAK_IMessage = new Comparator() {
public int compare(Object o1, Object o2) {
IMessage one = (IMessage) o1;
IMessage two = (IMessage) o2;
if (null == one) {
return (null == two ? 0 : 1);
} else if (null == two) {
return -1;
} else if (one == two) {
return 0;
} else {
IMessage.Kind kind1 = one.getKind();
IMessage.Kind kind2= two.getKind();
if (kind1 != kind2) {
return IMessage.Kind.COMPARATOR.compare(kind1, kind2);
} else {
ISourceLocation sl1 = one.getISourceLocation();
ISourceLocation sl2 = two.getISourceLocation();
return WEAK_ISourceLocation.compare(sl1, sl2);
}
}
}};
}

public static SourceLocation makeSourceLocation(String input) { // XXX only for testing, not production
return makeSourceLocation(input, (File) null);
}
public static SourceLocation makeSourceLocation(String input, String path) {
return makeSourceLocation(input, (null == path ? null : new File(path)));
}
/** attempt to create a source location from the input */
public static SourceLocation makeSourceLocation(String input, File defaultFile) {
/*
* Forms interpreted:
* # - line
* file - file
* file:# - file, line
* #:# - if defaultFile is not null, then file, line, column
* file:#:# - file, line, column
* file:#:#:? - file, line, column, message
*/
SourceLocation result = null;
if ((null == input) || (0 == input.length())) {
if (null == defaultFile) {
return null;
} else {
return new SourceLocation(defaultFile, 0, 0, 0);
}
}
input = input.trim();
String path = null;
int line = 0;
int endLine = 0;
int column = 0;
String message = null;
// first try line only
line = convert(input);
if (-1 != line) {
return new SourceLocation(defaultFile, line, line, 0);
}
// if not a line - must be > 2 characters
if (3 > input.length()) {
return null; // throw new IllegalArgumentException("too short: " + input);
}
final String fixTag = "FIXFIX";
if (input.charAt(1) == ':') { // windows drive ambiguates ":" file:line:col separator
input = fixTag + input.substring(0,1) + input.substring(2);
}
// expecting max: path:line:column:message
// if 1 colon, delimits line (to second colon or end of string)
// if 2 colon, delimits column (to third colon or end of string)
// if 3 colon, delimits column (to fourth colon or end of string)
// todo: use this instead??
final int colon1 = input.indexOf(":",2); // 2 to get past windows drives...
final int colon2 = (-1 == colon1?-1:input.indexOf(":", colon1+1));
final int colon3 = (-1 == colon2?-1:input.indexOf(":", colon2+1));
String s;
if (-1 == colon1) { // no colon; only path (number handled above)
path = input;
} else { // 1+ colon => file:line // XXX later or line:column
path = input.substring(0, colon1);
s = input.substring(colon1+1,(-1!=colon2?colon2:input.length())).trim();
line = convert(s);
if (-1 == line) {
return null;
//line = "expecting line(number) at \"" + line + "\" in " + input;
//throw new IllegalArgumentException(line);
} else if (-1 != colon2) { // 2+ colon => col
s = input.substring(colon2+1,(-1!=colon3?colon3:input.length())).trim();
column = convert(s);
if (-1 == column) {
return null;
//col = "expecting col(number) at \"" + col + "\" in " + input;
//throw new IllegalArgumentException(col);
} else if (-1 != colon3) { // 3 colon => message
message = input.substring(colon3+1); // do not trim message
}
}
}

if (path.startsWith(fixTag)) {
int len = fixTag.length();
path = path.substring(len, 1+len) + ":" +
path.substring(1+len);
}
if ((endLine == 0) && (line != 0)) {
endLine = line;
}
// XXX removed message/comment
return new SourceLocation(new File(path), line, endLine, column);
}

// XXX reconsider convert if used in production code
/**
* Convert String to int using ascii and optionally
* tolerating text
* @param s the String to convert
* @param permitText if true, pick a sequence of numbers
* within a possibly non-numeric String
* @param last if permitText, then if this is true the
* last sequence is used - otherwise the first is used
* XXX only default u.s. encodings..
* @return -1 or value if a valid, totally-numeric positive string 0..MAX_WIDTH
*/
private static int convert(String s) {
return convert(s, false, false);
}
// XXX reconsider convert if used in production code
/**
* Convert String to int using ascii and optionally
* tolerating text
* @param s the String to convert
* @param permitText if true, pick a sequence of numbers
* within a possibly non-numeric String
* @param last if permitText, then if this is true the
* last sequence is used - otherwise the first is used
* XXX only default u.s. encodings..
* @return -1 or value if a valid, positive string 0..MAX_WIDTH
*/
private static int convert(String s, boolean permitText,
boolean first) {
int result = -1;
int last = -1;
int max = s.length();
boolean reading = false;
for (int i = 0; i < max; i++) {
char c = s.charAt(i);
if ((c >= '0') && (c <= '9')) {
if (-1 == result) { // prefix loop
result = 0;
reading = true;
}
result = ((result * 10) + (c - '0'));
} else if (!permitText) {
return -1;
} else if (reading) { // from numeric -> non-numeric
if (first) {
return result;
} else {
last = result;
}
reading = false;
}
}
if (permitText && !first && (-1 != last) && (-1 == result)) {
result = last;
}
return ((0 < result) && (result < ISourceLocation.MAX_LINE) ? result : -1);
}

private BridgeUtil() {}

/** @return String for status header, counting children passed/failed */
public static String childString(IRunStatus runStatus, int numSkips, int numIncomplete) {
if (null == runStatus) {
return "((RunStatus) null)";
}
if (0 > numSkips) {
numSkips = 0;
}
if (0 > numIncomplete) {
numIncomplete = 0;
}
StringBuffer sb = new StringBuffer();
if (RunValidator.NORMAL.runPassed(runStatus)) {
sb.append("PASS ");
} else {
sb.append("FAIL ");
}
Object id = runStatus.getIdentifier();
if (null != id) {
sb.append(id.toString() + " ");
}
IRunStatus[] children = runStatus.getChildren();
final int numChildren = (null == children ? 0 : children.length);
final int numTests = numIncomplete + numChildren + numSkips;
int numFails = 0;
if (!LangUtil.isEmpty(children)) {
for (int i = 0; i < children.length; i++) {
if (!RunValidator.NORMAL.runPassed(children[i])) {
numFails++;
}
}
}
final int numPass = children.length - numFails;
sb.append(numTests + " tests");
if (0 < numTests) {
sb.append(" (");
}
if (0 < numSkips) {
sb.append(numSkips + " skipped");
if (0 < (numFails + numPass + numIncomplete)) {
sb.append(", ");
}
}
if (0 < numIncomplete) {
sb.append(numIncomplete + " incomplete");
if (0 < (numFails + numPass)) {
sb.append(", ");
}
}
if (0 < numFails) {
sb.append(numFails + " failed");
if (0 < numPass) {
sb.append(", ");
}
}
if (0 < numPass) {
sb.append(numPass + " passed)");
} else if (0 < numTests) {
sb.append(")");
}
return sb.toString().trim();
}

/** @return String for status header */
public static String toShortString(IRunStatus runStatus) {
if (null == runStatus) {
return "((RunStatus) null)";
}
StringBuffer sb = new StringBuffer();
if (RunValidator.NORMAL.runPassed(runStatus)) {
sb.append("PASS ");
} else {
sb.append("FAIL ");
}
Object id = runStatus.getIdentifier();
if (null != id) {
sb.append(id.toString() + " ");
}
sb.append(MessageUtil.renderCounts(runStatus));
return sb.toString().trim();
}

}

+ 88
- 0
testing/src/org/aspectj/testing/util/CollectorFileFilter.java View File

@@ -0,0 +1,88 @@
/* *******************************************************************
* Copyright (c) 1999-2000 Xerox Corporation.
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


package org.aspectj.testing.util;

import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
* Wrap FileFilter to collect any accepted
*/
public class CollectorFileFilter implements FileFilter {
/** returned from getFiles() when there are no files to get */
public static final List EMPTY
= Collections.unmodifiableList(new ArrayList(0));

/** used for collecting filters */
protected ArrayList files;

/** filter delegate - may be null */
protected final FileFilter filter;

/** return false from accept only when !alwaysTrue
* and filter is null or fails
*/
protected final boolean alwaysTrue;

/** this(null, true) */
public CollectorFileFilter() {
this(null, true);
}

/*
* @param filter the FileFilter delegate - may be null
* @param alwaysTrue return false from accept only when !alwaysTrue
* and filter is null or fails
*/
public CollectorFileFilter(FileFilter filter, boolean alwaysTrue){
this.filter = filter;
this.alwaysTrue = alwaysTrue;
}

/**
* Accept file into collection if filter is null or passes.
* @return false only when !alwaysTrue and filter fails.
*/
public boolean accept(File f) {
if ((null == filter) || filter.accept(f)) {
add(f);
return true;
}
return alwaysTrue;
}

/** gather files */
protected synchronized void add(File f) {
if (null != f) {
if (null == files) {
files = new ArrayList();
}
files.add(f);
}
}

/**
* return clone of gathered-files
* @return EMPTY if no files or a clone of the collection otherwise
*/
public synchronized List getFiles() {
if ((null == files) || (0 == files.size())) {
return EMPTY;
}
return (List) files.clone();
}
}

+ 103
- 0
testing/src/org/aspectj/testing/util/Diffs.java View File

@@ -0,0 +1,103 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.util;

import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.MessageUtil;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

/** result struct for expected/actual diffs for Collection */
public class Diffs {

// XXX List -> Collection b/c comparator orders
public static final Diffs NONE
= new Diffs("NONE", Collections.EMPTY_LIST, Collections.EMPTY_LIST);
/** name of the thing being diffed - used only for reporting */
public final String label;
/** immutable List */
public final List missing;

/** immutable List */
public final List unexpected;

/** true if there are any missing or unexpected */
public final boolean different;
private Diffs(String label, List missing, List unexpected) {
this.label = label;
this.missing = missing;
this.unexpected = unexpected;
different = ((0 != this.missing.size())
|| (0 != this.unexpected.size()));
}

public Diffs(String label, List expected, List actual, Comparator comparator) {
label = label.trim();
if (null == label) {
label = ": ";
} else if (!label.endsWith(":")) {
label += ": ";
}
this.label = " " + label;
ArrayList miss = new ArrayList();
ArrayList unexpect = new ArrayList();
org.aspectj.testing.util.LangUtil.makeSoftDiffs(expected, actual, miss, unexpect, comparator);
missing = Collections.unmodifiableList(miss);
unexpected = Collections.unmodifiableList(unexpect);
different = ((0 != this.missing.size())
|| (0 != this.unexpected.size()));
}
/**
* Report missing and extra items to handler.
* For each item in missing or unexpected, this creates a {kind} IMessage with
* the text "{missing|unexpected} {label}: {message}"
* where {message} is the result of
* <code>MessageUtil.renderMessage(IMessage)</code>.
* @param handler where the messages go - not null
* @param kind the kind of message to construct - not null
* @param label the prefix for the message text - if null, "" used
* @see MessageUtil#renderMessage(IMessage)
*/
public void report(IMessageHandler handler, IMessage.Kind kind) {
LangUtil.throwIaxIfNull(handler, "handler");
LangUtil.throwIaxIfNull(kind, "kind");
if (different) {
for (Iterator iter = missing.iterator(); iter.hasNext();) {
String s = MessageUtil.renderMessage((IMessage) iter.next());
MessageUtil.fail(handler, "missing " + label + s);
}
for (Iterator iter = unexpected.iterator(); iter.hasNext();) {
String s = MessageUtil.renderMessage((IMessage) iter.next());
MessageUtil.fail(handler, "unexpected " + label + s);
}
}
}
/** @return "{label}: (unexpected={#}, missing={#})" */
public String toString() {
return label + "(unexpected=" + unexpected.size()
+ ", missing=" + missing.size() + ")";
}
}


+ 745
- 0
testing/src/org/aspectj/testing/util/FileUtil.java View File

@@ -0,0 +1,745 @@
/* *******************************************************************
* Copyright (c) 1999-2000 Xerox Corporation.
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


package org.aspectj.testing.util;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringBufferInputStream;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;
import java.util.jar.Attributes;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

/**
* misc file utilities
*/
public class FileUtil {

/** default filename if URL has none (i.e., a directory URL): index.html */
public static final String DEFAULT_URL_FILENAME = "index.html";

/**
* @param args the String[]
* <code>{ "-copy", "-srcFile" | "-srcUrl", {src}, "-destFile", {destFile} }</code>
*/
public static void main (String[] args) {
if (null == args) return;
for (int i = 0; (i+4) < args.length; i++) {
if ("-copy".equals(args[i])) {
String arg = args[++i];
String src = null;
String destFile = null;
boolean srcIsFile = ("-srcFile".equals(arg));
if (srcIsFile) {
src = args[++i];
} else if ("-srcUrl".equals(arg)) {
src = args[++i];
}
if ((null != src) && ("-destFile".equals(args[++i]))) {
destFile = args[++i];
StringBuffer errs = new StringBuffer();
if (srcIsFile) {
copyFile(new File(src), new File(destFile), errs);
} else {
URL url = null;
try { url = new URL(src) ; }
catch (MalformedURLException e) { render(e, errs); }
if (null != url) {
copyURL(url, new File(destFile), errs);
}
}
if (0 < errs.length()) {
System.err.println("Error copying " + src + " to " + destFile);
System.err.println(errs.toString());
}
}
} // ("-copy".equals(args[i])){
}
} // end of main ()
/**
* Generate a list of missing and extra files by comparison to a
* timestamp, optionally excluding certain files.
* This is a call to select all files after a given time:
*
* <pre>Diffs d = dirDiffs(dir, givenTime, null, null, null);</pre>
*
* Given files
* <pre>classes/Foo.class
* classes/bar/Bash.class
* classes/Old.class
* classes/one/Unexpected.class
* classes/start.gif</pre>
* where only Old.class predated startTime, this is a call that
* reports "one/Unexpected.class" as unexpected and "Foo"
* as missing:
* <pre>String requireSuffix = ".class";
* String[] expectedPaths = new String[] { "Foo", "bar/Bas" };
* File file = new File("classes");
* Diffs d = dirDiffs(dir, startTime, requireSuffix,expectedPaths, true);</pre>
*
* @param label the String to use for the Diffs label
* @param dir the File for the dir to search
* @param startTime collect files modified after this time
* (ignored if less than 0)
* @param requireSuffix ignore all actual files without this suffix
* (ignored if null)
* @param expectedPaths paths (relative to dir) of the expected files
* (if null, none expected)
* @param acceptFilePrefix if true, then accept a file which
* differs from an expected file name only by a suffix
* (which need not begin with ".").
*/
public static Diffs dirDiffs( // XXX too complicated, weak prefix checking
final String label,
final File dir,
final long startTime,
final String requiredSuffix,
final String[] expectedPaths,
final boolean acceptFilePrefix) {
LangUtil.throwIaxIfNull(dir, "dir");
final boolean checkExpected = !LangUtil.isEmpty(expectedPaths);
// normalize sources to ignore
final ArrayList expected = (!checkExpected ? null : new ArrayList());
if (checkExpected) {
for (int i = 0; i < expectedPaths.length; i++) {
String srcPath = expectedPaths[i];
if (!LangUtil.isEmpty(srcPath)) {
expected.add(org.aspectj.util.FileUtil.weakNormalize(srcPath));
}
}
}
// gather, normalize paths changed
FileFilter touchedCollector = new FileFilter() {
/**
* For files complying with time and suffix rules,
* return true (accumulate - unexpected)
* unless they match expected files,
* (deleting any matches from sources
* so the remainder is missing).
* @return true for unexpected files after date */
public boolean accept(File file) {
if (file.isFile()
&& ((0 > startTime)
|| (startTime < file.lastModified()))) {
String path = file.getPath();
if ((null == requiredSuffix) || path.endsWith(requiredSuffix)) {
path = org.aspectj.util.FileUtil.weakNormalize(path);
if (checkExpected) {
if (!acceptFilePrefix) {
// File.equals(..) does lexical compare
if (expected.contains(path)) {
expected.remove(path);
// found - do not add to unexpected
return false;
}
} else {
for (Iterator iter = expected.iterator();
iter.hasNext();
) {
String exp = (String) iter.next();
if (path.startsWith(exp)) {
String suffix = path.substring(exp.length());
if (-1 == suffix.indexOf("/")) { // normalized...
expected.remove(path);
// found - do not add to unexpected
return false;
}
}
}
}
}
// add if is file, right time, and have or don't need suffix
return true;
}
}
// skip if not file or not right time
return false;
}
};
ArrayList unexp = new ArrayList();
unexp.addAll(Arrays.asList(dir.listFiles(touchedCollector)));
// report any unexpected changes
return new Diffs(label, expected, unexp, String.CASE_INSENSITIVE_ORDER);
}


/**
* Visit the entries in a zip file, halting when visitor balks.
* Errors are silently ignored.
* @throws IllegalArgumentException if zipfile or visitor is null
*/
public static void visitZipEntries(ZipFile zipfile, StringVisitor visitor) {
visitZipEntries(zipfile, visitor, (StringBuffer) null);
}
/**
* Visit the entries in a zip file, halting when visitor balks.
* Errors are reported in errs, if not null.
* @throws IllegalArgumentException if zipfile or visitor is null
*/
public static void visitZipEntries(ZipFile zipfile, StringVisitor visitor,
StringBuffer errs) {
if (null == zipfile) throw new IllegalArgumentException("null zipfile");
if (null == visitor) throw new IllegalArgumentException("null visitor");
int index = 0;
try {
Enumeration enum = zipfile.entries();
while (enum.hasMoreElements()) {
ZipEntry entry = (ZipEntry) enum.nextElement();
index++;
if (! visitor.accept(entry.getName())) {
break;
}
}
} catch (Throwable e) {
if (null != errs) {
errs.append("FileUtil.visitZipEntries error accessing entry " + index
+ ": " + e.getMessage());
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
errs.append(sw.toString());
}
} finally {
if (null != zipfile) {
try { zipfile.close(); }
catch (IOException x) {} // ignore
}
}
}

/**
* descend filesystem tree, invoking FileFilter.accept() on files.
* E.g., To list files from current directory:
* <code><pre>descendFileTree(new File("."), new FileFilter() {
* public boolean accept(File f){
* System.out.println(f.getAbsolutePath());
* return true;
* }});</code></pre>
* @param file root/starting point. If a file, the only one visited.
* @param filter supplies accept(File) routine
*/
public static void descendFileTree(File file, FileFilter filter) {
descendFileTree(file, filter, false);
}

/**
* Descend filesystem tree, invoking FileFilter.accept() on files
* and, if userRecursion, on dirs. If userRecursion, accept() must
* call descendFileTree() again to recurse down directories.
* This calls fileFilter.accept(File) on all files before doing any dirs.
* E.g., To list only files from Unix root:
* <code><pre>descendFileTree(new File("/"), new FileFilter() {
* public boolean run(File f){
* System.out.println(f.getAbsolutePath());
* return true;
* }}, false);</code></pre>
* To list files/dir from root using user recursion:
* <code><pre>descendFileTree(new File("/"), new FileFilter() {
* public boolean run(File f){
* System.out.println(f.getAbsolutePath());
* if (f.isDirectory() && (-1 == f.getName().indexOf("CVS")))
* return descendFileTree(f, this, true);
* return true;
* }}, true);</code></pre>
* @param file root/starting point. If a file, the only one visited.
* @param filter supplies boolean accept(File) method
* @param userRecursion - if true, do accept() on dirs; else, recurse
* @return false if any fileFilter.accept(File) did.
* @throws IllegalArgumentException if file or fileFilter is null
*/
public static boolean descendFileTree(File file, FileFilter fileFilter,
boolean userRecursion) {
if (null == file) {throw new IllegalArgumentException("parm File"); }
if (null == fileFilter){throw new IllegalArgumentException("parm FileFilter");}

if (!file.isDirectory()) {
return fileFilter.accept(file);
} else if (file.canRead()) {
// go through files first
File[] files = file.listFiles(ValidFileFilter.FILE_EXISTS);
if (null != files) {
for (int i = 0; i < files.length; i++) {
if (!fileFilter.accept(files[i])) {
return false;
}
}
}
// now recurse to handle directories
File[] dirs = file.listFiles(ValidFileFilter.DIR_EXISTS);
if (null != dirs) {
for (int i = 0; i < dirs.length; i++) {
if (userRecursion) {
if (!fileFilter.accept(dirs[i])) {
return false;
}
} else {
if (!descendFileTree(dirs[i], fileFilter,userRecursion)) {
return false;
}
}
}
}
} // readable directory (ignore unreadable ones)
return true;
} // descendFiles

/**
* Return the names of all files below a directory.
* If file is a directory, then all files under the directory
* are returned. If file is absolute or relative, all the files are.
* If file is a zip or jar file, then all entries in the zip or jar
* are listed. Entries inside those jarfiles/zipfiles are not listed.
* There are no guarantees about ordering.
* @param dir the File to list for
* @param results the Collection to use for the results (may be null)
* @throws IllegalArgumentException if null == dir
* @return a Collection of String of paths, including paths inside jars
*/
public static Collection directoryToString(File dir, Collection results) {
if (null == dir) throw new IllegalArgumentException("null dir");
final Collection result = (results != null? results : new Vector());
if (isZipFile(dir)) {
zipFileToString(dir, result);
} else if (!dir.isDirectory()) {
throw new IllegalArgumentException("not a dir: " + dir);
} else {
AccumulatingFileFilter acFilter = new AccumulatingFileFilter() {
public boolean accumulate(File file) {
String name = file.getPath();
result.add(name);
if (isZipFile(file)) {
zipFileToString(file, result);
}
return true;
}
};
descendFileTree(dir, acFilter, false);
}
return result;
} // directoryToString

/**
* Render as String the entries in a zip or jar file,
* converting each to String beforehand (as jarpath!jarentry)
* applying policies for whitespace, etc.
* @param file the File to enumerate ZipEntry for
* @param results the Colection to use to return the FileLine - may be null
* @return FileLines with string as text and
* canonical as string modified by any canonicalizing policies.
*/
public static Collection zipFileToString(final File zipfile, Collection results) {
Collection result = (results != null ? results : new Vector());
ZipFile zip = null;
try {
zip = new ZipFile(zipfile); // ZipFile.OPEN_READ| ZipFile.OPEN_DELETE); delete is 1.3 only
Enumeration enum = zip.entries();
while (enum.hasMoreElements()) {
results.add(renderZipEntry(zipfile, (ZipEntry) enum.nextElement()));
}
zip.close();
zip = null;
} catch (Throwable t) {
String err = "Error opening " + zipfile + " attempting to continue...";
System.err.println(err);
t.printStackTrace(System.err);
} finally {
if (null != zip) {
try { zip.close(); }
catch (IOException e) {
e.printStackTrace(System.err);
}
}
}
return result;
}

/**
* @return true if file represents an existing file with a zip extension
*/
public static boolean isZipFile(File f) {
String s = null;
if ((null == f) || (null == (s = f.getPath()))) {
return false;
} else {
return (f.canRead() && !f.isDirectory()
&& (s.endsWith(".zip")
|| (s.endsWith(".jar"))));
}
}

/**
* Render a zip/entry combination to String
*/
public static String renderZipEntry(File zipfile, ZipEntry entry) {
String filename = (null == zipfile ? "null File" : zipfile.getName());
String entryname = (null == entry ? "null ZipEntry" : entry.getName());
return filename + "!" + entryname;
}

/**
* Write all files in directory out to jarFile
* @param jarFile the File to create and write to
* @param directory the File representing the directory to read
* @param mainClass the value of the main class attribute - may be null
*/
public static boolean createJarFile(File jarFile, File directory,
String mainClass, FileFilter filter) {
String label = "createJarFile("+jarFile
+","+directory +","+mainClass +","+filter + "): ";
Log.signal(label + " start");
if (null == directory)
throw new IllegalArgumentException("null directory");
Manifest manifest = createManifest(mainClass);
Log.signal(label + " manifest=" + manifest);
JarOutputStream out = null;
try {
File jarFileDir = jarFile.getParentFile();
if (null == jarFileDir) {
Log.signal(label + " null jarFileDir");
} else if (!jarFileDir.exists() && !jarFileDir.mkdirs()) { // XXX convert to Error
Log.signal(label + " unable to create jarFileDir: " + jarFileDir);
}
OutputStream os = new FileOutputStream(jarFile);
out = (null == manifest ? new JarOutputStream(os)
: new JarOutputStream(os, manifest));
Log.signal(label + " out=" + out);
ZipAccumulator reader = new ZipAccumulator(directory, out, filter);
Log.signal(label + " reader=" + reader);
FileUtil.descendFileTree(directory, reader);
out.closeEntry();
return true;
} catch (IOException e) {
e.printStackTrace(System.err); // todo
} finally {
if (null != out) {
try { out.close();}
catch (IOException e) {} // todo ignored
}
}
return false;
}

protected static Manifest createManifest(String mainClass) {
final String mainKey = "Main-Class";
Manifest result = null;
if (null != mainClass) {
String entry = "Manifest-Version: 1.0\n"
+ mainKey + ": " + mainClass + "\n";
try {
result = new Manifest(new StringBufferInputStream(entry));
Attributes attributes = result.getMainAttributes();
String main = attributes.getValue(mainKey);
if (null == main) {
attributes.putValue(mainKey, mainClass);
main = attributes.getValue(mainKey);
if (null == main) {
Log.signal("createManifest unable to set main "
+ mainClass);
}
}
} catch (IOException e) { // todo ignoring
Log.signal(e, " IOException creating manifest with " + mainClass);
}
}
return result;
}


/** read a file out to the zip stream */
protected static void addFileToZip(File in, File parent,
ZipOutputStream out)
throws IOException {
String path = in.getCanonicalPath();
String parentPath = parent.getCanonicalPath();
if (!path.startsWith(parentPath)) {
throw new Error("not parent: " + parentPath + " of " + path);
} else {
path = path.substring(1+parentPath.length());
path = path.replace('\\', '/'); // todo: use filesep
}
ZipEntry entry = new ZipEntry(path);
entry.setTime(in.lastModified());
// todo: default behavior is DEFLATED

out.putNextEntry(entry);

InputStream input = null;
try {
input = new FileInputStream(in);
byte[] buf = new byte[1024];
int count;
while (0 < (count = input.read(buf, 0, buf.length))) {
out.write(buf, 0, count);
}
} finally {
if (null != input) input.close();
}
}


public static void returnTempDir(File dir) {
deleteDirectory(dir);
}

/** @return true if path ends with gif, properties, jpg */
public static boolean isResourcePath(String path) {
if (null == path) return false;
path = path.toLowerCase();
return (path.endsWith(".gif")
|| path.endsWith(".properties")
|| path.endsWith(".jpg")
|| path.endsWith(".jpeg")
|| path.endsWith(".props")
);
}

public static void render(Throwable t, StringBuffer err) { // todo: move
String name = t.getClass().getName();
int loc = name.lastIndexOf(".");
name = name.substring(1+loc);
err.append(name + ": " + t.getMessage() + "\n"); // todo
StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw));
err.append(sw.toString());
}

private static boolean report(StringBuffer err, String context, String status,
Throwable throwable) {
boolean failed = ((null != status) || (null != throwable));
if ((null != err) && (failed)) {
if (null != context) {
err.append(context);
}
if (null != status) {
err.append(status);
}
if (null != throwable) {
render(throwable, err);
}
}
return failed;
}

/**
* Copy file.
* @param src the File to copy - must exist
* @param dest the File for the target file or directory (will not create directories)
* @param err the StringBuffer for returning any errors - may be null
**/
public static boolean copyFile(File src, File dest, StringBuffer err) {
boolean result = false;
String label = "start";
Throwable throwable = null;
try {
if (!ValidFileFilter.FILE_EXISTS.accept(src)) {
label = "src file does not exist";
} else {
if (dest.isDirectory()) {
dest = new File(dest, src.getName());
}
if (ValidFileFilter.FILE_EXISTS.accept(dest)) {
label = "dest file exists";
}
boolean closeWhenDone = true;
result = copy(new FileInputStream(src),
new FileOutputStream(dest),
closeWhenDone);
}
label = null;
} catch (Throwable t) {
throwable = t;
}
String context = "FileUtil.copyFile(src, dest, err)";
boolean report = report(err, context, label, throwable);
return (result && report);
}

/**
* Copy URL to file.
* @param src the URL to copy - must exist
* @param dest the File for the target file or directory (will not create directories)
* @param err the StringBuffer for returning any errors - may be null
**/
public static boolean copyURL(URL url, File dest, StringBuffer err) { // todo untested.
boolean result = false;
String label = "start";
Throwable throwable = null;
try {
if (dest.isDirectory()) {
String filename = url.getFile();
if ((null == filename) || (0 == filename.length())) {
filename = DEFAULT_URL_FILENAME;
}
dest = new File(dest, filename);
}
if (ValidFileFilter.FILE_EXISTS.accept(dest)) {
label = "dest file exists";
}
boolean closeWhenDone = true;
result = copy(url.openConnection().getInputStream(),
new FileOutputStream(dest),
closeWhenDone);
label = null;
} catch (Throwable t) {
throwable = t;
}
String context = "FileUtil.copyURL(src, dest, err)"; // add actual parm to labels?
boolean report = report(err, context, label, throwable);
return (result && report);
}

/**
* Copy input to output - does not close either
* @param src the InputStream to copy - must exist
* @param dest the OutputStream for the target
* @param close if true, close when done
*/
public static boolean copy(InputStream src, OutputStream dest,
boolean close)
throws IOException {
boolean result = false;
IOException throwable = null;
try {
byte[] buf = new byte[8*1024];
int count;
while (0 < (count = src.read(buf, 0, buf.length))) {
dest.write(buf, 0, count);
}
result = true;
} catch (IOException t) {
throwable = t;
} finally {
if (close) {
try { if (null != src) src.close(); }
catch (IOException e) {
if (null == throwable) { throwable = e; }
}
try { if (null != dest) dest.close(); }
catch (IOException i) {
if (null == throwable) { throwable = i; }
}
}
}
if (null != throwable) throw throwable;
return result;
}

/**
* @return true if dir was an existing directory that is now deleted
*/
protected static boolean deleteDirectory(File dir) {
return ((null != dir)
&& dir.exists()
&& dir.isDirectory()
&& FileUtil.descendFileTree(dir, DELETE_FILES, false)
&& FileUtil.descendFileTree(dir, DELETE_DIRS, true)
&& dir.delete());
}

public static String[] getPaths(File[] files) { // util
String[] result = new String[files.length];
for (int i = 0; i < result.length; i++) {
result[i] = files[i].getPath(); // preserves absolute?
}
return result;
}

//-------- first-order, input and visible interface

protected static final FileFilter DELETE_DIRS = new FileFilter() {
public boolean accept(File file) {
return ((null != file) && file.isDirectory()
&& file.exists() && file.delete());
}
};
protected static final FileFilter DELETE_FILES = new FileFilter() {
public boolean accept(File file) {
return ((null != file) && !file.isDirectory()
&& file.exists() && file.delete());
}
};

} // class FileUtil

/**
* Localize FileUtil log/signals for now
* ordinary signals are ignored,
* but exceptions are printed to err
* and errors are thrown as Error
*/
class Log {
/** ordinary logging - may be suppressed */
public static final void signal(String s) {
//System.err.println(s);
}
/** print stack trace to System.err */
public static final void signal(Throwable t, String s) {
System.err.println(s);
t.printStackTrace(System.err);
}
/** @throws Error(s) always */
public static final void error(String s) {
throw new Error(s);
}
}

/** read each file out to the zip file */
class ZipAccumulator implements FileFilter {
final File parentDir;
final ZipOutputStream out;
final FileFilter filter;
public ZipAccumulator(File parentDir, ZipOutputStream out,
FileFilter filter) {
this.parentDir = parentDir;
this.out = out;
this.filter = filter;
}
public boolean accept(File f) {
if ((null != filter) && (!filter.accept(f))) {
return false;
}
try {
FileUtil.addFileToZip(f, parentDir, out);
return true;
} catch (IOException e) {
e.printStackTrace(System.err); // todo
}
return false;
}
}


+ 118
- 0
testing/src/org/aspectj/testing/util/IntRange.java View File

@@ -0,0 +1,118 @@
/* *******************************************************************
* Copyright (c) 1999-2000 Xerox Corporation.
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


package org.aspectj.testing.util;


import java.io.Serializable;

/**
* imutable class to enforce an integer range
*/
public class IntRange implements IntValidator, ObjectChecker, Serializable {
/** no values permitted */
public static final IntRange NONE = new IntRange(0, 0);
/** 0 permitted */
public static final IntRange ZERO = new IntRange(0, 1);
/** 1 permitted */
public static final IntRange ONE = new IntRange(1, 2);
/** 0..1 permitted */
public static final IntRange OPTIONAL = new IntRange(0, 2);
/** 1..1000 permitted */
public static final IntRange MANY = new IntRange(1, 1001);

/** all positive numbers permitted except Integer.MAX_VALUE */
public static final IntRange POSITIVE = new IntRange(1, Integer.MAX_VALUE);
/** all negative numbers permitted */
public static final IntRange NEGATIVE = new IntRange(Integer.MIN_VALUE, 0);
/** any int number permitted except Integer.MAX_VALUE */
public static final IntRange ANY = new IntRange(Integer.MIN_VALUE, Integer.MAX_VALUE);

/**
* Make an IntRange that accepts this value
* (using existing if available).
* @throws IllegalArgumentException if value is Integer.MAX_VALUE.
*/
public static final IntRange make(int value) {
switch (value) {
case (1) : return ONE;
case (0) : return ZERO;
case (Integer.MAX_VALUE) :
throw new IllegalArgumentException("illegal " + value);
default :
return new IntRange(value, value + 1);
}
}
public final int min;
public final int max;
private transient String cache;

/** use only for serialization
* @deprecated IntRange(int, int)
*/
protected IntRange() {
min = 0;
max = 0;
}
/**
* @param min minimum permitted value, inclusive
* @param max maximum permitted value, exclusive
*/
public IntRange(int min, int max) {
this.min = min;
this.max = max;
if (min > max) {
throw new IllegalArgumentException( min + " > " + max);
}
toString(); // create cache to view during debugging
}
/** @return true if integer instanceof Integer with acceptable intValue */
public final boolean isValid(Object integer) {
return ((integer instanceof Integer)
&& (acceptInt(((Integer) integer).intValue())));
}
/** @return true if min <= value < max */
public final boolean acceptInt(int value) {
return ((value >= min) && (value < max));
}

/**
* @deprecated acceptInt(int)
* @return true if min <= value < max
*/
public final boolean inRange(int value) {
return acceptInt(value);
}
/**
* @return true if, for any int x s.t. other.inRange(x)
* is true, this.inRange(x) is also true
*/
public final boolean inRange(IntRange other) {
return ((null != other) && (other.min >= min)
&& (other.max <= max));
}
// XXX equals(Object)
public String toString() {
if (null == cache) {
cache = "IntRange [" + min + ".." + max + "]";
}
return cache;
}
}

+ 21
- 0
testing/src/org/aspectj/testing/util/IntValidator.java View File

@@ -0,0 +1,21 @@
/* *******************************************************************
* Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.util;

/**
* @author isberg
*/
public interface IntValidator {
/** @return true if this is a valid value */
public boolean acceptInt(int value);
}

+ 147
- 0
testing/src/org/aspectj/testing/util/IteratorWrapper.java View File

@@ -0,0 +1,147 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.util;

import java.util.Iterator;
import java.util.List;

/**
* This iterates in order through the permutations of Lists.
* Order and numericity depend on the underlying list iterators
* and the order in which the lists are supplied to the constructor.
* @author isberg
*/
public class IteratorWrapper implements Iterator {

final List[] lists;
final Iterator[] iterators;
final Object[] current;
Object[] next;
/** number of elements in each array */
final int maxDepth;

/**
* Current level being iterated.
* Set to 0 whenever depth is incremented.
* Incremented when iterator for the level has no more elements.
*/
int currentLevel;
/**
* Maximum depth iterated-to thus far.
* Set to 0 on initialization.
* Incremented when incrementing currentLevel brings it past depth.
* Run completes when depth = maxDepth or any new iterator has no elements
*/
int depth;


/** @throws IllegalArgumentException if lists or any element null */
public IteratorWrapper(List[] lists) {
if (null == lists) {
throw new IllegalArgumentException("null lists");
}
maxDepth = lists.length;
currentLevel = 0;
depth = 0;
List[] temp = new List[maxDepth];
System.arraycopy(lists, 0, temp, 0, temp.length);
for (int i = 0; i < maxDepth; i++) {
if (null == temp[i]) {
throw new IllegalArgumentException("null List[" + i + "]");
}
}
this.lists = temp;
current = new Object[maxDepth];
iterators = new Iterator[maxDepth];
reset();
}
/** Reset to the initial state of the iterator */
public void reset() {
next = null;
for (int i = 0; i < lists.length; i++) {
iterators[i] = lists[i].iterator();
if (!iterators[i].hasNext()) { // one iterator is empty - never go
depth = maxDepth;
break;
} else {
current[i] = iterators[i].next();
}
}
if (depth < maxDepth) {
next = getCurrent();
}
}

/** @throws UnsupportedOperationException always */
public void remove() {
throw new UnsupportedOperationException("operation ambiguous");
}

public boolean hasNext() {
return (null != next);
}

/**
* @return Object[] with each element from the iterator of the
* corresponding list which was passed to the constructor for this.
*/
public Object next() {
Object result = next;
next = getNext();
return result;
}

private Object[] getCurrent() {
Object[] result = new Object[maxDepth];
System.arraycopy(current, 0, result, 0, maxDepth);
return result;
}
private Object[] getNext() {
int initialLevel = currentLevel;
while (depth < maxDepth) {
if (iterators[currentLevel].hasNext()) {
current[currentLevel] = iterators[currentLevel].next();
if (currentLevel > initialLevel) {
currentLevel = 0;
}
return getCurrent();
} else { // pop
// reset this level
iterators[currentLevel] = lists[currentLevel].iterator();
if (!iterators[currentLevel].hasNext()) { // empty iterator - quit
depth = maxDepth;
return null;
}
current[currentLevel] = iterators[currentLevel].next();
// do the next level
currentLevel++;
if (currentLevel > depth) {
depth++;
}
}
}
return null;
}
/** @return "IteratorWrapper({{field}={value}}..)" for current, ceiling, and max */
public String toString() {
return "IteratorWrapper(currentLevel=" + currentLevel
+ ", maxLevel=" + depth
+ ", size=" + maxDepth + ")";
}
}

+ 1192
- 0
testing/src/org/aspectj/testing/util/LangUtil.java
File diff suppressed because it is too large
View File


+ 204
- 0
testing/src/org/aspectj/testing/util/LineReader.java View File

@@ -0,0 +1,204 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


/*
* LineReader.java created on May 3, 2002
*
*/
package org.aspectj.testing.util;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.util.ArrayList;

/** LineNumberReader which absorbs our lines and renders as file:line */
public class LineReader extends LineNumberReader {
/** delimited multi-line output of readToBlankLine */
public static final String RETURN= "\n\r";
private static final String[] NONE = new String[0];
private static final String cSCRIPT = "#";
private static final String cJAVA = "//";
private static final String[] TESTER_LEAD = new String[] {cSCRIPT, cJAVA};
/**
* Convenience factory for tester suite files
* @return null if IOException or IllegalArgumentException thrown
*/
public static final LineReader createTester(File file) {
return create(file, TESTER_LEAD, null);
}
/**
* convenience factory
* @return null if IOException or IllegalArgumentException thrown
*/
public static final LineReader create(File file,
String[] leadComments, String[] eolComments) {
try {
FileReader reader = new FileReader(file);
return new LineReader(reader, file, leadComments, eolComments);
} catch (IllegalArgumentException e) {
} catch (IOException e) {
}
return null;
}
final private File file;
final private String[] eolComments;
final private String[] leadComments;
/**
* @param file the File used to open the FileReader
* @param leadComments the String[] to be taken as the start of
* comments when they are the first non-blank text on a line -
* pass null to signal none.
* @param leadComments the String[] to be taken as the start of
* comment anywhere on a line - pass null to signal none.
*@throws IllegalArgumentException if any String in
* leadComments or eolComments is null.
*/
public LineReader(FileReader reader, File file,
String[] leadComments, String[] eolComments) {
super(reader);
this.file = file;
this.eolComments = normalize(eolComments);
this.leadComments = normalize(leadComments);
}
public LineReader(FileReader reader, File file) {
this(reader, file, null, null);
}
/** @return file:line */
public String toString() {
return file.getPath() + ":" + getLineNumber();
}
/** @return underlying file */
public File getFile() { return file; }

/**
* Reader first..last (inclusive) and return in String[].
* This will return (1+(last-first)) elements only if this
* reader has not read past the first line and there are last lines
* and there are no IOExceptions during reads.
* @param first the first line to read - if negative, use 0
* @param last the last line to read (inclusive)
* - if less than first, use first
* @return String[] of first..last (inclusive) lines read or
*/
public String[] readLines(int first, int last) {
if (0 > first) first = 0;
if (first > last) last = first;
ArrayList list = new ArrayList();
try {
String line = null;
while (getLineNumber() < first) {
line = readLine();
if (null == line) {
break;
}
}
if (getLineNumber() > first) {
// XXX warn? something else read past line
}
if ((null != line) && (first == getLineNumber())) {
list.add(line);
while (last >= getLineNumber()) {
line = readLine();
if (null == line) {
break;
}
list.add(line);
}
}
} catch (IOException e) {
return NONE;
}
return (String[]) list.toArray(NONE);
}
/** Skip to next blank line
* @return the String containing all lines skipped (delimited with RETURN)
*/
public String readToBlankLine() throws IOException {
StringBuffer sb = new StringBuffer();
String input;
while (null != (input = nextLine(false))) { // get next empty line to restart
sb.append(input);
sb.append(RETURN);// XXX verify/ignore/correct
}
return sb.toString();
}

/**
* Get the next line from the input stream, stripping eol and
* leading comments.
* If emptyLinesOk is true, then this reads past lines which are
* empty after omitting comments and trimming until the next non-empty line.
* Otherwise, this returns null on reading an empty line.
* (The input stream is not exhausted until this
* returns null when emptyLines is true.)
* @param emptyLinesOk if false, return null if the line is empty
* @return next non-null, non-empty line in reader,
* ignoring comments
*/
public String nextLine(boolean emptyLinesOk) throws IOException {
int len = 0;
String result = null;
do {
result = readLine();
if (result == null)
return null;
result = result.trim();
for (int i = 0; i < eolComments.length; i++) {
int loc = result.indexOf(eolComments[i]);
if (-1 != loc) {
result = result.substring(0, loc);
break;
}
}
len = result.length();
if (0 < len) {
for (int i = 0; i < leadComments.length; i++) {
if (result.startsWith(leadComments[i])) {
result = "";
break;
}
}
len = result.length();
}
len = result.length();
if (!emptyLinesOk && (0 == len))
return null;
} while (0 == len);
return result;
}

private String[] normalize(String[] input) {
if ((null == input) || (0 == input.length)) return NONE;
String[] result = new String[input.length];
System.arraycopy(input, 0, result, 0, result.length);
for (int i = 0; i < result.length; i++) {
if ((null == result[i]) || (0 == result[i].length())) {
throw new IllegalArgumentException("empty input at [" + i + "]");
}
}
return result;
}

}


+ 162
- 0
testing/src/org/aspectj/testing/util/Node.java View File

@@ -0,0 +1,162 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


/*
* Node.java created on May 14, 2002
*
*/
package org.aspectj.testing.util;


import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;

/**
* A node in a tree containing other Node or SpecElements items.
*/
public class Node { // XXX render
public static final Node[] EMPTY_NODES = new Node[0];
public static final Object[] EMPTY_ITEMS = new Object[0];

/**
* Visit all the SpecElements (and Node) reachable from node
* in depth-first order, halting if checker objects.
* @param node the Node to pass to checker
* @param itemChecker the ObjectChecker to pass items to
* @param nodeVisitor if not null, then use instead of recursing
* @return false on first objection, true otherwise
* @throws IllegalArgumentExcpetion if checker is null
*/
public static final boolean visit(Node node, ObjectChecker itemChecker,
ObjectChecker nodeVisitor) {
if (null == node) {
return (null == itemChecker ? true : itemChecker.isValid(null));
}
boolean result = true;

Node[] nodes = node.getNodes();
for (int i = 0; result && (i < nodes.length); i++) {
result = (null == nodeVisitor
? visit(nodes[i], itemChecker, null)
: nodeVisitor.isValid(nodes[i]));
}
if (result) {
Object[] elements = node.getItems();
for (int i = 0; result && (i < elements.length); i++) {
result = itemChecker.isValid(elements[i]);
}
}

return result;
}

public final String name;
public final Class type;
public final Object key;
protected final Object[] typeArray;
protected final List nodes;
protected final List items;

public Node() {
this("Node");
}

public Node(String name) {
this(name, null);
}

/** use the name as the key */
public Node(String name, Class type) {
this(name, type, name);
}
/** */
public Node(String name, Class type, Object key) {
if (null == name) {
throw new IllegalArgumentException("null name");
}
if (null == key) {
throw new IllegalArgumentException("null key");
}
this.name = name;
this.type = type;
this.key = key;
nodes = new ArrayList();
items = new ArrayList();
if (type == null) {
type = Object.class;
}
typeArray = (Object[]) Array.newInstance(type, 0);
}

/**
* clear all items and nodes.
*/
public void clear() { // XXX synchronize
nodes.clear();
items.clear();
}
/**
* Add item to list of items
* unless it is null, of the wrong type, or the collection fails to add
* @return true if added
*/
public boolean add(Object item) {
if (null == item)
throw new IllegalArgumentException("null item");
if ((null != type) && (!type.isAssignableFrom(item.getClass()))) {
return false;
}
return items.add(item);
}

/**
* Add node to list of nodes
* unless it is null, of the wrong type, or the collection fails to add
* @return true if added
*/
public boolean addNode(Node node) {
if (null == node) {
throw new IllegalArgumentException("null node");
}
return nodes.add(node);
}

/**
* Get the current list of nodes - never null
*/
public Node[] getNodes() {
if ((null == nodes) || (1 > nodes.size())) {
return EMPTY_NODES;
}
return (Node[]) nodes.toArray(EMPTY_NODES);
}

/**
* Get the current list of items - never null
* @return items in current list, cast to type[] if type was not null
*/
public Object[] getItems() {
if ((null == items) || (1 > items.size())) {
return EMPTY_ITEMS;
}
return items.toArray(typeArray);
}

/** @return name */
public String toString() {
return name;
}
}

+ 95
- 0
testing/src/org/aspectj/testing/util/NullPrintStream.java View File

@@ -0,0 +1,95 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


/*
* NullPrintStream.java created on May 29, 2002
*
*/
package org.aspectj.testing.util;

import java.io.OutputStream;
import java.io.PrintStream;

/**
* Ignore any output to a NullPrintStream.
* Clients use singleton NULL_PrintStream or NULL_OutputStream.
* @author isberg
*/
public final class NullPrintStream extends PrintStream {

public static final OutputStream NULL_OutputStream = NullOutputStream.ME;
public static final PrintStream NULL_PrintStream = new NullPrintStream();

private NullPrintStream() {
super(NULL_OutputStream);
}
public void write(int b) {
}
public void write(byte[] b) {
}
public void write(byte[] b, int off, int len) {
}
public void print(boolean arg0) {
}
public void print(char arg0) {
}
public void print(char[] arg0) {
}
public void print(double arg0) {
}
public void print(float arg0) {
}
public void print(int arg0) {
}
public void print(long arg0) {
}
public void print(Object arg0) {
}
public void print(String arg0) {
}
public void println() {
}
public void println(boolean arg0) {
}
public void println(char arg0) {
}
public void println(char[] arg0) {
}
public void println(double arg0) {
}
public void println(float arg0) {
}
public void println(int arg0) {
}
public void println(long arg0) {
}
public void println(Object arg0) {
}
public void println(String arg0) {
}

}

final class NullOutputStream extends OutputStream {
static final OutputStream ME = new NullOutputStream();

private NullOutputStream() {
}
public void write(int b) {
}
public void write(byte[] b) {
}
public void write(byte[] b, int off, int len) {
}
}

+ 38
- 0
testing/src/org/aspectj/testing/util/ObjectChecker.java View File

@@ -0,0 +1,38 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


package org.aspectj.testing.util;

/**
* Check input for validity.
*/
public interface ObjectChecker {
/** this returns true for any input, even if null */
public static final ObjectChecker ANY = new ObjectChecker() {
public final boolean isValid(Object input) { return true; }
public final String toString() { return "ObjectChecker.ANY"; }
};
/** this returns true for any non-null object */
public static final ObjectChecker NOT_NULL = new ObjectChecker() {
public boolean isValid(Object input) { return (null != input); }
public String toString() { return "ObjectChecker.MOT_NULL"; }
};
/**
* Check input for validity.
* @param input the Object to check
* @return true if input is ok
*/
public boolean isValid(Object input);
}

+ 104
- 0
testing/src/org/aspectj/testing/util/ProxyPrintStream.java View File

@@ -0,0 +1,104 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.util;

import java.io.IOException;
import java.io.PrintStream;

/** Wrap a delegate PrintStream, permitting output to be suppressed. */
public class ProxyPrintStream extends PrintStream {

private final PrintStream delegate;
private boolean hiding;
public ProxyPrintStream(PrintStream delegate ) {
super(NullPrintStream.NULL_OutputStream);
LangUtil.throwIaxIfNull(delegate, "delegate");
this.delegate = delegate;
}
public void hide() {
hiding = true;
}
public void show() {
hiding = false;
}
public boolean isHiding() {
return hiding;
}
public void write(int b) {
if (!hiding) delegate.write(b);
}
public void write(byte[] b) throws IOException {
if (!hiding) delegate.write(b);
}
public void write(byte[] b, int off, int len) {
if (!hiding) delegate.write(b, off, len);
}
public void print(boolean arg0) {
if (!hiding) delegate.print(arg0);
}
public void print(char arg0) {
if (!hiding) delegate.print(arg0);
}
public void print(char[] arg0) {
if (!hiding) delegate.print(arg0);
}
public void print(double arg0) {
if (!hiding) delegate.print(arg0);
}
public void print(float arg0) {
if (!hiding) delegate.print(arg0);
}
public void print(int arg0) {
if (!hiding) delegate.print(arg0);
}
public void print(long arg0) {
if (!hiding) delegate.print(arg0);
}
public void print(Object arg0) {
if (!hiding) delegate.print(arg0);
}
public void print(String arg0) {
if (!hiding) delegate.print(arg0);
}
public void println() {
if (!hiding) delegate.println();
}
public void println(boolean arg0) {
if (!hiding) delegate.println(arg0);
}
public void println(char arg0) {
if (!hiding) delegate.println(arg0);
}
public void println(char[] arg0) {
if (!hiding) delegate.println(arg0);
}
public void println(double arg0) {
if (!hiding) delegate.println(arg0);
}
public void println(float arg0) {
if (!hiding) delegate.println(arg0);
}
public void println(int arg0) {
if (!hiding) delegate.println(arg0);
}
public void println(long arg0) {
if (!hiding) delegate.println(arg0);
}
public void println(Object arg0) {
if (!hiding) delegate.println(arg0);
}
public void println(String arg0) {
if (!hiding) delegate.println(arg0);
}
}

+ 368
- 0
testing/src/org/aspectj/testing/util/RunUtils.java View File

@@ -0,0 +1,368 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.util;

import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.IMessageHolder;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.bridge.MessageUtil.IMessageRenderer;
import org.aspectj.testing.harness.bridge.AbstractRunSpec;
import org.aspectj.testing.harness.bridge.IRunSpec;
import org.aspectj.testing.run.IRunStatus;
import org.aspectj.testing.run.RunValidator;
import org.aspectj.util.LangUtil;

import java.io.PrintStream;
import java.util.Iterator;

/**
*
*/
public class RunUtils {

/** enable verbose for this an any related AbstractRunSpec children */
public static void enableVerbose(AbstractRunSpec spec) { // instanceof hack
LangUtil.throwIaxIfNull(spec, "spec");
spec.runtime.setVerbose(true);
for (Iterator iter = spec.getChildren().iterator(); iter.hasNext();) {
IRunSpec child = (IRunSpec) iter.next();
if (child instanceof AbstractRunSpec) {
enableVerbose((AbstractRunSpec) child);
}
}
}
/**
* Calculate failures for this status.
* If the input status has no children and failed, the result is 1.
* If it has children and recurse is false, then
* the result is the number of children whose status has failed
* (so a failed status with some passing and no failing children
* will return 0).
* If it has children and recurse is true,
* then return the number of leaf failures in the tree,
* ignoring (roll-up) node failures.
* @return number of failures in children of this status
*/
public static int numFailures(IRunStatus status, boolean recurse) {
int numFails = 0;
IRunStatus[] children = status.getChildren();
int numChildren = (null == children? 0 : children.length);
if (0 == numChildren) {
if (!RunValidator.NORMAL.runPassed(status)) {
return 1;
}
} else {
int i = 0;
for (int j = 0; j < children.length; j++) {
if (recurse) {
numFails += numFailures(children[j], recurse);
} else {
if (!RunValidator.NORMAL.runPassed(children[j])) {
numFails++;
}
}
}
}
return numFails;
}
// ------------------------ printing status
public static void printShort(PrintStream out, IRunStatus status) {
if ((null == out) || (null == status)) {
return;
}
printShort(out, "", status);
}

public static void printShort(PrintStream out, String prefix, IRunStatus status) {
int numFails = numFailures(status, true);
String fails = (0 == numFails ? "" : " - " + numFails + " failures");
out.println(prefix + toShortString(status) + fails);
IRunStatus[] children = status.getChildren();
int numChildren = (null == children? 0 : children.length);
if (0 < numChildren) {
int i = 0;
for (int j = 0; j < children.length; j++) {
printShort(out, prefix + "[" + LangUtil.toSizedString(i++, 3) + "]: ", children[j]);
if (!RunValidator.NORMAL.runPassed(children[j])) {
numFails++;
}
}
}
}

public static void print(PrintStream out, IRunStatus status) {
if ((null == out) || (null == status)) {
return;
}
print(out, "", status);
}
public static void print(PrintStream out, String prefix, IRunStatus status) {
print(out, prefix, status, MessageUtil.MESSAGE_ALL, MessageUtil.PICK_ALL);
}

public static void print(PrintStream out, String prefix, IRunStatus status,
IMessageRenderer renderer, IMessageHandler selector) {
String label = status.getIdentifier()
+ (status.runResult() ? "PASS" : "FAIL");
out.println(prefix + label);
out.println(prefix + debugString(status));
IMessageHolder messageHolder = status;
if ((null != messageHolder) && (0 < messageHolder.numMessages(null, true))) {
MessageUtil.print(out, messageHolder, prefix, renderer, selector);
}
Throwable thrown = status.getThrown();
if (null != thrown) {
out.println(prefix + "--- printing stack trace for thrown");
thrown.printStackTrace(out);
}
IRunStatus[] children = status.getChildren();
int numChildren = (null == children? 0 : children.length);
int numFails = 0;
if (0 < numChildren) {
out.println(prefix + "--- printing children [" + numChildren + "]");
int i = 0;
for (int j = 0; j < children.length; j++) {
print(out, prefix + "[" + LangUtil.toSizedString(i++, 3) + "]: ", children[j]);
if (!RunValidator.NORMAL.runPassed(children[j])) {
numFails++;
}
}
}
if (0 < numFails) {
label = numFails + " fails " + label;
}
out.println("");
}

public static String debugString(IRunStatus status) {
if (null == status) {
return "null";
}
final String[] LABELS =
new String[] {
"runResult",
"id",
"result",
"numChildren",
"completed",
//"parent",
"abort",
"started",
"thrown",
"messages" };
String runResult = status.runResult() ? "PASS" : "FAIL";
Throwable thrown = status.getThrown();
String thrownString = LangUtil.unqualifiedClassName(thrown);
IRunStatus[] children = status.getChildren();
String numChildren = (null == children? "0" : ""+children.length);
String numMessages = ""+status.numMessages(null, IMessageHolder.EQUAL);
Object[] values =
new Object[] {
runResult,
status.getIdentifier(),
status.getResult(),
numChildren,
new Boolean(status.isCompleted()),
//status.getParent(), // costly if parent printing us
status.getAbortRequest(),
new Boolean(status.started()),
thrownString,
numMessages };
return org.aspectj.testing.util.LangUtil.debugStr(status.getClass(), LABELS, values);
}

public static String toShortString(IRunStatus status) {
if (null == status) {
return "null";
}
String runResult = status.runResult() ? " PASS: " : " FAIL: ";
return (runResult + status.getIdentifier());
}
/** renderer for IRunStatus */
public static interface IRunStatusPrinter {
void printRunStatus(PrintStream out, IRunStatus status);
}
public static final IRunStatusPrinter VERBOSE_PRINTER = new IRunStatusPrinter() {
public String toString() { return "VERBOSE_PRINTER"; }
/** Render IRunStatus produced by running an AjcTest */
public void printRunStatus(PrintStream out, IRunStatus status) {
printRunStatus(out, status, "");
}
private void printRunStatus(PrintStream out, IRunStatus status, String prefix) {
LangUtil.throwIaxIfNull(out, "out");
LangUtil.throwIaxIfNull(status, "status");
String label = (status.runResult() ? " PASS: " : " FAIL: ")
+ status.getIdentifier();
out.println(prefix + "------------ " + label);
out.println(prefix + "--- result: " + status.getResult());
if (0 < status.numMessages(null, true)) {
out.println(prefix + "--- messages ");
MessageUtil.print(out, status, prefix, MessageUtil.MESSAGE_ALL, MessageUtil.PICK_ALL);
}
Throwable thrown = status.getThrown();
if (null != thrown) {
out.println(prefix + "--- thrown");
thrown.printStackTrace(out);
}
IRunStatus[] children = status.getChildren();
for (int i = 0; i < children.length; i++) {
String number = "[" + LangUtil.toSizedString(i,3) + "] ";
printRunStatus(out, children[i], prefix + number);
}
}
};

/** print only status and fail/abort messages */
public static final IRunStatusPrinter TERSE_PRINTER = new IRunStatusPrinter() {
public String toString() { return "TERSE_PRINTER"; }

/** print only status and fail messages */
public void printRunStatus(PrintStream out, IRunStatus status) {
printRunStatus(out, status, "");
}
private void printRunStatus(PrintStream out, IRunStatus status, String prefix) {
LangUtil.throwIaxIfNull(out, "out");
LangUtil.throwIaxIfNull(status, "status");
String label = (status.runResult() ? "PASS: " : "FAIL: ")
+ status.getIdentifier();
out.println(prefix + label);
Object result = status.getResult();
if ((null != result) && (IRunStatus.PASS != result) && (IRunStatus.FAIL != result)) {
out.println(prefix + "--- result: " + status.getResult());
}
if (0 < status.numMessages(IMessage.FAIL, true)) {
MessageUtil.print(out, status, prefix, MessageUtil.MESSAGE_ALL, MessageUtil.PICK_FAIL_PLUS);
}
Throwable thrown = status.getThrown();
if (null != thrown) {
out.println(prefix + "--- thrown: " + LangUtil.renderException(thrown, true));
}
IRunStatus[] children = status.getChildren();
for (int i = 0; i < children.length; i++) {
if (!children[i].runResult()) {
String number = "[" + LangUtil.toSizedString(i,3) + "] ";
printRunStatus(out, children[i], prefix + number);
}
}
out.println("");
}
};

/** Render IRunStatus produced by running an AjcTest.Suite. */
public static final IRunStatusPrinter AJCSUITE_PRINTER = new IRunStatusPrinter() {
public String toString() { return "AJCSUITE_PRINTER"; }
/**
* Render IRunStatus produced by running an AjcTest.Suite.
* This renders only test failures and
* a summary at the end.
*/
public void printRunStatus(PrintStream out, IRunStatus status) {
LangUtil.throwIaxIfNull(out, "out");
LangUtil.throwIaxIfNull(status, "status");
final String prefix = "";
final boolean failed = status.runResult();
String label = (status.runResult() ? "PASS: " : "FAIL: ")
+ status.getIdentifier();
out.println(prefix + label);
// print all messages - these are validator comments
if (0 < status.numMessages(null, true)) {
MessageUtil.print(out, status, "init", MessageUtil.MESSAGE_ALL, MessageUtil.PICK_ALL);
}
// XXX ignore thrown if failed - will be printed as message anyway?
Throwable thrown = status.getThrown();
if ((null != thrown) && !failed) {
out.println(prefix + "--- printing stack trace for thrown");
thrown.printStackTrace(out);
}
IRunStatus[] children = status.getChildren();
int numChildren = (null == children? 0 : children.length);
int numFails = 0;
if (0 < numChildren) {
for (int j = 0; j < children.length; j++) {
if (!RunValidator.NORMAL.runPassed(children[j])) {
numFails++;
}
}
}
if (0 < numFails) {
out.println(prefix + "--- " + numFails + " failures when running " + children.length + " tests");
for (int j = 0; j < children.length; j++) {
if (!RunValidator.NORMAL.runPassed(children[j])) {
print(out, prefix + "[" + LangUtil.toSizedString(j, 3) + "]: ", children[j]);
out.println("");
}
}
}
label = "ran " + children.length + " tests"
+ (numFails == 0 ? "" : "(" + numFails + " fails)");
out.println("");
}
};
/** Render IRunStatus produced by running an AjcTest (verbose) */
public static final IRunStatusPrinter AJCTEST_PRINTER = VERBOSE_PRINTER;

/** print this with messages, then children using AJCRUN_PRINTER */
public static final IRunStatusPrinter AJC_PRINTER = new IRunStatusPrinter() {
public String toString() { return "AJC_PRINTER"; }
/** Render IRunStatus produced by running an AjcTest */
public void printRunStatus(PrintStream out, IRunStatus status) {
LangUtil.throwIaxIfNull(out, "out");
LangUtil.throwIaxIfNull(status, "status");
String label = (status.runResult() ? " PASS: " : " FAIL: ")
+ status.getIdentifier();
out.println("------------ " + label);
MessageUtil.print(out, status, "", MessageUtil.MESSAGE_ALL, MessageUtil.PICK_ALL);
IRunStatus[] children = status.getChildren();
for (int i = 0; i < children.length; i++) {
AJCRUN_PRINTER.printRunStatus(out, children[i]);
}
//out.println("------------ END " + label);
out.println("");
}
};


/** print only fail messages */
public static final IRunStatusPrinter AJCRUN_PRINTER = new IRunStatusPrinter() {
public String toString() { return "AJCRUN_PRINTER"; }
/** Render IRunStatus produced by running an AjcTest child */
public void printRunStatus(PrintStream out, IRunStatus status) {
LangUtil.throwIaxIfNull(out, "out");
LangUtil.throwIaxIfNull(status, "status");
final boolean orGreater = false;
int numFails = status.numMessages(IMessage.FAIL, orGreater);
if (0 < numFails) {
out.println("--- " + status.getIdentifier());
IMessage[] fails = status.getMessages(IMessage.FAIL, orGreater);
for (int i = 0; i < fails.length; i++) {
out.println("[fail " + LangUtil.toSizedString(i, 3) + "]: "
+ MessageUtil.MESSAGE_ALL.renderToString(fails[i]));
}
}
}
};

private RunUtils() {
}
}

+ 185
- 0
testing/src/org/aspectj/testing/util/SFileReader.java View File

@@ -0,0 +1,185 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.util;

import org.aspectj.bridge.AbortException;
import org.aspectj.bridge.IMessage;
import org.aspectj.util.LangUtil;
import org.aspectj.util.LineReader;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;


/**
* This reads a structured (config) file, which may have
* lines with @ signalling a recursive read
* and EOL comments # or //.
* This duplicates ConfigFileUtil in some sense.
*/
public class SFileReader {
// XXX move into LineReader, but that forces util to depend on AbortException?
// Formerly in SuiteReader
/**
* Read args as config files and echo to stderr.
* @param args String[] of fully-qualified paths to config files
*/
public static void main(String[] args) throws IOException {
ArrayList result = new ArrayList();
ObjectChecker collector = new StandardObjectChecker(String.class, result);
SFileReader me = new SFileReader(null);
for (int i = 0; i < args.length; i++) {
Node node = me.readNodes(new File(args[i]), null, true, System.err);
if (!Node.visit(node, collector, null)) {
System.err.println("halted during copy of " +args[i]);
} else {
String s = org.aspectj.testing.util.LangUtil.debugStr(null, "\n ", null,
null, result.toArray(), "\n ", "");
System.err.println(args[i] + ": " + s);
}
}
}
/*
* readSuite(..) reads .txt file, and for each test case specification
* creates a spec using readTestSpecifications
* and (if the specifications match the constraints)
* creates a test case using creatTestCase.
*/

/** pass this to protected methods requiring String[] if you have none */
protected static final String[] NONE = new String[0];
final Maker maker;
/** @param maker the Maker factory to use - if null, use Maker.ECHO */
public SFileReader(Maker maker) {
this.maker = (null == maker ? Maker.ECHO : maker);
}

/**
* Creates a (potentially recursive) tree of node
* by reading from the file and constructing using the maker.
* Clients may read results in Node tree form when complete
* or snoop the selector for a list of objects made.
* The selector can prevent collection in the node by
* returning false.
* Results are guaranteed by the Maker to be of the Maker's type.
* @param file an absolute path to a structured file
* @param selector determines whether not to keep an object made.
* (if null, then all are kept)
* @return Node with objects available from getItems()
* and sub-suite Node available from getNodes()
* @throws Error on any read error if abortOnReadError (default)
*/
public Node readNodes(
final File file,
final ObjectChecker selector,
final boolean abortOnReadError,
final PrintStream err)
throws IOException {
final Node result = new Node(file.getPath(), maker.getType());
if (null == file) {
throw new IllegalArgumentException("null file");
} else if (!file.isAbsolute()) {
throw new IllegalArgumentException("file not absolute");
}
LineReader reader = null;
try {
reader = LineReader.createTester(file);
if (null == reader) {
throw new IOException("no reader for " + file);
}
final String baseDir = file.getParent();
String line;
boolean skipEmpties = true;
while (null != (line = reader.nextLine(skipEmpties))) {
if (line.charAt(0) == '@') {
if (line.length() > 1) {
String newFilePath = line.substring(1).trim();
File newFile = new File(newFilePath);
if (!newFile.isAbsolute()) {
newFile = new File(baseDir, newFilePath);
}
Node node = readNodes(newFile, selector, abortOnReadError, err);
if (!result.addNode(node)) {
// XXX signal error?
System.err.println("warning: unable to add node: " + node);
break;
}
}
} else {
try {
Object made = maker.make(reader);
if ((null == selector) || (selector.isValid(made))) {
if (!result.add(made)) {
break; // XXX signal error?
}
}
} catch (AbortException e) {
if (abortOnReadError) { // XXX todo - verify message has context?
throw e;
}
if (null != err) {
String m;
IMessage mssg = e.getIMessage();
if (null != mssg) {
m = "Message: " + mssg;
} else {
m = LangUtil.unqualifiedClassName(e) + "@" + e.getMessage();
}
err.println(m);
}
reader.readToBlankLine();
}
}
}
} finally {
try {
if (null != reader) {
reader.close();
}
} catch (IOException e) {
} // ignore
}
return result;
}
/** factory produces objects by reading LineReader */
public interface Maker {
/**
* Make the result using the input from the LineReader,
* starting with lastLine().
*/
Object make(LineReader reader) throws AbortException, IOException;
/** @return type of the Object made */
Class getType();
/** This echoes each line, prefixed by the reader.
* @return file:line: {line}
*/
static final Maker ECHO = new Maker() {
public Object make(LineReader reader) {
return reader + ": " + reader.lastLine();
}
public Class getType() { return String.class; }
};
}
}

+ 127
- 0
testing/src/org/aspectj/testing/util/StandardObjectChecker.java View File

@@ -0,0 +1,127 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


/*
* StandardObjectChecker.java created on May 7, 2002
*
*/
package org.aspectj.testing.util;

import java.util.List;

/**
* Superclass for checkers that require non-null input
* of a given type.
* Clients may supply delegator for further checks,
* or a list to collect results.
* Subclasses may instead implement doIsValid().
* @author isberg
*/
public class StandardObjectChecker implements ObjectChecker {

public final Class type;
private final ObjectChecker delegate;
private final List collector;
private final boolean collectionResult;
/**
* Create one with no delegate.
* @param type the Class of the type required of the input
*/
public StandardObjectChecker(Class type) {
this(type, ANY, (List) null, true);
}
/**
* @param type the Class of the type required of the input
* @param delegate the ObjectChecker to delegate to after
* checking for non-null input of the correct type.
*/
public StandardObjectChecker(Class type, ObjectChecker delegate) {
this(type, delegate, null, true);
}

/**
* same as StandardObjectChecker(type, collector, true)
* @param type the Class of the type required of the input
* @param collector the list to collect valid entries
*/
public StandardObjectChecker(Class type, List collector) {
this(type, ANY, collector, true);
}
/**
* @param type the Class of the type required of the input
* @param collector the list to collect valid entries
* @param collectionResult the value to return when entry was added
*/
public StandardObjectChecker(Class type, List collector, boolean collectionResult) {
this(type, ANY, collector, collectionResult);
}
/**
* @param type the Class of the type required of the input
* @param collector the list to collect valid entries
*/
public StandardObjectChecker(Class type, ObjectChecker delegate,
List collector, boolean collectionResult) {
if (null == type) throw new IllegalArgumentException("null type");
this.type = type;
this.delegate = delegate;
this.collector = collector;
this.collectionResult = collectionResult;
}
/**
* Check if object is valid by confirming is is non-null and of the
* right type, then delegating to any delegate or calling doIsValid(),
* then (if true) passing to any collector, and returning
* false if the collector failed or the collection result otherwise.
* @see ObjectChecker#isValid(Object)
* @return true unless input is null or wrong type
* or if one of subclass (doIsValid(..)) or delegates
* (list, collector) returns false.
*/
public final boolean isValid(Object input) {
if ((null == input) || (!(type.isAssignableFrom(input.getClass())))) {
return false;
} else if (null != delegate) {
if (!delegate.isValid(input)) {
return false;
}
}
if (!doIsValid(input)) {
return false;
}
if (null == collector) {
return true;
} else if (!collector.add(input)) {
return false;
} else {
return collectionResult;
}
}
/**
* Delegate of isValid guarantees that the input
* is not null as is assignable to the specified type.
* Subclasses implement their funtionality here.
* This implementation returns true;
* @return true
*/
public boolean doIsValid(Object input) {
return true;
}

}

+ 76
- 0
testing/src/org/aspectj/testing/util/StreamSniffer.java View File

@@ -0,0 +1,76 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


/*
* StreamGrabber.java created on May 16, 2002
*
*/
package org.aspectj.testing.util;

import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
* Listen to a stream using StringBuffer.
* Clients install and remove buffer to enable/disable listening.
* Does not affect data passed to underlying stream
*/
public class StreamSniffer extends FilterOutputStream {
StringBuffer buffer;
/** have to use delegate, not super, because super we will double-count input */
final OutputStream delegate;
public StreamSniffer(OutputStream stream) {
super(stream);
delegate = stream;
}

/** set to null to stop copying */
public void setBuffer(StringBuffer sb) {
buffer = sb;
}

//---------------- FilterOutputStream
public void write(int b) throws IOException {
StringBuffer sb = buffer;
if (null != sb) {
if ((b > Character.MAX_VALUE)
|| (b < Character.MIN_VALUE)) {
throw new Error("don't know double-byte"); // XXX
} else {
sb.append((char) b);
}
}
delegate.write(b);
}
public void write(byte[] b) throws IOException {
StringBuffer sb = buffer;
if (null != sb) {
String s = new String(b);
sb.append(s);
}
delegate.write(b);
}
public void write(byte[] b, int offset, int length) throws IOException {
StringBuffer sb = buffer;
if (null != sb) {
String s = new String(b, offset, length);
sb.append(s);
}
delegate.write(b, offset, length);
}
}

+ 217
- 0
testing/src/org/aspectj/testing/util/StreamsHandler.java View File

@@ -0,0 +1,217 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.util;

import java.io.PrintStream;

/**
* Manage system err and system out streams.
* Clients can suppress stream output during StreamsHandler lifecycle
* and intermittantly listen to both streams if signalled on construction.
* To print to the underlying streams (without hiding or listening),
* use either the log methods (which manage lineation)
* or the out and err fields.
* <pre>
* boolean hideStreams = true;
* boolean listen = true;
* StreamsHander streams = new StreamsHander(hideStreams, listen);
* streams.startListening();
* ...
* streams.out.println("this goes out to without listening"); XXX verify
* StreamsHandler.Result result = streams.stopListening();
* streams.restoreStreams();
* System.out.println("Suppressed output stream follows");
* System.out.print(result.out);
* System.out.println("Suppressed error stream follows");
* System.out.print(result.err);
* </pre>
* Warning: does not distinguish streams from different threads.
*/
public class StreamsHandler {

/** real output stream and sink for log if logToOut */
public final PrintStream out;

/** real error stream and sink for log if !logToOut */
public final PrintStream err;

/** if true, then can listen using startListening() */
protected final boolean listening;

/** if logToOut, then out, else err */
private final PrintStream log;
/** true if the last logged item was a newline */
private boolean loggedLine;

/** sniffs stream to gather test output to System.out */
protected StreamSniffer outSniffer;

/** sniffs stream to gather test output to System.err */
protected StreamSniffer errSniffer;

/** permits us to hide output stream (after sniffing by outSniffer */
protected ProxyPrintStream outDelegate;

/** permits us to hide error stream (after sniffing by errSniffer */
protected ProxyPrintStream errDelegate;

/** when sniffing, this has sniffed contents of output stream */
protected StringBuffer outListener;

/** when sniffing, this has sniffed contents of error stream */
protected StringBuffer errListener;

/** @param hide if true, then suppress stream output (can still listen) */
public StreamsHandler(boolean listen) {
this(listen, false);
}

/**
* @param listen possible to sniff streams only if true
* @param logToOut if true, then log methods go to System.out -- otherwise, System.err.
*/
public StreamsHandler(
boolean listen,
boolean logToOut) {
this.err = System.err;
this.out = System.out;
outDelegate = new ProxyPrintStream(System.out);
errDelegate = new ProxyPrintStream(System.err);
this.listening = listen;
final PrintStream HIDE = NullPrintStream.NULL_PrintStream;
outSniffer = new StreamSniffer(outDelegate);
System.setOut(new PrintStream(outSniffer));
errSniffer = new StreamSniffer(errDelegate);
System.setErr(new PrintStream(errSniffer));
log = (logToOut ? this.out : this.err);
loggedLine = true;
}

/** render output and error streams (after sniffing) */
public void show() {
outDelegate.show();
errDelegate.show();
}

/** suppress output and error streams (after sniffing) */
public void hide() {
outDelegate.hide();
errDelegate.hide();
}

/** restore streams. Do not use this after restoring. */
public void restoreStreams() {
if (null != outSniffer) {
outSniffer = null;
errSniffer = null;
System.setOut(out);
System.setErr(err);
}
}

/** @return PrintStream used for direct logging */
public PrintStream getLogStream() {
return log;
}
/** log item without newline. */
public void log(String s) {
log.print(s);
if (loggedLine) {
loggedLine = false;
}
}

/**
* Log item with newline.
* If previous log did not have a newline,
* then this prepends a newline.
*/
public void lnlog(String s) {
if (!loggedLine) {
log.println("");
}
log.println(s);
}

/**
* Start listening to both streams.
* Tosses any old data captured.
* (Has no effect if not listening.)
* @throws IllegalStateException if called after restoreStreams()
* @see endListening()
*/
public void startListening() {
if (null == outSniffer) {
throw new IllegalStateException("no listening after restore");
}
if (listening) {
if (null != outListener) {
outListener.setLength(0);
errListener.setLength(0);
} else {
outListener = new StringBuffer();
outSniffer.setBuffer(outListener);
errListener = new StringBuffer();
errSniffer.setBuffer(errListener);
}
}
}

/**
* End listening to both streams and return data captured.
* Must call startListening() first.
* @throws IllegalStateException if called when not listening
* @return Result with sniffed output and error String
* @see startListening()
*/
public Result endListening() {
return endListening(true);
}

/**
* End listening to both streams and return data captured.
* Must call startListening() first.
* @param getResult if false, return Result.EMPTY
* and avoid converting buffer to String.
* @throws IllegalStateException if called when not listening
* @return Result with sniffed output and error String
* @see startListening()
*/
public Result endListening(boolean getResult) {
if (!listening) {
return Result.EMPTY;
}
if (null == outListener) {
throw new IllegalStateException("listening not started");
}
Result result = (!getResult ? Result.EMPTY
: new Result(outListener.toString(), errListener.toString()));
errListener = null;
outListener = null;
return result;
}

/** output and error String */
public static class Result {
static final Result EMPTY = new Result(null, null);
public final String out;
public final String err;
private Result(String out, String err) {
this.out = out;
this.err = err;
}
}
}

+ 99
- 0
testing/src/org/aspectj/testing/util/StringAccumulator.java View File

@@ -0,0 +1,99 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


/*
* StringAccumulator.java created on May 14, 2002
*
*/
package org.aspectj.testing.util;



/**
* Accumulate String with delimiters.
*/
public class StringAccumulator implements ObjectChecker {

private final String prefix;
private final String infix;
private final String suffix;
private final String nullString;
private final StringBuffer sb;
private int index;

/**
* Accumulate string with delimiter between elements,
* treaing null elements as "".
*/
public StringAccumulator(String delimiter) {
this(delimiter, null, null, "");
}
/**
* Constructor for StringAccumulator which specifies how to
* process each result, optionally postfixing or prefixing
* or infixing (adding index plus infix to prefix). e.g.,
* for prefix="[", infix="]\"", postfix="\"\n", then each entry
* becomes a line: <pre>"[{index}]"{entry}"\n</pre>
*
* @param prefix if not null, prepend to each result
* @param infix if not null, the add index and infix before each result, after prefix
* @param postfix if not null, append to each result
* @param nullString if null, ignore null completely (no index); otherwise render null as nullString
* @param type
*/
public StringAccumulator(String prefix, String infix, String suffix, String nullString) {
this.prefix = prefix;
this.infix = infix;
this.suffix = suffix;
this.nullString = nullString;
sb = new StringBuffer();
}
/** Clear buffer and index */
public synchronized void clear() {
sb.setLength(0);
index = 0;
}

/**
* Accumulate input.toString into
* @return true
* @see StandardObjectChecker#doIsValid(Object)
*/
public synchronized boolean isValid(Object input) {
if (input == null) {
if (nullString == null) return true; // ignore
input = nullString;
}
if (null != prefix) sb.append(prefix);
if (null != infix) {
sb.append(index++ + infix);
}
sb.append(input.toString());
if (null != suffix) sb.append(suffix);
return true;
}
/** @return result accumulated so far */
public String toString() {
return sb.toString();
}
/** @return result accumulated so far */
public String debugString() {
return "StringAccumulator prefix=" + prefix + " infix=" + infix + " suffix=" + suffix
+ " nullString=" + nullString + " index=" + index + " toString=" + toString();
}

}

+ 28
- 0
testing/src/org/aspectj/testing/util/StringVisitor.java View File

@@ -0,0 +1,28 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

// todo: non-distribution license?

package org.aspectj.testing.util;

/**
* Visitor interface for String
*/
public interface StringVisitor {
/**
* @param input the String to evaluate - may be null
* @return true if input is accepted and/or process should continue
*/
public boolean accept(String input);
}


+ 154
- 0
testing/src/org/aspectj/testing/util/TestClassLoader.java View File

@@ -0,0 +1,154 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
* Load classes as File from File[] dirs or URL[] jars.
*/
public class TestClassLoader extends URLClassLoader {

List /*File*/ dirs;
public TestClassLoader(URL[] urls, File[] dirs) {
super(urls);
if (null == dirs) {
throw new IllegalArgumentException("null dir");
}
for (int i = 0; i < dirs.length; i++) {
if (null == dirs[i]) {
throw new IllegalArgumentException("null dir[" + i + "]");
}
}
ArrayList dcopy = new ArrayList();
if ((null != dirs) && (0 < dirs.length)) {
dcopy.addAll(Arrays.asList(dirs));
}
this.dirs = Collections.unmodifiableList(dcopy);
}

public URL getResource(String name) {
return ClassLoader.getSystemResource(name);
}
public InputStream getResourceAsStream(String name) {
return ClassLoader.getSystemResourceAsStream(name);
}
/** We don't expect test classes to have prefixes java, org, or com */
protected boolean maybeTestClassName(String name) {
return (null != name)
&& !name.startsWith("java")
&& !name.startsWith("org.")
&& !name.startsWith("com.");
}
public synchronized Class loadClass(String name, boolean resolve)
throws ClassNotFoundException {
// search the cache, our dirs (if maybe test),
// the system, the superclass (URL[]),
// and our dirs again (if not maybe test)
ClassNotFoundException thrown = null;
final boolean maybeTestClass = maybeTestClassName(name);
Class result = findLoadedClass(name);
if (null != result) {
resolve = false;
} else if (maybeTestClass) {
// subvert the dominant paradigm...
byte[] data = readClass(name);
if (data != null) {
result = defineClass(name, data, 0, data.length);
} // handle ClassFormatError?
}
if (null == result) {
try {
result = findSystemClass(name);
} catch (ClassNotFoundException e) {
thrown = e;
}
}
if (null == result) {
try {
result = super.loadClass(name, resolve);
} catch (ClassNotFoundException e) {
thrown = e;
}
if (null != result) { // resolved by superclass
return result;
}
}
if ((null == result) && !maybeTestClass) {
byte[] data = readClass(name);
if (data != null) {
result = defineClass(name, data, 0, data.length);
} // handle ClassFormatError?
}
if (null == result) {
throw (null != thrown ? thrown : new ClassNotFoundException(name));
}
if (resolve) {
resolveClass(result);
}
return result;
}
private byte[] readClass(String className) throws ClassNotFoundException {
byte[] data= null;
final String fileName = className.replace('.', '/')+".class";
for (Iterator iter = dirs.iterator(); iter.hasNext();) {
File file = new File((File) iter.next(), fileName);
if (file.canRead()) {
return getClassData(file);
}
if (data != null) {
return data;
}
}
throw new ClassNotFoundException(className); // expensive - fix?
}
private byte[] getClassData(File f) {
try {
FileInputStream stream= new FileInputStream(f);
ByteArrayOutputStream out= new ByteArrayOutputStream(1000);
byte[] b= new byte[4096];
int n;
while ((n= stream.read(b)) != -1) {
out.write(b, 0, n);
}
stream.close();
out.close();
return out.toByteArray();
} catch (IOException e) {
}
return null;
}
}


+ 360
- 0
testing/src/org/aspectj/testing/util/TestDiffs.java View File

@@ -0,0 +1,360 @@
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
* 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.util;

import org.aspectj.util.LangUtil;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

/**
* Calculated differences between two test runs
* based on their output files
* assuming that tests are logged with prefix [PASS|FAIL]
* (as they are when using <tt>-traceTestsMin</tt> with the Harness).
* @see org.aspectj.testing.drivers.Harness
*/
public class TestDiffs { // XXX pretty dumb implementation

/** @param args expected, actual test result files */
public static void main(String[] args) {
if ((null == args) || (2 > args.length)) {
System.err.println("java " + TestDiffs.class.getName() + " expectedFile actualFile {test}");
return;
}
File expected = new File(args[0]);
File actual = new File(args[1]);

TestDiffs result = compareResults(expected, actual);

System.out.println("## Differences between test runs");
print(System.out, result.added, "added");
print(System.out, result.missing, "missing");
print(System.out, result.fixed, "fixed");
print(System.out, result.broken, "broken");
System.out.println("## Summary");
System.out.println(" # expected " + result.expected.size() + " tests: " + args[0] );
System.out.println(" # actual " + result.actual.size() + " tests: " + args[1]);
StringBuffer sb = new StringBuffer();
append(sb, result.added, " added");
append(sb, result.missing, " missing");
append(sb, result.broken, " broken");
append(sb, result.fixed, " fixed");
append(sb, result.stillPassing, " still passing");
append(sb, result.stillFailing, " still failing");
System.out.println(" # diffs: " + sb);
}
/**
* @param expected the expected/old File results with Harness -traceTestsMin lines
* @param actual the actual/new File results with Harness -traceTestsMin lines
* @return TestDiffs null if error, valid otherwise
*/
public static TestDiffs compareResults(File expected, File actual) {
ArrayList exp = null;
ArrayList act = null;
File reading = expected;
try {
exp = TestDiffs.readTestResults(expected, expected.getPath());
reading = actual;
act = TestDiffs.readTestResults(actual, actual.getPath());
Diffs tests = new Diffs("tests", exp, act, TestResult.BY_NAME);
// remove missing/unexpected (removed, added) tests from results
// otherwise, unexpected-[pass|fail] look like [fixes|broken]
ArrayList expResults = trimByName(exp, tests.missing);
ArrayList actResults = trimByName(act, tests.unexpected);
Diffs results = new Diffs("results", expResults, actResults, TestResult.BY_PASSNAME);

// broken tests show up in results as unexpected-fail or missing-pass
// fixed tests show up in results as unexpected-pass or missing-fail
ArrayList broken = new ArrayList();
ArrayList fixed = new ArrayList();
split(results.unexpected, fixed, broken);

return new TestDiffs(
exp,
act,
tests.missing,
tests.unexpected,
broken,
fixed);
} catch (IOException e) {
System.err.println("error reading " + reading);
e.printStackTrace(System.err); // XXX
return null;
}
}

private static void append(StringBuffer sb, List list, String label) {
if (!LangUtil.isEmpty(list)) {
if (0 < sb.length()) {
sb.append(" ");
}
sb.append(list.size() + label);
}
}
private static void print(PrintStream out, List list, String label) {
if ((null == out) || LangUtil.isEmpty(list)) {
return;
}
int i = 0;
final String suffix = " " + label;
final String LABEL = list.size() + suffix;
out.println("## START " + LABEL);
for (Iterator iter = list.iterator(); iter.hasNext();) {
TestResult result = (TestResult) iter.next();
out.println(" " + result.test + " ## " + suffix);
}
out.println("## END " + LABEL);
}
/**
* Create ArrayList with input TestResult list
* but without elements in trim list,
* comparing based on test name only.
* @param input
* @param trim
* @return ArrayList with all input except those in trim (by name)
*/
private static ArrayList trimByName(List input, List trim) {
ArrayList result = new ArrayList();
result.addAll(input);
if (!LangUtil.isEmpty(input) && !LangUtil.isEmpty(trim)) {
for (ListIterator iter = result.listIterator(); iter.hasNext();) {
TestResult inputItem = (TestResult) iter.next();
for (Iterator iterator = trim.iterator();
iterator.hasNext();
) {
TestResult trimItem = (TestResult) iterator.next();
if (inputItem.test.equals(trimItem.test)) {
iter.remove();
break;
}
}
}
}
return result;
}

/** split input List by whether the TestResult element passed or failed */
private static void split(List input, ArrayList pass, ArrayList fail) {
for (ListIterator iter = input.listIterator(); iter.hasNext();) {
TestResult result = (TestResult) iter.next();
if (result.pass) {
pass.add(result);
} else {
fail.add(result);
}
}
}

/**
* Read a file of test results,
* defined as lines starting with [PASS|FAIL]
* (produced by Harness option <tt>-traceTestsmin</tt>).
* @return ArrayList of TestResult, one for every -traceTestsMin line in File
*/
private static ArrayList readTestResults(File file, String config) throws IOException {
LangUtil.throwIaxIfNull(file, "file");
if (null == config) {
config = file.getPath();
}
ArrayList result = new ArrayList();
FileReader in = null;
try {
in = new FileReader(file);
BufferedReader input = new BufferedReader(in);
String line;
// XXX handle stream interleaving more carefully
// XXX clip trailing ()
// XXX fix elision in test name rendering by -traceTestsMin?
while (null != (line = input.readLine())) {
boolean pass = line.startsWith("PASS");
boolean fail = false;
if (!pass) {
fail = line.startsWith("FAIL");
}
if (pass || fail) {
String test = line.substring(4).trim();
result.add(new TestResult(test, config, pass));
}
}
} finally {
if (null != in) {
try { in.close(); }
catch (IOException e) {} // ignore
}
}
return result;
}
private static List safeList(List list) {
return (null == list
? Collections.EMPTY_LIST
: Collections.unmodifiableList(list));
}
/** List of TestResult results from expected run. */
public final List expected;

/** List of TestResult results from actual run. */
public final List actual;
/** List of TestResult tests disappeared from test suite between expected and actual runs. */
public final List missing;
/** List of TestResult tests added to test suite between expected and actual runs. */
public final List added;
/** List of TestResult tests in both runs, expected to pass but actually failed */
public final List broken;
/** List of TestResult tests in both runs, expected to fail but actually passed */
public final List fixed;
/** List of TestResult passed tests in expected run */
public final List expectedPassed;

/** List of TestResult failed tests in expected run */
public final List expectedFailed;

/** List of TestResult passed tests in actual run */
public final List actualPassed;

/** List of TestResult tests failed in actual run */
public final List actualFailed;

/** List of TestResult tests passed in both expected and actual run */
public final List stillPassing;

/** List of TestResult tests failed in both expected and actual run */
public final List stillFailing;
private TestDiffs(
List expected,
List actual,
List missing,
List added,
List broken,
List fixed) {
this.expected = safeList(expected);
this.actual = safeList(actual);
this.missing = safeList(missing);
this.added = safeList(added);
this.broken = safeList(broken);
this.fixed = safeList(fixed);
// expected[Passed|Failed]
ArrayList passed = new ArrayList();
ArrayList failed = new ArrayList();
split(this.expected, passed, failed);
expectedPassed = safeList(passed);
expectedFailed = safeList(failed);

// actual[Passed|Failed]
passed = new ArrayList();
failed = new ArrayList();
split(this.actual, passed, failed);
actualPassed = safeList(passed);
actualFailed = safeList(failed);

// stillPassing: expected.passed w/o broken, missingPasses
passed = new ArrayList();
passed.addAll(expectedPassed);
passed = trimByName(passed, this.broken);
ArrayList missingPasses = new ArrayList();
ArrayList missingFails = new ArrayList();
split(this.missing, missingPasses, missingFails);
passed = trimByName(passed, missingPasses);
stillPassing = safeList(passed);

// stillFailing: expected.failed w/o fixed, missingFails
failed = new ArrayList();
failed.addAll(expectedFailed);
failed = trimByName(failed, this.fixed);
failed = trimByName(failed, missingFails);
stillFailing = safeList(failed);
}
/** results of a test */
public static class TestResult {
public static final Comparator BY_PASSNAME = new Comparator() {
public int compare(Object o1, Object o2) {
if (o1 == o2) {
return 0;
}
TestResult lhs = (TestResult) o1;
TestResult rhs = (TestResult) o2;
return (lhs.pass == rhs.pass
? lhs.test.compareTo(rhs.test)
: (lhs.pass ? 1 : -1 ));
}
public boolean equals(Object lhs, Object rhs) {
return (0 == compare(lhs, rhs));
}
};
public static final Comparator BY_NAME = new Comparator() {
public int compare(Object o1, Object o2) {
if (o1 == o2) {
return 0;
}
TestResult lhs = (TestResult) o1;
TestResult rhs = (TestResult) o2;
return lhs.test.compareTo(rhs.test);
}
public boolean equals(Object lhs, Object rhs) {
return (0 == compare(lhs, rhs));
}
};
//private static final ArrayList TESTS = new ArrayList();
public static final String FIELDSEP = "\t";
public final String test;
public final String config;
public final boolean pass;
private final String toString;
public TestResult(String test, String config, boolean pass) {
LangUtil.throwIaxIfNull(test, "test");
LangUtil.throwIaxIfNull(test, "config");
this.test = test;
this.config = config;
this.pass = pass;
toString = (pass ? "PASS" : "FAIL") + FIELDSEP + test + FIELDSEP + config;

}
/** @return [PASS|FAIL]{FIELDSEP}test{FIELDSEP}config */
public String toString() {
return toString;
}
}
}

+ 123
- 0
testing/src/org/aspectj/testing/util/ValidFileFilter.java View File

@@ -0,0 +1,123 @@
/* *******************************************************************
* Copyright (c) 1999-2000 Xerox Corporation.
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/

package org.aspectj.testing.util;

import java.io.File;
import java.io.FileFilter;

/**
* FileFilter that accepts existing files
* with static singleton variants
* made from inner subclasses.
*/
public class ValidFileFilter implements FileFilter {
//----------------------------- singleton variants
public static final FileFilter EXIST = new ValidFileFilter();
public static final FileFilter FILE_EXISTS = new FilesOnlyFilter();
public static final FileFilter DIR_EXISTS = new DirsOnlyFilter();
public static final FileFilter CLASS_FILE = new ClassOnlyFilter();
public static final FileFilter JAVA_FILE = new JavaOnlyFilter();
public static final FileFilter RESOURCE = new ResourcesOnlyFilter();

//----------------------------- members
protected final FileFilter delegate;
protected ValidFileFilter(){
this(null);
}
protected ValidFileFilter(FileFilter delegate){
this.delegate = delegate;
}

/**
* Implement <code>FileFilter.accept(File)</code> by checking
* taht input is not null, exists, and is accepted by any delegate.
*/
public boolean accept(File f) {
return ((null != f) && (f.exists())
&& ((null == delegate) || delegate.accept(f)));
}

//----------------------------- inner subclasses
static class FilesOnlyFilter extends ValidFileFilter {
public boolean accept(File f) {
return (super.accept(f) && (!f.isDirectory()));
}
}
static class ResourcesOnlyFilter extends FilesOnlyFilter {
public boolean accept(File f) {
return (super.accept(f) && (FileUtil.isResourcePath(f.getPath())));
}
}
static class DirsOnlyFilter extends ValidFileFilter {
public final boolean accept(File f) {
return (super.accept(f) && (f.isDirectory()));
}
}
// todo: StringsFileFilter, accepts String[] variants for each
static class StringFileFilter extends ValidFileFilter {
public static final boolean IGNORE_CASE = true;
protected final String prefix;
protected final String substring;
protected final String suffix;
protected final boolean ignoreCase;
/** true if one of the String specifiers is not null */
protected final boolean haveSpecifier;
public StringFileFilter(String prefix, String substring,
String suffix, boolean ignoreCase) {
this.ignoreCase = ignoreCase;
this.prefix = preprocess(prefix);
this.substring = preprocess(substring);
this.suffix = preprocess(suffix);
haveSpecifier = ((null != prefix) || (null != substring)
|| (null != suffix));
}
private final String preprocess(String input) {
if ((null != input) && ignoreCase) {
input = input.toLowerCase();
}
return input;
}
public boolean accept(File f) {
if (!(super.accept(f))) {
return false;
} else if (haveSpecifier) {
String path = preprocess(f.getPath());
if ((null == path) || (0 == path.length())) {
return false;
}
if ((null != prefix) && (!(path.startsWith(prefix)))) {
return false;
}
if ((null != substring) && (-1 == path.indexOf(substring))) {
return false;
}
if ((null != suffix) && (!(path.endsWith(suffix)))) {
return false;
}
}
return true;
}
} // class StringFileFilter

static class ClassOnlyFilter extends StringFileFilter {
ClassOnlyFilter() {
super(null, null, ".class", IGNORE_CASE);
}
}
static class JavaOnlyFilter extends StringFileFilter {
JavaOnlyFilter() {
super(null, null, ".java", IGNORE_CASE);
}
}
} // class ValidFileFilter


+ 208
- 0
testing/src/org/aspectj/testing/util/WebInstall.java View File

@@ -0,0 +1,208 @@
/* *******************************************************************
* Copyright (c) 1999-2000 Xerox Corporation.
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Xerox/PARC initial implementation
* ******************************************************************/


package org.aspectj.testing.util;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

/**
* Install programmatically using http URL.
* (Very strange that java tool classpath does not accept http URL's.)
*
* Example:
* <p><code>java -classpath aj-testing.jar org.aspectj.testing.util.WebInstall
* http://aspectj.org/download/distribution/aspectj10-tools.jar -text
* install.properties</code>
*
* <p>You can omit the <code>-text install.properties</code> if there is
* a file called "install.properties" in the current directory.
*
* <p>The properties file must define the properties
* <code>output.dir</code> and <code>context.javaPath</code>
* in properties- and platform specifie ways.
* For property values, a backslash must be escaped with another backslash,
* and directory separators should be valid. E.g., on Windows:
* <pre>output.dir=c:\\output\\dir
* context.javaPath=c:\\apps\\jdk1.3.1</pre>
*
* For an installer to complete programmatically,
* the output directory must be empty of colliding files.
* This will fail with a stack trace if anything goes wrong, except for
* simple input errors.
*
* <p>You may also use this as a driver for the known installers
* by specifying the following options (-baseurl must be first):<pre>
* -baseurl {baseurl}
* -version {version}
* -context.javaPath {path to JDK} (properties form)
* -output.dir {path to outDir} (properties form, including trailing /)
* -outdir {path to outDir} (actual form) </pre>
* such that URL=
* <code>{baseurl}<packagePrefix>{version}<packageSuffix>.jar</code>
* and paths to context.javaPath and output.dir are specified in
* properties-compliant format
*
* @see ant script test-product.xml for example of installing from files
* which can be driven from the command-line.
*/
public class WebInstall {
private static final String EOL = "\n"; // todo where is this defined?
public static final String SYNTAX
= "java WebInstall url {args}" + EOL
+ " url - to installer" + EOL
+ " args - normally -text install.properties" + EOL
+ " (if n/a, use install.properties)" + EOL;

/** default arguments assume file <code>install.properties</code>
* is in current directory */
private static final String[] ARGS = new String[]
{ "-text", "install.properties" };

/** @param args the String[] <code>{ "<url>" {, "-text", "<propsPath>" }</code> */
public static void main(String[] args) throws Exception {
if ((null != args) && (args.length > 0)
&& ("-baseurl".equals(args[0]))) {
driver(args);
} else {
try {
new WebInstall().install(args);
} catch (Throwable t) {
System.err.println("Error installing args ");
for (int i = 0; i < args.length; i++) {
System.err.println(" " + i + ": " + args[i]);
}
t.printStackTrace(System.err);
}
}
}

/** known .jar packages {(prefix, suffix}...} */
protected static String[] packages = new String[]
{ "aspectj-tools-", ""
, "aspectj-docs-", ""
, "ajde-forteModule-", ""
, "ajde-jbuilderOpenTool-", ""
};

/**
* Drive install of all jar-based installers.
* @param args the String[] containing<pre>
* -baseurl {baseurl}
* -version {version}
* -context.javaPath {path to JDK} (properties form)
* -output.dir {path to outDir} (properties form, including trailing /)
* -outdir {path to outDir} (actual form) </pre>
* such that URL=
* <code>{baseurl}<packagePrefix>{version}<packageSuffix>.jar</code>
* and paths to context.javaPath and output.dir are specified in
* properties-compliant format
*/
protected static void driver(String[] args) throws Exception {
String baseurl = null;
String version = null;
String outputDir = null;
File outdir = null;
String jdk = null;
for (int i = 0; i < args.length; i++) {
if ("-baseurl".equals(args[i])) {
baseurl = args[++i];
} else if ("-version".equals(args[i])) {
version = args[++i];
} else if ("-context.javaPath".equals(args[i])) {
jdk = args[++i];
} else if ("-output.dir".equals(args[i])) {
outputDir=args[++i];
} else if ("-outdir".equals(args[i])) {
outdir = new File(args[++i]).getCanonicalFile();
if (!outdir.isDirectory()) {
outdir.mkdir();
}
}
}
final File props = File.createTempFile("WebInstall", null);
final String[] ARGS = new String [] {null, "-text", props.getCanonicalPath()};
for (int i = 0; i < packages.length; i++) {
String name = packages[i++] + version + packages[i];
File outDir = new File(outdir, name);
FileWriter fw = null;
try {
if (!outDir.isDirectory()) {
outDir.mkdir();
}
fw = new FileWriter(props);
fw.write("output.dir=" + outputDir + name + "\n");
fw.write("context.javaPath=" + jdk + "\n");
fw.close();
fw = null;
ARGS[0] = baseurl + name + ".jar";
main(ARGS);
} finally {
try { if (null != fw) fw.close(); }
catch (java.io.IOException e) {} // ignore
}
}
if (props.exists()) props.delete();
} // driver

private static boolean printError(String err) {
if (null != err) System.err.println(err);
System.err.println(SYNTAX);
return (null != err);
}

/**
* Create a classloader using the first argument (presumed to be URL for classloader),
* construct the installer, and invoke it using remaining arguments (or default args).
*/
protected void install(String[] args) throws Exception {
if ((null == args) || (args.length < 1)
|| (null == args[0]) || (1 > args[0].length())) {
if (printError("expecting installer URL")) return;
}
URL[] urls = new URL[] { new URL(args[0]) };
//System.err.println("before: " + render(args));
args = getArgs(args);
//System.err.println("after: " + render(args));
URLClassLoader cl = new URLClassLoader(urls);
Class c = cl.loadClass("$installer$.org.aspectj.Main"); // todo: dependency on class name
Method ms = c.getMethod("main", new Class[]{String[].class});
ms.invoke(null, new Object[] { args });
}
public static final String render(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append("[");
for (int i = 0; i < args.length; i++) {
if (0 < i) sb.append(", ");
sb.append("" + args[i]);
}
sb.append("]");
return sb.toString();
}

/** @return args less args[0] or default args if less than 3 arguments */

protected String[] getArgs(String[] args) {
if ((null == args) || (args.length < 3)) {
return ARGS;
} else {
String[] result = new String[args.length-1];
System.arraycopy(args, 1, result, 0, result.length);
return result;
}
}

}

+ 0
- 0
testing/src/org/aspectj/testing/xml/AjcSpecXmlReader.java View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save