Add experimental safariYieldStrategy: "time" config option

This greatly improves speed, at least in jasmine-core's own tests.
This commit is contained in:
Steve Gravrock
2025-11-12 21:08:59 -08:00
parent 7b2807b321
commit 9c2ffae2f9
7 changed files with 224 additions and 36 deletions

View File

@@ -15,7 +15,8 @@ describe('Configuration', function() {
'seed',
'specFilter',
'extraItStackFrames',
'extraDescribeStackFrames'
'extraDescribeStackFrames',
'safariYieldStrategy'
];
Object.freeze(standardBooleanKeys);
Object.freeze(allKeys);
@@ -36,6 +37,7 @@ describe('Configuration', function() {
expect(subject.detectLateRejectionHandling).toEqual(false);
expect(subject.extraItStackFrames).toEqual(0);
expect(subject.extraDescribeStackFrames).toEqual(0);
expect(subject.safariYieldStrategy).toEqual('count');
});
describe('copy()', function() {
@@ -137,5 +139,28 @@ describe('Configuration', function() {
subject.update({ extraDescribeStackFrames: 100000 });
expect(subject.extraDescribeStackFrames).toEqual(100000);
});
it('sets safariYieldStrategy when valid', function() {
const subject = new privateUnderTest.Configuration();
subject.update({ safariYieldStrategy: undefined });
expect(subject.safariYieldStrategy).toEqual('count');
subject.update({ safariYieldStrategy: 'time' });
expect(subject.safariYieldStrategy).toEqual('time');
subject.update({ safariYieldStrategy: 'count' });
expect(subject.safariYieldStrategy).toEqual('count');
});
it('rejcts invalid safariYieldStrategy values', function() {
const subject = new privateUnderTest.Configuration();
expect(function() {
subject.update({ safariYieldStrategy: 'thyme' });
}).toThrowError(
"Invalid safariYieldStrategy value. Valid values are 'count' and 'time'."
);
});
});
});

View File

@@ -180,30 +180,82 @@ describe('StackClearer', function() {
expect(called).toBe(true);
});
it('uses setTimeout instead of queueMicrotask every 10 calls to make sure we release the CPU', function() {
const queueMicrotask = jasmine.createSpy('queueMicrotask');
const setTimeout = jasmine.createSpy('setTimeout');
const global = {
...makeGlobal(),
queueMicrotask,
setTimeout
};
const { clearStack } = privateUnderTest.getStackClearer(global);
function hasSetTimeoutBehavior(configure) {
it('uses setTimeout instead of queueMicrotask every 10 calls', function() {
const queueMicrotask = jasmine.createSpy('queueMicrotask');
const setTimeout = jasmine.createSpy('setTimeout');
const global = {
...makeGlobal(),
queueMicrotask,
setTimeout
};
const stackClearer = privateUnderTest.getStackClearer(global);
for (let i = 0; i < 9; i++) {
clearStack(function() {});
}
if (configure) {
configure(stackClearer);
}
expect(queueMicrotask).toHaveBeenCalled();
expect(setTimeout).not.toHaveBeenCalled();
for (let i = 0; i < 9; i++) {
stackClearer.clearStack(function() {});
}
clearStack(function() {});
expect(queueMicrotask).toHaveBeenCalledTimes(9);
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(queueMicrotask).toHaveBeenCalled();
expect(setTimeout).not.toHaveBeenCalled();
clearStack(function() {});
expect(queueMicrotask).toHaveBeenCalledTimes(10);
expect(setTimeout).toHaveBeenCalledTimes(1);
stackClearer.clearStack(function() {});
expect(queueMicrotask).toHaveBeenCalledTimes(9);
expect(setTimeout).toHaveBeenCalledTimes(1);
stackClearer.clearStack(function() {});
expect(queueMicrotask).toHaveBeenCalledTimes(10);
expect(setTimeout).toHaveBeenCalledTimes(1);
});
}
hasSetTimeoutBehavior();
describe('With yield strategy explicitly set to count', function() {
hasSetTimeoutBehavior(function(stackClearer) {
stackClearer.setSafariYieldStrategy('count');
});
});
describe('With yield strategy set to time', function() {
beforeEach(function() {
jasmine.clock().install();
jasmine.clock().mockDate();
});
afterEach(function() {
jasmine.clock().uninstall();
});
it('uses setTimeout instead of queueMicrotask every 25 milliseconds', function() {
const queueMicrotask = jasmine.createSpy('queueMicrotask');
const setTimeout = jasmine.createSpy('setTimeout');
const global = {
...makeGlobal(),
queueMicrotask,
setTimeout
};
const stackClearer = privateUnderTest.getStackClearer(global);
stackClearer.setSafariYieldStrategy('time');
// 10+ counts should not trigger a setTimeout if they happen fast enough
jasmine.clock().tick(24);
for (let i = 0; i < 11; i++) {
stackClearer.clearStack(function() {});
}
expect(queueMicrotask).toHaveBeenCalled();
expect(setTimeout).not.toHaveBeenCalled();
queueMicrotask.calls.reset();
jasmine.clock().tick(1);
stackClearer.clearStack(function() {});
expect(queueMicrotask).not.toHaveBeenCalled();
expect(setTimeout).toHaveBeenCalledTimes(1);
});
});
}

View File

@@ -28,7 +28,8 @@ module.exports = {
'helpers/resetEnv.js'
],
env: {
forbidDuplicateNames: true
forbidDuplicateNames: true,
safariYieldStrategy: 'time'
},
random: true,
browser: {