Skip to content

Commit

Permalink
Merge 657894f into 842f839
Browse files Browse the repository at this point in the history
  • Loading branch information
jeswr committed Jan 3, 2023
2 parents 842f839 + 657894f commit 57a5ded
Show file tree
Hide file tree
Showing 5 changed files with 298 additions and 3 deletions.
67 changes: 65 additions & 2 deletions lib/matchers/toBeRdfIsomorphic.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,55 @@
import {isomorphic} from "rdf-isomorphic";
import * as RDF from "@rdfjs/types";
import {quadToStringQuad} from "rdf-string";
import { getGraphBlankNodes, getQuadsWithBlankNodes, hashTerms, isomorphic, ITermHash, uniqGraph } from "rdf-isomorphic";
import { quadToStringQuad } from "rdf-string";
import { everyTerms, someTerms } from 'rdf-terms';

function getNonBlankDiff<Q extends RDF.BaseQuad = RDF.Quad>(a1: Q[], a2: Q[]) {
return a1.filter(
quad =>
everyTerms(quad, term => term.termType !== 'BlankNode')
&& a2.every(q2 => !q2.equals(quad))
)
}


export function getDiff(hash1: ITermHash, hash2: ITermHash) {
const diffed: string[] = [];
const values = new Set(Object.values(hash2));
for (const key in hash1) {
if (!values.has(hash1[key])) {
diffed.push(key);
}
}
return diffed;
}

export function unGroundHashes<Q extends RDF.BaseQuad = RDF.Quad>(graph: Q[]) {
return hashTerms(uniqGraph(getQuadsWithBlankNodes(graph)), getGraphBlankNodes(graph), {})[1]
}

export function getBnodeDiff<Q extends RDF.BaseQuad = RDF.Quad>(receivedQuads: Q[], expectedQuads: Q[]) {

// Hash every term based on the signature of the quads if appears in.
const ungroundedHashesA = unGroundHashes(receivedQuads);
const ungroundedHashesB = unGroundHashes(expectedQuads);
const blankA = uniqGraph(getQuadsWithBlankNodes(receivedQuads));
const blankB = uniqGraph(getQuadsWithBlankNodes(expectedQuads))

const received: Record<string, Q[]> = {}
const expected: Record<string, Q[]> = {}

for (const elem of getDiff(ungroundedHashesA, ungroundedHashesB)) {
received[elem] = blankA.filter(quad => someTerms(quad, (term) => term.termType === 'BlankNode' && term.value === elem.slice(2)));
}

for (const elem of getDiff(ungroundedHashesB, ungroundedHashesA)) {
expected[elem] = blankB.filter(quad => someTerms(quad, (term) => term.termType === 'BlankNode' && term.value === elem.slice(2)));
}

return {
received, expected
}
}

