All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.basex.query.func.unit.Unit Maven / Gradle / Ivy
package org.basex.query.func.unit;
import static org.basex.query.QueryError.*;
import static org.basex.query.ann.Annotation.*;
import static org.basex.query.func.unit.Constants.*;
import static org.basex.util.Token.*;
import java.io.*;
import java.util.*;
import org.basex.core.*;
import org.basex.core.jobs.*;
import org.basex.io.*;
import org.basex.query.*;
import org.basex.query.ann.*;
import org.basex.query.func.*;
import org.basex.query.iter.*;
import org.basex.query.util.list.*;
import org.basex.query.value.*;
import org.basex.query.value.item.*;
import org.basex.query.value.node.*;
import org.basex.util.*;
/**
* XQUnit tests: Testing single modules.
*
* @author BaseX Team 2005-22, BSD License
* @author Christian Gruen
*/
final class Unit {
/** Database context. */
private final Context ctx;
/** File. */
private final IOFile file;
/** Parent job. */
private final Job job;
/** Query string. */
private String input;
/** Currently processed function. */
private StaticFunc current;
/** Failures. */
int failures;
/** Errors. */
int errors;
/** Skipped. */
int skipped;
/** Tests. */
int tests;
/**
* Constructor.
* @param file file
* @param ctx database context
* @param job job
*/
Unit(final IOFile file, final Context ctx, final Job job) {
this.file = file;
this.ctx = ctx;
this.job = job;
}
/**
* Runs all tests.
* @param suites root element
* @throws IOException query exception
*/
public void test(final FElem suites) throws IOException {
final FElem suite = new FElem(TESTSUITE).add(NAME, file.url());
final ArrayList beforeModule = new ArrayList<>(0);
final ArrayList afterModule = new ArrayList<>(0);
final ArrayList before = new ArrayList<>(0);
final ArrayList after = new ArrayList<>(0);
final ArrayList beforeFilter = new ArrayList<>();
final ArrayList afterFilter = new ArrayList<>();
final ArrayList test = new ArrayList<>(0);
final Performance perf = new Performance();
try(QueryContext qc = new QueryContext(ctx)) {
input = file.string();
qc.parse(input, file.path());
// loop through all functions
for(final StaticFunc sf : qc.functions.funcs()) {
// find Unit annotations
final AnnList anns = sf.anns;
boolean unit = false;
for(final Ann ann : anns) {
unit = unit || ann.definition != null && eq(ann.definition.uri, QueryText.UNIT_URI);
}
if(!unit) continue;
// Unit function:
if(anns.contains(PRIVATE)) throw UNIT_PRIVATE_X.get(null, sf.name.local());
if(sf.params.length > 0) throw UNIT_NOARGS_X.get(null, sf.name.local());
if(anns.contains(_UNIT_BEFORE_MODULE)) beforeModule.add(sf);
if(anns.contains(_UNIT_AFTER_MODULE)) afterModule.add(sf);
final Ann b = anns.get(_UNIT_BEFORE);
if(b != null) {
before.add(sf);
beforeFilter.add(name(sf, b));
}
final Ann a = anns.get(_UNIT_AFTER);
if(a != null) {
after.add(sf);
afterFilter.add(name(sf, a));
}
if(anns.contains(_UNIT_TEST)) test.add(sf);
}
// call initializing functions before first test
for(final StaticFunc sf : beforeModule) eval(sf);
for(final StaticFunc sf : test) {
// check arguments
final AnnList anns = sf.anns;
final Ann ann = anns.get(_UNIT_TEST);
final Value value = ann.value();
final long vs = value.size();
// expected error code
QNm code = null;
if(vs == 2 && eq(EXPECTED, value.itemAt(0).string(null))) {
code = QNm.resolve(value.itemAt(1).string(null), QueryText.ERROR_URI, sf.sc, sf.info);
} else if(vs != 0) {
throw BASEX_ANNOTATION2_X_X.get(ann.info, ann, arguments(vs));
}
final FElem testcase = new FElem(TESTCASE).add(NAME, sf.name.local());
tests++;
final Performance perf2 = new Performance();
final Ann ignore = anns.get(_UNIT_IGNORE);
if(ignore == null) {
try {
// call functions marked with "before"
int l = before.size();
for(int i = 0; i < l; i++) {
final QNm name = beforeFilter.get(i);
if(name == null || name.eq(sf.name)) eval(before.get(i));
}
// call function
eval(sf);
// call functions marked with "after"
l = after.size();
for(int i = 0; i < l; i++) {
final QNm name = afterFilter.get(i);
if(name == null || name.eq(sf.name)) eval(after.get(i));
}
if(code != null) {
failures++;
testcase.add(new FElem(FAILURE).add(new FElem(EXPECTED).add(code.prefixId())));
}
} catch(final QueryException ex) {
addError(ex, testcase, code);
}
} else {
// skip test
final Value ignored = ignore.value();
testcase.add(SKIPPED, ignored.isEmpty() ? EMPTY : ignored.itemAt(0).string(null));
skipped++;
}
testcase.add(TIME, time(perf2));
suite.add(testcase);
}
// run finalizing tests
for(final StaticFunc sf : afterModule) eval(sf);
} catch(final QueryException ex) {
if(current == null) {
// handle errors caused by parsing or compilation
addError(ex, suite, null);
} else {
// handle errors caused by initializing or finalizing unit functions
final FElem tc = new FElem(TESTINIT).add(NAME, current.name.local()).add(TIME, time(perf));
suite.add(tc);
addError(ex, tc, null);
}
}
if(suite.hasChildren()) {
suite.add(TIME, time(perf));
suite.add(TESTS, token(tests));
suite.add(FAILURES, token(failures));
suite.add(ERRORS, token(errors));
suite.add(SKIPPED, token(skipped));
suites.add(suite);
}
}
/**
* Returns an annotation argument as QName.
* @param sf static function
* @param ann annotation
* @return QName or {@code null}
* @throws QueryException query exception
*/
private static QNm name(final StaticFunc sf, final Ann ann) throws QueryException {
final Value value = ann.value();
if(!value.isEmpty()) {
final byte[] name = value.itemAt(0).string(null);
if(name.length != 0) return QNm.resolve(name, sf.name.uri(), sf.sc, sf.info);
}
return null;
}
/**
* Adds an error element to the specified test case.
* @param ex exception
* @param testcase testcase element
* @param code error code (can be {@code null})
*/
private void addError(final QueryException ex, final FElem testcase, final QNm code) {
final QNm name = ex.qname();
if(code == null || !code.eq(name)) {
final FElem error;
final boolean fail = UNIT_FAIL.eq(name);
if(fail) {
failures++;
error = new FElem(FAILURE);
} else {
errors++;
error = new FElem(ERROR);
}
error.add(LINE, token(ex.line()));
error.add(COLUMN, token(ex.column()));
final String url = IO.get(ex.file()).url();
if(!file.url().equals(url)) error.add(URI, url);
if(ex instanceof UnitException) {
// unit exception: add expected and returned values
final UnitException ue = (UnitException) ex;
error.add(element(ue.returned, RETURNED, ue.count));
error.add(element(ue.expected, EXPECTED, ue.count));
} else if(!fail) {
// exception other than failure: add type
error.add(TYPE, ex.qname().prefixId(QueryText.ERROR_URI));
}
// add info
final Value value = ex.value();
if(value != null && value.isItem()) {
error.add(element((Item) value, INFO, -1));
} else {
// otherwise, add error message
error.add(new FElem(INFO).add(ex.getLocalizedMessage()));
}
testcase.add(error);
}
}
/**
* Creates a new element.
* @param item item
* @param name name (expected/returned)
* @param count item count (ignore it {@code -1})
* @return new element
*/
private static FElem element(final Item item, final byte[] name, final int count) {
final FElem exp = new FElem(name);
if(item != null) {
if(item instanceof ANode) {
exp.add((ANode) item);
} else {
try {
exp.add(item.string(null));
} catch(final QueryException ex) {
Util.debug(ex);
exp.add(item.toString());
}
}
if(count != -1) exp.add(ITEM, token(count)).add(TYPE, item.type.toString());
}
return exp;
}
/**
* Evaluates a function.
* @param func function to evaluate
* @throws QueryException query exception
*/
private void eval(final StaticFunc func) throws QueryException {
current = func;
try(QueryContext qc = job.pushJob(new QueryContext(ctx))) {
qc.parse(input, file.path());
qc.assign(func);
// ignore results
for(final Iter iter = qc.iter(); qc.next(iter) != null;);
} finally {
job.popJob();
}
}
/**
* Returns a token representation of the measured time.
* @param p performance
* @return time
*/
static byte[] time(final Performance p) {
return DTDur.get(p.ns() / 1000000).string(null);
}
}