![JAR search and dependency download from the Maven repository](/logo.png)
de.tsl2.nano.autotest.creator.AutoTestGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tsl2.nano.autotest Show documentation
Show all versions of tsl2.nano.autotest Show documentation
Generates unit tests from code or annotated methods
The newest version!
/*
* created by: Tom
* created on: 31.03.2017
*
* Copyright: (c) Thomas Schneider 2017, all rights reserved
*/
package de.tsl2.nano.autotest.creator;
import static de.tsl2.nano.autotest.creator.AFunctionCaller.def;
import static de.tsl2.nano.autotest.creator.AutoTest.CHECK_TYPECONVERSION;
import static de.tsl2.nano.autotest.creator.AutoTest.CLEAN;
import static de.tsl2.nano.autotest.creator.AutoTest.DONTTEST;
import static de.tsl2.nano.autotest.creator.AutoTest.DUPLICATION;
import static de.tsl2.nano.autotest.creator.AutoTest.FAST_CLASSSCAN;
import static de.tsl2.nano.autotest.creator.AutoTest.FILENAME;
import static de.tsl2.nano.autotest.creator.AutoTest.FILTER;
import static de.tsl2.nano.autotest.creator.AutoTest.FILTER_COMPLEXTYPES;
import static de.tsl2.nano.autotest.creator.AutoTest.FILTER_ERROR_TYPES;
import static de.tsl2.nano.autotest.creator.AutoTest.FILTER_EXCLUDE;
import static de.tsl2.nano.autotest.creator.AutoTest.FILTER_FAILING;
import static de.tsl2.nano.autotest.creator.AutoTest.FILTER_NONINSTANCEABLES;
import static de.tsl2.nano.autotest.creator.AutoTest.FILTER_NULLRESULTS;
import static de.tsl2.nano.autotest.creator.AutoTest.FILTER_SINGELTONS;
import static de.tsl2.nano.autotest.creator.AutoTest.FILTER_TEST;
import static de.tsl2.nano.autotest.creator.AutoTest.FILTER_UNSUCCESSFUL;
import static de.tsl2.nano.autotest.creator.AutoTest.FILTER_VOID_PARAMETER;
import static de.tsl2.nano.autotest.creator.AutoTest.FILTER_VOID_RETURN;
import static de.tsl2.nano.autotest.creator.AutoTest.MAX_LINE_LENGTH;
import static de.tsl2.nano.autotest.creator.AutoTest.MODIFIER;
import static de.tsl2.nano.autotest.creator.AutoTest.PARALLEL;
import static de.tsl2.nano.autotest.creator.AutoTest.PRECHECK_TWICE;
import static de.tsl2.nano.autotest.creator.AutoTest.TESTNEVERFAIL;
import static org.junit.Assert.assertArrayEquals;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Scanner;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import de.tsl2.nano.autotest.ValueRandomizer;
import de.tsl2.nano.core.IPreferences;
import de.tsl2.nano.core.ManagedException;
import de.tsl2.nano.core.cls.BeanClass;
import de.tsl2.nano.core.cls.ClassFinder;
import de.tsl2.nano.core.exception.ExceptionHandler;
import de.tsl2.nano.core.execution.ProgressBar;
import de.tsl2.nano.core.log.LogFactory;
import de.tsl2.nano.core.util.ByteUtil;
import de.tsl2.nano.core.util.ConcurrentUtil;
import de.tsl2.nano.core.util.DateUtil;
import de.tsl2.nano.core.util.FileUtil;
import de.tsl2.nano.core.util.FormatUtil;
import de.tsl2.nano.core.util.MethodUtil;
import de.tsl2.nano.core.util.NumberUtil;
import de.tsl2.nano.core.util.ObjectUtil;
import de.tsl2.nano.core.util.StringUtil;
import de.tsl2.nano.core.util.Util;
import de.tsl2.nano.core.util.parser.JSon;
/**
* generates and prints {@link Expectations} annotations on all methods, fitting
* the given class and method filter.
*
* these methods will be run with randomized parameter values. the results will
* be used to fill the {@link Expect} values of the {@link Expectations}
* annoation.
*
* so, the generated {@link Expectations} contain values to specify the code
* implementation as is!
*
* works only on methods with simple parameter and return types.
*
* Usage:
* call createExpectationTesters() in your junit test with @parameterized annotation.
*
*
* @author ts
*/
public class AutoTestGenerator {
private static final String REGEX_UNMATCH = "XXXXXXXX";
static String fileName = def(FILENAME, String.class).replace("autotest/", "");
AtomicInteger methods_loaded = new AtomicInteger();
List counts = Collections.synchronizedList(new ArrayList<>(def(DUPLICATION, int.class)));
AtomicInteger fails = new AtomicInteger();
AtomicInteger nullresults = new AtomicInteger();
AtomicInteger load_method_error = new AtomicInteger();
AtomicInteger load_unsuccessful = new AtomicInteger();
AtomicInteger filter_typeconversions = new AtomicInteger();
AtomicInteger filter_errors = new AtomicInteger();
AtomicInteger filter_unsuccessful = new AtomicInteger();
AtomicInteger filter_nullresults = new AtomicInteger();
AtomicInteger filter_complextypes = new AtomicInteger();
private Statistics statistics = new Statistics();
private AtomicReference filteredFunctionWriter = new AtomicReference();
private ExceptionHandler uncaughtExceptionHandler = new ExceptionHandler();
static ProgressBar progress;
private AFunctionCaller maxDurationFct, maxMemUsageFct;
private long start;
public static void main(String[] args) {
if (args.length == 0) {
log("Please provide a comma-separated list of full classnames to be loaded!");
return;
}
String[] clsNames = args[0].split("\\s*,\\s*");
Arrays.stream(clsNames).forEach(c -> ClassFinder.getClassesInPackage(BeanClass.load(c).getPackage().getName(), null));
new AutoTestGenerator().createExpectationTesters();
}
private void printStartParameters() {
Util.trY( () -> FileUtil.writeBytes((IPreferences.printInfo(AutoTest.class)).getBytes(), getTimedFileName() + "statistics.txt", false), false);
}
public String getTimedFileName() {
return fileName + DateUtil.getShortTimestamp(start) + "-";
}
@SuppressWarnings("rawtypes")
public Collection extends AFunctionTester> createExpectationTesters() {
if (def(DONTTEST, false)) {
log("\n##############################################");
log(" donttest=true ==> Leaving AutoTestGenerator!");
log("##############################################");
return new LinkedList<>();
}
start = System.currentTimeMillis();
def("timefilename", getTimedFileName()); //provide timed file name for other instances
int duplication = def(DUPLICATION, 10);
for (int i = 0; i < duplication; i++)
counts.add(0);
Collection testers = Collections.synchronizedList(new LinkedList<>());
try {
printStartParameters();
FileUtil.delete(fileName + "initialization-error.txt");
List methods = getMethods();
progress = new ProgressBar(methods.size() * duplication);
ArrayList dupList = NumberUtil.numbers(duplication);
Util.stream(dupList, def(PARALLEL, false)).forEach(i -> {
Thread.currentThread().setUncaughtExceptionHandler(uncaughtExceptionHandler );
if (!getFile(i).exists() || def(CLEAN, false)) {
generateExpectations(i, methods);
}
});
Util.stream(dupList, def(PARALLEL, false)).forEach(i -> {
Thread.currentThread().setUncaughtExceptionHandler(uncaughtExceptionHandler );
if (getFile(i).exists() || counts.get(i) > 0)
testers.addAll(readExpectations(i));
});
if (def(FILTER_UNSUCCESSFUL, true))
load_unsuccessful.addAndGet(FunctionCheck.filterFailingTest(testers, fileName));
return testers;
} catch (Throwable e) {
Util.trY( () -> ManagedException.writeError(e, fileName + "initialization-error.txt"), false);
ConcurrentUtil.sleep(3000);
log("JUNIT TEST PARAMETERS FAILING:");
e.printStackTrace();
ManagedException.forward(e);
return null;
} finally {
if (statistics.statuss.isEmpty()) // -> no generation but only reading
testers.forEach( t -> statistics.add(t));
printStatistics(duplication +1, testers, statistics.getInfo(22));
if (filteredFunctionWriter.get() != null) {
Util.trY(() -> filteredFunctionWriter.get().close(), false);
filteredFunctionWriter.set(null); // to avoid access with additional flush on parallel use
}
if (uncaughtExceptionHandler.hasExceptions()) {
Util.trY( () -> FileUtil.writeBytes(uncaughtExceptionHandler.toString().getBytes(), getTimedFileName() + "uncaught-exceptions.txt", false), false);
// throw new IllegalStateException(uncaughtExceptionHandler.toString());
}
if (progress != null)
progress.setFinished();
}
}
private List getMethods() throws IOException {
prepareFilteredWriter();
List methods;
if (def(FAST_CLASSSCAN, true)) {
methods = ClassFinder.self().findMethods(def(FILTER, ""), def(MODIFIER, -1), null);
} else {
methods = ClassFinder.self().find(def(FILTER, ""), Method.class, def(MODIFIER, -1), null);
}
FileUtil.writeBytes(("\nmatching methods in classpath: " + methods.size()).getBytes(),
getTimedFileName() + "statistics.txt", true);
filterMethods(methods);
return methods;
}
private void filterMethods(List methods) {
int filterExcludes = filterExcludes(methods);
int filterTestClasses = filterTestClasses(methods);
int filterSingeltons = filterSingeltons(methods);
int filterNonInstanceable = filterNonInstanceable(methods);
FileUtil.writeBytes(("\nfiltered methods : "
+ methods.size() + " ("
+ FILTER_EXCLUDE + "->" + filterExcludes + " "
+ FILTER_TEST + "->" + filterTestClasses + " "
+ FILTER_SINGELTONS + "->" + filterSingeltons + " "
+ FILTER_NONINSTANCEABLES + "->" + filterNonInstanceable + " "
+ ")").getBytes(), getTimedFileName() + "statistics.txt", true);
}
private void prepareFilteredWriter() throws IOException {
if (!getFile(0).exists() || def(CLEAN, false))
FileUtil.delete(fileName + "filtered.txt");
filteredFunctionWriter.set(FileUtil.getBAWriter(fileName + "filtered.txt"));
}
private static int filter(List methods, Predicate myFilter) {
int size = methods.size();
methods.removeIf(myFilter);
return size - methods.size();
}
private static int filterExcludes(List methods) {
return filter(methods, m -> m.toGenericString().matches(def(FILTER_EXCLUDE, REGEX_UNMATCH)));
}
private static int filterTestClasses(List methods) {
return filter(methods, m -> m.getDeclaringClass().getName().matches(def(FILTER_TEST, ".*(Test|IT)")));
}
private static int filterSingeltons(List methods) {
if (def(FILTER_SINGELTONS, true))
return filter(methods, m -> BeanClass.getBeanClass(m.getDeclaringClass()).isSingleton());
return 0;
}
private static int filterNonInstanceable(List methods) {
if (def(FILTER_NONINSTANCEABLES, true))
return filter(methods, m -> !BeanClass.isStatic(m) && !Util.isInstanceable((m.getDeclaringClass())));
return 0;
}
private static boolean filterErrorType(Throwable e) {
return ManagedException.getRootCause(e).toString().matches(def(FILTER_ERROR_TYPES, REGEX_UNMATCH));
}
void generateExpectations(int iteration, List methods) {
LogFactory.setPrintToConsole(false);
String p = "\n" + StringUtil.fixString(79, '~') + "\n";
try (BufferedWriter writer = FileUtil.getBAWriter(getFile(iteration).getPath())) {
methods_loaded.set(methods.size());
log(p + "calling " + methods.size() + " methods to create expectations -> " + getFile(iteration) + p);
AtomicReference refWriter = new AtomicReference<>(writer);
Util.stream(methods, def(PARALLEL, false)).forEach(m -> writeExpectation(new AFunctionCaller(iteration, m), refWriter));
} catch (Exception e1) {
ManagedException.forward(e1);
} finally {
if (filteredFunctionWriter != null)
Util.trY( () ->filteredFunctionWriter.get().flush(), false);
ConcurrentUtil.sleep(200);
// if (count > 0)
// log(new String(FileUtil.getFileBytes(getFile(iteration).getPath(), null)));
log(p + counts.get(iteration) + " expectations written into '" + getFile(iteration) + p);
}
}
File getFile(int iteration) {
return FileUtil.userDirFile(fileName + iteration + ".txt");
}
private void writeExpectation(AFunctionCaller f, AtomicReference writer) {
Thread.currentThread().setUncaughtExceptionHandler(uncaughtExceptionHandler );
try {
log("writeExpectation: " + f.cloneIndex + ": " + f.getFunctionDescription(), progress);
String then = null;
try {
if (f.source.isSynthetic() || f.source.isBridge() || f.source.getName().startsWith("$")) // -> e.g. $jacocoInit() -> javaagent code enhancing
f.status = Status.FUNC_SYNTHETIC;
if (def(FILTER_VOID_PARAMETER, false) && f.source.getParameterCount() == 0 && Modifier.isStatic(f.source.getModifiers()))
f.status = Status.FUNC_WITHOUT_INPUT;
else if (def(FILTER_VOID_RETURN, false) && void.class.isAssignableFrom(f.source.getReturnType()))
f.status = Status.FUNC_WITHOUT_OUTPUT;
else if (def(FILTER_COMPLEXTYPES, false) && !FunctionCheck.hasSimpleTypes(f))
f.status = Status.FUNC_COMPLEX_INPUT;
if (f.status.isRefused()) {
writeFilteredFunctionCall(f);
filter_complextypes.incrementAndGet();
return;
}
f.runWithTimeout();
} catch (Exception | AssertionError e) {
if (f.status.in(StatusTyp.NEW) || f.status.isFatal() || def(FILTER_FAILING, false) || filterErrorType(e)) {
writeFilteredFunctionCall(f);
filter_errors.incrementAndGet();
return;
}
fails.incrementAndGet();
then = "fail(" + AFunctionTester.getErrorMsg(e) + ")";
}
if (then == null) {
if (f.getResult() == null) {
if (def(FILTER_NULLRESULTS, false)) {
filter_nullresults.incrementAndGet();
writeFilteredFunctionCall(f);
return;
} else {
then = "null";
}
}
}
if (f.getResult() != null && def(CHECK_TYPECONVERSION, false)
&& !FunctionCheck.checkTypeConversion(f.getResult())) {
writeFilteredFunctionCall(f);
filter_typeconversions.incrementAndGet();
return;
}
String expect = ExpectationCreator.createExpectationString(f, then);
if (expect == null) {
writeFilteredFunctionCall(f);
filter_typeconversions.incrementAndGet();
return;
}
Status testStatus;
if (def(FILTER_UNSUCCESSFUL, true) && (testStatus = FunctionCheck.checkTestSuccessful(f, expect)) != null) {
f.status = testStatus;
writeFilteredFunctionCall("STATUS: " + f.status + expect);
filter_unsuccessful.incrementAndGet();
return;
}
counts.set(f.cloneIndex, counts.get(f.cloneIndex) + 1);
maxDurationFct = maxDurationFct == null || maxDurationFct.duration < f.duration ? f : maxDurationFct;
maxMemUsageFct = maxMemUsageFct == null || maxMemUsageFct.memusage < f.memusage ? f : maxMemUsageFct;
Util.trY(() -> writer.get().append(expect));
} finally {
statistics.add(f);
}
}
private void writeFilteredFunctionCall(AFunctionCaller f) {
String fct;
try {
fct = f.getFunctionDescription();
if (fct.length() > def(MAX_LINE_LENGTH, Integer.class)) {
log(f.getFunctionDescription() + " with to long parameter json string: " + fct.length());
fct = fct.substring(0, def(MAX_LINE_LENGTH, Integer.class)) + "...";
}
} catch (Throwable e) {
e.printStackTrace();
fct = f.cloneIndex + ":" + f.getFunctionDescription()
+ " caused error on AFunctionCaller.toString() on writingFilterFunctinoCall(): " + e.getMessage();
}
writeFilteredFunctionCall(fct + "\n");
}
private void writeFilteredFunctionCall(String call) {
Util.trY(() -> filteredFunctionWriter.get().append(call), false);
}
public Collection readExpectations(int iteration) {
return readExpectations(iteration, getFile(iteration));
}
Collection readExpectations(int iteration, File file) {
log("\nREADING " + counts.get(iteration) + " EXPECTATIONS FROM " + file.getPath() + "...\n");
LinkedHashSet expTesters = new LinkedHashSet<>();
Scanner sc = Util.trY( () ->new Scanner(file));
ProgressBar progress = new ProgressBar(
(int) (counts.get(iteration) > 0 ? counts.get(iteration) : file.length() / 1000));
Expectations exp = null;
Method method = null;
while (sc.hasNextLine()) {
String l = sc.nextLine().trim();
if (l.length() == 0 || l.startsWith("#") || (exp == null && !l.startsWith("@")))
continue;
if (exp == null) {
exp = ExpectationCreator.createExpectationFromLine(l);
} else {
if (l.matches(MethodUtil.REGEX_FULL_METHOD_EXPRESSION)) {
method = ExpectationCreator.extractMethod(l);
progress.increase(" " + iteration + ": " + (method != null
? " " + method.getDeclaringClass().getSimpleName() + "." + method.getName()
: " ..."));
if (method != null)
expTesters.add(new ExpectationFunctionTester(iteration, method, exp));
} else {
load_method_error.incrementAndGet();
log("ERROR: method-format for " + StringUtil.toString(exp, 120) + " -> " + l + "\n");
}
exp = null;
}
}
log("\nEXPECTATION READING ON ITERATION " + iteration + " FINSIHED!\n");
return expTesters;
}
private static void log(Object obj) {
log(obj, null);
}
private static void log(Object obj, ProgressBar progress) {
if (progress == null)
AFunctionCaller.log(obj + "\n");
else
// synchronized (progress) {
progress.increase(" " + obj.toString());
// }
}
private void printStatistics(int iterations, Collection testers, String groupByState) {
String p = "\n" + StringUtil.fixString(79, '=') + "\n";
Integer dup = def(DUPLICATION, 10);
int count = counts.stream().reduce(0, Integer::sum);
String s = AutoTestGenerator.class.getSimpleName() + " created " + count + " expectations in file pattern: '" + fileName + "...'"
+ "\n\tend time : " + DateUtil.getFormattedDateTime(new Date()) + "\tduration: " + DateUtil.getFormattedTime(System.currentTimeMillis() - start)
+ "\n\ttestneverfail : " + def(TESTNEVERFAIL, false)
+ "\n\tclassfinder cls/mthds : " + ClassFinder.self().getLoadedClassCount() + " / " + ClassFinder.self().getLoadedMethodCount()
+ "\n\tmethods loaded : " + methods_loaded.get() + "\t(rate: "
+ methods_loaded.get() / (float) ClassFinder.self().getLoadedMethodCount() + ")"
+ "\n\tduplications : " + dup + "\t(methods loaded * duplications: "
+ methods_loaded.get() * dup + ")"
+ "\nGENERATION PROCESS:"
+ "\n\tcreated with fail : " + fails.get()
+ "\n\tcreated with null : " + nullresults.get()
+ "\n\tcreated totally : " + count
+ "\n\tfiltered type error : " + filter_typeconversions.get()
+ "\n\tfiltered complex types: " + filter_complextypes.get()
+ "\n\tfiltered errors : " + filter_errors.get()
+ "\n\tfiltered nulls : " + filter_nullresults.get()
+ "\n\tmax duration : " + (maxDurationFct != null ? maxDurationFct.duration + " msec\t\t<- " + maxDurationFct.cloneIndex + ":" + maxDurationFct.getFunctionDescription() : "")
+ "\n\tmax mem usage : " + (maxMemUsageFct != null ? ByteUtil.amount(maxMemUsageFct.memusage) + "\t\t\t<- " + maxMemUsageFct.cloneIndex + ":" + maxMemUsageFct.getFunctionDescription() : "")
+ "\n\tuncaught exceptions : " + uncaughtExceptionHandler.getExceptions().size()
+ groupByState
+ "\nLOADING PROCESS (Unit Testing):"
+ "\n\tfiltered unsuccessful : " + filter_unsuccessful.get()
+ "\n\tload errors : " + load_method_error.get()
+ "\n\tloaded unsuccessful : " + load_unsuccessful.get()
+ "\n\ttotally loaded/tested : " + testers.size() + " (load-rate: "
+ (testers.size() / (float) (dup + 1)) / (float) methods_loaded.get() + ", total-rate: "
+ (testers.size() / (dup + 1)) / (float) ClassFinder.self().getLoadedMethodCount() + ")"
+ "\n\n" + ValueRandomizer.getDependencyInjector();
AFunctionTester.log(p + s +p);
FileUtil.writeBytes((p + s + p).getBytes(), getTimedFileName() + "statistics.txt", true);
}
}
class Statistics {
Collection statuss = Collections.synchronizedCollection(new LinkedList<>());
public void add(AFunctionCaller fc) { statuss.add(fc.status);}
public String getInfo(int keyWidth) {
int[] count = new int[StatusTyp.values().length];
statuss.forEach(s -> ++count[s.typ.ordinal()]);
final StringBuilder buf = new StringBuilder("\n\tGENERATED FUNCTION TESTERS GROUPED BY STATE:");
for (int i = 0; i < count.length; i++) {
buf.append("\n\t\t" + StringUtil.fixString(StatusTyp.values()[i], keyWidth) + ": " + count[i]);
}
buf.append("\n\t\t" + StringUtil.fixString("<<< TOTALLY >>>", keyWidth) + ": " + statuss.size());
return buf.toString();
}
}
class ExpectationCreator {
private static final String PREF_WHEN = "@" + Expectations.class.getSimpleName() + "({@"
+ Expect.class.getSimpleName() + "( when = [";
private static final String PREF_THEN = "then = \"-->";
private static final String POST_WHEN = "] " + PREF_THEN;
private static final String POST_THEN = "<--\"";
private static final String PREF_CONSTRUCT_TYPES = "constructTypes = [";
private static final String PREF_CONSTRUCT = "construct = [";
private static final String POST_CONSTRUCT_TYPES = "] " + PREF_CONSTRUCT;
private static final String POST_END = "])}";
static String createExpectationString(AFunctionCaller f, String then) {
try {
if ((then == null || then.equals("null")) && void.class.isAssignableFrom(f.source.getReturnType())) {
if (f.getParameter().length > 0 && f.getParameter()[0] != null)
then = asString(f.getParameter()[0]); // see ExpectationTester.getResultIndex()
}
then = (then != null || f.getResult() == null ? then : asString(f.getResult()));
String expect = "\n@" + Expectations.class.getSimpleName() + "({@" + Expect.class.getSimpleName()
+ "( when = " + Util.toJson(f.getParameter())
+ " " + PREF_THEN + then + POST_THEN
+ (f.construction != null && f.construction.parameter != null ?
" constructTypes = " + Util.toJson(f.getConstruction().constructor.getParameterTypes())
+ " construct = " + Util.toJson(prepareParameter(f.getConstruction().parameter))
: "")
+ ")})\n" + f.source + "\n";
return expect;
} catch (Exception e) {
f.status = new Status(StatusTyp.STORE_ERROR, null, e);
return null;
}
}
private static Object[] prepareParameter(Object[] parameter) {
for (int i = 0; i < parameter.length; i++) {
if (parameter[i] instanceof Class) {
parameter[i] = ((Class) parameter[i]).getName();
}
}
return parameter;
}
private static String asString(Object obj) {
return AFunctionTester.convertMultilineString(ObjectUtil.isSingleValueType(obj.getClass()) ? FormatUtil.format(obj) : Util.toJson(obj));
}
static Expectations createExpectationFromLine(String l) {
String when[], then, construct[];
when = extractArray(l, PREF_WHEN, POST_WHEN);
then = StringUtil.substring(l, PREF_THEN, POST_THEN, 0, true);
Class[] constructTypes = loadClasses(extractArray(l, PREF_CONSTRUCT_TYPES, POST_CONSTRUCT_TYPES));
construct = extractArray(l, PREF_CONSTRUCT, POST_END);
return ExpectationCreator.createExpectation(when, then, constructTypes, construct);
}
private static Class[] loadClasses(String[] typenames) {
if (typenames == null)
return null;
Class[] types = new Class[typenames.length];
for (int i = 0; i < typenames.length; i++) {
types[i] = ObjectUtil.wrap(typenames[i], Class.class);
}
return types;
}
private static String[] extractArray(String l, String prefix, String postfix) {
String all = StringUtil.substring(l, prefix, postfix, 0, true);
return all != null ? new JSon().splitArray("[" + all + "]") : null;
}
static ExpectationsImpl createExpectation(String[] when, String then, Class[] constructTypes, String[] construct) {
return new ExpectationsImpl(new ExpectImpl(-1, null, when, StringUtil.toString(then), -1, constructTypes, construct));
}
@SuppressWarnings("unchecked")
static Method extractMethod(String m) {
String name = StringUtil.substring(m, " ", "(", false);
name = StringUtil.substring(name, " ", "(", true);
String methodName = StringUtil.substring(name, ".", null, true);
String clsName = StringUtil.substring(name, null, "." + methodName);
String pars = StringUtil.substring(m, "(", ")");
try {
return BeanClass.load(clsName).getDeclaredMethod(methodName, Util.isEmpty(pars) ? new Class[0] : createParameterTypes(pars.split("[,]")));
} catch (Exception e) {
AFunctionTester.log(e.toString() + "\n");
return null;
}
}
@SuppressWarnings("rawtypes")
static Class[] createParameterTypes(String[] pars) {
Class[] types = new Class[pars.length];
for (int i = 0; i < types.length; i++) {
types[i] = pars[i].contains("[]")
? BeanClass.loadArrayClass(pars[i])
: ObjectUtil.loadClass(pars[i]);
}
return types;
}
}
class FunctionCheck {
static Status checkTestSuccessful(AFunctionCaller t, String expect) {
ExpectationFunctionTester tester = null;
try {
tester = new ExpectationFunctionTester(t.cloneIndex, t.source, ExpectationCreator.createExpectationFromLine(expect));
if (def(CHECK_TYPECONVERSION, false))
checkExpectationLoading(t, tester);
tester.testMe();
if (def(PRECHECK_TWICE, true))
tester.testMe(); //do it twice, sometimes a value changes the first time. would be better to do the initial run() twice!
t.status = tester.status;
return null;
} catch (Exception | AssertionError e) {
return tester != null ? tester.status
: new Status(StatusTyp.PARSING_ERROR, "tester couldn't be created in cause of parsing problems", e);
}
}
private static void checkExpectationLoading(AFunctionCaller t, ExpectationFunctionTester tester) {
try {
if (t.getConstruction() != null && tester.getConstruction() != null)
assertArrayEquals(t.getConstruction().parameter, tester.getConstruction().parameter);
} catch (Exception e) {
tester.status = new Status(StatusTyp.TYPECONVERSION_CHECK_FAIL, "expectation instance construction failed",
e);
}
try {
if (t.getParameter() != null)
assertArrayEquals(t.getParameter(), tester.getParameter());
} catch (Exception e) {
tester.status = new Status(StatusTyp.TYPECONVERSION_CHECK_FAIL, "loading expectation failed", e);
}
}
static boolean checkTypeConversion(Object result) {
try {
String strResult = FormatUtil.format(result);
Object recreatedResult = null;
recreatedResult = ObjectUtil.wrap(strResult, result.getClass());
return Objects.deepEquals(AFunctionTester.best(result), AFunctionTester.best(recreatedResult));
} catch (Throwable e) { //catch Throwable as it is possible that something like OutOfMemoryError occur
return false;
}
}
static boolean hasSimpleTypes(AFunctionCaller f) {
if (!isSimpleType(f.source.getReturnType()))
return false;
return Arrays.stream(f.source.getParameterTypes()).allMatch(t -> isSimpleType(t));
}
static boolean isSimpleType(Class> t) {
return ObjectUtil.isStandardType(t) && ObjectUtil.isSingleValueType(t);
}
@SuppressWarnings("rawtypes")
static int filterFailingTest(Collection testers, String filePrefix) {
Collection failing = Collections.synchronizedCollection(new LinkedList<>());
int size = testers.size();
try ( BufferedWriter removedFunctionWriter = FileUtil.getBAWriter(filePrefix + "removed-functions.txt")) {
AtomicReference refWriter = new AtomicReference<>(removedFunctionWriter);
Util.stream(testers, def(PARALLEL, false)).forEach( t -> {
try {
t.testMe();
} catch (Throwable e) {
failing.add(t);
Util.trY( () -> refWriter.get().append(t.getID() + "\n"), false);
}
});
testers.removeAll(failing);
if (testers.size() != size-failing.size())
throw new IllegalStateException("failing testers were not removed from list - check your equals()!");
} catch (IOException e1) {
ManagedException.forward(e1);
}
return failing.size();
}
}
// TODO: replace this implementation by using the AdapterProxy
class ExpectationsImpl implements Expectations {
private Expect[] value;
public ExpectationsImpl(Expect... value) {
this.value = value;
}
@Override
public Class extends Annotation> annotationType() {
return Expectations.class;
}
@Override
public Expect[] value() {
return value;
}
@Override
public String toString() {
return Util.toJson(this);
}
}
class ExpectImpl implements Expect {
private int parIndex;
private String whenPar;
private String[] when;
private String then;
private int resultIndex;
private String[] construct;
private Class[] constructTypes;
@SuppressWarnings("rawtypes")
public ExpectImpl(int parIndex, String whenPar, String[] when, String then, int resultIndex, Class[] constructTypes, String[] construct) {
super();
this.parIndex = parIndex;
this.whenPar = whenPar;
this.when = when;
this.then = then;
this.resultIndex = resultIndex;
this.constructTypes = constructTypes;
this.construct = construct;
}
@Override
public Class extends Annotation> annotationType() {
return Expect.class;
}
@Override
public int parIndex() {
return parIndex;
}
@Override
public String whenPar() {
return whenPar;
}
@Override
public String[] when() {
return when;
}
@Override
public String then() {
return then;
}
@Override
public int resultIndex() {
return resultIndex;
}
@Override
public Class[] constructTypes() {
return constructTypes;
}
@Override
public String[] construct() {
return construct;
}
@Override
public String toString() {
return Util.toJson(this);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy