123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548 |
- /* *******************************************************************
- * 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 Eclipse Public License v1.0
- * which accompanies this distribution and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Xerox/PARC initial implementation
- * ******************************************************************/
-
- package org.aspectj.testing.util;
-
- import java.io.File;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.Comparator;
- import java.util.Iterator;
- import java.util.List;
- import java.util.ListIterator;
-
- import org.aspectj.bridge.IMessage;
- import org.aspectj.bridge.IMessageHandler;
- import org.aspectj.bridge.ISourceLocation;
- import org.aspectj.bridge.MessageUtil;
- import org.aspectj.util.FileUtil;
- import org.aspectj.util.LangUtil;
-
- /**
- * Result struct for expected/actual diffs for Collection
- */
- public class Diffs {
-
- /**
- * Compare IMessage.Kind based on kind priority.
- */
- public static final Comparator KIND_PRIORITY = new Comparator() {
- /**
- * Compare IMessage.Kind based on kind priority.
- * @throws NullPointerException if anything is null
- */
- public int compare(Object lhs, Object rhs) {
- return ((IMessage.Kind) lhs).compareTo((IMessage.Kind) rhs);
- }
- };
- /**
- * Sort ISourceLocation based on line, file path.
- */
- public static final Comparator SORT_SOURCELOC = new Comparator() {
- /**
- * Compare ISourceLocation based on line, file path.
- * @throws NullPointerException if anything is null
- */
- public int compare(Object lhs, Object rhs) {
- ISourceLocation l = (ISourceLocation) lhs;
- ISourceLocation r = (ISourceLocation) rhs;
- int result = getLine(l) - getLine(r);
- if (0 != result) {
- return result;
- }
- String lp = getSourceFile(l).getPath();
- String rp = getSourceFile(r).getPath();
- return lp.compareTo(rp);
- }
- };
-
- /**
- * Compare IMessages based on kind and source location line (only).
- */
- public static final Comparator<IMessage> MESSAGE_LINEKIND = new Comparator<IMessage>() {
- /**
- * Compare IMessages based on kind and source location line (only).
- * @throws NullPointerException if anything is null
- */
- public int compare(IMessage lhs, IMessage rhs) {
- IMessage lm = (IMessage) lhs;
- IMessage rm = (IMessage) rhs;
- ISourceLocation ls = (lm == null ? null : lm.getSourceLocation());
- ISourceLocation rs = (rm == null ? null : rm.getSourceLocation());
- int left = (ls == null ? -1 : ls.getLine());
- int right = (rs == null ? -1 : rs.getLine());
- int result = left - right;
- if (0 == result) {
- result = lm.getKind().compareTo(rm.getKind());
- }
- return result;
- }
- };
- public static final Filter ACCEPT_ALL = new Filter() {
- public boolean accept(Object o) {
- return true;
- }
- };
- // // XXX List -> Collection b/c comparator orders
- // public static final Diffs NONE
- // = new Diffs("NONE", Collections.EMPTY_LIST, Collections.EMPTY_LIST);
-
- public static Diffs makeDiffs(
- String label,
- List expected,
- List actual,
- Comparator comparator) {
- return makeDiffs(
- label,
- expected,
- actual,
- comparator,
- ACCEPT_ALL,
- ACCEPT_ALL);
- }
-
- public static Diffs makeDiffs(
- String label,
- IMessage[] expected,
- IMessage[] actual) {
- return makeDiffs(label, expected, actual, null, null);
- }
-
- private static int getLine(ISourceLocation loc) {
- int result = -1;
- if (null != loc) {
- result = loc.getLine();
- }
- return result;
- }
- private static int getLine(IMessage message) {
- int result = -1;
- if ((null != message)) {
- result = getLine(message.getSourceLocation());
- }
- return result;
- }
-
- private static File getSourceFile(ISourceLocation loc) {
- File result = ISourceLocation.NO_FILE;
- if (null != loc) {
- result = loc.getSourceFile();
- }
- return result;
- }
-
- public static Diffs makeDiffs(
- String label,
- IMessage[] expected,
- IMessage[] actual,
- IMessage.Kind[] ignoreExpectedKinds,
- IMessage.Kind[] ignoreActualKinds) {
- ArrayList exp = getExcept(expected, ignoreExpectedKinds);
- ArrayList act = getExcept(actual, ignoreActualKinds);
-
- ArrayList missing = new ArrayList();
- List unexpected = new ArrayList();
-
- if (LangUtil.isEmpty(expected)) {
- unexpected.addAll(act);
- } else if (LangUtil.isEmpty(actual)) {
- missing.addAll(exp);
- } else {
- ListIterator expectedIterator = exp.listIterator();
- int lastLine = Integer.MIN_VALUE + 1;
- ArrayList expectedFound = new ArrayList();
- ArrayList expectedForLine = new ArrayList();
- for (ListIterator iter = act.listIterator(); iter.hasNext();) {
- IMessage actualMessage = (IMessage) iter.next();
- int actualLine = getLine(actualMessage);
- if (actualLine != lastLine) {
- // new line - get all messages expected for it
- if (lastLine > actualLine) {
- throw new Error("sort error");
- }
- lastLine = actualLine;
- expectedForLine.clear();
- while (expectedIterator.hasNext()) {
- IMessage curExpected =
- (IMessage) expectedIterator.next();
- int curExpectedLine = getLine(curExpected);
- if (actualLine == curExpectedLine) {
- expectedForLine.add(curExpected);
- } else {
- expectedIterator.previous();
- break;
- }
- }
- }
- // now check actual against all expected on that line
- boolean found = false;
- IMessage expectedMessage = null;
- for (Iterator iterator = expectedForLine.iterator();
- !found && iterator.hasNext();
- ) {
- expectedMessage = (IMessage) iterator.next();
- found = expectingMessage(expectedMessage, actualMessage);
- }
- if (found) {
- iter.remove();
- if (expectedFound.contains(expectedMessage)) {
- // XXX warn: expected message matched two actual
- } else {
- expectedFound.add(expectedMessage);
- }
- } else {
- // unexpected: any actual result not found
- unexpected.add(actualMessage);
- }
- }
- // missing: all expected results not found
- exp.removeAll(expectedFound);
- missing.addAll(exp);
- }
- return new Diffs(label, missing, unexpected);
- }
-
- public static Diffs makeDiffs(
- String label,
- List expected,
- List actual,
- Comparator comparator,
- Filter missingFilter,
- Filter unexpectedFilter) {
- label = label.trim();
- if (null == label) {
- label = ": ";
- } else if (!label.endsWith(":")) {
- label += ": ";
- }
- final String thisLabel = " " + label;
- ArrayList miss = new ArrayList();
- ArrayList unexpect = new ArrayList();
-
- org.aspectj.testing.util.LangUtil.makeSoftDiffs(
- expected,
- actual,
- miss,
- unexpect,
- comparator);
- if (null != missingFilter) {
- for (ListIterator iter = miss.listIterator(); iter.hasNext();) {
- if (!missingFilter.accept(iter.next())) {
- iter.remove();
- }
- }
- }
- if (null != unexpectedFilter) {
- for (ListIterator iter = unexpect.listIterator();
- iter.hasNext();
- ) {
- if (!unexpectedFilter.accept(iter.next())) {
- iter.remove();
- }
- }
- }
- return new Diffs(thisLabel, miss, unexpect);
- }
-
- // /**
- // * Shift over elements in sink if they are of one of the specified kinds.
- // * @param sink the IMessage[] to shift elements from
- // * @param kinds
- // * @return length of sink after shifting
- // * (same as input length if nothing shifted)
- // */
- // public static int removeKinds(IMessage[] sink, IMessage.Kind[] kinds) {
- // if (LangUtil.isEmpty(kinds)) {
- // return sink.length;
- // } else if (LangUtil.isEmpty(sink)) {
- // return 0;
- // }
- // int from = -1;
- // int to = -1;
- // for (int j = 0; j < sink.length; j++) {
- // from++;
- // if (null == sink[j]) {
- // continue;
- // }
- // boolean remove = false;
- // for (int i = 0; !remove && (i < kinds.length); i++) {
- // IMessage.Kind kind = kinds[i];
- // if (null == kind) {
- // continue;
- // }
- // if (0 == kind.compareTo(sink[j].getKind())) {
- // remove = true;
- // }
- // }
- // if (!remove) {
- // to++;
- // if (to != from) {
- // sink[to] = sink[from];
- // }
- // }
- // }
- // return to+1;
- // }
-
- /**
- * @param expected the File from the expected source location
- * @param actual the File from the actual source location
- * @return true if exp is ISourceLocation.NO_FILE
- * or exp path is a suffix of the actual path
- * (after using FileUtil.weakNormalize(..) on both)
- */
- static boolean expectingFile(File expected, File actual) {
- if (null == expected) {
- return (null == actual);
- } else if (null == actual) {
- return false;
- }
- if (expected != ISourceLocation.NO_FILE) {
- String expPath = FileUtil.weakNormalize(expected.getPath());
- String actPath = FileUtil.weakNormalize(actual.getPath());
- if (!actPath.endsWith(expPath)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Soft comparison for expected message will not check a corresponding
- * element in the actual message unless defined in the expected message.
- * <pre>
- * message
- * kind must match (constant/priority)
- * message only requires substring
- * thrown ignored
- * column ignored
- * endline ignored
- * details only requires substring
- * sourceLocation
- * line must match, unless expected < 0
- * file ignored if ISourceLocation.NOFILE
- * matches if expected is a suffix of actual
- * after changing any \ to /
- * extraSourceLocation[]
- * if any are defined in expected, then there
- * must be exactly the actual elements as are
- * defined in expected (so it is an error to
- * not define all if you define any)
- * <pre>
- * @param expected
- * @param actual
- * @return true if we are expecting the line, kind, file, message,
- * details, and any extra source locations.
- * (ignores column/endline, thrown) XXX
- */
- static boolean expectingMessage(IMessage expected, IMessage actual) {
- if (null == expected) {
- return (null == actual);
- } else if (null == actual) {
- return false;
- }
- if (0 != expected.getKind().compareTo(actual.getKind())) {
- return false;
- }
- if (!expectingSourceLocation(expected.getSourceLocation(),
- actual.getSourceLocation())) {
- return false;
- }
- if (!expectingText(expected.getMessage(), actual.getMessage())) {
- return false;
- }
- if (!expectingText(expected.getDetails(), actual.getDetails())) {
- return false;
- }
- ISourceLocation[] esl =
- (ISourceLocation[]) expected.getExtraSourceLocations().toArray(
- new ISourceLocation[0]);
- ISourceLocation[] asl =
- (ISourceLocation[]) actual.getExtraSourceLocations().toArray(
- new ISourceLocation[0]);
-
- Arrays.sort(esl, SORT_SOURCELOC);
- Arrays.sort(asl, SORT_SOURCELOC);
- if (!expectingSourceLocations(esl, asl)) {
- return false;
- }
- return true;
- }
-
- /**
- * This returns true if no ISourceLocation are specified
- * (i.e., it ignored any extra source locations if no expectations stated).
- * XXX need const like NO_FILE.
- * @param expected the sorted ISourceLocation[] expected
- * @param expected the actual sorted ISourceLocation[]
- * @return true if any expected element is expected by the corresponding actual element.
- */
- static boolean expectingSourceLocations(
- ISourceLocation[] expected,
- ISourceLocation[] actual) {
- if (LangUtil.isEmpty(expected)) {
- return true;
- } else if (LangUtil.isEmpty(actual)) {
- return false;
- } else if (actual.length != expected.length) {
- return false;
- }
- for (int i = 0; i < actual.length; i++) {
- if (!expectingSourceLocation(expected[i], actual[i])) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * @param expected
- * @param actual
- * @return true if any expected line/file matches the actual line/file,
- * accepting a substring as a file match
- */
- static boolean expectingSourceLocation(
- ISourceLocation expected,
- ISourceLocation actual) {
- int eline = getLine(expected);
- int aline = getLine(actual);
- if ((-1 < eline) && (eline != aline)) {
- return false;
- }
- if (!expectingFile(getSourceFile(expected), getSourceFile(actual))) {
- return false;
- }
- return true;
- }
-
- /**
- * @param expected the String in the expected message
- * @param actual the String in the actual message
- * @return true if both are null or actual contains expected
- */
- static boolean expectingText(String expected, String actual) {
- if (null == expected) {
- return true; // no expectations
- } else if (null == actual) {
- return false; // expected something
- } else {
- return (-1 != actual.indexOf(expected));
- }
- }
-
- private static ArrayList getExcept(
- IMessage[] source,
- IMessage.Kind[] skip) {
- ArrayList<IMessage> sink = new ArrayList<IMessage>();
- if (LangUtil.isEmpty(source)) {
- return sink;
- }
-
- if (LangUtil.isEmpty(skip)) {
- sink.addAll(Arrays.asList(source));
- Collections.sort(sink, MESSAGE_LINEKIND);
- return sink;
- }
- for (int i = 0; i < source.length; i++) {
- IMessage message = source[i];
- IMessage.Kind mkind = message.getKind();
- boolean skipping = false;
- for (int j = 0; !skipping && (j < skip.length); j++) {
- if (0 == mkind.compareTo(skip[j])) {
- skipping = true;
- }
- }
- if (!skipping) {
- sink.add(message);
- }
- }
- Collections.sort(sink, MESSAGE_LINEKIND);
- return sink;
- }
-
- private static List harden(List list) {
- return (
- LangUtil.isEmpty(list)
- ? Collections.EMPTY_LIST
- : Collections.unmodifiableList(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;
-
- /**
- * Struct-constructor stores these values,
- * wrapping the lists as unmodifiable.
- * @param label the String label for these diffs
- * @param missing the List of missing elements
- * @param unexpected the List of unexpected elements
- */
- public Diffs(String label, List missing, List unexpected) {
- this.label = label;
- this.missing = harden(missing);
- this.unexpected = harden(unexpected);
- 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()
- + ")";
- }
- public static interface Filter {
- /** @return true to keep input in list of messages */
- boolean accept(Object input);
- }
- }
|