diff --git a/app/utils/expectations.js b/app/utils/expectations.js index 3c57f34c4..2c3fe5e6b 100644 --- a/app/utils/expectations.js +++ b/app/utils/expectations.js @@ -12,7 +12,7 @@ export const multiExpect = (...expectations) => (element) => // DECLARATION EXPECTATIONS export const doSomething = (declaration) => - newExpectation(`within ${toEDLString(declaration)} count(calls) >= 1`, 'do_something', { declaration }) + newExpectation(`${countCallsWithin(declaration)} >= 1`, 'do_something', { declaration }) export const isUsed = (declaration) => newExpectation(`calls ${toEDLString(declaration)}`, 'is_used', { declaration }) @@ -21,15 +21,20 @@ export const isUsedFromMain = (declaration) => newExpectation(`through ${toEDLString(entryPointType)} calls ${toEDLString(declaration)}`, 'is_used_from_main', { declaration }) export const notTooLong = (limit = 7) => (declaration) => - newExpectation(`within ${toEDLString(declaration)} count(calls) <= ${limit - 1}`, 'too_long', { declaration, limit }) + newExpectation(`${countCallsWithin(declaration)} <= ${limit - 1}`, 'too_long', { declaration, limit }) export const doesNotUseRecursion = (declaration) => - newExpectation(`through ${toEDLString(declaration)} ! calls ${toEDLString(declaration)}`, doesNotUseRecursionId, { declaration }) + newExpectation(`not (through ${toEDLString(declaration)} calls ${toEDLString(declaration)})`, doesNotUseRecursionId, { declaration }) // UTILS -const newExpectation = (expect, id, opts = {}) => +export const newExpectation = (expect, id, opts = {}) => `expectation "${stringify(id, opts)}": ${expect};` +// Use this to count number of calls inside a procedure, including recursive calls +// Mulang count does not count recursive calls +export const countCallsWithin = (declaration) => + `within ${toEDLString(declaration)} count(calls) + count(calls ${toEDLString(declaration)})` + export const stringify = (id, opts) => `${expectationName(id)}|${Object.entries(opts).map(([key, value]) => `${key}=${value}`).join(';')}` diff --git a/tests/unit/services/mulang-expectations-test.js b/tests/unit/services/mulang-expectations-test.js index 6409ade7d..a2ca46fb9 100644 --- a/tests/unit/services/mulang-expectations-test.js +++ b/tests/unit/services/mulang-expectations-test.js @@ -1,6 +1,6 @@ import { module, test } from 'qunit' import { entryPointType } from '../../../utils/blocks' -import { declaresAnyProcedure, doSomething, isUsed, isUsedFromMain, notTooLong, parseExpect, doesNotUseRecursion, stringify, expectationId, isCritical, doesNotUseRecursionId } from '../../../utils/expectations' +import { declaresAnyProcedure, doSomething, isUsed, isUsedFromMain, notTooLong, parseExpect, doesNotUseRecursion, stringify, expectationId, isCritical, doesNotUseRecursionId, newExpectation, countCallsWithin } from '../../../utils/expectations' import { procedure, entryPoint, rawSequence, application } from '../../helpers/astFactories' import { setupPBUnitTest, setUpTestWorkspace } from '../../helpers/utils' @@ -28,7 +28,13 @@ module('Unit | Service | Mulang | Expectations', function (hooks) { application('PRIMITIVE') ) ]) - + + expectationTestOk('doSomething', doSomething(declaration), [ + procedure(declaration, [], + application(declaration) + ) + ], 'Recursion should count as doing something') + expectationTestFail('doSomething', doSomething('EMPTY'), [ procedure('EMPTY', []) ]) @@ -84,10 +90,18 @@ module('Unit | Service | Mulang | Expectations', function (hooks) { application('PRIMITIVE'), ) ]) - + + expectationTestFail('notTooLong', notTooLong(limit)(declaration), [ + procedure(declaration, [], + application(declaration), + application(declaration), + application(declaration) + ) + ], 'Recursive calls should count as being too long ') + expectationTestOk('doesNotUseRecursion', doesNotUseRecursion(declaration), [ procedure(declaration, [], - application("PROCEDURE2") + application("PROCEDURE2") ), procedure("PROCEDURE2", []) ]) @@ -95,31 +109,47 @@ module('Unit | Service | Mulang | Expectations', function (hooks) { // Direct recursion expectationTestFail('doesNotUseRecursion', doesNotUseRecursion(declaration), [ procedure(declaration, [], - application(declaration) + application(declaration) ) ]) - /* + // Indirect recursion expectationTestFail('doesNotUseRecursion', doesNotUseRecursion(declaration), [ procedure(declaration, [], - application("PROCEDURE2") + application("PROCEDURE2") ), procedure("PROCEDURE2", [], application(declaration) ) - ]) - */ + ], 'Indirect recursion should count as recursion') + + expectationTestFail('doesNotUseRecursion', doesNotUseRecursion(declaration), [ + procedure(declaration, [], + application(declaration), + application("PROCEDURE2") + ), + procedure("PROCEDURE2", [], + application('PRIMITIVE')) + ], 'Direct recursion with another procedure call should count as recursion') + + expectationTestOk('countCallsWithin', newExpectation(`${countCallsWithin(declaration)} = 2`, 'counts', { declaration }), [ + procedure(declaration, [], + application("PROCEDURE2"), + application(declaration) + ), + procedure("PROCEDURE2", []) + ], 'countCallsWithin includes recursive calls') - function expectationTestOk(expectationName, expectation, astNodes) { - expectationTest(expectationName, expectation, astNodes, true) + function expectationTestOk(expectationName, expectation, astNodes, testName) { + expectationTest(expectationName, expectation, astNodes, true, testName) } - function expectationTestFail(expectationName, expectation, astNodes) { - expectationTest(expectationName, expectation, astNodes, false) + function expectationTestFail(expectationName, expectation, astNodes, testName) { + expectationTest(expectationName, expectation, astNodes, false, testName) } - function expectationTest(expectationName, edl, astNodes, shouldPass) { - test(`Expectation ${expectationName} - ${shouldPass ? 'ok' : 'fail'}`, function (assert) { + function expectationTest(expectationName, edl, astNodes, shouldPass, testName = '') { + test(`Expectation ${expectationName} - ${testName || (shouldPass ? 'ok' : 'fail')}`, function (assert) { const mulangResult = mulang .astCode(rawSequence(astNodes)) .customExpect(edl)