execution-engine
Version:
A TypeScript library for tracing and visualizing code execution workflows.
143 lines (142 loc) • 6.96 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
const memoize_decorator_1 = require("./memoize.decorator");
describe('memoize decorator', () => {
it('should memoize Fibonacci results and prevent redundant function calls', async () => {
let memoizationCheckCount = 0;
let memoizedCalls = 0;
let totalFunctionCalls = 0;
class Calculator {
fibonacci(n) {
totalFunctionCalls++;
if (n <= 1) {
return n;
}
return this.fibonacci(n - 1) + this.fibonacci(n - 2);
}
}
__decorate([
(0, memoize_decorator_1.memoize)((memoContext) => {
memoizationCheckCount++;
if (memoContext.isMemoized) {
memoizedCalls++;
}
})
], Calculator.prototype, "fibonacci", null);
const calculator = new Calculator();
memoizationCheckCount = 0;
memoizedCalls = 0;
totalFunctionCalls = 0;
const fib3 = calculator.fibonacci(3);
expect(memoizedCalls).toBe(0);
expect(totalFunctionCalls).toBe(5); // fib(3) = (fib(2) = fib(1) + fib(0)) + fib(1)
expect(memoizationCheckCount).toEqual(totalFunctionCalls + memoizedCalls);
expect(fib3).toBe(2);
memoizationCheckCount = 0;
memoizedCalls = 0;
totalFunctionCalls = 0;
// first call:
const fib50_1 = calculator.fibonacci(50);
expect(memoizedCalls).toBeGreaterThan(0);
expect(totalFunctionCalls).toBeLessThan(1274); // 1274 calls for fibonacci(50) if all exist
expect(memoizationCheckCount).toEqual(totalFunctionCalls + memoizedCalls);
expect(fib50_1).toBe(12586269025);
const memoizedCallsAfterFirstCall = memoizedCalls;
const totalFunctionCallsAfterFirstCall = totalFunctionCalls;
// second call:
const fib50_2 = calculator.fibonacci(50);
expect(memoizedCalls).toBe(memoizedCallsAfterFirstCall + 1); // a new get of memoized fib50
expect(totalFunctionCalls).toBe(totalFunctionCallsAfterFirstCall); // no new call, fib50 is memoized
expect(memoizationCheckCount).toEqual(totalFunctionCalls + memoizedCalls);
expect(fib50_2).toBe(12586269025);
memoizationCheckCount = 0;
memoizedCalls = 0;
totalFunctionCalls = 0;
const fib51 = calculator.fibonacci(51);
expect(totalFunctionCalls).toBe(1); // we need 1 extra call to get fibonacci of 51 as we did fibonacci(50)
expect(memoizedCalls).toBe(2); // yes fib(51-1) and fib(51-2) are memoized
expect(memoizationCheckCount).toBe(3); // 2memoized and 1 call
expect(fib51).toBe(20365011074);
memoizationCheckCount = 0;
memoizedCalls = 0;
totalFunctionCalls = 0;
const fib5 = calculator.fibonacci(6);
expect(totalFunctionCalls).toBe(0); // no need for extra call to get fibonacci of 5 as we did fibonacci(50)
expect(memoizedCalls).toBe(1); // yes fib(5) is memoized implicitly
expect(memoizationCheckCount).toBe(1); // 1memoized
expect(fib5).toBe(8);
});
it('should memoize async function results and prevent redundant calls', async () => {
let memoizationCheckCount = 0;
let memoizedCalls = 0;
let totalFunctionCalls = 0;
class DataService {
async fetchData(id) {
totalFunctionCalls++;
return new Promise((resolve) => setTimeout(() => resolve(`Data for ID: ${id}`), 100));
}
async throwData(name) {
totalFunctionCalls++;
throw new Error(`hello ${name} but I throw!`);
}
}
__decorate([
(0, memoize_decorator_1.memoize)(async (memoContext) => {
memoizationCheckCount++;
if (memoContext.isMemoized) {
memoizedCalls++;
}
})
], DataService.prototype, "fetchData", null);
__decorate([
(0, memoize_decorator_1.memoize)(async (memoContext) => {
memoizationCheckCount++;
if (memoContext.isMemoized) {
memoizedCalls++;
}
})
], DataService.prototype, "throwData", null);
const service = new DataService();
memoizationCheckCount = 0;
memoizedCalls = 0;
totalFunctionCalls = 0;
const result1 = await service.fetchData(1);
expect(result1).toBe('Data for ID: 1');
expect(memoizedCalls).toBe(0);
expect(totalFunctionCalls).toBe(1);
expect(memoizationCheckCount).toBe(1); // Called once
const result2 = await service.fetchData(1);
expect(result2).toBe('Data for ID: 1');
expect(memoizedCalls).toBe(1); // Now it should be memoized
expect(totalFunctionCalls).toBe(1); // No new calls
expect(memoizationCheckCount).toBe(2); // Checked twice
const result3 = await service.fetchData(2);
expect(result3).toBe('Data for ID: 2');
expect(memoizedCalls).toBe(1); // No extra memoized calls yet
expect(totalFunctionCalls).toBe(2); // New call for different ID
expect(memoizationCheckCount).toBe(3); // Three checks (1st, 2nd for ID 1, and 3rd for ID 2)
const result4 = await service.fetchData(2);
expect(result4).toBe('Data for ID: 2');
expect(memoizedCalls).toBe(2); // ID 2 result is now memoized
expect(totalFunctionCalls).toBe(2); // No extra new calls
expect(memoizationCheckCount).toBe(4); // 4 checks in total
// test memoize a throwing async method
memoizationCheckCount = 0;
memoizedCalls = 0;
totalFunctionCalls = 0;
await Promise.all([
expect(service.throwData('akram')).rejects.toThrow('hello akram but I throw!'),
expect(service.throwData('akram')).rejects.toThrow('hello akram but I throw!'),
expect(service.throwData('akram')).rejects.toThrow('hello akram but I throw!')
]);
expect(memoizationCheckCount).toEqual(totalFunctionCalls + memoizedCalls);
expect(memoizedCalls).toEqual(2);
expect(totalFunctionCalls).toBe(1); // No extra new calls
});
});
;