function quadArrayToString<Q extends RDF.BaseQuad = RDF.Quad>(quadArray: Q[]): string {
return '[\n' + quadArray.map((quad) => ' ' + JSON.stringify(quadToStringQuad(quad))).join(',\n') + '\n]';
Expand All @@ -12,6 +61,8 @@ export default {
const actualArray = [...actual];

if (!isomorphic(receivedArray, actualArray)) {
const { received: receivedBnodes, expected: actualBnodes } = getBnodeDiff(receivedArray, actualArray)

return {
message: () => `expected two graphs to be isomorphic.
Expand All @@ -20,6 +71,18 @@ ${quadArrayToString(actualArray)}
Actual:
${quadArrayToString(receivedArray)}
Missing Quads (that don't contain Blank Nodes):
${quadArrayToString(getNonBlankDiff(actualArray, receivedArray))}
Additional Quads (that don't contain Blank Nodes):
${quadArrayToString(getNonBlankDiff(receivedArray, actualArray))}
Missing Blank Node Patterns:
${Object.entries(actualBnodes).map(([bnode, quads]) => bnode + ' : ' + quadArrayToString(quads)).join('\n')}
Additional Blank Node Patterns:
${Object.entries(receivedBnodes).map(([bnode, quads]) => bnode + ' : ' + quadArrayToString(quads)).join('\n')}
`,
pass: false,
};
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
"dependencies": {
"@rdfjs/types": "*",
"rdf-isomorphic": "^1.3.0",
"rdf-string": "^1.6.0"
"rdf-string": "^1.6.0",
"rdf-terms": "^1.9.1"
}
}
114 changes: 114 additions & 0 deletions test/matchers/__snapshots__/toBeRdfIsomorphic-test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,23 @@ exports[`#toBeRdfIsomorphic should fail for quad arrays with different length 1`
{\\"subject\\":\\"s2\\",\\"predicate\\":\\"p2\\",\\"object\\":\\"o2\\",\\"graph\\":\\"g2\\"},
{\\"subject\\":\\"s3\\",\\"predicate\\":\\"p3\\",\\"object\\":\\"o3\\",\\"graph\\":\\"g3\\"}
]
Missing Quads (that don't contain Blank Nodes):
[
]
Additional Quads (that don't contain Blank Nodes):
[
{\\"subject\\":\\"s2\\",\\"predicate\\":\\"p2\\",\\"object\\":\\"o2\\",\\"graph\\":\\"g2\\"},
{\\"subject\\":\\"s3\\",\\"predicate\\":\\"p3\\",\\"object\\":\\"o3\\",\\"graph\\":\\"g3\\"}
]
Missing Blank Node Patterns:
Additional Blank Node Patterns:
"
`;

Expand All @@ -33,6 +50,23 @@ exports[`#toBeRdfIsomorphic should fail for quad arrays with equal length but di
{\\"subject\\":\\"s2\\",\\"predicate\\":\\"p2\\",\\"object\\":\\"o2\\",\\"graph\\":\\"g2\\"},
{\\"subject\\":\\"s3\\",\\"predicate\\":\\"p3\\",\\"object\\":\\"o3\\",\\"graph\\":\\"g3\\"}
]
Missing Quads (that don't contain Blank Nodes):
[
]
Additional Quads (that don't contain Blank Nodes):
[
{\\"subject\\":\\"s2\\",\\"predicate\\":\\"p2\\",\\"object\\":\\"o2\\",\\"graph\\":\\"g2\\"},
{\\"subject\\":\\"s3\\",\\"predicate\\":\\"p3\\",\\"object\\":\\"o3\\",\\"graph\\":\\"g3\\"}
]
Missing Blank Node Patterns:
Additional Blank Node Patterns:
"
`;

Expand All @@ -54,3 +88,83 @@ exports[`#toBeRdfIsomorphic should not fail for equal quad arrays 1`] = `
]
"
`;

exports[`#toBeRdfIsomorphic should not succeed for quad arrays with equal length but different contents (bnodes isomorphic) 1`] = `
"expected two graphs to be isomorphic.
Expected:
[
{\\"subject\\":\\"_:s1\\",\\"predicate\\":\\"p1\\",\\"object\\":\\"o1\\",\\"graph\\":\\"g1\\"},
{\\"subject\\":\\"s1\\",\\"predicate\\":\\"p1\\",\\"object\\":\\"o1\\",\\"graph\\":\\"g1\\"},
{\\"subject\\":\\"s1\\",\\"predicate\\":\\"p1\\",\\"object\\":\\"o1\\",\\"graph\\":\\"g1\\"},
{\\"subject\\":\\"s1\\",\\"predicate\\":\\"p1\\",\\"object\\":\\"o1\\",\\"graph\\":\\"g1\\"}
]
Actual:
[
{\\"subject\\":\\"_:s1\\",\\"predicate\\":\\"p1\\",\\"object\\":\\"o1\\",\\"graph\\":\\"g1\\"},
{\\"subject\\":\\"s1\\",\\"predicate\\":\\"p1\\",\\"object\\":\\"o1\\",\\"graph\\":\\"g1\\"},
{\\"subject\\":\\"s2\\",\\"predicate\\":\\"p2\\",\\"object\\":\\"o2\\",\\"graph\\":\\"g2\\"},
{\\"subject\\":\\"s3\\",\\"predicate\\":\\"p3\\",\\"object\\":\\"o3\\",\\"graph\\":\\"g3\\"}
]
Missing Quads (that don't contain Blank Nodes):
[
]
Additional Quads (that don't contain Blank Nodes):
[
{\\"subject\\":\\"s2\\",\\"predicate\\":\\"p2\\",\\"object\\":\\"o2\\",\\"graph\\":\\"g2\\"},
{\\"subject\\":\\"s3\\",\\"predicate\\":\\"p3\\",\\"object\\":\\"o3\\",\\"graph\\":\\"g3\\"}
]
Missing Blank Node Patterns:
Additional Blank Node Patterns:
"
`;

exports[`#toBeRdfIsomorphic should not succeed for quad arrays with equal length but different contents (bnodes non-isomorphic) 1`] = `
"expected two graphs to be isomorphic.
Expected:
[
{\\"subject\\":\\"_:s1\\",\\"predicate\\":\\"p1\\",\\"object\\":\\"o2\\",\\"graph\\":\\"g1\\"},
{\\"subject\\":\\"s1\\",\\"predicate\\":\\"p1\\",\\"object\\":\\"o1\\",\\"graph\\":\\"g1\\"},
{\\"subject\\":\\"s1\\",\\"predicate\\":\\"p1\\",\\"object\\":\\"o1\\",\\"graph\\":\\"g1\\"},
{\\"subject\\":\\"s1\\",\\"predicate\\":\\"p1\\",\\"object\\":\\"o1\\",\\"graph\\":\\"g1\\"}
]
Actual:
[
{\\"subject\\":\\"_:s1\\",\\"predicate\\":\\"p1\\",\\"object\\":\\"o1\\",\\"graph\\":\\"g1\\"},
{\\"subject\\":\\"s1\\",\\"predicate\\":\\"p1\\",\\"object\\":\\"o1\\",\\"graph\\":\\"g1\\"},
{\\"subject\\":\\"s2\\",\\"predicate\\":\\"p2\\",\\"object\\":\\"o2\\",\\"graph\\":\\"g2\\"},
{\\"subject\\":\\"s3\\",\\"predicate\\":\\"p3\\",\\"object\\":\\"o3\\",\\"graph\\":\\"g3\\"}
]
Missing Quads (that don't contain Blank Nodes):
[
]
Additional Quads (that don't contain Blank Nodes):
[
{\\"subject\\":\\"s2\\",\\"predicate\\":\\"p2\\",\\"object\\":\\"o2\\",\\"graph\\":\\"g2\\"},
{\\"subject\\":\\"s3\\",\\"predicate\\":\\"p3\\",\\"object\\":\\"o3\\",\\"graph\\":\\"g3\\"}
]
Missing Blank Node Patterns:
_:s1 : [
{\\"subject\\":\\"_:s1\\",\\"predicate\\":\\"p1\\",\\"object\\":\\"o2\\",\\"graph\\":\\"g1\\"}
]
Additional Blank Node Patterns:
_:s1 : [
{\\"subject\\":\\"_:s1\\",\\"predicate\\":\\"p1\\",\\"object\\":\\"o1\\",\\"graph\\":\\"g1\\"}
]
"
`;
108 changes: 108 additions & 0 deletions test/matchers/toBeRdfIsomorphic-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,114 @@ describe('#toBeRdfIsomorphic', () => {
]);
});

it('should not succeed for quad arrays with equal length but different contents (bnodes isomorphic)', () => {
return expect(() => expect([
DF.quad(
DF.blankNode('s1'),
DF.namedNode('p1'),
DF.namedNode('o1'),
DF.namedNode('g1'),
),
DF.quad(
DF.namedNode('s1'),
DF.namedNode('p1'),
DF.namedNode('o1'),
DF.namedNode('g1'),
),
DF.quad(
DF.namedNode('s2'),
DF.namedNode('p2'),
DF.namedNode('o2'),
DF.namedNode('g2'),
),
DF.quad(
DF.namedNode('s3'),
DF.namedNode('p3'),
DF.namedNode('o3'),
DF.namedNode('g3'),
),
]).toBeRdfIsomorphic([
DF.quad(
DF.blankNode('s1'),
DF.namedNode('p1'),
DF.namedNode('o1'),
DF.namedNode('g1'),
),
DF.quad(
DF.namedNode('s1'),
DF.namedNode('p1'),
DF.namedNode('o1'),
DF.namedNode('g1'),
),
DF.quad(
DF.namedNode('s1'),
DF.namedNode('p1'),
DF.namedNode('o1'),
DF.namedNode('g1'),
),
DF.quad(
DF.namedNode('s1'),
DF.namedNode('p1'),
DF.namedNode('o1'),
DF.namedNode('g1'),
),
])).toThrowErrorMatchingSnapshot();
});

it('should not succeed for quad arrays with equal length but different contents (bnodes non-isomorphic)', () => {
return expect(() => expect([
DF.quad(
DF.blankNode('s1'),
DF.namedNode('p1'),
DF.namedNode('o1'),
DF.namedNode('g1'),
),
DF.quad(
DF.namedNode('s1'),
DF.namedNode('p1'),
DF.namedNode('o1'),
DF.namedNode('g1'),
),
DF.quad(
DF.namedNode('s2'),
DF.namedNode('p2'),
DF.namedNode('o2'),
DF.namedNode('g2'),
),
DF.quad(
DF.namedNode('s3'),
DF.namedNode('p3'),
DF.namedNode('o3'),
DF.namedNode('g3'),
),
]).toBeRdfIsomorphic([
DF.quad(
DF.blankNode('s1'),
DF.namedNode('p1'),
DF.namedNode('o2'),
DF.namedNode('g1'),
),
DF.quad(
DF.namedNode('s1'),
DF.namedNode('p1'),
DF.namedNode('o1'),
DF.namedNode('g1'),
),
DF.quad(
DF.namedNode('s1'),
DF.namedNode('p1'),
DF.namedNode('o1'),
DF.namedNode('g1'),
),
DF.quad(
DF.namedNode('s1'),
DF.namedNode('p1'),
DF.namedNode('o1'),
DF.namedNode('g1'),
),
])).toThrowErrorMatchingSnapshot();
});

it('should not succeed for quad arrays with nested quads with equal length but different contents', () => {
return expect([
DF.quad(
Expand Down
9 changes: 9 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3311,6 +3311,15 @@ rdf-terms@^1.7.0:
lodash.uniqwith "^4.5.0"
rdf-data-factory "^1.1.0"

rdf-terms@^1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/rdf-terms/-/rdf-terms-1.9.1.tgz#5f66a773d4b2fe6e071ebc8a2985beff7e0dfd7f"
integrity sha512-GrE8CbQSvuVEFRCywMu6VOgV1AFE6X+nFYcAhEc5pwYKI13bUvz4voiVufQiy3V8rzQKu21Sgl+dS2qcJavy7w==
dependencies:
"@rdfjs/types" "*"
rdf-data-factory "^1.1.0"
rdf-string "^1.6.0"

react-is@^16.12.0:
version "16.12.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c"
Expand Down

0 comments on commit 57a5ded

Please sign in to comment.