Repository navigation
Conversation
… were not executed
|
Can you take a look at the failing tests? |
|
After debugging, I found out why a single test passes, but the entire file fails . Due to the following test cases of flutter/packages/flutter/test/scheduler/scheduler_test.dart Lines 109 to 137 in ce889fa Here is my debugging processI just kept flutter/packages/flutter/test/scheduler/scheduler_test.dart Lines 109 to 137 in ce889fa and my test cases, and added log at the start and end of the task The problem is that when the above test finishes, I dont know why |
|
Sounds like we need some cleanup method at the end of each test |
|
Yeah,I think there are two problems One is Why is |
| ); | ||
| controller.forward(); | ||
|
|
||
| // If the task is not scheduled round-robin, this code will block the test, test will be timeout |
There was a problem hiding this comment.
Having this time out is not great. Can this run a a fake async zone where we can control time to ensure that the task will execute after the expected time has elapsed?
There was a problem hiding this comment.
I have fixed the other issues, but when writing the test using the FakeAsync Zone, the test failed. After debugging, I found that the Timer. run in FakeAsync will be immediately added to the _timers set, and the tasks in the set will be processed synchronously while _firetimersWhile, so the EventLoop will schedule and add the tasks in a cycle. No other code will continue to run. But this is not the same as actually running on the device.
The following related code:
class FakeAsync {
/// Creates a new timer controlled by `this` that fires [callback] after
/// [duration] (or every [duration] if [periodic] is `true`).
Timer _createTimer(Duration duration, Function callback, bool periodic) {
var timer = FakeTimer._(duration, callback, periodic, this);
_timers.add(timer);
return timer;
}
}
/// Invoke the callback for each timer until [predicate] returns `false` for
/// the next timer that would be fired.
///
/// Microtasks are flushed before and after each timer is fired. Before each
/// timer fires, [_elapsed] is updated to the appropriate duration.
void _fireTimersWhile(bool Function(FakeTimer timer) predicate) {
flushMicrotasks();
for (;;) {
if (_timers.isEmpty) break;
var timer = minBy(_timers, (FakeTimer timer) => timer._nextCall)!;
if (!predicate(timer)) break;
_elapseTo(timer._nextCall);
timer._fire();
flushMicrotasks();
}
}There was a problem hiding this comment.
cc when you have time~ thanks @goderbauer @Hixie
There was a problem hiding this comment.
I don't fully understand what you mean by this. It looks like the timeout I was concerned about is now gone from the test?
There was a problem hiding this comment.
If it is not an EventLoop, timeout appears in the following line of code
await scheduler.scheduleTask(() => taskExecuted = true, Priority.idle);
There was a problem hiding this comment.
Because in the original code, once 'schedulingStrategy' has returned the fase, it will not be executed regardless of whether there is a queue left in the _taskQueue, so this task will never be executed, causing the test to time out.
There was a problem hiding this comment.
I tried to use FakeAsync to control the animation time, but the Timer.run in FakeAsync is a synchronous queue, which causes the system to execute Timer.run(_runTask) in a loop, thus timed out. So I can only write test cases in this way in the end. If you have better ideas, can you give me some suggestions
|
This pull request executed golden file tests, but it has not been updated in a while (20+ days). Test results from Gold expire after as many days, so this pull request will need to be updated with a fresh commit in order to get results from Gold. For more guidance, visit Writing a golden file test for Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. |
| _hasRequestedAnEventLoopCallback = false; | ||
| if (handleEventLoopCallback()) | ||
| _ensureEventLoopCallback(); // runs next task when there's time | ||
| if (handleEventLoopCallback() || _taskQueue.isNotEmpty) { |
There was a problem hiding this comment.
It looks like this can be simplified since there isn't really a case where handleEventLoopCallback returns true and _taskQueue.isNotEmpty is false, which means only _taskQueue.isNotEmpty decides here whether we queue up another event loop callback.
There was a problem hiding this comment.
Actually, this doesn't look correct: If handleEventLoopCallback returns false because we're locked (see line 457) we are not allowed to call _ensureEventLoopCallback because of the assert(!locked) in that method.
There was a problem hiding this comment.
Yeah, I also think it doesn't look correct with the previous change. After I thought it over and over again, I think the problem is inside the handleEventLoopCallback.
Because

According to the source code, The handleEventLoopCallback returns false in three cases:
- _taskQueue.isEmpty || locked
schedulingStrategy()return true, and_taskQueuebecomes empty after performing a taskschedulingStrategy()return false , although _taskQueue is not empty
I think the third case is problematic, because going to the third logic says the _taskQueue is not empty and locked is false . This indicates that a task still needs to be executed, except that this time the schedulingStrategy is not satisfied. It should return true to continue the _ensureEventLoopCallback like this.
There was a problem hiding this comment.
This seems incorrect to me, since it can cause tasks to fire even if the locked is true.
There was a problem hiding this comment.
There was something wrong with my previous revision, you can check out my latest comment .
https://github.com/flutter/flutter/pull/82781/files#r671808835
I will revert the previous changes. And I think the problem is just that handleEventLoopCallback .
If locked is true, this method will return fasle at the first step and the event will not be executed

|
|
||
| /// When a scheduling error occurs, resets the relative state of EventLoop. | ||
| @visibleForTesting | ||
| void resetEventLoop() { |
There was a problem hiding this comment.
Call this debugResetEventLoop to make it clear that this shouldn't be called by an app and make it only do something when asserts are enabled.
There was a problem hiding this comment.
Yeah I'll try to change it
There was a problem hiding this comment.
done you can review it
| /// When a scheduling error occurs, resets the relative state of EventLoop. | ||
| @visibleForTesting | ||
| void resetEventLoop() { | ||
| _hasRequestedAnEventLoopCallback = false; |
There was a problem hiding this comment.
This is odd though, because the scheduled Timer.run would still execute...
There was a problem hiding this comment.
This flag _hasRequestedAnEventLoopCallback was corrected by the previous test error, as you can see from my previous debugging procedure
| ); | ||
| controller.forward(); | ||
|
|
||
| // If the task is not scheduled round-robin, this code will block the test, test will be timeout |
There was a problem hiding this comment.
I don't fully understand what you mean by this. It looks like the timeout I was concerned about is now gone from the test?
|
Can you rebase this to the latest master to get the checks to pass? |
|
Should I re-open a PR after the discussion? |
@goderbauer have time to cc |
|
All you have to do to rebase (and get the tests running again) is probably a |
… were not executed
|
It looks like this needs a rebase/merge again. |
|
|
||
| test('The task was successfully executed after the animation', () async { | ||
| scheduler.schedulingStrategy = defaultSchedulingStrategy; | ||
| scheduler.debugResetEventLoop(); |
There was a problem hiding this comment.
I think we can remove this right? you get a new scheduler for each test.
It'd be best not to expose new API if we can avoid it.
There was a problem hiding this comment.
I have try to new scheduler , but it's TestSchedulerBinding class with BindingBase. So it can only initialize once. In the end, I had to do it this way
| import 'scheduler_tester.dart'; | ||
|
|
||
| class TestSchedulerBinding extends BindingBase with SchedulerBinding, ServicesBinding { | ||
| class TestSchedulerBinding extends BindingBase with SchedulerBinding, ServicesBinding, SemanticsBinding { |
There was a problem hiding this comment.
Do we need semantics binding here?
There was a problem hiding this comment.
Yeah, I have used the animation in the test, it needs the SemanticsBinding .
dnfield
left a comment
There was a problem hiding this comment.
See comments. I'm sorry it took me so long to get to this, I was OOO for a bit.
|
|
||
| test('The task was successfully executed after the animation', () async { | ||
| scheduler.schedulingStrategy = defaultSchedulingStrategy; | ||
| scheduler.debugResetEventLoop(); |
There was a problem hiding this comment.
I have try to new scheduler , but it's TestSchedulerBinding class with BindingBase. So it can only initialize once. In the end, I had to do it this way
| import 'scheduler_tester.dart'; | ||
|
|
||
| class TestSchedulerBinding extends BindingBase with SchedulerBinding, ServicesBinding { | ||
| class TestSchedulerBinding extends BindingBase with SchedulerBinding, ServicesBinding, SemanticsBinding { |
There was a problem hiding this comment.
Yeah, I have used the animation in the test, it needs the SemanticsBinding .
| _hasRequestedAnEventLoopCallback = false; | ||
| if (handleEventLoopCallback()) | ||
| _ensureEventLoopCallback(); // runs next task when there's time | ||
| if (handleEventLoopCallback() || _taskQueue.isNotEmpty) { |
There was a problem hiding this comment.
There was something wrong with my previous revision, you can check out my latest comment .
https://github.com/flutter/flutter/pull/82781/files#r671808835
I will revert the previous changes. And I think the problem is just that handleEventLoopCallback .
If locked is true, this method will return fasle at the first step and the event will not be executed

|
I think we're hitting an impasse here. I'm going to close this PR to get it off of review queues for now - we can discuss this further on Discord if you want. This PR in its current state cannot be merged. We cannot accept a test that relies on real timers running for a certain amount of time - it will not be clear if a timeout is due to a test failure or if infra is flaky, and the test will likely get ignored or disabled or deleted if it starts failing. It's also not clear to me that it's safe to remove the "locked" mechanism from the scheduler binding, which this PR wants to do. |
|
Again I appreciate the time you've put into this, and I'm sorry for the length of time it took to get a review. |
|
OK. Thanks again for your reply @dnfield ,I think the problem is pretty much fixed, but the test case does need to be reconsidered.I'll launch PR after I've perfected the test case,Thank you for your patient reply. ~ |



Related Issues
#82016
Description
This PR is for solving that tasks scheduled through 'SchedulerBinding.instance.scheduleTask' were not executed.
Before modification, if a task has a priority of idle, it will not be scheduled when the on-screen animation ends.
The problem is that once the 'handleEventLoopCallback()' returns false, the _TaskQueue will not be scheduled again until the next scheduleTask.
Therefore, when 'handleEventLoopCallback()' returns false, it is determined whether the queue is empty or not to continue scheduling.
To avoid frequent scheduling, wait until the next frame when 'handleEventLoopCallback() 'returns false (because I assume that by default the return value of 'handleEventLoopCallback()' does not change within the same frame).
Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.