Add and implement `Graph.equals` (#369)
It turns out we forgot to add this to the API, so I added it. I also implemented it. The tests are pretty thorough; as an added innovation over our previous tests (e.g. in #312 and #61), we now consistently test that equality is commutative. In contrast to our previous implementations, this one is massively simpler. That's an upside of using primitive ES6 data structures to store all of the graph's information... which is itself an upside of not trying to store arbitrary additional information in the graph. Now we can just do a deep equality check on the underlying nodes set and edges map! We might be able to performance tune this method by taking advantage of the structure of our nodes and edges. This should suffice for now, though. Paired with @wchargin Test plan: Unit tests were added. Run `yarn travis`
This commit is contained in:
parent
4a441eb287
commit
feef119250
|
@ -1,5 +1,7 @@
|
|||
// @flow
|
||||
|
||||
import deepEqual from "lodash.isequal";
|
||||
|
||||
import {makeAddressModule, type AddressModule} from "./address";
|
||||
|
||||
export opaque type NodeAddressT: string = string;
|
||||
|
@ -292,6 +294,15 @@ export class Graph {
|
|||
this._checkForComodification(initialModificationCount);
|
||||
}
|
||||
|
||||
equals(that: Graph): boolean {
|
||||
if (!(that instanceof Graph)) {
|
||||
throw new Error(`Expected Graph, got ${String(that)}`);
|
||||
}
|
||||
return (
|
||||
deepEqual(this._nodes, that._nodes) && deepEqual(this._edges, that._edges)
|
||||
);
|
||||
}
|
||||
|
||||
copy(): Graph {
|
||||
throw new Error("copy");
|
||||
}
|
||||
|
|
|
@ -703,6 +703,100 @@ describe("core/graph", () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("equals", () => {
|
||||
const src = NodeAddress.fromParts(["src"]);
|
||||
const dst = NodeAddress.fromParts(["dst"]);
|
||||
const edge1 = () => ({
|
||||
src,
|
||||
dst,
|
||||
address: EdgeAddress.fromParts(["edge"]),
|
||||
});
|
||||
const conflictingEdge = () => ({
|
||||
src: dst,
|
||||
dst: src,
|
||||
address: EdgeAddress.fromParts(["edge"]),
|
||||
});
|
||||
const edge2 = () => ({
|
||||
src: dst,
|
||||
dst: src,
|
||||
address: EdgeAddress.fromParts(["edge", "2"]),
|
||||
});
|
||||
function expectEquality(g1, g2, isEqual: boolean) {
|
||||
expect(g1.equals(g2)).toBe(isEqual);
|
||||
expect(g2.equals(g1)).toBe(isEqual);
|
||||
}
|
||||
|
||||
it("empty graph equals itself", () => {
|
||||
expectEquality(new Graph(), new Graph(), true);
|
||||
});
|
||||
it("empty graph doesn't equal nonempty graph", () => {
|
||||
const g = new Graph().addNode(src);
|
||||
expectEquality(g, new Graph(), false);
|
||||
});
|
||||
it("adding and removing a node doesn't change equality", () => {
|
||||
const g = new Graph().addNode(src).removeNode(src);
|
||||
expectEquality(g, new Graph(), true);
|
||||
});
|
||||
it("adding an edge changes equality", () => {
|
||||
const g1 = new Graph().addNode(src).addNode(dst);
|
||||
const g2 = new Graph()
|
||||
.addNode(src)
|
||||
.addNode(dst)
|
||||
.addEdge(edge1());
|
||||
expectEquality(g1, g2, false);
|
||||
});
|
||||
it("adding nodes in different order doesn't change equality", () => {
|
||||
const g1 = new Graph().addNode(src).addNode(dst);
|
||||
const g2 = new Graph().addNode(dst).addNode(src);
|
||||
expectEquality(g1, g2, true);
|
||||
});
|
||||
it("graphs with conflicting edges are not equal", () => {
|
||||
const g1 = new Graph()
|
||||
.addNode(src)
|
||||
.addNode(dst)
|
||||
.addEdge(edge1());
|
||||
const g2 = new Graph()
|
||||
.addNode(src)
|
||||
.addNode(dst)
|
||||
.addEdge(conflictingEdge());
|
||||
expectEquality(g1, g2, false);
|
||||
});
|
||||
it("adding edges in different order doesn't change equality", () => {
|
||||
const g1 = new Graph()
|
||||
.addNode(src)
|
||||
.addNode(dst)
|
||||
.addEdge(edge1())
|
||||
.addEdge(edge2());
|
||||
const g2 = new Graph()
|
||||
.addNode(src)
|
||||
.addNode(dst)
|
||||
.addEdge(edge2())
|
||||
.addEdge(edge1());
|
||||
expectEquality(g1, g2, true);
|
||||
});
|
||||
it("adding and removing an edge doesn't change equality", () => {
|
||||
const g1 = new Graph().addNode(src).addNode(dst);
|
||||
const g2 = new Graph()
|
||||
.addNode(src)
|
||||
.addNode(dst)
|
||||
.addEdge(edge1())
|
||||
.removeEdge(edge1().address);
|
||||
expectEquality(g1, g2, true);
|
||||
});
|
||||
it("throws error on null", () => {
|
||||
// $ExpectFlowError
|
||||
expect(() => new Graph().equals(null)).toThrow("null");
|
||||
});
|
||||
it("throws error on undefined", () => {
|
||||
// $ExpectFlowError
|
||||
expect(() => new Graph().equals(undefined)).toThrow("undefined");
|
||||
});
|
||||
it("throws error on non-graph object", () => {
|
||||
// $ExpectFlowError
|
||||
expect(() => new Graph().equals({})).toThrow("object");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("edgeToString", () => {
|
||||
|
|
Loading…
Reference in New Issue