| export declare class UnionGraph { | ||
| /** | ||
| * Store the rootNode corresponding to a given node | ||
| * | ||
| * `{ [node]: rootNode }` | ||
| */ | ||
| private readonly parentNode; | ||
| /** | ||
| * Store all the nodes linked to a given rootNode (including itself) | ||
| * | ||
| * `{ [ rootNode ]: Set<nodes including rootNode> }` | ||
| */ | ||
| private readonly links; | ||
| /** | ||
| * Create a link between two nodes (must have different names) | ||
| * | ||
| * Unknown nodes will be created | ||
| */ | ||
| addLink(nodeA: string, nodeB: string): void; | ||
| /** | ||
| * Extract all the relationships declared so far | ||
| */ | ||
| values(): string[][]; | ||
| } |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| class UnionGraph { | ||
| constructor() { | ||
| /** | ||
| * Store the rootNode corresponding to a given node | ||
| * | ||
| * `{ [node]: rootNode }` | ||
| */ | ||
| this.parentNode = new Map(); | ||
| /** | ||
| * Store all the nodes linked to a given rootNode (including itself) | ||
| * | ||
| * `{ [ rootNode ]: Set<nodes including rootNode> }` | ||
| */ | ||
| this.links = new Map(); | ||
| } | ||
| /** | ||
| * Create a link between two nodes (must have different names) | ||
| * | ||
| * Unknown nodes will be created | ||
| */ | ||
| addLink(nodeA, nodeB) { | ||
| if (nodeA === nodeB) { | ||
| // stop: no need for a link | ||
| return; | ||
| } | ||
| const parentNodeA = this.parentNode.get(nodeA); | ||
| const parentNodeB = this.parentNode.get(nodeB); | ||
| if (parentNodeA && parentNodeB) { | ||
| if (parentNodeA === parentNodeB) { | ||
| // stop: there is already a link between nodeA and nodeB | ||
| // as they reference the same parent | ||
| return; | ||
| } | ||
| // merge A and B | ||
| const linksForParentA = this.links.get(parentNodeA); | ||
| const linksForParentB = this.links.get(parentNodeB); | ||
| for (const lnkB of linksForParentB) | ||
| linksForParentA.add(lnkB); | ||
| // update the parent for B and its brothers to be the same as the one for A | ||
| for (const lnkB of linksForParentB) | ||
| this.parentNode.set(lnkB, parentNodeA); | ||
| // remove B | ||
| this.links.delete(parentNodeB); | ||
| } | ||
| else if (parentNodeA) { | ||
| // append nodeB into already known this.links for nodeA | ||
| this.links.get(parentNodeA).add(nodeB); | ||
| // declare the parent for B to be the same as the one for A | ||
| this.parentNode.set(nodeB, parentNodeA); | ||
| } | ||
| else if (parentNodeB) { | ||
| // append nodeA into already known this.links for nodeB | ||
| this.links.get(parentNodeB).add(nodeA); | ||
| // declare the parent for A to be the same as the one for B | ||
| this.parentNode.set(nodeA, parentNodeB); | ||
| } | ||
| else { | ||
| // create a new equlity relation | ||
| this.links.set(nodeA, new Set([nodeA, nodeB])); | ||
| // declare the parent of A and B to be A | ||
| this.parentNode.set(nodeA, nodeA); | ||
| this.parentNode.set(nodeB, nodeA); | ||
| } | ||
| } | ||
| /** | ||
| * Extract all the relationships declared so far | ||
| */ | ||
| values() { | ||
| return Array.from(this.links.values()).map(vs => Array.from(vs)); | ||
| } | ||
| } | ||
| exports.UnionGraph = UnionGraph; | ||
| //# sourceMappingURL=union-graph.js.map |
| {"version":3,"file":"union-graph.js","sourceRoot":"","sources":["../../src/internal/union-graph.ts"],"names":[],"mappings":";;AAAA,MAAa,UAAU;IAAvB;QACE;;;;WAIG;QACc,eAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;QAExD;;;;WAIG;QACc,UAAK,GAAG,IAAI,GAAG,EAAuB,CAAC;IAuD1D,CAAC;IArDC;;;;OAIG;IACH,OAAO,CAAC,KAAa,EAAE,KAAa;QAClC,IAAI,KAAK,KAAK,KAAK,EAAE;YACnB,2BAA2B;YAC3B,OAAO;SACR;QAED,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC/C,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAE/C,IAAI,WAAW,IAAI,WAAW,EAAE;YAC9B,IAAI,WAAW,KAAK,WAAW,EAAE;gBAC/B,wDAAwD;gBACxD,0CAA0C;gBAC1C,OAAO;aACR;YACD,gBAAgB;YAChB,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAE,CAAC;YACrD,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAE,CAAC;YACrD,KAAK,MAAM,IAAI,IAAI,eAAe;gBAAE,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC9D,2EAA2E;YAC3E,KAAK,MAAM,IAAI,IAAI,eAAe;gBAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;YAC3E,WAAW;YACX,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;SAChC;aAAM,IAAI,WAAW,EAAE;YACtB,uDAAuD;YACvD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACxC,2DAA2D;YAC3D,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;SACzC;aAAM,IAAI,WAAW,EAAE;YACtB,uDAAuD;YACvD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACxC,2DAA2D;YAC3D,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;SACzC;aAAM;YACL,gCAAgC;YAChC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;YAC/C,wCAAwC;YACxC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAClC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;SACnC;IACH,CAAC;IAED;;OAEG;IACH,MAAM;QACJ,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IACnE,CAAC;CACF;AApED,gCAoEC"} |
| export {}; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| const union_graph_1 = require("./union-graph"); | ||
| test('should be able to declare an empty graph', () => { | ||
| // Arrange / Act | ||
| const union = new union_graph_1.UnionGraph(); | ||
| // Act | ||
| expect(reorder(union.values())).toEqual([]); | ||
| }); | ||
| test('should be able to declare a first link', () => { | ||
| // Arrange | ||
| const union = new union_graph_1.UnionGraph(); | ||
| // Act | ||
| union.addLink('a', 'b'); | ||
| // Assert | ||
| expect(reorder(union.values())).toEqual([['a', 'b']]); | ||
| }); | ||
| test('should be able to add nodes without any of them from the tree', () => { | ||
| // Arrange | ||
| const union = new union_graph_1.UnionGraph(); | ||
| // Act | ||
| union.addLink('a', 'b'); | ||
| union.addLink('c', 'd'); | ||
| // Assert | ||
| expect(reorder(union.values())).toEqual([['a', 'b'], ['c', 'd']]); | ||
| }); | ||
| test('should be able to add a new node to an existing tree', () => { | ||
| // Arrange | ||
| const union = new union_graph_1.UnionGraph(); | ||
| // Act | ||
| union.addLink('a', 'b'); | ||
| union.addLink('a', 'c'); | ||
| // Assert | ||
| expect(reorder(union.values())).toEqual([['a', 'b', 'c']]); | ||
| }); | ||
| test('should be able to add a new node to an existing tree by referencing the other node', () => { | ||
| // Arrange | ||
| const union = new union_graph_1.UnionGraph(); | ||
| // Act | ||
| union.addLink('a', 'b'); | ||
| union.addLink('b', 'c'); | ||
| // Assert | ||
| expect(reorder(union.values())).toEqual([['a', 'b', 'c']]); | ||
| }); | ||
| test('should be able to merge two trees together', () => { | ||
| // Arrange | ||
| const union = new union_graph_1.UnionGraph(); | ||
| // Act | ||
| union.addLink('a', 'b'); | ||
| union.addLink('c', 'd'); | ||
| union.addLink('b', 'd'); | ||
| // Assert | ||
| expect(reorder(union.values())).toEqual([['a', 'b', 'c', 'd']]); | ||
| }); | ||
| test('should be able to add new nodes on merged trees', () => { | ||
| // Arrange | ||
| const union = new union_graph_1.UnionGraph(); | ||
| // Act | ||
| union.addLink('a', 'b'); | ||
| union.addLink('c', 'd'); | ||
| union.addLink('b', 'd'); | ||
| union.addLink('b', 'e'); | ||
| union.addLink('c', 'f'); | ||
| // Assert | ||
| expect(reorder(union.values())).toEqual([['a', 'b', 'c', 'd', 'e', 'f']]); | ||
| }); | ||
| // Helper | ||
| function reorder(values) { | ||
| return values.map(vs => vs.sort()).sort((vsA, vsB) => vsA[0].localeCompare(vsB[0])); | ||
| } | ||
| //# sourceMappingURL=union-graph.spec.js.map |
| {"version":3,"file":"union-graph.spec.js","sourceRoot":"","sources":["../../src/internal/union-graph.spec.ts"],"names":[],"mappings":"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"} |
+10
-33
@@ -6,2 +6,3 @@ "use strict"; | ||
| const fast_check_1 = require("fast-check"); | ||
| const union_graph_1 = require("./internal/union-graph"); | ||
| var SpecDefType; | ||
@@ -134,28 +135,3 @@ (function (SpecDefType) { | ||
| .noShrink(); | ||
| const equalities = new Map(); | ||
| const addEquality = (specA, specB) => { | ||
| if (specA === specB) { | ||
| return; | ||
| } | ||
| const alreadyKnowA = equalities.has(specA); | ||
| const alreadyKnowB = equalities.has(specB); | ||
| if (alreadyKnowA && alreadyKnowB) { | ||
| // merge A and B | ||
| equalities.set(specA, new Set([...equalities.get(specA), ...equalities.get(specB)])); | ||
| // remove B | ||
| equalities.delete(specB); | ||
| } | ||
| else if (alreadyKnowA) { | ||
| // append specB into already known equalities for specA | ||
| equalities.get(specA).add(specB); | ||
| } | ||
| else if (alreadyKnowB) { | ||
| // append specA into already known equalities for specB | ||
| equalities.get(specB).add(specA); | ||
| } | ||
| else { | ||
| // create a new equlity relation | ||
| equalities.set(specA, new Set([specA, specB])); | ||
| } | ||
| }; | ||
| const union = new union_graph_1.UnionGraph(); | ||
| const previousSpecs = new Map(); | ||
@@ -178,5 +154,8 @@ for (const spec of fast_check_1.sample(specArb, settings && settings.numSamples)) { | ||
| // Combination only rely on constants | ||
| if (lodash_isequal_1.default(spec.build1([]), spec.build2([]))) { | ||
| addEquality(minSpecLabel, maxSpecLabel); | ||
| try { | ||
| if (lodash_isequal_1.default(spec.build1([]), spec.build2([]))) { | ||
| union.addLink(minSpecLabel, maxSpecLabel); | ||
| } | ||
| } | ||
| catch (_err) { } | ||
| } | ||
@@ -187,12 +166,10 @@ else { | ||
| return lodash_isequal_1.default(spec.build1(t), spec.build2(t)); | ||
| }), { numRuns: settings && settings.numFuzz }); | ||
| }), { numRuns: settings && settings.numFuzz, endOnFailure: true }); | ||
| if (!out.failed) | ||
| addEquality(minSpecLabel, maxSpecLabel); | ||
| union.addLink(minSpecLabel, maxSpecLabel); | ||
| } | ||
| } | ||
| return Array.from(equalities.values()).map(vs => Array.from(vs) | ||
| .sort() | ||
| .join(' == ')); | ||
| return union.values().map(vs => vs.sort().join(' == ')); | ||
| } | ||
| exports.findSpecs = findSpecs; | ||
| //# sourceMappingURL=fast-spec.js.map |
+17
-1
| { | ||
| "name": "fast-spec", | ||
| "version": "0.0.4", | ||
| "version": "0.0.5", | ||
| "description": "Discover laws in your code like with QuickSpec", | ||
@@ -10,2 +10,4 @@ "main": "lib/fast-spec.js", | ||
| "build": "tsc", | ||
| "test": "jest", | ||
| "example": "ts-node examples/index.ts", | ||
| "format": "prettier --write \"**/*.{js,ts}\"", | ||
@@ -35,7 +37,21 @@ "format:check": "prettier --list-different \"**/*.{js,ts}\"" | ||
| "devDependencies": { | ||
| "@types/jest": "^24.0.19", | ||
| "@types/lodash.isequal": "^4.5.5", | ||
| "@types/node": "^12.11.1", | ||
| "jest": "^24.9.0", | ||
| "prettier": "^1.18.2", | ||
| "ts-jest": "^24.1.0", | ||
| "ts-node": "^8.4.1", | ||
| "typescript": "^3.6.4" | ||
| }, | ||
| "jest": { | ||
| "globals": { | ||
| "ts-jest": { | ||
| "tsConfig": "tsconfig.json" | ||
| } | ||
| }, | ||
| "transform": { | ||
| "^.+\\.ts$": "ts-jest" | ||
| } | ||
| } | ||
| } |
+1
-1
@@ -49,2 +49,2 @@ # fast-spec | ||
| *Live example available at https://runkit.com/dubzzz/hello-world-fast-spec* | ||
| *Live example available at https://runkit.com/dubzzz/hello-world-fast-spec-v2* |
| export {}; |
-34
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| const tslib_1 = require("tslib"); | ||
| const fc = tslib_1.__importStar(require("fast-check")); | ||
| const fast_spec_1 = require("./fast-spec"); | ||
| console.log(fast_spec_1.findSpecs([ | ||
| fast_spec_1.funcDef("concat", 2, (a, b) => [...a, ...b]), | ||
| fast_spec_1.funcDef("reverse", 1, (a) => [...a].reverse()), | ||
| fast_spec_1.instDef("[]", []), | ||
| fast_spec_1.varDef("x", fc.array(fc.char())) | ||
| ])); | ||
| console.log(fast_spec_1.findSpecs([ | ||
| fast_spec_1.funcDef("concat", 2, (a, b) => [...a, ...b]), | ||
| fast_spec_1.funcDef("reverse", 1, (a) => [...a].reverse()), | ||
| fast_spec_1.funcDef("length", 1, (a) => a.length), | ||
| fast_spec_1.funcDef("map", 1, (a) => a.map(s => s + s)), | ||
| fast_spec_1.instDef("[]", []), | ||
| fast_spec_1.varDef("x", fc.array(fc.char())) | ||
| ])); | ||
| console.log(fast_spec_1.findSpecs([ | ||
| fast_spec_1.funcDef("concat", 2, (a, b) => [...a, ...b]), | ||
| fast_spec_1.instDef("[]", []), | ||
| fast_spec_1.varDef("x", fc.array(fc.char())) | ||
| ])); | ||
| console.log(fast_spec_1.findSpecs([ | ||
| fast_spec_1.funcDef("mul", 2, (a, b) => a * b), | ||
| fast_spec_1.funcDef("div", 2, (a, b) => a / b), | ||
| fast_spec_1.funcDef("plus", 2, (a, b) => a + b), | ||
| fast_spec_1.funcDef("minus", 2, (a, b) => a - b), | ||
| fast_spec_1.instDef("1", 1), | ||
| fast_spec_1.instDef("0", 0), | ||
| fast_spec_1.varDef("x", fc.integer()) | ||
| ])); | ||
| //# sourceMappingURL=tryit.js.map |
| {"version":3,"file":"tryit.js","sourceRoot":"","sources":["../src/tryit.ts"],"names":[],"mappings":";;;AAAA,uDAAiC;AACjC,2CAAkE;AAElE,OAAO,CAAC,GAAG,CACT,qBAAS,CAAC;IACR,mBAAO,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAQ,EAAE,CAAQ,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAC1D,mBAAO,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;IACrD,mBAAO,CAAC,IAAI,EAAE,EAAE,CAAC;IACjB,kBAAM,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;CACjC,CAAC,CACH,CAAC;AAEF,OAAO,CAAC,GAAG,CACT,qBAAS,CAAC;IACR,mBAAO,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAQ,EAAE,CAAQ,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAC1D,mBAAO,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;IACrD,mBAAO,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;IAC5C,mBAAO,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAClD,mBAAO,CAAC,IAAI,EAAE,EAAE,CAAC;IACjB,kBAAM,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;CACjC,CAAC,CACH,CAAC;AAEF,OAAO,CAAC,GAAG,CACT,qBAAS,CAAC;IACR,mBAAO,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAQ,EAAE,CAAQ,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAC1D,mBAAO,CAAC,IAAI,EAAE,EAAE,CAAC;IACjB,kBAAM,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;CACjC,CAAC,CACH,CAAC;AAEF,OAAO,CAAC,GAAG,CACT,qBAAS,CAAC;IACR,mBAAO,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAS,EAAE,CAAS,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;IAClD,mBAAO,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAS,EAAE,CAAS,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;IAClD,mBAAO,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAS,EAAE,CAAS,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;IACnD,mBAAO,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAS,EAAE,CAAS,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;IACpD,mBAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACf,mBAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACf,kBAAM,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC;CAC1B,CAAC,CACH,CAAC"} |
Sorry, the diff of this file is not supported yet
-266
| import isEqual from 'lodash.isequal'; | ||
| import { | ||
| array, | ||
| memo, | ||
| constant, | ||
| constantFrom, | ||
| genericTuple, | ||
| shuffledSubarray, | ||
| oneof, | ||
| tuple, | ||
| sample, | ||
| check, | ||
| property, | ||
| Arbitrary | ||
| } from 'fast-check'; | ||
| export enum SpecDefType { | ||
| Function = 'function', | ||
| Instance = 'instance', | ||
| Variable = 'variable' | ||
| } | ||
| export type SpecDefFunction = { | ||
| type: SpecDefType.Function; | ||
| name: string; | ||
| numParameters: number; | ||
| value: (...args: any[]) => any; | ||
| }; | ||
| export function funcDef(name: string, numParameters: number, value: (...args: any[]) => any): SpecDefFunction { | ||
| return { name, type: SpecDefType.Function, value, numParameters }; | ||
| } | ||
| export type SpecDefInstance = { | ||
| type: SpecDefType.Instance; | ||
| name: string; | ||
| value: any; | ||
| }; | ||
| export function instDef(name: string, value: any): SpecDefInstance { | ||
| return { name, type: SpecDefType.Instance, value }; | ||
| } | ||
| export type SpecDefVariable = { | ||
| type: SpecDefType.Variable; | ||
| name: string; | ||
| value: Arbitrary<any>; | ||
| }; | ||
| export function varDef(name: string, value: Arbitrary<any>): SpecDefVariable { | ||
| return { name, type: SpecDefType.Variable, value }; | ||
| } | ||
| export type FindSpecElement = SpecDefFunction | SpecDefInstance | SpecDefVariable; | ||
| export type FindSpecSettings = { | ||
| /** | ||
| * Number of combinations to try - default: 100 | ||
| */ | ||
| numSamples?: number; | ||
| /** | ||
| * Complexity of the combinations - default: 2 | ||
| * | ||
| * Higher complexity will produce combinations with more nested calls. | ||
| */ | ||
| complexity?: number; | ||
| /** | ||
| * Number of inputs to try to confirm a combination - default: 100 | ||
| */ | ||
| numFuzz?: number; | ||
| }; | ||
| type FindSpecsInternal = { | ||
| numArbs: number; | ||
| build: (ins: any[], offset: number) => { value: any; nextOffset: number }; | ||
| repr: (ins: string[], offset: number) => { value: string; nextOffset: number }; | ||
| }; | ||
| type FindSpecsInternalBuilder = (n?: number) => Arbitrary<FindSpecsInternal>; | ||
| export function findSpecs(def: FindSpecElement[], settings?: FindSpecSettings): string[] { | ||
| const baseArbs: { name: string; arb: Arbitrary<any> }[] = []; | ||
| const specTermArbBuilder: FindSpecsInternalBuilder[] = []; | ||
| for (const el of def) { | ||
| switch (el.type) { | ||
| case SpecDefType.Variable: { | ||
| baseArbs.push({ name: el.name, arb: el.value }); | ||
| specTermArbBuilder.push( | ||
| memo(() => | ||
| constant({ | ||
| numArbs: 1, | ||
| build: (vs: any[], offset: number) => { | ||
| return { value: vs[offset], nextOffset: offset + 1 }; | ||
| }, | ||
| repr: (xn: string[], offset: number) => { | ||
| return { value: xn[offset], nextOffset: offset + 1 }; | ||
| } | ||
| }) | ||
| ) | ||
| ); | ||
| break; | ||
| } | ||
| case SpecDefType.Instance: { | ||
| specTermArbBuilder.push( | ||
| memo(() => | ||
| constant({ | ||
| numArbs: 0, | ||
| build: (vs: any[], offset: number) => { | ||
| return { value: el.value, nextOffset: offset }; | ||
| }, | ||
| repr: (xn: string[], offset: number) => { | ||
| return { value: el.name, nextOffset: offset }; | ||
| } | ||
| }) | ||
| ) | ||
| ); | ||
| break; | ||
| } | ||
| case SpecDefType.Function: { | ||
| const elArb = memo(n => | ||
| n <= 1 || el.numParameters === 0 | ||
| ? constant({ | ||
| numArbs: el.numParameters, | ||
| build: (vs: any[], offset: number) => { | ||
| const nextOffset = offset + el.numParameters; | ||
| return { | ||
| value: el.value(...vs.slice(offset, nextOffset)), | ||
| nextOffset | ||
| }; | ||
| }, | ||
| repr: (xn: string[], offset: number) => { | ||
| const nextOffset = offset + el.numParameters; | ||
| return { | ||
| value: `${el.name}(${xn.slice(offset, nextOffset).join(', ')})`, | ||
| nextOffset | ||
| }; | ||
| } | ||
| }) | ||
| : genericTuple([...Array(el.numParameters)].map(() => oneof(...specTermArbBuilder.map(a => a())))).map( | ||
| t => { | ||
| return { | ||
| numArbs: t.reduce((acc, cur) => acc + cur.numArbs, 0), | ||
| build: (ins: any[], offset: number) => { | ||
| let nextOffset = offset; | ||
| const vs: any = []; | ||
| for (let idx = 0; idx !== el.numParameters; ++idx) { | ||
| const tmp = t[idx].build(ins, nextOffset); | ||
| nextOffset = tmp.nextOffset; | ||
| vs.push(tmp.value); | ||
| } | ||
| return { | ||
| value: el.value(...vs), | ||
| nextOffset | ||
| }; | ||
| }, | ||
| repr: (xn: string[], offset: number) => { | ||
| let nextOffset = offset; | ||
| const vs: string[] = []; | ||
| for (let idx = 0; idx !== el.numParameters; ++idx) { | ||
| const tmp = t[idx].repr(xn, nextOffset); | ||
| nextOffset = tmp.nextOffset; | ||
| vs.push(tmp.value); | ||
| } | ||
| return { | ||
| value: `${el.name}(${vs.join(', ')})`, | ||
| nextOffset | ||
| }; | ||
| } | ||
| }; | ||
| } | ||
| ) | ||
| ); | ||
| specTermArbBuilder.push(elArb); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| const maxDepth = settings && settings.complexity !== undefined ? settings.complexity : 2; | ||
| const specTermArb = oneof(...specTermArbBuilder.map(a => a(maxDepth))); | ||
| const specArb = tuple(specTermArb, specTermArb) | ||
| .chain(([t1, t2]) => { | ||
| const numArbs = t1.numArbs > t2.numArbs ? t1.numArbs : t2.numArbs; | ||
| const variableIndexes = [...Array(numArbs)].map((_, i) => i); | ||
| return tuple( | ||
| oneof(constant(variableIndexes), shuffledSubarray(variableIndexes, numArbs, numArbs)), | ||
| numArbs > 0 | ||
| ? array(constantFrom(...baseArbs), numArbs, numArbs) // throw if no baseArbs | ||
| : constant([]) | ||
| ).map(([reindex, inputsDef]) => { | ||
| const applyReindex = (ins: any[]) => { | ||
| return reindex.map(ri => ins[ri]); | ||
| }; | ||
| const variableNames = inputsDef.map((inputDef, i) => `${inputDef.name}${i}`); | ||
| return { | ||
| inputArbs: inputsDef.map(inputDef => inputDef.arb), | ||
| build1: (ins: any[]) => t1.build(ins, 0).value, | ||
| build2: (ins: any[]) => t2.build(applyReindex(ins), 0).value, | ||
| spec1: `${t1.repr(variableNames, 0).value}`, | ||
| spec2: `${t2.repr(applyReindex(variableNames), 0).value}` | ||
| }; | ||
| }); | ||
| }) | ||
| .filter(d => d.spec1 !== d.spec2) | ||
| .noShrink(); | ||
| const equalities = new Map<string, Set<string>>(); | ||
| const addEquality = (specA: string, specB: string) => { | ||
| if (specA === specB) { | ||
| return; | ||
| } | ||
| const alreadyKnowA = equalities.has(specA); | ||
| const alreadyKnowB = equalities.has(specB); | ||
| if (alreadyKnowA && alreadyKnowB) { | ||
| // merge A and B | ||
| equalities.set(specA, new Set([...equalities.get(specA)!, ...equalities.get(specB)!])); | ||
| // remove B | ||
| equalities.delete(specB); | ||
| } else if (alreadyKnowA) { | ||
| // append specB into already known equalities for specA | ||
| equalities.get(specA)!.add(specB); | ||
| } else if (alreadyKnowB) { | ||
| // append specA into already known equalities for specB | ||
| equalities.get(specB)!.add(specA); | ||
| } else { | ||
| // create a new equlity relation | ||
| equalities.set(specA, new Set([specA, specB])); | ||
| } | ||
| }; | ||
| const previousSpecs = new Map<string, Set<string>>(); | ||
| for (const spec of sample(specArb, settings && settings.numSamples)) { | ||
| // Sort labels of specs to have a consistent way to store | ||
| // the already investigated combinations | ||
| const minSpecLabel = spec.spec1 < spec.spec2 ? spec.spec1 : spec.spec2; | ||
| const maxSpecLabel = spec.spec1 < spec.spec2 ? spec.spec2 : spec.spec1; | ||
| // Skip already covered combinations | ||
| if (previousSpecs.has(minSpecLabel) && previousSpecs.get(minSpecLabel)!.has(maxSpecLabel)) { | ||
| continue; | ||
| } | ||
| // Add combination to the list of covered ones | ||
| if (!previousSpecs.has(minSpecLabel)) { | ||
| previousSpecs.set(minSpecLabel, new Set()); | ||
| } | ||
| previousSpecs.get(minSpecLabel)!.add(maxSpecLabel); | ||
| if (spec.inputArbs.length === 0) { | ||
| // Combination only rely on constants | ||
| if (isEqual(spec.build1([]), spec.build2([]))) { | ||
| addEquality(minSpecLabel, maxSpecLabel); | ||
| } | ||
| } else { | ||
| // Combination rely on non-constant values | ||
| const out = check( | ||
| property(genericTuple(spec.inputArbs), t => { | ||
| return isEqual(spec.build1(t), spec.build2(t)); | ||
| }), | ||
| { numRuns: settings && settings.numFuzz } | ||
| ); | ||
| if (!out.failed) addEquality(minSpecLabel, maxSpecLabel); | ||
| } | ||
| } | ||
| return Array.from(equalities.values()).map(vs => | ||
| Array.from(vs) | ||
| .sort() | ||
| .join(' == ') | ||
| ); | ||
| } |
-42
| import * as fc from "fast-check"; | ||
| import { funcDef, instDef, varDef, findSpecs } from "./fast-spec"; | ||
| console.log( | ||
| findSpecs([ | ||
| funcDef("concat", 2, (a: any[], b: any[]) => [...a, ...b]), | ||
| funcDef("reverse", 1, (a: any[]) => [...a].reverse()), | ||
| instDef("[]", []), | ||
| varDef("x", fc.array(fc.char())) | ||
| ]) | ||
| ); | ||
| console.log( | ||
| findSpecs([ | ||
| funcDef("concat", 2, (a: any[], b: any[]) => [...a, ...b]), | ||
| funcDef("reverse", 1, (a: any[]) => [...a].reverse()), | ||
| funcDef("length", 1, (a: any[]) => a.length), | ||
| funcDef("map", 1, (a: any[]) => a.map(s => s + s)), | ||
| instDef("[]", []), | ||
| varDef("x", fc.array(fc.char())) | ||
| ]) | ||
| ); | ||
| console.log( | ||
| findSpecs([ | ||
| funcDef("concat", 2, (a: any[], b: any[]) => [...a, ...b]), | ||
| instDef("[]", []), | ||
| varDef("x", fc.array(fc.char())) | ||
| ]) | ||
| ); | ||
| console.log( | ||
| findSpecs([ | ||
| funcDef("mul", 2, (a: number, b: number) => a * b), | ||
| funcDef("div", 2, (a: number, b: number) => a / b), | ||
| funcDef("plus", 2, (a: number, b: number) => a + b), | ||
| funcDef("minus", 2, (a: number, b: number) => a - b), | ||
| instDef("1", 1), | ||
| instDef("0", 0), | ||
| varDef("x", fc.integer()) | ||
| ]) | ||
| ); |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
32176
-86.72%8
100%404
-30.58%1
Infinity%