Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: run eye tests #632

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ node_modules
coverage
dist
bundle
eye
lib/eye.ts
perf/output.txt
135 changes: 135 additions & 0 deletions __tests__/upstream-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/* eslint-disable */
// TODO: Remove the above before merging
import fs from 'fs-extra';
import path from 'path';
import { Parser, Store, DataFactory as DF } from 'n3';
import { mapTerms } from 'rdf-terms';
import { execFileSync, exec, execSync } from 'child_process';

import { n3reasoner } from '../dist';
import 'jest-rdf';
import { Quad } from '@rdfjs/types';

const examplesPath = path.join(__dirname, '..', 'eye', 'reasoning');
const expectedStart = 'eye "$@"';
const cacheComponent = ' --wcache http://eyereasoner.github.io/eye/reasoning .. ';
const longStart = expectedStart + cacheComponent;

const ignoreFolders = [
// n3reasoner does not support extra images
'dt', 'image',
// n3reasoner does not support multiquery
'mq',
];

// These exceptions should eventually be removed
const ignoreOutputs = [
// This complains because `$` is used as a term
'bi/biA.n3',
// This complains because of https://github.com/rdfjs/N3.js/issues/328
'crypto/crypto-proof.n3',
// This just states `true .` but it's not a valid N3 file
'entail/socrates-check.n3',
// This complains because of https://github.com/rdfjs/N3.js/issues/328
'preduction/palindrome-proof.n3',
// This complains because of https://github.com/rdfjs/N3.js/issues/328
'preduction/palindrome2-proof.n3',
];

function readFile(subPath: string) {
return fs.readFileSync(path.join(examplesPath, subPath)).toString();
}

function dereference(subPath: string) {
const parser = new Parser({ format: 'text/n3', baseIRI: `http://eyereasoner.github.io/eye/reasoning${subPath}` });
// @ts-expect-error
parser._supportsRDFStar = true;
return parser.parse(readFile(subPath));
}

function loadFiles(files: string[]) {
return [...new Store(files.map((file) => dereference(file)).flat())]
// Workaround for https://github.com/rdfjs/N3.js/issues/332
.map((quad) => mapTerms(quad, (term) => (term.termType === 'BlankNode' ? DF.blankNode(term.value.replace(/^\./, '')) : term)));
}

const invMapping = {
derivations: '--pass-only-new',
deductive_closure: '--pass',
deductive_closure_plus_rules: '--pass-all',
grounded_deductive_closure_plus_rules: '--pass-all-ground',
} as const;

describe('Testing examples from eye repository', () => {
const cases: [string, string[]][] = [];

for (const folder of fs.readdirSync(examplesPath).filter(folder => fs.statSync(path.join(examplesPath, folder)).isDirectory())) {
if (ignoreFolders.includes(folder))
continue;

try {
// FIXME: Don't have stderr show in the console
const tests = execSync(
fs.readFileSync(path.join(examplesPath, folder, 'test')).toString().replace(/eye /g, 'echo ')
).toString().split('\n').slice(0, -1);

// FIXME: Add a verification to make sure we are only skipping what we want
if (tests.length > 0) {
cases.push([folder, tests])
}
} catch (e) {
if (!(e as any).toString().includes('swipl')) {
throw e;
}
// Don't error if the problem is just that the test is trying to test the swipl command
}
}

describe.each(cases)('Testing examples for %s', (folder, args) => {
it.each(args)('%s', async (args) => {
let argsArray = args.split(/\ |\=/g)
// Remove the --turtle flag because it's just a performance optimisation
.filter((arg) => !['--turtle'].includes(arg))
//
.map(arg => arg.replace('http://eyereasoner.github.io/eye/reasoning/', ''));


if (argsArray[argsArray.length - 2] !== '--output') {
throw new Error(`Expected --output to be the last argument on [${args}]`);
}

if (argsArray[0] !== '--wcache' || argsArray[1] !== 'http://eyereasoner.github.io/eye/reasoning' || argsArray[2] !== '..') {
throw new Error(`Expected --wcache to be the first argument on [${args}]`);
}

const subPath = path.join(folder, argsArray[argsArray.length - 1]);
argsArray = argsArray.slice(3, -2);

let query: Quad[] | undefined = undefined;

if (argsArray.includes('--query')) {
const qind = argsArray.indexOf('--query');
query = loadFiles([argsArray[qind + 1]]);
argsArray = [...argsArray.slice(0, qind), ...argsArray.slice(qind + 2)];
}

const output = (Object.keys(invMapping) as (keyof typeof invMapping)[]).find(key => argsArray.includes(invMapping[key]));

if (output) {
argsArray = argsArray.filter(arg => arg !== invMapping[output]);
}

if (argsArray.includes('--nope') && !ignoreOutputs.includes(subPath)) {
await expect(
n3reasoner(
loadFiles(argsArray.filter(arg => arg !== '--nope')),
query,
{ output },
),
)
.resolves
.toBeRdfIsomorphic(dereference(subPath));
}
});
});
});
110 changes: 57 additions & 53 deletions lib/n3Writer.temp.ts
Original file line number Diff line number Diff line change
@@ -1,64 +1,68 @@

// This is a workaround for https://github.com/rdfjs/N3.js/issues/316
import { Quad, Term } from '@rdfjs/types';
import { DataFactory as DF, Store, Writer } from 'n3';
// import { Quad, Term } from '@rdfjs/types';
// import { DataFactory as DF, Store, Writer } from 'n3';
export { write } from "@jeswr/pretty-turtle";

