-
Notifications
You must be signed in to change notification settings - Fork 27.4k
fix($interval): change $interval ngMock service to handle second argu… #15953
fix($interval): change $interval ngMock service to handle second argu… #15953
Conversation
We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for the commit author(s). If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google. |
1 similar comment
We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for the commit author(s). If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google. |
src/ngMock/angular-mocks.js
Outdated
@@ -556,9 +556,13 @@ angular.mock.$IntervalProvider = function() { | |||
*/ | |||
$interval.flush = function(millis) { | |||
now += millis; | |||
while (repeatFns.length && repeatFns[0].nextTime <= now) { | |||
var firstTask = repeatFns[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused
src/ngMock/angular-mocks.js
Outdated
var task = repeatFns[0]; | ||
task.fn(); | ||
if (!task.delay) { | ||
task.delay = task.nextTime = 1; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrong indentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I set two spaces as indentation. Is it wrong or I'm missing something else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, it turned out I had a Chrome extension that was messing the GitHub diffs. Indentation is fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No worries 👍
src/ngMock/angular-mocks.js
Outdated
var task = repeatFns[0]; | ||
task.fn(); | ||
if (!task.delay) { | ||
task.delay = task.nextTime = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why set nextTime
to 0 (since we are also incrementing it below)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If delay
is zero then nextTime
will also be zero. Setting to 1 means the next round it'll be 2 since in fact will be the round number 2. Thanks to first set to 1 and then increasing it means that passing a value of 1 to ms
will run only once and not twice the function. I hope this makes sense :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if it'd be helpful to write this down as a comment. What do you think? @gkalpak
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A short comment wouldn't hurt for sure 😃
(Feel free to ping me when this is ready for another review.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey thanks! I was afraid of bothering you 😄
It should be ready now, please let me know if I'm missing something else.
test/ngMock/angular-mocksSpec.js
Outdated
expect(counter).toBe(2000); | ||
$interval.flush(1000); | ||
expect(counter).toBe(4000); | ||
})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need a test with other, non-zero-delay timeouts, to verify that the relative orders are correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are specs with non-zero-delay timeouts but maybe you mean mixing them. What do you reckon? Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I meant mixing zero and non-zero intervals in the same test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Brilliant!
test/ngMock/angular-mocksSpec.js
Outdated
$interval(function() {counter++;}, 0); | ||
|
||
$interval.flush(1000); | ||
expect(counter).toBe(2000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would expect this to be 1000. Can you explain why it isn't?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When calling interval it checks the second argument to be a number or equivalent to zero, hence undefined gives the same results. Since above two statements are increasing counter on a flush of 1000 both functions are run and counter finish with a value of 2000.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duh! Both callbacks increment the same counter.
Still it might be worth having two separate counters to be safer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea! Will do
CLAs look good, thanks! |
1 similar comment
CLAs look good, thanks! |
src/ngMock/angular-mocks.js
Outdated
// to avoid running the while loop | ||
// one more time than the specified | ||
if (!task.delay) { | ||
task.delay = task.nextTime = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I don't think this will work if the zero-delay timer was not added from the beginning (i.e. if there was a flush before adding it). E.g.:
var counter = 0;
$interval.flush(1000);
$interval(function() { counter++; });
$interval.flush(1000);
expect(counter).toBe(1000); // <-- Will probably be 2000
Also, due to how repeatFns.nextTime
is set up initially (nextTime: now + delay
), leaving the delay undefined would behave differently than using 0
(which is not desirable).
I think we should change both $interval
and $interval.flush
to something like:
$interval = function(fn, delay, ...) {
...
repeatFns.push({
nextTime: (now + (delay || 0)),
delay: delay || 1,
...
});
$interval.flush = function(millis) {
var before = now;
now += millis;
while (repeatFns.length && repeatFns[0].nextTime <= now) {
...
if (task.nextTime === before) {
// This can only happen the first time a zero-delay interval gets triggered.
task.nextTime++;
}
task.nextTime += task.delay;
...
Then we need to have more tests that verify the correct behavior of zero-delay intervals (with both delay: 0
and delay: undefined
) for at least the following cases:
- Interval being added when
now
> 0. - Calling
flush(0)
(should only trigger zero-delay intervals and only trigger them once). - Calling
flush(0)
for a second time (should not trigger any intervals).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! I wasn't sure about changing $interval
but I like this implementation. Thanks!
src/ngMock/angular-mocks.js
Outdated
now += millis; | ||
while (repeatFns.length && repeatFns[0].nextTime <= now) { | ||
var task = repeatFns[0]; | ||
task.fn(); | ||
if (task.nextTime === before) { | ||
// this can only happen the first time | ||
// a zero-delay interval get triggered |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
get --> gets
test/ngMock/angular-mocksSpec.js
Outdated
|
||
expect(counterA).toBe(0); | ||
expect(counterB).toBe(0); | ||
})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need another $interval.flush(X)
and assertions that the callbacks where run only X times.
…ment equivalent to zero Enable test interval calls with delay set to zero without infinite loop. If a spec has `$interval.flush(ms)` and the code to test has `$interval(callback, 0)` then instead of running an infinite while loop it would move forward `ms` executing the $interval's callback. Fixes #15952
@gkalpak updated changes. Thanks! |
Previously, trying to test code thaat contained zero-delay intervals (e.g. `$interval(fn, 0)` or `$interval(fn)`) would result in an infinite loop. This commit avoids the infinite loop, by treating zero-delay intervals as one second intervals (except for the initial trigger, where they can also be executed with `$interval.flush(0)`). Fixes #15952 Closes #15953
…ment equivalent to zero
Enable test interval calls with delay set to zero without infinite loop.
If a spec has
$interval.flush(ms)
and the code to test has$interval(callback, 0)
then instead of running an infinite while loopit would move forward
ms
executing the $interval's callback.Fixes #15952
What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
Bug fix
What is the current behavior? (You can also link to an open issue here)
#15952
What is the new behavior (if this is a feature change)?
Does this PR introduce a breaking change?
No.
Please check if the PR fulfills these requirements
Other information: