mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
@@ -14,14 +14,14 @@ before_install:
|
||||
|
||||
install:
|
||||
- sudo apt-get install libsdl1.2-dev luarocks nasm
|
||||
- git clone https://github.com/Olivine-Labs/busted/
|
||||
- cd busted && git checkout v1.10.0 && sudo luarocks make && cd ..
|
||||
- sudo luarocks install busted
|
||||
- sudo luarocks install luacov
|
||||
- sudo luarocks install luacov-coveralls --server=http://rocks.moonscript.org/dev
|
||||
|
||||
script:
|
||||
- make fetchthirdparty all
|
||||
- sudo cp base/build/*/luajit /usr/bin/
|
||||
- sudo ln -sf /usr/bin/luajit /usr/bin/lua
|
||||
- make testfront
|
||||
|
||||
after_success:
|
||||
|
||||
4
Makefile
4
Makefile
@@ -93,14 +93,14 @@ $(INSTALL_DIR)/koreader/.luacov:
|
||||
ln -sf ../../.luacov $(INSTALL_DIR)/koreader
|
||||
|
||||
testfront: $(INSTALL_DIR)/koreader/.busted
|
||||
cd $(INSTALL_DIR)/koreader && busted -l ./luajit
|
||||
cd $(INSTALL_DIR)/koreader && busted
|
||||
|
||||
test:
|
||||
$(MAKE) -C $(KOR_BASE) test
|
||||
$(MAKE) testfront
|
||||
|
||||
coverage: $(INSTALL_DIR)/koreader/.luacov
|
||||
cd $(INSTALL_DIR)/koreader && busted -c -l ./luajit --exclude-tags=nocov
|
||||
cd $(INSTALL_DIR)/koreader && busted -c --exclude-tags=nocov
|
||||
# coverage report summary
|
||||
cd $(INSTALL_DIR)/koreader && tail -n \
|
||||
+$$(($$(grep -nm1 Summary luacov.report.out|cut -d: -f1)-1)) \
|
||||
|
||||
@@ -253,12 +253,12 @@ http://ccache.samba.org
|
||||
|
||||
[base-readme]:https://github.com/koreader/koreader-base/blob/master/README.md
|
||||
[nb-script]:https://github.com/koreader/koreader-misc/blob/master/koreader-nightlybuild/koreader-nightlybuild.sh
|
||||
[travis-badge]:https://travis-ci.org/koreader/koreader.png?branch=master
|
||||
[travis-badge]:https://travis-ci.org/koreader/koreader.svg?branch=master
|
||||
[travis-link]:https://travis-ci.org/koreader/koreader
|
||||
[travis-conf]:https://github.com/koreader/koreader-base/blob/master/.travis.yml
|
||||
[linux-vm]:http://www.howtogeek.com/howto/11287/how-to-run-ubuntu-in-windows-7-with-vmware-player/
|
||||
[l10n-readme]:https://github.com/koreader/koreader/blob/master/l10n/README.md
|
||||
[koreader-transifex]:https://www.transifex.com/projects/p/koreader/
|
||||
[coverage-badge]:https://coveralls.io/repos/koreader/koreader/badge.png
|
||||
[coverage-badge]:https://coveralls.io/repos/koreader/koreader/badge.svg
|
||||
[coverage-link]:https://coveralls.io/r/koreader/koreader
|
||||
[licence-badge]:http://img.shields.io/badge/licence-AGPL-brightgreen.svg
|
||||
|
||||
2
base
2
base
Submodule base updated: 9aa76dc818...7025830053
52
frontend/httpclient.lua
Normal file
52
frontend/httpclient.lua
Normal file
@@ -0,0 +1,52 @@
|
||||
local UIManager = require("ui/uimanager")
|
||||
local DEBUG = require("dbg")
|
||||
|
||||
local HTTPClient = {
|
||||
headers = {},
|
||||
input_timeouts = 0,
|
||||
INPUT_TIMEOUT = 100*1000,
|
||||
}
|
||||
|
||||
function HTTPClient:new()
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end
|
||||
|
||||
function HTTPClient:addHeader(header, value)
|
||||
self.headers[header] = value
|
||||
end
|
||||
|
||||
function HTTPClient:removeHeader(header)
|
||||
self.headers[header] = nil
|
||||
end
|
||||
|
||||
function HTTPClient:request(request, response_callback, error_callback)
|
||||
request.on_headers = function(headers)
|
||||
for header, value in pairs(self.headers) do
|
||||
headers[header] = value
|
||||
end
|
||||
end
|
||||
request.connect_timeout = 10
|
||||
request.request_timeout = 20
|
||||
UIManager:initLooper()
|
||||
UIManager:handleTask(function()
|
||||
-- avoid endless waiting for input
|
||||
UIManager.INPUT_TIMEOUT = self.INPUT_TIMEOUT
|
||||
self.input_timeouts = self.input_timeouts + 1
|
||||
local turbo = require("turbo")
|
||||
local res = coroutine.yield(
|
||||
turbo.async.HTTPClient():fetch(request.url, request))
|
||||
-- reset INPUT_TIMEOUT to nil when all HTTP requests are fullfilled.
|
||||
self.input_timeouts = self.input_timeouts - 1
|
||||
UIManager.INPUT_TIMEOUT = self.input_timeouts > 0 and self.INPUT_TIMEOUT or nil
|
||||
if res.error and error_callback then
|
||||
error_callback(res)
|
||||
elseif response_callback then
|
||||
response_callback(res)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
return HTTPClient
|
||||
@@ -6,6 +6,7 @@ local Geom = require("ui/geometry")
|
||||
local util = require("ffi/util")
|
||||
local DEBUG = require("dbg")
|
||||
local _ = require("gettext")
|
||||
local ffi = require("ffi")
|
||||
|
||||
-- there is only one instance of this
|
||||
local UIManager = {
|
||||
@@ -246,6 +247,7 @@ end
|
||||
function UIManager:quit()
|
||||
DEBUG("quit uimanager")
|
||||
self._running = false
|
||||
self._run_forever = nil
|
||||
for i = #self._window_stack, 1, -1 do
|
||||
table.remove(self._window_stack, i)
|
||||
end
|
||||
@@ -256,6 +258,10 @@ function UIManager:quit()
|
||||
self._zeromqs[i]:stop()
|
||||
table.remove(self._zeromqs, i)
|
||||
end
|
||||
if self.looper then
|
||||
self.looper:close()
|
||||
self.looper = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- transmit an event to registered widgets
|
||||
@@ -430,75 +436,112 @@ function UIManager:_repaint()
|
||||
self.refresh_counted = false
|
||||
end
|
||||
|
||||
function UIManager:handleInput()
|
||||
local wait_until, now
|
||||
-- run this in a loop, so that paints can trigger events
|
||||
-- that will be honored when calculating the time to wait
|
||||
-- for input events:
|
||||
repeat
|
||||
wait_until, now = self:_checkTasks()
|
||||
|
||||
--DEBUG("---------------------------------------------------")
|
||||
--DEBUG("exec stack", self._execution_stack)
|
||||
--DEBUG("window stack", self._window_stack)
|
||||
--DEBUG("dirty stack", self._dirty)
|
||||
--DEBUG("---------------------------------------------------")
|
||||
|
||||
-- stop when we have no window to show
|
||||
if #self._window_stack == 0 and not self._run_forever then
|
||||
DEBUG("no dialog left to show")
|
||||
self:quit()
|
||||
return nil
|
||||
end
|
||||
|
||||
self:_repaint()
|
||||
until not self._execution_stack_dirty
|
||||
|
||||
-- wait for next event
|
||||
-- note that we will skip that if we have tasks that are ready to run
|
||||
local input_event = nil
|
||||
if not wait_until then
|
||||
if #self._zeromqs > 0 then
|
||||
-- pending message queue, wait 100ms for input
|
||||
input_event = Input:waitEvent(1000*100)
|
||||
if not input_event or input_event.handler == "onInputError" then
|
||||
for _, zeromq in ipairs(self._zeromqs) do
|
||||
input_event = zeromq:waitEvent()
|
||||
if input_event then break end
|
||||
end
|
||||
end
|
||||
else
|
||||
-- no pending task, wait without timeout
|
||||
input_event = Input:waitEvent(self.INPUT_TIMEOUT)
|
||||
end
|
||||
elseif wait_until[1] > now[1]
|
||||
or wait_until[1] == now[1] and wait_until[2] > now[2] then
|
||||
local wait_for = { s = wait_until[1] - now[1], us = wait_until[2] - now[2] }
|
||||
if wait_for.us < 0 then
|
||||
wait_for.s = wait_for.s - 1
|
||||
wait_for.us = 1000000 + wait_for.us
|
||||
end
|
||||
-- wait until next task is pending
|
||||
input_event = Input:waitEvent(wait_for.us, wait_for.s)
|
||||
end
|
||||
|
||||
-- delegate input_event to handler
|
||||
if input_event then
|
||||
local handler = self.event_handlers[input_event]
|
||||
if handler then
|
||||
handler(input_event)
|
||||
else
|
||||
self.event_handlers["__default__"](input_event)
|
||||
end
|
||||
end
|
||||
|
||||
-- handle next input
|
||||
self:handleTask(function() self:handleInput() end)
|
||||
end
|
||||
|
||||
-- handle task(callback function) in Turbo I/O looper
|
||||
-- or run task immediately if looper is not available
|
||||
function UIManager:handleTask(task)
|
||||
if self.looper then
|
||||
DEBUG("handle task in turbo I/O looper")
|
||||
self.looper:add_callback(task)
|
||||
else
|
||||
DEBUG("run task")
|
||||
task()
|
||||
end
|
||||
end
|
||||
|
||||
function UIManager:initLooper()
|
||||
if not self.looper then
|
||||
TURBO_SSL = true
|
||||
local turbo = require("turbo")
|
||||
self.looper = turbo.ioloop.instance()
|
||||
end
|
||||
end
|
||||
|
||||
-- this is the main loop of the UI controller
|
||||
-- it is intended to manage input events and delegate
|
||||
-- them to dialogs
|
||||
function UIManager:run()
|
||||
self._running = true
|
||||
while self._running do
|
||||
local wait_until, now
|
||||
-- run this in a loop, so that paints can trigger events
|
||||
-- that will be honored when calculating the time to wait
|
||||
-- for input events:
|
||||
repeat
|
||||
wait_until, now = self:_checkTasks()
|
||||
|
||||
--DEBUG("---------------------------------------------------")
|
||||
--DEBUG("exec stack", self._execution_stack)
|
||||
--DEBUG("window stack", self._window_stack)
|
||||
--DEBUG("dirty stack", self._dirty)
|
||||
--DEBUG("---------------------------------------------------")
|
||||
|
||||
-- stop when we have no window to show
|
||||
if #self._window_stack == 0 then
|
||||
DEBUG("no dialog left to show")
|
||||
self:quit()
|
||||
return nil
|
||||
end
|
||||
|
||||
self:_repaint()
|
||||
until not self._execution_stack_dirty
|
||||
|
||||
-- wait for next event
|
||||
-- note that we will skip that if we have tasks that are ready to run
|
||||
local input_event = nil
|
||||
if not wait_until then
|
||||
if #self._zeromqs > 0 then
|
||||
-- pending message queue, wait 100ms for input
|
||||
input_event = Input:waitEvent(1000*100)
|
||||
if not input_event or input_event.handler == "onInputError" then
|
||||
for _, zeromq in ipairs(self._zeromqs) do
|
||||
input_event = zeromq:waitEvent()
|
||||
if input_event then break end
|
||||
end
|
||||
end
|
||||
else
|
||||
-- no pending task, wait without timeout
|
||||
input_event = Input:waitEvent()
|
||||
end
|
||||
elseif wait_until[1] > now[1]
|
||||
or wait_until[1] == now[1] and wait_until[2] > now[2] then
|
||||
local wait_for = { s = wait_until[1] - now[1], us = wait_until[2] - now[2] }
|
||||
if wait_for.us < 0 then
|
||||
wait_for.s = wait_for.s - 1
|
||||
wait_for.us = 1000000 + wait_for.us
|
||||
end
|
||||
-- wait until next task is pending
|
||||
input_event = Input:waitEvent(wait_for.us, wait_for.s)
|
||||
end
|
||||
|
||||
-- delegate input_event to handler
|
||||
if input_event then
|
||||
local handler = self.event_handlers[input_event]
|
||||
if handler then
|
||||
handler(input_event)
|
||||
else
|
||||
self.event_handlers["__default__"](input_event)
|
||||
end
|
||||
end
|
||||
if ffi.os == "Windows" then
|
||||
self:handleInput()
|
||||
else
|
||||
self:initLooper()
|
||||
self:handleTask(function() self:handleInput() end)
|
||||
self.looper:start()
|
||||
end
|
||||
end
|
||||
|
||||
-- run uimanager forever for testing purpose
|
||||
function UIManager:runForever()
|
||||
self._run_forever = true
|
||||
self:run()
|
||||
end
|
||||
|
||||
UIManager:init()
|
||||
return UIManager
|
||||
|
||||
|
||||
@@ -18,11 +18,6 @@ ffi.cdef[[
|
||||
]]
|
||||
if ffi.os == "Windows" then
|
||||
ffi.C._putenv("PATH=libs;common;")
|
||||
else
|
||||
ffi.C.putenv("LD_LIBRARY_PATH="
|
||||
.. util.realpath("libs") .. ":"
|
||||
.. util.realpath("common") ..":"
|
||||
.. ffi.string(ffi.C.getenv("LD_LIBRARY_PATH")))
|
||||
end
|
||||
|
||||
local DocSettings = require("docsettings")
|
||||
|
||||
@@ -3,6 +3,7 @@ local DocumentRegistry = require("document/documentregistry")
|
||||
|
||||
describe("PDF document module", function()
|
||||
local sample_pdf = "spec/front/unit/data/tall.pdf"
|
||||
local doc
|
||||
it("should open document", function()
|
||||
doc = DocumentRegistry:openDocument(sample_pdf)
|
||||
assert.truthy(doc)
|
||||
@@ -38,6 +39,7 @@ end)
|
||||
|
||||
describe("EPUB document module", function()
|
||||
local sample_epub = "spec/front/unit/data/leaves.epub"
|
||||
local doc
|
||||
it("should open document", function()
|
||||
doc = DocumentRegistry:openDocument(sample_epub)
|
||||
assert.truthy(doc)
|
||||
|
||||
36
spec/unit/httpclient_spec.lua
Normal file
36
spec/unit/httpclient_spec.lua
Normal file
@@ -0,0 +1,36 @@
|
||||
require("commonrequire")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local HTTPClient = require("httpclient")
|
||||
local DEBUG = require("dbg")
|
||||
--DEBUG:turnOn()
|
||||
|
||||
describe("HTTP client module", function()
|
||||
local requests = 0
|
||||
local function response_callback(res)
|
||||
requests = requests - 1
|
||||
if requests == 0 then UIManager:quit() end
|
||||
assert(res.body)
|
||||
end
|
||||
local function error_callback(res)
|
||||
requests = requests - 1
|
||||
if requests == 0 then UIManager:quit() end
|
||||
assert(false, "error occurs")
|
||||
end
|
||||
local async_client = HTTPClient:new()
|
||||
it("should get response from async GET request", function()
|
||||
UIManager:quit()
|
||||
local urls = {
|
||||
"http://www.example.com",
|
||||
"http://www.example.org",
|
||||
"https://www.example.com",
|
||||
"https://www.example.org",
|
||||
}
|
||||
requests = #urls
|
||||
for _, url in ipairs(urls) do
|
||||
async_client:request({
|
||||
url = url,
|
||||
}, response_callback, error_callback)
|
||||
end
|
||||
UIManager:runForever()
|
||||
end)
|
||||
end)
|
||||
Reference in New Issue
Block a user