blob: 1fdf0311cc7f87f6464c989679ffc9feee9307fb [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.testing.framework;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.Vector;
/**
* <p><b>Purpose</b>:Creates several variations of the passed TestCase
* (or of each member of a Vector of TestCases) using all combinations
* of values of the specified boolean attributes on the specified object.
* Suppose we want to run an existing test with four different DatabasePlatform settings:
* shouldBindAllParameters shouldCacheAllStatements
* false false
* false true
* true false
* true true
* All we should do is to substitute:
* testSuite.addTest(test);
* for:
* Object obj = getSession().getPlatform();
* String str = "shouldBindAllParameters shouldCacheAllStatements"
* testSuite.addTests(TestVariation.get(obj, str, test));
* The attributes names should be separated by a space.
* Reflection is used to find a getter and a setter for the specified attribute:
* the list of all the methods with appropriate signature is examined, and the first
* method with a name containing the specified name (case insensitive comparison) is used.
* Therefore if the name passed is "foo", both "getFoo" and "setFoo" will be found,
* but if there also a "getFooo" and "setFooo" methods, they could be wrongly picked up.
* If the getter/setter pair, or a field for the specified name is not found,
* a TestWarningException is thrown.
*/
public class TestVariation {
protected static char separator = ' ';
public static Vector get(Object object, String str, TestCase test) {
Vector testsIn = new Vector(1);
testsIn.add(test);
return get(object, str, testsIn);
}
public static Vector get(Object object, String str, Vector testsIn) {
Vector names = getNames(str);
Vector testsOut = new Vector();
if (names.isEmpty() || testsIn.isEmpty()) {
return testsOut;
}
Vector getters = new Vector();
Vector setters = new Vector();
Vector fields = new Vector();
getMembers(object.getClass(), names, getters, setters, fields, true);
int numberOfTests = (int)java.lang.Math.pow(2, names.size());
for (int i = 0; i < numberOfTests; i++) {
Enumeration enumtr = testsIn.elements();
while (enumtr.hasMoreElements()) {
TestWrapper testWrapper = createTest(i, object, names, getters, setters, fields, (TestCase)enumtr.nextElement());
testsOut.addElement(testWrapper);
}
}
return testsOut;
}
protected static Vector getNames(String str) {
Vector names = new Vector();
String[] strPair = splitString(str);
while (strPair[0] != null) {
names.addElement(strPair[0]);
strPair = splitString(strPair[1]);
}
return names;
}
protected static InnerTestWrapper createTest(int index, Object object, Vector names, Vector getters, Vector setters, Vector fields, TestCase test) {
boolean[] values = new boolean[names.size()];
int i = 0;
while (index > 0) {
values[i] = (index % 2) == 1;
i++;
index = index / 2;
}
String[] namesArray = new String[names.size()];
namesArray = (String[])names.toArray(namesArray);
Method[] gettersArray = new Method[names.size()];
gettersArray = (Method[])getters.toArray(gettersArray);
Method[] settersArray = new Method[names.size()];
settersArray = (Method[])setters.toArray(settersArray);
Field[] fieldsArray = new Field[names.size()];
fieldsArray = (Field[])fields.toArray(fieldsArray);
return new InnerTestWrapper(test, object, values, namesArray, gettersArray, settersArray, fieldsArray);
}
protected static String[] splitString(String str) {
String[] out = new String[2];
out[0] = null;
out[1] = null;
if (str == null) {
return out;
}
int first = -1;
int lastPlusOne = str.length();
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if (first == -1) {
if (ch != separator) {
first = i;
}
} else {
if (ch == separator) {
lastPlusOne = i;
break;
}
}
}
if (first == -1) {
return out;
}
out[0] = str.substring(first, lastPlusOne);
out[1] = str.substring(lastPlusOne, str.length());
return out;
}
protected static void getMembers(Class cls, Vector names, Vector getters, Vector setters, Vector fields, boolean throwExceptionIfNotFound) {
Method[] allMethods = cls.getMethods();
Field[] allFields = cls.getFields();
Vector candidateGetters = new Vector();
Vector candidateSetters = new Vector();
Vector candidateFields = new Vector();
// Need those to save lower case names
Vector candidateGettersNames = new Vector();
Vector candidateSettersNames = new Vector();
Vector candidateFieldsNames = new Vector();
for (int i = 0; i < allMethods.length; i++) {
Class returnType = allMethods[i].getReturnType();
Class[] parameterTypes = allMethods[i].getParameterTypes();
if (returnType.equals(boolean.class) && (parameterTypes.length == 0)) {
candidateGetters.addElement(allMethods[i]);
candidateGettersNames.addElement(allMethods[i].getName().toLowerCase());
} else if (returnType.equals(void.class) && (parameterTypes.length == 1)) {
if (parameterTypes[0].equals(boolean.class)) {
candidateSetters.addElement(allMethods[i]);
candidateSettersNames.addElement(allMethods[i].getName().toLowerCase());
}
}
}
for (int i = 0; i < allFields.length; i++) {
Class type = allFields[i].getType();
if (type.equals(boolean.class)) {
candidateFields.addElement(allFields[i]);
candidateFieldsNames.addElement(allFields[i].getName().toLowerCase());
}
}
for (int i = 0; i < names.size(); i++) {
Object getter = null;
Object setter = null;
Object field = null;
String name = ((String)names.elementAt(i)).toLowerCase();
for (int j = 0; j < candidateGetters.size(); j++) {
if (((String)candidateGettersNames.elementAt(j)).indexOf(name) != -1) {
getter = candidateGetters.elementAt(j);
candidateGetters.remove(j);
candidateGettersNames.remove(j);
break;
}
}
for (int j = 0; j < candidateSetters.size(); j++) {
if (((String)candidateSettersNames.elementAt(j)).indexOf(name) != -1) {
setter = candidateSetters.elementAt(j);
candidateSetters.remove(j);
candidateSettersNames.remove(j);
break;
}
}
for (int j = 0; j < candidateFields.size(); j++) {
if (((String)candidateFieldsNames.elementAt(j)).indexOf(name) != -1) {
field = candidateFields.elementAt(j);
candidateFields.remove(j);
candidateFieldsNames.remove(j);
break;
}
}
if ((field != null) || ((setter != null) && (getter != null))) {
setters.add(setter);
getters.add(getter);
fields.add(field);
} else {
if (throwExceptionIfNotFound) {
throw new TestWarningException("Can't find a setter/getter or field for " + names.elementAt(i));
} else {
names.remove(i);
i--;
}
}
}
}
protected static class InnerTestWrapper extends TestWrapper {
protected Object object;
private Field[] fields;
private Method[] getters;
private Method[] setters;
private boolean[] required;
private boolean[] original;
public InnerTestWrapper(TestCase test, Object object, boolean[] values, String[] names, Method[] getters, Method[] setters, Field[] fields) {
super(test);
this.object = object;
original = new boolean[values.length];
required = new boolean[values.length];
this.getters = getters;
this.setters = setters;
this.fields = fields;
System.arraycopy(values, 0, required, 0, values.length);
for (int i = 0; i < values.length; i++) {
String name;
if (fields[i] != null) {
name = fields[i].getName();
} else {
name = names[i];
}
setName(getName() + " " + name + "=" + values[i]);
}
}
@Override
protected void setup() throws Throwable {
for (int i = 0; i < required.length; i++) {
if (getters[i] != null) {
Object[] args = { };
Boolean res = (Boolean)getters[i].invoke(object, args);
original[i] = res;
} else {
original[i] = fields[i].getBoolean(object);
}
if (setters[i] != null) {
Object[] args = {required[i]};
setters[i].invoke(object, args);
} else {
fields[i].setBoolean(object, required[i]);
}
}
super.setup();
}
@Override
public void reset() throws Throwable {
super.reset();
for (int i = required.length - 1; i >= 0; i--) {
if (setters[i] != null) {
Object[] args = {original[i]};
setters[i].invoke(object, args);
} else {
fields[i].setBoolean(object, original[i]);
}
}
}
}
}