/*
* Copyright (c) 2012, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.engine;
import com.google.common.base.Objects;
import com.google.dart.engine.ast.AstNode;
import com.google.dart.engine.ast.visitor.NodeLocator;
import com.google.dart.engine.element.Element;
import com.google.dart.engine.element.MethodElement;
import com.google.dart.engine.element.PropertyAccessorElement;
import com.google.dart.engine.internal.context.AnalysisContextImpl;
import com.google.dart.engine.scanner.KeywordToken;
import com.google.dart.engine.scanner.StringToken;
import com.google.dart.engine.scanner.Token;
import com.google.dart.engine.scanner.TokenType;
import com.google.dart.engine.source.SourceFactory;
import com.google.dart.engine.type.InterfaceType;
import com.google.dart.engine.utilities.io.PrintStringWriter;
import junit.framework.Assert;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* The class {@code EngineTestCase} defines utility methods for making assertions.
*/
public class EngineTestCase extends TestCase {
private static final int PRINT_RANGE = 6;
/**
* Assert that the tokens in the actual stream of tokens have the same types and lexemes as the
* tokens in the expected stream of tokens. Note that this does not assert anything about the
* offsets of the tokens (although the lengths will be equal).
*
* @param expectedStream the head of the stream of tokens that were expected
* @param actualStream the head of the stream of tokens that were actually found
* @throws AssertionFailedError if the two streams of tokens are not the same
*/
public static void assertAllMatch(Token expectedStream, Token actualStream) {
Token left = expectedStream;
Token right = actualStream;
while (left.getType() != TokenType.EOF && right.getType() != TokenType.EOF) {
assertMatches(left, right);
left = left.getNext();
right = right.getNext();
}
}
/**
* Assert that the given collection is non-{@code null} and has the expected number of elements.
*
* @param expectedSize the expected number of elements
* @param c the collection being tested
* @throws AssertionFailedError if the list is {@code null} or does not have the expected number
* of elements
*/
public static void assertCollectionSize(int expectedSize, Collection<?> c) {
if (c == null) {
fail("Expected collection of size " + expectedSize + "; found null");
} else if (c.size() != expectedSize) {
fail("Expected collection of size " + expectedSize + "; contained " + c.size() + " elements");
}
}
/**
* Assert that the given array is non-{@code null} and contains the expected elements. The
* elements can appear in any order.
*
* @param array the array being tested
* @param expectedElements the expected elements
* @throws AssertionFailedError if the array is {@code null} or does not contain the expected
* elements
*/
public static void assertContains(Object[] array, Object... expectedElements) {
int expectedSize = expectedElements.length;
if (array == null) {
fail("Expected array of length " + expectedSize + "; found null");
}
if (array.length != expectedSize) {
fail("Expected array of length " + expectedSize + "; contained " + array.length + " elements");
}
boolean[] found = new boolean[expectedSize];
for (int i = 0; i < expectedSize; i++) {
privateAssertContains(array, found, expectedElements[i]);
}
}
/**
* Assert that the array of actual values contain exactly the same values as those in the array of
* expected value, with the exception that the order of the elements is not required to be the
* same.
*
* @param expectedValues the values that are expected to be found
* @param actualValues the actual values that are being compared against the expected values
*/
public static void assertEqualsIgnoreOrder(Object[] expectedValues, Object[] actualValues) {
assertNotNull(actualValues);
int expectedLength = expectedValues.length;
assertEquals(expectedLength, actualValues.length);
boolean[] found = new boolean[expectedLength];
for (int i = 0; i < expectedLength; i++) {
found[i] = false;
}
for (Object actualValue : actualValues) {
boolean wasExpected = false;
for (int i = 0; i < expectedLength; i++) {
if (!found[i] && expectedValues[i].equals(actualValue)) {
found[i] = true;
wasExpected = true;
break;
}
}
if (!wasExpected) {
fail("The actual value " + actualValue + " was not expected");
}
}
}
/**
* Assert that a given String is equal to an expected value.
*
* @param expected the expected String value
* @param actual the actual String value
*/
public static void assertEqualString(String expected, String actual) {
if (actual == null || expected == null) {
if (actual == expected) {
return;
}
if (actual == null) {
Assert.assertTrue("Content not as expected: is 'null' expected: " + expected, false);
} else {
Assert.assertTrue("Content not as expected: expected 'null' is: " + actual, false);
}
}
int diffPos = getDiffPos(expected, actual);
if (diffPos != -1) {
int diffAhead = Math.max(0, diffPos - PRINT_RANGE);
int diffAfter = Math.min(actual.length(), diffPos + PRINT_RANGE);
String diffStr = actual.substring(diffAhead, diffPos) + '^'
+ actual.substring(diffPos, diffAfter);
// use detailed message
String message = "Content not as expected: is\n" + actual + "\nDiffers at pos " + diffPos + ": " + diffStr + "\nexpected:\n" + expected; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
assertEquals(message, expected, actual);
}
}
/**
* Assert that the given array is non-{@code null} and has exactly expected elements.
*
* @param array the array being tested
* @param expectedElements the expected elements
* @throws AssertionFailedError if the array is {@code null} or does not have the expected
* elements
*/
public static void assertExactElementsInArray(Object array[], Object... expectedElements) {
int expectedSize = expectedElements.length;
if (array == null) {
fail("Expected array of size " + expectedSize + "; found null");
}
if (array.length != expectedSize) {
fail("Expected array of size " + expectedSize + "; contained " + array.length + " elements");
}
for (int i = 0; i < expectedSize; i++) {
Object element = array[i];
Object expectedElement = expectedElements[i];
if (!Objects.equal(element, expectedElement)) {
fail("Expected " + expectedElement + " at [" + i + "]; found " + element);
}
}
}
/**
* Assert that the given list is non-{@code null} and has exactly expected elements.
*
* @param list the list being tested
* @param expectedElements the expected elements
* @throws AssertionFailedError if the list is {@code null} or does not have the expected elements
*/
public static void assertExactElementsInList(List<?> list, Object... expectedElements) {
int expectedSize = expectedElements.length;
if (list == null) {
fail("Expected list of size " + expectedSize + "; found null");
}
if (list.size() != expectedSize) {
fail("Expected list of size " + expectedSize + "; contained " + list.size() + " elements");
}
for (int i = 0; i < expectedSize; i++) {
Object element = list.get(i);
Object expectedElement = expectedElements[i];
if (!Objects.equal(element, expectedElement)) {
fail("Expected " + expectedElement + " at [" + i + "]; found " + element);
}
}
}
/**
* Assert that the given list is non-{@code null} and has exactly expected elements.
*
* @param set the list being tested
* @param expectedElements the expected elements
* @throws AssertionFailedError if the list is {@code null} or does not have the expected elements
*/
public static void assertExactElementsInSet(Set<?> set, Object... expectedElements) {
int expectedSize = expectedElements.length;
if (set == null) {
fail("Expected list of size " + expectedSize + "; found null");
}
if (set.size() != expectedSize) {
fail("Expected list of size " + expectedSize + "; contained " + set.size() + " elements");
}
for (int i = 0; i < expectedSize; i++) {
Object expectedElement = expectedElements[i];
if (!set.contains(expectedElement)) {
fail("Expected " + expectedElement + " in set" + set);
}
}
}
/**
* Assert that the given object is an instance of the expected class.
*
* @param expectedClass the class that the object is expected to be an instance of
* @param object the object being tested
* @return the object that was being tested
* @throws Exception if the object is not an instance of the expected class
*/
@SuppressWarnings("unchecked")
public static <E> E assertInstanceOf(Class<E> expectedClass, Object object) {
if (!expectedClass.isInstance(object)) {
fail("Expected instance of " + expectedClass.getName() + ", found "
+ (object == null ? "null" : object.getClass().getName()));
}
return (E) object;
}
/**
* Assert that the given array is non-{@code null} and has the expected number of elements.
*
* @param expectedLength the expected number of elements
* @param array the array being tested
* @throws AssertionFailedError if the array is {@code null} or does not have the expected number
* of elements
*/
public static void assertLength(int expectedLength, Object[] array) {
if (array == null) {
fail("Expected array of length " + expectedLength + "; found null");
} else if (array.length != expectedLength) {
fail("Expected array of length " + expectedLength + "; contained " + array.length
+ " elements");
}
}
/**
* Assert that the actual token has the same type and lexeme as the expected token. Note that this
* does not assert anything about the offsets of the tokens (although the lengths will be equal).
*
* @param expectedToken the token that was expected
* @param actualToken the token that was found
* @throws AssertionFailedError if the two tokens are not the same
*/
public static void assertMatches(Token expectedToken, Token actualToken) {
assertEquals(expectedToken.getType(), actualToken.getType());
if (expectedToken instanceof KeywordToken) {
assertInstanceOf(KeywordToken.class, actualToken);
assertEquals(
((KeywordToken) expectedToken).getKeyword(),
((KeywordToken) actualToken).getKeyword());
} else if (expectedToken instanceof StringToken) {
assertInstanceOf(StringToken.class, actualToken);
assertEquals(
((StringToken) expectedToken).getLexeme(),
((StringToken) actualToken).getLexeme());
}
}
/**
* Assert that the given list is non-{@code null} and has the expected number of elements.
*
* @param expectedSize the expected number of elements
* @param list the list being tested
* @throws AssertionFailedError if the list is {@code null} or does not have the expected number
* of elements
*/
public static void assertSizeOfList(int expectedSize, List<?> list) {
if (list == null) {
fail("Expected list of size " + expectedSize + "; found null");
} else if (list.size() != expectedSize) {
fail("Expected list of size " + expectedSize + "; contained " + list.size() + " elements");
}
}
/**
* Assert that the given map is non-{@code null} and has the expected number of elements.
*
* @param expectedSize the expected number of elements
* @param map the map being tested
* @throws AssertionFailedError if the map is {@code null} or does not have the expected number of
* elements
*/
public static void assertSizeOfMap(int expectedSize, Map<?, ?> map) {
if (map == null) {
fail("Expected map of size " + expectedSize + "; found null");
} else if (map.size() != expectedSize) {
fail("Expected map of size " + expectedSize + "; contained " + map.size() + " elements");
}
}
/**
* Assert that the given set is non-{@code null} and has the expected number of elements.
*
* @param expectedSize the expected number of elements
* @param set the set being tested
* @throws AssertionFailedError if the set is {@code null} or does not have the expected number of
* elements
*/
public static void assertSizeOfSet(int expectedSize, Set<?> set) {
if (set == null) {
fail("Expected set of size " + expectedSize + "; found null");
} else if (set.size() != expectedSize) {
fail("Expected set of size " + expectedSize + "; contained " + set.size() + " elements");
}
}
/**
* Convert the given array of lines into a single source string.
*
* @param lines the lines to be merged into a single source string
* @return the source string composed of the given lines
*/
public static String createSource(String... lines) {
@SuppressWarnings("resource")
PrintStringWriter writer = new PrintStringWriter();
for (String line : lines) {
writer.println(line);
}
return writer.toString();
}
/**
* @return the {@link AstNode} with requested type at offset of the "prefix".
*/
public static <T extends AstNode> T findNode(AstNode root, String code, String prefix,
Class<T> clazz) {
int offset = code.indexOf(prefix);
if (offset == -1) {
throw new IllegalArgumentException("Not found '" + prefix + "'.");
}
AstNode node = new NodeLocator(offset).searchWithin(root);
return node.getAncestor(clazz);
}
/**
* Calculate the offset where the given strings differ.
*
* @param str1 the first String to compare
* @param str2 the second String to compare
* @return the offset at which the strings differ (or <code>-1</code> if they do not)
*/
private static int getDiffPos(String str1, String str2) {
int len1 = Math.min(str1.length(), str2.length());
int diffPos = -1;
for (int i = 0; i < len1; i++) {
if (str1.charAt(i) != str2.charAt(i)) {
diffPos = i;
break;
}
}
if (diffPos == -1 && str1.length() != str2.length()) {
diffPos = len1;
}
return diffPos;
}
private static void privateAssertContains(Object[] array, boolean[] found, Object element) {
if (element == null) {
for (int i = 0; i < array.length; i++) {
if (!found[i]) {
if (array[i] == null) {
found[i] = true;
return;
}
}
}
fail("Does not contain null");
} else {
for (int i = 0; i < array.length; i++) {
if (!found[i]) {
if (element.equals(array[i])) {
found[i] = true;
return;
}
}
}
fail("Does not contain " + element);
}
}
/**
* Assert that the given collection has the same number of elements as the number of specified
* names, and that for each specified name, a corresponding element can be found in the given
* collection with that name.
*
* @param elements the elements
* @param names the names
*/
protected void assertNamedElements(Element[] elements, String... names) {
for (String elemName : names) {
boolean found = false;
for (Element elem : elements) {
if (elem.getName().equals(elemName)) {
found = true;
break;
}
}
if (!found) {
StringBuilder msg = new StringBuilder();
msg.append("Expected element named: ");
msg.append(elemName);
msg.append("\n but found: ");
for (Element elem : elements) {
msg.append(elem.getName());
msg.append(", ");
}
fail(msg.toString());
}
}
assertLength(names.length, elements);
}
protected AnalysisContextImpl createAnalysisContext() {
AnalysisContextImpl context = new AnalysisContextImpl();
context.setSourceFactory(new SourceFactory());
return context;
}
/**
* Return the getter in the given type with the given name. Inherited getters are ignored.
*
* @param type the type in which the getter is declared
* @param getterName the name of the getter to be returned
* @return the property accessor element representing the getter with the given name
*/
protected PropertyAccessorElement getGetter(InterfaceType type, String getterName) {
for (PropertyAccessorElement accessor : type.getElement().getAccessors()) {
if (accessor.isGetter() && accessor.getName().equals(getterName)) {
return accessor;
}
}
fail("Could not find getter named " + getterName + " in " + type.getDisplayName());
return null;
}
/**
* Return the method in the given type with the given name. Inherited methods are ignored.
*
* @param type the type in which the method is declared
* @param methodName the name of the method to be returned
* @return the method element representing the method with the given name
*/
protected MethodElement getMethod(InterfaceType type, String methodName) {
for (MethodElement method : type.getElement().getMethods()) {
if (method.getName().equals(methodName)) {
return method;
}
}
fail("Could not find method named " + methodName + " in " + type.getDisplayName());
return null;
}
}