branch: elpa/with-simulated-input
commit ddd7bd71b178435035e7339e785eb8f30ee11277
Author: Ryan C. Thompson <[email protected]>
Commit: Ryan C. Thompson <[email protected]>
Add "wsi-simulate-idle-time" and tests for it
Fixes #1.
---
tests/test-with-simulated-input.el | 44 ++++++++++++++++++++++++++++++++
with-simulated-input.el | 52 ++++++++++++++++++++++++++++++++++++++
2 files changed, 96 insertions(+)
diff --git a/tests/test-with-simulated-input.el
b/tests/test-with-simulated-input.el
index 6524e68a4a..273ccffc3d 100644
--- a/tests/test-with-simulated-input.el
+++ b/tests/test-with-simulated-input.el
@@ -90,4 +90,48 @@
(read-string "Enter a string: "))
:to-equal "hello world"))))
+(defun idle-canary ())
+(defvar timers-to-cancel nil)
+(defvar orig-timer--activate (symbol-function 'timer--activate))
+
+(describe "`wsi-simulate-idle-time'"
+ (spy-on 'idle-canary)
+ (spy-on 'timer--activate
+ :and-call-fake
+ (lambda (timer &rest args)
+ (push timer timers-to-cancel)
+ (apply orig-timer--activate timer args)))
+ (after-each
+ (mapcar #'cancel-timer timers-to-cancel)
+ (setq timers-to-cancel nil)
+ (spy-calls-reset 'idle-canary))
+ (it "should run idle timers"
+ (run-with-idle-timer 500 nil 'idle-canary)
+ (wsi-simulate-idle-time 501)
+ (expect 'idle-canary :to-have-been-called))
+ (it "should not run idle times with longer times"
+ (run-with-idle-timer 500 nil 'set 'idle-canary)
+ (wsi-simulate-idle-time 100)
+ (expect 'idle-canary :not :to-have-been-called))
+ (it "should run idle timers added by other idle timers"
+ (run-with-idle-timer
+ 100 nil 'run-with-idle-timer
+ 200 nil 'idle-canary)
+ (wsi-simulate-idle-time 500)
+ (expect 'idle-canary :to-have-been-called))
+ (it "should run idle timers added by other idle timers when the new timer is
in the past"
+ (run-with-idle-timer
+ 100 nil 'run-with-idle-timer
+ 50 nil 'idle-canary)
+ (wsi-simulate-idle-time 500)
+ (expect 'idle-canary :to-have-been-called))
+
+ (describe "used within `with-simulated-input'"
+ (it "should allow idle timers to trigger during simulated input"
+ (run-with-idle-timer 500 nil 'insert "world")
+ (expect
+ (with-simulated-input '("hello SPC" (wsi-simulate-idle-time 501) "RET")
+ (read-string "Enter a string: "))
+ :to-equal "hello world"))))
+
;;; test-with-simulated-input.el ends here
diff --git a/with-simulated-input.el b/with-simulated-input.el
index 83c352ff3e..ae3c07558b 100644
--- a/with-simulated-input.el
+++ b/with-simulated-input.el
@@ -208,6 +208,58 @@ in `progn'."
(error "Reached end of simulated input while evaluating body")
result)))
+(defvar wsi-simulated-idle-time nil)
+
+(defadvice current-idle-time (around simulat-idle-time activate)
+ "Return the faked value while simulating idle time.
+
+While executing `wsi-simulate-idle-time', this advice causes the
+simulated idle time to be returned instead of the real value."
+ (if wsi-simulated-idle-time
+ (setq ad-return-value
+ (when (> (float-time wsi-simulated-idle-time) 0)
+ (seconds-to-time wsi-simulated-idle-time)))
+ ad-do-it))
+
+(cl-defun wsi-simulate-idle-time (secs &optional actually-wait)
+ "Run all idle timers with delay less than SECS.
+
+This simulates resetting the idle time to zero and then being
+idle for SECS seconds. If ACTUALLY-WAIT is non-nil, this function
+will also wait for the specified amount of time before running
+each timers.
+
+While each timer is running, `current-idle-time' will be
+overridden to return the current simulated idle time."
+ (interactive
+ "nSeconds of idle time: \nP")
+ (cl-loop
+ with already-run-timers = nil
+ with stop-time = (float-time secs)
+ with wsi-simulated-idle-time = 0.0
+ ;; We have to search `timer-idle-list' from the beginning each time
+ ;; through the loop because each timer that runs might add more
+ ;; timers to the list, and picking up at the same list position
+ ;; would ignore those new timers.
+ for next-timer = (car (cl-member-if-not
+ (lambda (timer) (memq timer already-run-timers))
+ timer-idle-list))
+ while next-timer
+ for previous-idle-time = wsi-simulated-idle-time
+ maximize (float-time (timer--time next-timer))
+ into wsi-simulated-idle-time
+ when actually-wait
+ do (sleep-for (float-time (time-subtract wsi-simulated-idle-time
+ previous-idle-time)))
+ while (time-less-p wsi-simulated-idle-time stop-time)
+ when (not (timer--triggered next-timer))
+ do (timer-event-handler next-timer)
+ do (push next-timer already-run-timers)
+ finally do
+ (when actually-wait
+ (sleep-for (float-time (time-subtract stop-time
+ wsi-simulated-idle-time))))))
+
(provide 'with-simulated-input)
;;; with-simulated-input.el ends here