mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
Move kobo auto-suspension logic out of UIManager (#2933)
This commit is contained in:
@@ -47,7 +47,10 @@ function Dbg:turnOn()
|
||||
return unpack(values)
|
||||
end
|
||||
end
|
||||
Dbg.dassert = function(check, msg) assert(check, msg) end
|
||||
Dbg.dassert = function(check, msg)
|
||||
assert(check, msg)
|
||||
return check
|
||||
end
|
||||
|
||||
-- TODO: close ev.log fd for children
|
||||
-- create or clear ev log file
|
||||
@@ -60,7 +63,9 @@ function Dbg:turnOff()
|
||||
logger:setLevel(logger.levels.info)
|
||||
function Dbg_mt.__call() end
|
||||
function Dbg.guard() end
|
||||
function Dbg.dassert() end
|
||||
Dbg.dassert = function(check)
|
||||
return check
|
||||
end
|
||||
if self.ev_log then
|
||||
io.close(self.ev_log)
|
||||
self.ev_log = nil
|
||||
|
||||
@@ -59,4 +59,22 @@ function Device:init()
|
||||
Generic.init(self)
|
||||
end
|
||||
|
||||
function Device:simulateSuspend()
|
||||
local InfoMessage = require("ui/widget/infomessage")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local _ = require("gettext")
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = _("Suspend")
|
||||
})
|
||||
end
|
||||
|
||||
function Device:simulateResume()
|
||||
local InfoMessage = require("ui/widget/infomessage")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local _ = require("gettext")
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = _("Resume")
|
||||
})
|
||||
end
|
||||
|
||||
return Device
|
||||
|
||||
101
frontend/ui/hook_container.lua
Normal file
101
frontend/ui/hook_container.lua
Normal file
@@ -0,0 +1,101 @@
|
||||
--[[--
|
||||
HookContainer allows listeners to register and unregister a hook for speakers to execute.
|
||||
|
||||
It's an experimental feature: use with cautions, it can easily pin an object in memory and unblock
|
||||
GC from recycling the memory.
|
||||
]]
|
||||
|
||||
local HookContainer = {}
|
||||
|
||||
function HookContainer:new(o)
|
||||
o = o or {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end
|
||||
|
||||
function HookContainer:_assertIsValidName(name)
|
||||
assert(self ~= nil)
|
||||
assert(type(name) == "string")
|
||||
assert(string.len(name) > 0)
|
||||
end
|
||||
|
||||
function HookContainer:_assertIsValidFunction(func)
|
||||
assert(self ~= nil)
|
||||
assert(type(func) == "function" or type(func) == "table")
|
||||
end
|
||||
|
||||
function HookContainer:_assertIsValidFunctionOrNil(func)
|
||||
assert(self ~= nil)
|
||||
if func == nil then return end
|
||||
self:_assertIsValidFunction(func)
|
||||
end
|
||||
|
||||
--- Register a function to name. Must be called with self.
|
||||
-- @tparam string name The name of the hook. Can only be an non-empty string.
|
||||
-- @tparam function func The function to handle the hook. Can only be a function.
|
||||
function HookContainer:register(name, func)
|
||||
self:_assertIsValidName(name)
|
||||
self:_assertIsValidFunction(func)
|
||||
if self[name] == nil then
|
||||
self[name] = {}
|
||||
end
|
||||
table.insert(self[name], func)
|
||||
end
|
||||
|
||||
--- Register a widget to name. Must be called with self.
|
||||
-- @tparam string name The name of the hook. Can only be an non-empty string.
|
||||
-- @tparam table widget The widget to handle the hook. Can only be a table with required functions.
|
||||
function HookContainer:registerWidget(name, widget)
|
||||
self:_assertIsValidName(name)
|
||||
assert(type(widget) == "table")
|
||||
self:register(name, function(args)
|
||||
local f = widget["on" .. name]
|
||||
self:_assertIsValidFunction(f)
|
||||
f(widget, args)
|
||||
end)
|
||||
local original_close_widget = widget.onCloseWidget
|
||||
self:_assertIsValidFunctionOrNil(original_close_widget)
|
||||
widget.onCloseWidget = function()
|
||||
if original_close_widget then original_close_widget(widget) end
|
||||
self:unregister(name, widget["on" .. name])
|
||||
end
|
||||
end
|
||||
|
||||
--- Unregister a function from name. Must be called with self.
|
||||
-- @tparam string name The name of the hook. Can only be an non-empty string.
|
||||
-- @tparam function func The function to handle the hook. Can only be a function.
|
||||
-- @treturn boolean Return true if the function is found and removed, otherwise false.
|
||||
function HookContainer:unregister(name, func)
|
||||
self:_assertIsValidName(name)
|
||||
self:_assertIsValidFunction(func)
|
||||
if self[name] == nil then
|
||||
return false
|
||||
end
|
||||
|
||||
for i, f in ipairs(self[name]) do
|
||||
if f == func then
|
||||
table.remove(self[name], i)
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--- Execute all registered functions of name. Must be called with self.
|
||||
-- @tparam string name The name of the hook. Can only be an non-empty string.
|
||||
-- @param args Any kind of arguments sending to the functions.
|
||||
-- @treturn number The number of functions have been executed.
|
||||
function HookContainer:execute(name, args)
|
||||
self:_assertIsValidName(name)
|
||||
if self[name] == nil or #self[name] == 0 then
|
||||
return 0
|
||||
end
|
||||
|
||||
for _, f in ipairs(self[name]) do
|
||||
f(args)
|
||||
end
|
||||
return #self[name]
|
||||
end
|
||||
|
||||
return HookContainer
|
||||
@@ -8,11 +8,10 @@ local Geom = require("ui/geometry")
|
||||
local dbg = require("dbg")
|
||||
local logger = require("logger")
|
||||
local util = require("ffi/util")
|
||||
local _ = require("gettext")
|
||||
local Input = Device.input
|
||||
local Screen = Device.screen
|
||||
local _ = require("gettext")
|
||||
|
||||
local noop = function() end
|
||||
local MILLION = 1000000
|
||||
|
||||
-- there is only one instance of this
|
||||
@@ -34,6 +33,8 @@ local UIManager = {
|
||||
_refresh_func_stack = {},
|
||||
_entered_poweroff_stage = false,
|
||||
_exit_code = nil,
|
||||
|
||||
event_hook = require("ui/hook_container"):new()
|
||||
}
|
||||
|
||||
function UIManager:init()
|
||||
@@ -72,28 +73,12 @@ function UIManager:init()
|
||||
-- We do not want auto suspend procedure to waste battery during
|
||||
-- suspend. So let's unschedule it when suspending, and restart it after
|
||||
-- resume.
|
||||
self:_initAutoSuspend()
|
||||
self.event_handlers["Suspend"] = function()
|
||||
self:_beforeSuspend()
|
||||
if self._stopAutoSuspend then
|
||||
-- TODO(Hzj-jie): Why _stopAutoSuspend could be nil in test cases.
|
||||
--[[
|
||||
frontend/ui/uimanager.lua:62: attempt to call method _stopAutoSuspend (a nil value)
|
||||
|
||||
stack traceback:
|
||||
frontend/ui/uimanager.lua:62: in function Suspend
|
||||
frontend/ui/uimanager.lua:119: in function __default__
|
||||
frontend/ui/uimanager.lua:662: in function handleInput
|
||||
frontend/ui/uimanager.lua:707: in function run
|
||||
spec/front/unit/readerui_spec.lua:32: in function <spec/front/unit/readerui_spec.lua:28>
|
||||
--]]
|
||||
self:_stopAutoSuspend()
|
||||
end
|
||||
Device:onPowerEvent("Suspend")
|
||||
end
|
||||
self.event_handlers["Resume"] = function()
|
||||
Device:onPowerEvent("Resume")
|
||||
self:_startAutoSuspend()
|
||||
self:_afterResume()
|
||||
end
|
||||
self.event_handlers["PowerPress"] = function()
|
||||
@@ -167,6 +152,15 @@ function UIManager:init()
|
||||
Device:usbPlugOut()
|
||||
self:_afterNotCharging()
|
||||
end
|
||||
elseif Device:isSDL() then
|
||||
self.event_handlers["Suspend"] = function()
|
||||
self:_beforeSuspend()
|
||||
Device:simulateSuspend()
|
||||
end
|
||||
self.event_handlers["Resume"] = function()
|
||||
Device:simulateResume()
|
||||
self:_afterResume()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -714,7 +708,9 @@ function UIManager:handleInput()
|
||||
|
||||
-- delegate input_event to handler
|
||||
if input_event then
|
||||
self:_resetAutoSuspendTimer()
|
||||
if input_event.handler ~= "onInputError" then
|
||||
self.event_hook:execute("InputEvent", input_event)
|
||||
end
|
||||
local handler = self.event_handlers[input_event]
|
||||
if handler then
|
||||
handler(input_event)
|
||||
@@ -780,57 +776,6 @@ function UIManager:runForever()
|
||||
return self:run()
|
||||
end
|
||||
|
||||
-- Kobo does not have an auto suspend function, so we implement it ourselves.
|
||||
function UIManager:_initAutoSuspend()
|
||||
local function isAutoSuspendEnabled()
|
||||
return Device:isKobo() and self.auto_suspend_sec > 0
|
||||
end
|
||||
|
||||
local sec = G_reader_settings:readSetting("auto_suspend_timeout_seconds")
|
||||
if sec then
|
||||
self.auto_suspend_sec = sec
|
||||
else
|
||||
-- default setting is 60 minutes
|
||||
self.auto_suspend_sec = 60 * 60
|
||||
end
|
||||
|
||||
if isAutoSuspendEnabled() then
|
||||
self.auto_suspend_action = function()
|
||||
local now = os.time()
|
||||
-- Do not repeat auto suspend procedure after suspend.
|
||||
if self.last_action_sec + self.auto_suspend_sec <= now then
|
||||
self:suspend()
|
||||
else
|
||||
self:scheduleIn(
|
||||
self.last_action_sec + self.auto_suspend_sec - now,
|
||||
self.auto_suspend_action)
|
||||
end
|
||||
end
|
||||
|
||||
function UIManager:_startAutoSuspend()
|
||||
self.last_action_sec = os.time()
|
||||
self:nextTick(self.auto_suspend_action)
|
||||
end
|
||||
dbg:guard(UIManager, '_startAutoSuspend',
|
||||
function()
|
||||
assert(isAutoSuspendEnabled())
|
||||
end)
|
||||
|
||||
function UIManager:_stopAutoSuspend()
|
||||
self:unschedule(self.auto_suspend_action)
|
||||
end
|
||||
|
||||
function UIManager:_resetAutoSuspendTimer()
|
||||
self.last_action_sec = os.time()
|
||||
end
|
||||
|
||||
self:_startAutoSuspend()
|
||||
else
|
||||
self._startAutoSuspend = noop
|
||||
self._stopAutoSuspend = noop
|
||||
end
|
||||
end
|
||||
|
||||
-- The common operations should be performed before suspending the device. Ditto.
|
||||
function UIManager:_beforeSuspend()
|
||||
self:flushSettings()
|
||||
@@ -853,7 +798,7 @@ end
|
||||
-- Executes all the operations of a suspending request. This function usually puts the device into
|
||||
-- suspension.
|
||||
function UIManager:suspend()
|
||||
if Device:isKobo() then
|
||||
if Device:isKobo() or Device:isSDL() then
|
||||
self.event_handlers["Suspend"]()
|
||||
elseif Device:isKindle() then
|
||||
self.event_handlers["IntoSS"]()
|
||||
@@ -862,7 +807,7 @@ end
|
||||
|
||||
-- Executes all the operations of a resume request. This function usually wakes up the device.
|
||||
function UIManager:resume()
|
||||
if Device:isKobo() then
|
||||
if Device:isKobo() or Device:isSDL() then
|
||||
self.event_handlers["Resume"]()
|
||||
elseif Device:isKindle() then
|
||||
self.event_handlers["OutOfSS"]()
|
||||
@@ -879,7 +824,5 @@ function UIManager:restartKOReader()
|
||||
self._exit_code = 85
|
||||
end
|
||||
|
||||
UIManager._resetAutoSuspendTimer = noop
|
||||
|
||||
UIManager:init()
|
||||
return UIManager
|
||||
|
||||
121
plugins/autosuspend.koplugin/main.lua
Normal file
121
plugins/autosuspend.koplugin/main.lua
Normal file
@@ -0,0 +1,121 @@
|
||||
local Device = require("device")
|
||||
|
||||
if not Device:isKobo() and not Device:isSDL() then return { disabled = true, } end
|
||||
|
||||
local DataStorage = require("datastorage")
|
||||
local LuaSettings = require("luasettings")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
||||
local logger = require("logger")
|
||||
local _ = require("gettext")
|
||||
|
||||
local AutoSuspend = {
|
||||
settings = LuaSettings:open(DataStorage:getSettingsDir() .. "/koboautosuspend.lua"),
|
||||
settings_id = 0,
|
||||
last_action_sec = os.time(),
|
||||
}
|
||||
|
||||
function AutoSuspend:_readTimeoutSecFrom(settings)
|
||||
local sec = settings:readSetting("auto_suspend_timeout_seconds")
|
||||
if type(sec) == "number" then
|
||||
return sec
|
||||
end
|
||||
return -1
|
||||
end
|
||||
|
||||
function AutoSuspend:_readTimeoutSec()
|
||||
local candidates = { self.settings, G_reader_settings }
|
||||
for _, candidate in ipairs(candidates) do
|
||||
local sec = self:_readTimeoutSecFrom(candidate)
|
||||
if sec ~= -1 then
|
||||
return sec
|
||||
end
|
||||
end
|
||||
|
||||
-- default setting is 60 minutes
|
||||
return 60 * 60
|
||||
end
|
||||
|
||||
function AutoSuspend:_enabled()
|
||||
return self.auto_suspend_sec > 0
|
||||
end
|
||||
|
||||
function AutoSuspend:_schedule(settings_id)
|
||||
if not self:_enabled() then
|
||||
logger.dbg("AutoSuspend:_schedule is disabled")
|
||||
return
|
||||
end
|
||||
if self.settings_id ~= settings_id then
|
||||
logger.dbg("AutoSuspend:_schedule registered settings_id ",
|
||||
settings_id,
|
||||
" does not equal to current one ",
|
||||
self.settings_id)
|
||||
return
|
||||
end
|
||||
|
||||
local delay = self.last_action_sec + self.auto_suspend_sec - os.time()
|
||||
if delay <= 0 then
|
||||
logger.dbg("AutoSuspend: will suspend the device")
|
||||
UIManager:suspend()
|
||||
else
|
||||
logger.dbg("AutoSuspend: schedule at ", os.time() + delay)
|
||||
UIManager:scheduleIn(delay, function() self:_schedule(settings_id) end)
|
||||
end
|
||||
end
|
||||
|
||||
function AutoSuspend:_deprecateLastTask()
|
||||
self.settings_id = self.settings_id + 1
|
||||
logger.dbg("AutoSuspend: deprecateLastTask ", self.settings_id)
|
||||
end
|
||||
|
||||
function AutoSuspend:_start()
|
||||
if self:_enabled() then
|
||||
logger.dbg("AutoSuspend: start at ", os.time())
|
||||
self.last_action_sec = os.time()
|
||||
self:_schedule(self.settings_id)
|
||||
end
|
||||
end
|
||||
|
||||
function AutoSuspend:init()
|
||||
UIManager.event_hook:registerWidget("InputEvent", self)
|
||||
self.auto_suspend_sec = self:_readTimeoutSec()
|
||||
self:_deprecateLastTask()
|
||||
self:_start()
|
||||
end
|
||||
|
||||
function AutoSuspend:onInputEvent()
|
||||
logger.dbg("AutoSuspend: onInputEvent")
|
||||
self.last_action_sec = os.time()
|
||||
end
|
||||
|
||||
-- We do not want auto suspend procedure to waste battery during suspend. So let's unschedule it
|
||||
-- when suspending and restart it after resume.
|
||||
function AutoSuspend:onSuspend()
|
||||
logger.dbg("AutoSuspend: onSuspend")
|
||||
self:_deprecateLastTask()
|
||||
end
|
||||
|
||||
function AutoSuspend:onResume()
|
||||
logger.dbg("AutoSuspend: onResume")
|
||||
self:_start()
|
||||
end
|
||||
|
||||
AutoSuspend:init()
|
||||
|
||||
local AutoSuspendWidget = WidgetContainer:new{
|
||||
name = "AutoSuspend",
|
||||
}
|
||||
|
||||
function AutoSuspendWidget:onInputEvent()
|
||||
AutoSuspend:onInputEvent()
|
||||
end
|
||||
|
||||
function AutoSuspendWidget:onSuspend()
|
||||
AutoSuspend:onSuspend()
|
||||
end
|
||||
|
||||
function AutoSuspendWidget:onResume()
|
||||
AutoSuspend:onResume()
|
||||
end
|
||||
|
||||
return AutoSuspendWidget
|
||||
74
spec/unit/autosuspend_spec.lua
Normal file
74
spec/unit/autosuspend_spec.lua
Normal file
@@ -0,0 +1,74 @@
|
||||
describe("AutoSuspend widget tests", function()
|
||||
setup(function()
|
||||
require("commonrequire")
|
||||
package.unloadAll()
|
||||
end)
|
||||
|
||||
before_each(function()
|
||||
local Device = require("device")
|
||||
stub(Device, "isKobo")
|
||||
Device.isKobo.returns(true)
|
||||
Device.input.waitEvent = function() end
|
||||
local UIManager = require("ui/uimanager")
|
||||
stub(UIManager, "suspend")
|
||||
UIManager._run_forever = true
|
||||
G_reader_settings:saveSetting("auto_suspend_timeout_seconds", 10)
|
||||
require("mock_time"):install()
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
require("device").isKobo:revert()
|
||||
require("ui/uimanager").suspend:revert()
|
||||
G_reader_settings:delSetting("auto_suspend_timeout_seconds")
|
||||
require("mock_time"):uninstall()
|
||||
end)
|
||||
|
||||
it("should be able to execute suspend when timing out", function()
|
||||
local mock_time = require("mock_time")
|
||||
local widget_class = dofile("plugins/autosuspend.koplugin/main.lua")
|
||||
local widget = widget_class:new()
|
||||
local UIManager = require("ui/uimanager")
|
||||
mock_time:increase(5)
|
||||
UIManager:handleInput()
|
||||
assert.stub(UIManager.suspend).was.called(0)
|
||||
mock_time:increase(6)
|
||||
UIManager:handleInput()
|
||||
assert.stub(UIManager.suspend).was.called(1)
|
||||
mock_time:uninstall()
|
||||
end)
|
||||
|
||||
it("should be able to initialize several times", function()
|
||||
local mock_time = require("mock_time")
|
||||
-- AutoSuspend plugin set the last_action_sec each time it is initialized.
|
||||
local widget_class = dofile("plugins/autosuspend.koplugin/main.lua")
|
||||
local widget1 = widget_class:new()
|
||||
-- So if one more initialization happens, it won't sleep after another 5 seconds.
|
||||
mock_time:increase(5)
|
||||
local widget2 = widget_class:new()
|
||||
local UIManager = require("ui/uimanager")
|
||||
mock_time:increase(6)
|
||||
UIManager:handleInput()
|
||||
assert.stub(UIManager.suspend).was.called(1)
|
||||
mock_time:uninstall()
|
||||
end)
|
||||
|
||||
it("should be able to deprecate last task", function()
|
||||
local mock_time = require("mock_time")
|
||||
local widget_class = dofile("plugins/autosuspend.koplugin/main.lua")
|
||||
local widget = widget_class:new()
|
||||
mock_time:increase(5)
|
||||
local UIManager = require("ui/uimanager")
|
||||
UIManager:handleInput()
|
||||
assert.stub(UIManager.suspend).was.called(0)
|
||||
widget:onInputEvent()
|
||||
widget:onSuspend()
|
||||
widget:onResume()
|
||||
mock_time:increase(6)
|
||||
UIManager:handleInput()
|
||||
assert.stub(UIManager.suspend).was.called(0)
|
||||
mock_time:increase(5)
|
||||
UIManager:handleInput()
|
||||
assert.stub(UIManager.suspend).was.called(1)
|
||||
mock_time:uninstall()
|
||||
end)
|
||||
end)
|
||||
@@ -18,12 +18,11 @@ describe("device module", function()
|
||||
end
|
||||
}
|
||||
require("commonrequire")
|
||||
package.unloadAll()
|
||||
end)
|
||||
|
||||
before_each(function()
|
||||
package.loaded['ffi/framebuffer_mxcfb'] = mock_fb
|
||||
package.loaded['device/kindle/device'] = nil
|
||||
package.loaded['device/kobo/device'] = nil
|
||||
mock_input = require('device/input')
|
||||
stub(mock_input, "open")
|
||||
stub(os, "getenv")
|
||||
@@ -171,7 +170,6 @@ describe("device module", function()
|
||||
stub(Device, "isKobo")
|
||||
|
||||
Device.isKobo.returns(true)
|
||||
local saved_noop = UIManager._resetAutoSuspendTimer
|
||||
UIManager:init()
|
||||
|
||||
ReaderUI:doShowReader(sample_pdf)
|
||||
@@ -185,9 +183,6 @@ describe("device module", function()
|
||||
Device.powerd.beforeSuspend:revert()
|
||||
Device.isKobo:revert()
|
||||
readerui.onFlushSettings:revert()
|
||||
UIManager._startAutoSuspend = nil
|
||||
UIManager._stopAutoSuspend = nil
|
||||
UIManager._resetAutoSuspendTimer = saved_noop
|
||||
readerui:onClose()
|
||||
end)
|
||||
end)
|
||||
@@ -251,6 +246,7 @@ describe("device module", function()
|
||||
end)
|
||||
|
||||
it("oasis should interpret orientation event", function()
|
||||
package.unload('device/kindle/device')
|
||||
io.open = function(filename, mode)
|
||||
if filename == "/proc/usid" then
|
||||
return {
|
||||
|
||||
@@ -2,11 +2,12 @@ describe("FileManager module", function()
|
||||
local FileManager, lfs, docsettings, UIManager, Screen, util
|
||||
setup(function()
|
||||
require("commonrequire")
|
||||
package.unloadAll()
|
||||
FileManager = require("apps/filemanager/filemanager")
|
||||
lfs = require("libs/libkoreader-lfs")
|
||||
docsettings = require("docsettings")
|
||||
UIManager = require("ui/uimanager")
|
||||
Screen = require("device").screen
|
||||
UIManager = require("ui/uimanager")
|
||||
docsettings = require("docsettings")
|
||||
lfs = require("libs/libkoreader-lfs")
|
||||
util = require("ffi/util")
|
||||
end)
|
||||
it("should show file manager", function()
|
||||
|
||||
72
spec/unit/hook_container_spec.lua
Normal file
72
spec/unit/hook_container_spec.lua
Normal file
@@ -0,0 +1,72 @@
|
||||
describe("HookContainer tests", function()
|
||||
setup(function()
|
||||
require("commonrequire")
|
||||
end)
|
||||
|
||||
it("should register and unregister functions", function()
|
||||
local HookContainer = require("ui/hook_container"):new()
|
||||
local f1 = spy.new(function() end)
|
||||
local f2 = spy.new(function() end)
|
||||
local f3 = spy.new(function() end)
|
||||
HookContainer:register("a", f1)
|
||||
HookContainer:register("a", f2)
|
||||
HookContainer:register("b", f3)
|
||||
assert.are.equal(HookContainer:execute("a", 100), 2)
|
||||
assert.are.equal(HookContainer:execute("b", 200), 1)
|
||||
assert.spy(f1).was_called(1)
|
||||
assert.spy(f1).was_called_with(100)
|
||||
assert.spy(f2).was_called(1)
|
||||
assert.spy(f2).was_called_with(100)
|
||||
assert.spy(f3).was_called(1)
|
||||
assert.spy(f3).was_called_with(200)
|
||||
|
||||
assert.is.truthy(HookContainer:unregister("a", f1))
|
||||
assert.is.falsy(HookContainer:unregister("b", f2))
|
||||
|
||||
assert.are.equal(HookContainer:execute("a", 300), 1)
|
||||
assert.are.equal(HookContainer:execute("b", 400), 1)
|
||||
assert.spy(f1).was_called(1)
|
||||
assert.spy(f1).was_called_with(100)
|
||||
assert.spy(f2).was_called(2)
|
||||
assert.spy(f2).was_called_with(300)
|
||||
assert.spy(f3).was_called(2)
|
||||
assert.spy(f3).was_called_with(400)
|
||||
end)
|
||||
|
||||
it("should register and automatically unregister widget", function()
|
||||
local HookContainer = require("ui/hook_container"):new()
|
||||
local widget = require("ui/widget/widget"):new()
|
||||
widget.onEvent = spy.new(function() end)
|
||||
local close_widget = spy.new(function() end)
|
||||
widget.onCloseWidget = close_widget
|
||||
HookContainer:registerWidget("Event", widget)
|
||||
assert.are.equal(HookContainer:execute("Event", { a = 100, b = 200 }), 1)
|
||||
assert.spy(widget.onEvent).was_called(1)
|
||||
assert.spy(widget.onEvent).was_called_with(widget, { a = 100, b = 200 })
|
||||
|
||||
widget:onCloseWidget()
|
||||
assert.spy(close_widget).was_called(1)
|
||||
assert.spy(close_widget).was_called_with(widget)
|
||||
end)
|
||||
|
||||
it("should pass widget itself", function()
|
||||
local HookContainer = require("ui/hook_container"):new()
|
||||
local widget = require("ui/widget/widget"):new()
|
||||
local onEvent_called = false
|
||||
local onCloseWidget_called = false
|
||||
function widget:onEvent(args)
|
||||
assert.is.truthy(self ~= nil)
|
||||
assert.are.same(args, { c = 300, d = 400 })
|
||||
onEvent_called = true
|
||||
end
|
||||
function widget:onCloseWidget()
|
||||
assert.is.truthy(self ~= nil)
|
||||
onCloseWidget_called = true
|
||||
end
|
||||
HookContainer:registerWidget("Event", widget)
|
||||
assert.are.equal(HookContainer:execute("Event", { c = 300, d = 400 }), 1)
|
||||
widget:onCloseWidget()
|
||||
assert.is.truthy(onEvent_called)
|
||||
assert.is.truthy(onCloseWidget_called)
|
||||
end)
|
||||
end)
|
||||
@@ -1,3 +1,5 @@
|
||||
local logger = require("logger")
|
||||
|
||||
local MockTime = {
|
||||
original_os_time = os.time,
|
||||
original_util_time = nil,
|
||||
@@ -11,8 +13,14 @@ function MockTime:install()
|
||||
self.original_util_time = util.gettime
|
||||
assert(self.original_util_time ~= nil)
|
||||
end
|
||||
os.time = function() return self.value end
|
||||
util.gettime = function() return self.value, 0 end
|
||||
os.time = function()
|
||||
logger.dbg("MockTime:os.time: ", self.value)
|
||||
return self.value
|
||||
end
|
||||
util.gettime = function()
|
||||
logger.dbg("MockTime:util.gettime: ", self.value)
|
||||
return self.value, 0
|
||||
end
|
||||
end
|
||||
|
||||
function MockTime:uninstall()
|
||||
@@ -30,6 +38,7 @@ function MockTime:set(value)
|
||||
return false
|
||||
end
|
||||
self.value = math.floor(value)
|
||||
logger.dbg("MockTime:set ", self.value)
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -39,6 +48,7 @@ function MockTime:increase(value)
|
||||
return false
|
||||
end
|
||||
self.value = math.floor(self.value + value)
|
||||
logger.dbg("MockTime:increase ", self.value)
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
52
spec/unit/mock_time_spec.lua
Normal file
52
spec/unit/mock_time_spec.lua
Normal file
@@ -0,0 +1,52 @@
|
||||
describe("MockTime tests", function()
|
||||
teardown(function()
|
||||
require("mock_time"):uninstall()
|
||||
end)
|
||||
|
||||
it("should be able to install and uninstall", function()
|
||||
local mock_time = require("mock_time")
|
||||
local util = require("ffi/util")
|
||||
local current_time = os.time()
|
||||
local current_highres_sec = util.gettime()
|
||||
mock_time:install()
|
||||
assert.is.truthy(mock_time:set(10))
|
||||
assert.are.equal(os.time(), 10)
|
||||
local sec, usec = util.gettime()
|
||||
assert.are.equal(sec, 10)
|
||||
assert.are.equal(usec, 0)
|
||||
mock_time:uninstall()
|
||||
assert.is.truthy(os.time() >= current_time)
|
||||
assert.is.truthy(util.gettime() >= current_highres_sec)
|
||||
end)
|
||||
|
||||
it("should be able to install several times", function()
|
||||
local mock_time = require("mock_time")
|
||||
local util = require("ffi/util")
|
||||
local current_time = os.time()
|
||||
local current_highres_sec = util.gettime()
|
||||
mock_time:install()
|
||||
mock_time:install()
|
||||
mock_time:uninstall()
|
||||
assert.is.truthy(os.time() >= current_time)
|
||||
assert.is.truthy(util.gettime() >= current_highres_sec)
|
||||
end)
|
||||
|
||||
it("should reject invalid value", function()
|
||||
local mock_time = require("mock_time")
|
||||
assert.is.falsy(mock_time:set("100"))
|
||||
assert.is.falsy(mock_time:set(true))
|
||||
assert.is.falsy(mock_time:set(nil))
|
||||
assert.is.falsy(mock_time:set(function() end))
|
||||
end)
|
||||
|
||||
it("should increase time", function()
|
||||
local mock_time = require("mock_time")
|
||||
local current_time = os.time()
|
||||
mock_time:install()
|
||||
assert.is.truthy(mock_time:set(10.1))
|
||||
assert.are.equal(os.time(), 10)
|
||||
mock_time:increase(1)
|
||||
assert.are.equal(os.time(), 11)
|
||||
mock_time:uninstall()
|
||||
end)
|
||||
end)
|
||||
@@ -4,13 +4,13 @@ describe("Readerfooter module", function()
|
||||
|
||||
setup(function()
|
||||
require("commonrequire")
|
||||
DocumentRegistry = require("document/documentregistry")
|
||||
ReaderUI = require("apps/reader/readerui")
|
||||
ReaderUI = require("apps/reader/readerui")
|
||||
DocSettings = require("docsettings")
|
||||
UIManager = require("ui/uimanager")
|
||||
MenuSorter = require("ui/menusorter")
|
||||
package.unloadAll()
|
||||
DEBUG = require("dbg")
|
||||
DocumentRegistry = require("document/documentregistry")
|
||||
DocSettings = require("docsettings")
|
||||
MenuSorter = require("ui/menusorter")
|
||||
ReaderUI = require("apps/reader/readerui")
|
||||
UIManager = require("ui/uimanager")
|
||||
purgeDir = require("ffi/util").purgeDir
|
||||
Screen = require("device").screen
|
||||
|
||||
|
||||
@@ -2,13 +2,14 @@ describe("Readerhighlight module", function()
|
||||
local DocumentRegistry, ReaderUI, UIManager, Screen, Geom, dbg, Event
|
||||
setup(function()
|
||||
require("commonrequire")
|
||||
package.unloadAll()
|
||||
DocumentRegistry = require("document/documentregistry")
|
||||
ReaderUI = require("apps/reader/readerui")
|
||||
UIManager = require("ui/uimanager")
|
||||
Screen = require("device").screen
|
||||
Geom = require("ui/geometry")
|
||||
dbg = require("dbg")
|
||||
Event = require("ui/event")
|
||||
Geom = require("ui/geometry")
|
||||
ReaderUI = require("apps/reader/readerui")
|
||||
Screen = require("device").screen
|
||||
UIManager = require("ui/uimanager")
|
||||
dbg = require("dbg")
|
||||
end)
|
||||
|
||||
local function highlight_single_word(readerui, pos0)
|
||||
|
||||
@@ -3,6 +3,7 @@ describe("ReaderLink module", function()
|
||||
|
||||
setup(function()
|
||||
require("commonrequire")
|
||||
package.unloadAll()
|
||||
DocumentRegistry = require("document/documentregistry")
|
||||
Event = require("ui/event")
|
||||
ReaderUI = require("apps/reader/readerui")
|
||||
|
||||
@@ -3,6 +3,7 @@ describe("Readerview module", function()
|
||||
|
||||
setup(function()
|
||||
require("commonrequire")
|
||||
package.unloadAll()
|
||||
DocumentRegistry = require("document/documentregistry")
|
||||
Blitbuffer = require("ffi/blitbuffer")
|
||||
ReaderUI = require("apps/reader/readerui")
|
||||
|
||||
@@ -164,35 +164,6 @@ describe("UIManager spec", function()
|
||||
assert.is_true(UIManager._task_queue_dirty)
|
||||
end)
|
||||
|
||||
it("should setup auto suspend on kobo", function()
|
||||
local old_reset_timer = UIManager._resetAutoSuspendTimer
|
||||
local noop = old_reset_timer
|
||||
assert.falsy(UIManager._startAutoSuspend)
|
||||
assert.falsy(UIManager._stopAutoSuspend)
|
||||
assert.truthy(old_reset_timer)
|
||||
G_reader_settings:saveSetting("auto_suspend_timeout_seconds", 3600)
|
||||
|
||||
UIManager:run()
|
||||
UIManager:quit()
|
||||
-- should skip on non-kobo devices
|
||||
UIManager:_initAutoSuspend()
|
||||
assert.is.same(noop, UIManager._startAutoSuspend)
|
||||
assert.is.same(noop, UIManager._stopAutoSuspend)
|
||||
assert.truthy(old_reset_timer)
|
||||
assert.is.same(#UIManager._task_queue, 0)
|
||||
-- now test kobo devices
|
||||
local old_is_kobo = Device.isKobo
|
||||
Device.isKobo = function() return true end
|
||||
UIManager:_initAutoSuspend()
|
||||
assert.truthy(UIManager._startAutoSuspend)
|
||||
assert.truthy(UIManager._stopAutoSuspend)
|
||||
assert.is_not.same(UIManager._resetAutoSuspendTimer, old_reset_timer)
|
||||
assert.is.same(#UIManager._task_queue, 1)
|
||||
assert.is.same(UIManager._task_queue[1].action,
|
||||
UIManager.auto_suspend_action)
|
||||
Device.isKobo = old_is_kobo
|
||||
end)
|
||||
|
||||
it("should check active widgets in order", function()
|
||||
local call_signals = {false, false, false}
|
||||
UIManager._window_stack = {
|
||||
|
||||
Reference in New Issue
Block a user