function isQuoted(term: Term, store: Store) {
return term.termType === 'BlankNode' && store.getQuads(null, null, null, term).length > 0;
}
// function isQuoted(term: Term, store: Store) {
// return term.termType === 'BlankNode' && store.getQuads(null, null, null, term).length > 0;
// }

export class N3Writer {
private _writer: any = new Writer();
// export class N3Writer {
// private _writer: any = new Writer();

_encodePredicate(term: Term): string {
if (term.termType === 'NamedNode') {
switch (term.value) {
case 'http://www.w3.org/2000/10/swap/log#implies':
return '=>';
case 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type':
return 'a'
}
}
return this._writer._encodeIriOrBlank(term);
}
// _encodePredicate(term: Term): string {
// if (term.termType === 'NamedNode') {
// switch (term.value) {
// case 'http://www.w3.org/2000/10/swap/log#implies':
// return '=>';
// case 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type':
// return 'a'
// }
// }
// return this._writer._encodeIriOrBlank(term);
// }

_encodeSubject(entity: Term, store: Store): string {
if (isQuoted(entity, store)) {
return `{${this.quadsStoreToString(store, entity)}}`
}
return this._writer._encodeSubject(entity)
}
// _encodeSubject(entity: Term, store: Store): string {
// if (isQuoted(entity, store)) {
// return `{${this.quadsStoreToString(store, entity)}}`
// }
// if (entity.termType === 'Literal') {
// return this._writer._encodeObject(entity);
// }
// return this._writer._encodeSubject(entity);
// }

_encodeObject(entity: Term, store: Store): string {
if (isQuoted(entity, store)) {
return `{${this.quadsStoreToString(store, entity)}}`
}
return this._writer._encodeObject(entity)
}
// _encodeObject(entity: Term, store: Store): string {
// if (isQuoted(entity, store)) {
// return `{${this.quadsStoreToString(store, entity)}}`
// }
// return this._writer._encodeObject(entity)
// }

// ### `quadToString` serializes a quad as a string
quadToString(t: Quad, store: Store): string {
return `${
this._encodeSubject(t.subject, store)
} ${
this._encodePredicate(t.predicate as any)
} ${
this._encodeObject(t.object, store)
}`;
}
// // ### `quadToString` serializes a quad as a string
// quadToString(t: Quad, store: Store): string {
// return `${
// this._encodeSubject(t.subject, store)
// } ${
// this._encodePredicate(t.predicate as any)
// } ${
// this._encodeObject(t.object, store)
// }`;
// }

// ### `quadsToString` serializes an array of quads as a string
quadsStoreToString(store: Store, graph: Term = DF.defaultGraph()): string {
return store.getQuads(null, null, null, graph)
.map(t => this.quadToString(t, store))
.join(' . ') + ' . ';
}
// // ### `quadsToString` serializes an array of quads as a string
// quadsStoreToString(store: Store, graph: Term = DF.defaultGraph()): string {
// return store.getQuads(null, null, null, graph)
// .map(t => this.quadToString(t, store))
// .join(' . ') + ' . ';
// }

quadsToString(quads: Quad[]): string {
return this.quadsStoreToString(new Store(quads))
}
}
// quadsToString(quads: Quad[]): string {
// return this.quadsStoreToString(new Store(quads))
// }
// }

export function write(quads: Quad[]) {
return (new N3Writer).quadsToString(quads);
}
// export function write(quads: Quad[]) {
// return (new N3Writer).quadsToString(quads);
// }
12 changes: 6 additions & 6 deletions lib/transformers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ export function runQuery(
queryString?: string,
{ output }: Options = {},
): SWIPLModule {
const args: string[] = ['--nope', '--quiet', 'data.nq'];
const args: string[] = ['--nope', '--quiet', 'data.n3s'];

if (queryString) {
if (output) {
throw new Error('Cannot use explicit output with explicit query');
}
Module.FS.writeFile('query.nq', queryString);
args.push('--query', './query.nq');
Module.FS.writeFile('query.n3s', queryString);
args.push('--query', './query.n3s');
} else {
switch (output) {
case undefined:
Expand All @@ -76,7 +76,7 @@ export function runQuery(
}
}

Module.FS.writeFile('data.nq', data);
Module.FS.writeFile('data.n3s', data);

queryOnce(Module, 'main', args);
return Module;
Expand Down Expand Up @@ -122,8 +122,8 @@ export async function executeBasicEyeQuery(swipl: typeof SWIPL, data: Data, quer
});
runQuery(
Module,
typeof data === 'string' ? data : write(data),
query && (typeof query === 'string' ? query : write(query)),
typeof data === 'string' ? data : await write(data, { format: 'text/n3' }),
query && (typeof query === 'string' ? query : await write(query, { format: 'text/n3' })),
options,
);

Expand Down
24 changes: 24 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"eye:pvm:test": "ts-node scripts/run-pvm",
"eye:prepare": "npm run eye:pvm",
"eye:update": "ts-node scripts/update",
"clone:eye": "rm -rf ./eye && git clone https://github.com/eyereasoner/eye ./eye --depth 1 --branch $npm_package_config_eye_name",
"perf": "ts-node perf/bench 2>&1 | tee perf/output.txt"
},
"repository": {
Expand Down Expand Up @@ -58,6 +59,7 @@
},
"homepage": "https://github.com/eyereasoner/eye-js#readme",
"devDependencies": {
"@jeswr/pretty-turtle": "^1.4.0",
"@memlab/api": "^1.0.20",
"@qiwi/semantic-release-gh-pages-plugin": "^5.2.4",
"@rollup/plugin-commonjs": "^25.0.0",
Expand Down
Loading