From bacdc889c042bf7d07ade41de19ff5b089cda583 Mon Sep 17 00:00:00 2001 From: mihacooper Date: Fri, 20 Jan 2017 17:45:01 +0300 Subject: [PATCH] add luaunit framework, extend lua tests, fix shared-table bug (values were not updatable) --- .gitmodules | 3 ++ CMakeLists.txt | 4 +-- lua-api/woofer.lua | 6 ---- lua-tests/luaunit | 1 + lua-tests/run_tests.lua | 59 ++++++++++++++++++++++++++++++++++++++++ lua-tests/smoke_test.lua | 52 +++++++++++++++++++++++++++++++++++ src/lua-module.cpp | 22 +++++++++++---- src/shared-table.cpp | 2 +- src/threading.cpp | 30 +++++++++++++++----- src/threading.h | 3 +- tests/lua/lunatest | 1 + tests/smoke_test.lua | 55 ------------------------------------- 12 files changed, 159 insertions(+), 79 deletions(-) delete mode 100644 lua-api/woofer.lua create mode 160000 lua-tests/luaunit create mode 100755 lua-tests/run_tests.lua create mode 100644 lua-tests/smoke_test.lua create mode 160000 tests/lua/lunatest delete mode 100644 tests/smoke_test.lua diff --git a/.gitmodules b/.gitmodules index 6ad9de7..75e7707 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "src/sol"] path = src/sol url = https://github.com/ThePhD/sol2.git +[submodule "lua-tests/luaunit"] + path = lua-tests/luaunit + url = https://github.com/bluebird75/luaunit diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d585c3..2a2b613 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,4 @@ set_target_properties(woofer PROPERTIES COMPILE_FLAGS "${ENABLE_WARNINGS} ${GENE # INSTALL - #---------- -FILE(GLOB TESTS tests/*) -FILE(GLOB LUA_SOURCES lua-api/*.lua) -install(FILES ${TESTS} ${LUA_SOURCES} DESTINATION ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) \ No newline at end of file +install(FILES ${LUA_SOURCES} DESTINATION ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) \ No newline at end of file diff --git a/lua-api/woofer.lua b/lua-api/woofer.lua deleted file mode 100644 index cbf9e3c..0000000 --- a/lua-api/woofer.lua +++ /dev/null @@ -1,6 +0,0 @@ ---local thr = require('libbevy') -package.cpath = package.cpath .. ";./?.dylib" -- MAC OS support -local api = require('libwoofer') -api.thread.thread_id = nil -api.thread.join = nil -return api \ No newline at end of file diff --git a/lua-tests/luaunit b/lua-tests/luaunit new file mode 160000 index 0000000..d2f1ffa --- /dev/null +++ b/lua-tests/luaunit @@ -0,0 +1 @@ +Subproject commit d2f1ffa86582d51b77dc29b1f7216e75fe2bc6d0 diff --git a/lua-tests/run_tests.lua b/lua-tests/run_tests.lua new file mode 100755 index 0000000..53ea882 --- /dev/null +++ b/lua-tests/run_tests.lua @@ -0,0 +1,59 @@ +#!/usr/bin/lua + +package.cpath = package.cpath .. ";../build/?.so;;../build/?.dylib" +test = require "luaunit.luaunit" + +do + -- Hack input arguments to make tests verbose by default + local found = false + for _, v in ipairs(arg) do + if v == '-o' or v == '--output' then + found = true + break + end + end + if not found then + table.insert(arg, '-o') + table.insert(arg, 'TAP') + end +end + +function log(...) + local msg = '@\t\t' .. os.date('%Y-%m-%d %H:%M:%S ',os.time()) + for _, val in ipairs({...}) do + msg = msg .. tostring(val) .. ' ' + end + io.write(msg .. '\n') + io.flush() +end + +function wait(timeInSec, condition) + local startTime = os.time() + while ( (os.time() - startTime) <= timeInSec) do + if condition ~= nil then + if type(condition) == 'function' then + if condition() then + return true + end + else + if condition then + return true + end + end + end + end + return false +end + +function sleep(timeInSec) + wait(timeInMsec, nil) +end + +----------- +-- TESTS -- +----------- + +require("smoke_test") + +----------- +os.exit( test.LuaUnit.run() ) \ No newline at end of file diff --git a/lua-tests/smoke_test.lua b/lua-tests/smoke_test.lua new file mode 100644 index 0000000..82b41a2 --- /dev/null +++ b/lua-tests/smoke_test.lua @@ -0,0 +1,52 @@ +TestSmoke = {} + +function TestSmoke:testGeneralWorkability() + local woofer = require('libwoofer') + local share = woofer.share() + + share["number"] = 100500 + share["string"] = "string value" + share["bool"] = true + + log "Start thread" + local thread = woofer.thread( + function(share) + share["child.number"] = share["number"] + share["child.string"] = share["string"] + share["child.bool"] = share["bool"] + end, + share + ) + log "Join thread" + thread:join() + + log "Check values" + test.assertEquals(share["child.number"], share["number"], + "'number' fields are not equal") + test.assertEquals(share["child.string"], share["string"], + "'string' fields are not equal") + test.assertEquals(share["child.bool"], share["bool"], + "'bool' fields are not equal") +end + +function TestSmoke:testDetach() + local woofer = require('libwoofer') + local share = woofer.share() + + share["finished"] = false + log "Start thread" + local thread = woofer.thread( + function(share) + local startTime = os.time() + while ( (os.time() - startTime) <= 3) do --[[ Like we are working 3sec ... ]] end + share["finished"] = true + end, + share + ) + log "Detach thread" + thread:detach() + + log "Waiting for thread completion..." + test.assertEquals(wait(4, function() return share["finished"] end) , true) + log "Stop waiting" +end diff --git a/src/lua-module.cpp b/src/lua-module.cpp index 5d2219c..5bbf001 100644 --- a/src/lua-module.cpp +++ b/src/lua-module.cpp @@ -1,16 +1,26 @@ -#include "lua.hpp" - #include "threading.h" #include "shared-table.h" +#include + +static sol::object create_thread(sol::this_state lua, sol::function func, const sol::variadic_args& args) +{ + return sol::make_object(lua, std::make_unique(func, args)); +} + +static sol::object create_share(sol::this_state lua) +{ + return sol::make_object(lua, std::make_unique()); +} + extern "C" int luaopen_libwoofer(lua_State *L) { sol::state_view lua(L); - auto thread_obj = threading::LuaThread::get_user_type(lua); - auto share_obj = share_data::SharedTable::get_user_type(lua); + threading::LuaThread::get_user_type(lua); + share_data::SharedTable::get_user_type(lua); sol::table public_api = lua.create_table_with( - "thread", thread_obj, - "share", share_obj + "thread", create_thread, + "share", create_share ); sol::stack::push(lua, public_api); return 1; diff --git a/src/shared-table.cpp b/src/shared-table.cpp index d84c812..7fbb396 100644 --- a/src/shared-table.cpp +++ b/src/shared-table.cpp @@ -27,7 +27,7 @@ void SharedTable::luaSet(sol::stack_object luaKey, sol::stack_object luaValue) n } else { StoredObject value(luaValue); std::lock_guard g(lock_); - data_.emplace(std::make_pair(std::move(key), std::move(value))); + data_[std::move(key)] = std::move(value); } } diff --git a/src/threading.cpp b/src/threading.cpp index f7f0962..1e736af 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -27,11 +27,11 @@ LuaThread::LuaThread(const sol::function& function, const sol::variadic_args& ar void LuaThread::store_args(const sol::variadic_args& args) noexcept { - const auto end = --args.end(); - for(auto iter = args.begin(); iter != end; iter++) + p_arguments_ = std::make_shared>(); + for(auto iter = args.begin(); iter != args.end(); iter++) { share_data::StoredObject store(iter->get()); - arguments_.push_back(store.unpack(sol::this_state{p_state_->lua_state()})); + p_arguments_->push_back(store.unpack(sol::this_state{p_state_->lua_state()})); } } @@ -42,16 +42,31 @@ void LuaThread::join() noexcept p_thread_->join(); p_thread_.reset(); } - arguments_.clear(); + if (p_arguments_.get()) + p_arguments_.reset(); if (p_state_.get()) p_state_.reset(); } +void LuaThread::detach() noexcept +{ + p_thread_->detach(); +} + void LuaThread::work() noexcept { - sol::state& lua = *p_state_; - sol::function_result func = lua["loadstring"](str_function_); - func.get()(sol::as_args(arguments_)); + if (p_state_.get() && p_arguments_.get()) + { + std::string func_owner = std::move(str_function_); + std::shared_ptr state_owner = p_state_; + std::shared_ptr> arguments_owner = p_arguments_; + sol::function_result func = (*state_owner)["loadstring"](func_owner); + func.get()(sol::as_args(*arguments_owner)); + } + else + { + throw sol::error("Internal error: invalid thread Lua state"); + } } std::string LuaThread::thread_id() noexcept @@ -66,6 +81,7 @@ sol::object LuaThread::get_user_type(sol::state_view& lua) noexcept static sol::usertype type( sol::call_construction(), sol::constructors>(), "join", &LuaThread::join, + "detach", &LuaThread::detach, "thread_id", &LuaThread::thread_id ); sol::stack::push(lua, type); diff --git a/src/threading.h b/src/threading.h index d5a0be1..9b1381b 100644 --- a/src/threading.h +++ b/src/threading.h @@ -16,6 +16,7 @@ public: LuaThread(const sol::function& function, const sol::variadic_args& args) noexcept; virtual ~LuaThread() noexcept = default; void join() noexcept; + void detach() noexcept; static std::string thread_id() noexcept; static sol::object get_user_type(sol::state_view& lua) noexcept; @@ -27,7 +28,7 @@ private: std::string str_function_; std::shared_ptr p_state_; std::shared_ptr p_thread_; - std::vector arguments_; + std::shared_ptr> p_arguments_; }; } diff --git a/tests/lua/lunatest b/tests/lua/lunatest new file mode 160000 index 0000000..32e3ecf --- /dev/null +++ b/tests/lua/lunatest @@ -0,0 +1 @@ +Subproject commit 32e3ecfdb64d37c7f5e72b33bd32fdc3992b967d diff --git a/tests/smoke_test.lua b/tests/smoke_test.lua deleted file mode 100644 index bdbb923..0000000 --- a/tests/smoke_test.lua +++ /dev/null @@ -1,55 +0,0 @@ -function compare(o1, o2) - if o1 == o2 then return true end - local o1Type = type(o1) - local o2Type = type(o2) - if o1Type ~= o2Type then return false end - if o1Type ~= 'table' then return false end - - local keySet = {} - for key1, value1 in pairs(o1) do - local value2 = o2[key1] - if value2 == nil or equals(value1, value2) == false then - return false - end - keySet[key1] = true - end - - for key2, _ in pairs(o2) do - if not keySet[key2] then return false end - end - return true -end - -function check(left, right) - if not compare(left, right) then - print("ERROR") - end -end - ------------ --- TESTS -- ------------ - -do -- Simple smoke - package.cpath = package.cpath .. ";./?.dylib" -- MAC OS support - local woofer = require('libwoofer') - local share = woofer.share() - - share["number"] = 100500 - share["string"] = "string value" - share["bool"] = true - - local thread = woofer.thread( - function(share) - share["child.number"] = share["number"] - share["child.string"] = share["string"] - share["child.bool"] = share["bool"] - end, - share - ) - thread:join() - - check(share["child.number"], share["number"]) - check(share["child.string"], share["string"]) - check(share["child.bool"], share["bool"]) -end