From f02ccab5573a43fede9d4ee83ebd878ea098e461 Mon Sep 17 00:00:00 2001 From: xiaojin Date: Wed, 4 Aug 2021 16:05:55 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=B3=20chore(=E5=B7=A5=E5=85=B7):=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20=E5=90=AF=E5=8A=A8=20=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 4 + framework/lualib-src/lua-ecs/luaecs.c | 7 +- framework/lualib-src/lua-ecs/test9.lua | 35 + framework/lualib/3rd/misc/ecs.lua | 33 + framework/shells/misc_scripts/lnx_fwk.sh | 12 + framework/shells/misc_scripts/lnx_fwk_defs.sh | 12 + .../shells/misc_scripts/lnx_fwk_faketime.sh | 60 ++ framework/shells/misc_scripts/lnx_fwk_log.sh | 18 + framework/shells/misc_scripts/lnx_fwk_path.sh | 27 + .../shells/misc_scripts/lnx_fwk_progress.sh | 71 ++ .../shells/misc_scripts/lnx_fwk_server.sh | 230 ++++++ .../shells/misc_scripts/lnx_fwk_uwsgi.sh | 139 ++++ framework/shells/misc_scripts/lnx_logic.sh | 16 + .../shells/misc_scripts/lnx_logic_defs.sh | 31 + .../shells/misc_scripts/lnx_logic_misc.sh | 147 ++++ .../misc_scripts/lnx_logic_serverbuild.sh | 65 ++ .../misc_scripts/lnx_logic_serverconf.sh | 37 + .../misc_scripts/lnx_logic_serverctrl.sh | 699 ++++++++++++++++++ .../misc_scripts/lnx_logic_serverhotfix.sh | 88 +++ .../misc_scripts/lnx_logic_serverhttp.sh | 56 ++ .../misc_scripts/lnx_logic_serversql.sh | 122 +++ .../misc_scripts/lnx_logic_serverstatus.sh | 130 ++++ .../misc_scripts/lnx_logic_servertar.sh | 314 ++++++++ .../misc_scripts/lnx_logic_serverupdate.sh | 77 ++ .../shells/misc_scripts/lnx_logic_updatedb.sh | 30 + .../shells/misc_scripts/mac_logic_updatedb.sh | 53 ++ .../shells/misc_scripts/mysql_handler.py | 24 + framework/shells/run_linux | 78 ++ framework/shells/run_mac | 163 ++++ logs/.gitkeep | 0 server/run | 21 + shell/.gitkeep | 0 tools/lqc/cli/app.lua | 127 ---- tools/lqc/cli/arg_parser.lua | 43 -- tools/lqc/cli/loader.lua | 77 -- tools/lqc/config.lua | 46 -- tools/lqc/fsm.lua | 102 --- tools/lqc/fsm/action.lua | 37 - tools/lqc/fsm/algorithm.lua | 471 ------------ tools/lqc/fsm/command.lua | 104 --- tools/lqc/fsm/state.lua | 59 -- tools/lqc/fsm/var.lua | 29 - tools/lqc/generator.lua | 43 -- tools/lqc/generators/any.lua | 30 - tools/lqc/generators/bool.lua | 25 - tools/lqc/generators/byte.lua | 14 - tools/lqc/generators/char.lua | 36 - tools/lqc/generators/float.lua | 30 - tools/lqc/generators/int.lua | 108 --- tools/lqc/generators/string.lua | 200 ----- tools/lqc/generators/table.lua | 184 ----- tools/lqc/helpers/deep_copy.lua | 39 - tools/lqc/helpers/deep_equals.lua | 37 - tools/lqc/helpers/filter.lua | 23 - tools/lqc/helpers/fs.lua | 109 --- tools/lqc/helpers/map.lua | 19 - tools/lqc/helpers/reduce.lua | 33 - tools/lqc/helpers/vector.lua | 115 --- tools/lqc/lqc_gen.lua | 123 --- tools/lqc/property.lua | 228 ------ tools/lqc/property_result.lua | 13 - tools/lqc/quickcheck.lua | 81 -- tools/lqc/random.lua | 29 - tools/lqc/report.lua | 126 ---- tools/lqc/threading/msg_processor.lua | 44 -- tools/lqc/threading/thread_pool.lua | 83 --- 66 files changed, 2797 insertions(+), 2869 deletions(-) create mode 100644 framework/lualib-src/lua-ecs/test9.lua create mode 100644 framework/shells/misc_scripts/lnx_fwk.sh create mode 100644 framework/shells/misc_scripts/lnx_fwk_defs.sh create mode 100644 framework/shells/misc_scripts/lnx_fwk_faketime.sh create mode 100644 framework/shells/misc_scripts/lnx_fwk_log.sh create mode 100644 framework/shells/misc_scripts/lnx_fwk_path.sh create mode 100644 framework/shells/misc_scripts/lnx_fwk_progress.sh create mode 100644 framework/shells/misc_scripts/lnx_fwk_server.sh create mode 100644 framework/shells/misc_scripts/lnx_fwk_uwsgi.sh create mode 100644 framework/shells/misc_scripts/lnx_logic.sh create mode 100644 framework/shells/misc_scripts/lnx_logic_defs.sh create mode 100644 framework/shells/misc_scripts/lnx_logic_misc.sh create mode 100644 framework/shells/misc_scripts/lnx_logic_serverbuild.sh create mode 100644 framework/shells/misc_scripts/lnx_logic_serverconf.sh create mode 100644 framework/shells/misc_scripts/lnx_logic_serverctrl.sh create mode 100644 framework/shells/misc_scripts/lnx_logic_serverhotfix.sh create mode 100644 framework/shells/misc_scripts/lnx_logic_serverhttp.sh create mode 100644 framework/shells/misc_scripts/lnx_logic_serversql.sh create mode 100644 framework/shells/misc_scripts/lnx_logic_serverstatus.sh create mode 100644 framework/shells/misc_scripts/lnx_logic_servertar.sh create mode 100644 framework/shells/misc_scripts/lnx_logic_serverupdate.sh create mode 100644 framework/shells/misc_scripts/lnx_logic_updatedb.sh create mode 100644 framework/shells/misc_scripts/mac_logic_updatedb.sh create mode 100644 framework/shells/misc_scripts/mysql_handler.py create mode 100755 framework/shells/run_linux create mode 100755 framework/shells/run_mac delete mode 100644 logs/.gitkeep create mode 100755 server/run delete mode 100644 shell/.gitkeep delete mode 100644 tools/lqc/cli/app.lua delete mode 100644 tools/lqc/cli/arg_parser.lua delete mode 100644 tools/lqc/cli/loader.lua delete mode 100644 tools/lqc/config.lua delete mode 100644 tools/lqc/fsm.lua delete mode 100644 tools/lqc/fsm/action.lua delete mode 100644 tools/lqc/fsm/algorithm.lua delete mode 100644 tools/lqc/fsm/command.lua delete mode 100644 tools/lqc/fsm/state.lua delete mode 100644 tools/lqc/fsm/var.lua delete mode 100644 tools/lqc/generator.lua delete mode 100644 tools/lqc/generators/any.lua delete mode 100644 tools/lqc/generators/bool.lua delete mode 100644 tools/lqc/generators/byte.lua delete mode 100644 tools/lqc/generators/char.lua delete mode 100644 tools/lqc/generators/float.lua delete mode 100644 tools/lqc/generators/int.lua delete mode 100644 tools/lqc/generators/string.lua delete mode 100644 tools/lqc/generators/table.lua delete mode 100644 tools/lqc/helpers/deep_copy.lua delete mode 100644 tools/lqc/helpers/deep_equals.lua delete mode 100644 tools/lqc/helpers/filter.lua delete mode 100644 tools/lqc/helpers/fs.lua delete mode 100644 tools/lqc/helpers/map.lua delete mode 100644 tools/lqc/helpers/reduce.lua delete mode 100644 tools/lqc/helpers/vector.lua delete mode 100644 tools/lqc/lqc_gen.lua delete mode 100644 tools/lqc/property.lua delete mode 100644 tools/lqc/property_result.lua delete mode 100644 tools/lqc/quickcheck.lua delete mode 100644 tools/lqc/random.lua delete mode 100644 tools/lqc/report.lua delete mode 100644 tools/lqc/threading/msg_processor.lua delete mode 100644 tools/lqc/threading/thread_pool.lua diff --git a/docs/README.md b/docs/README.md index a220c18..6d787b6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,3 +1,7 @@ +删除所有 容器和镜像 + +docker stop $(docker ps -a -q) && docker system prune --all --force + 代码安全指南: https://github.com/Tencent/secguide diff --git a/framework/lualib-src/lua-ecs/luaecs.c b/framework/lualib-src/lua-ecs/luaecs.c index b403f48..6f32c81 100644 --- a/framework/lualib-src/lua-ecs/luaecs.c +++ b/framework/lualib-src/lua-ecs/luaecs.c @@ -1099,7 +1099,7 @@ update_iter(lua_State *L, int world_index, int lua_index, struct group_iter *ite id = entity_sibling_index_(iter->world, dead_tag, 0, k->id); entity_disable_tag_(iter->world, dead_tag, 0, dead_tag); } else { - id = entity_new_(iter->world, k->id, NULL, L, world_index); + id = entity_new_(iter->world, k->id, NULL, L, world_index) + 1; } if (c->stride == STRIDE_LUA) { if (lua_getiuservalue(L, world_index, k->id * 2 + 2) != LUA_TTABLE) { @@ -1180,7 +1180,10 @@ read_component_in_field(lua_State *L, int lua_index, const char *name, int n, st return; } if (lua_getfield(L, lua_index, name) != LUA_TTABLE) { - luaL_error(L, ".%s is missing", name); + lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -1); + lua_setfield(L, lua_index, name); } read_component(L, n , f, lua_gettop(L), buffer); lua_pop(L, 1); diff --git a/framework/lualib-src/lua-ecs/test9.lua b/framework/lualib-src/lua-ecs/test9.lua new file mode 100644 index 0000000..e618540 --- /dev/null +++ b/framework/lualib-src/lua-ecs/test9.lua @@ -0,0 +1,35 @@ +local ecs = require "ecs" + +local w = ecs.world() + +w:register { + name = "vector", + "x:float", + "y:float", +} + +w:register { + name = "text", + type = "lua", +} + +w:register { + name = "mark" +} + +w:new { + vector = { x = 1, y = 2 }, + text = "Hello World", + mark = true, +} + +for v in w:select "mark" do + w:readall(v) + for k,v in pairs(v) do + print(k,v) + end +end + + + + diff --git a/framework/lualib/3rd/misc/ecs.lua b/framework/lualib/3rd/misc/ecs.lua index 41b6ca1..93bf6b5 100644 --- a/framework/lualib/3rd/misc/ecs.lua +++ b/framework/lualib/3rd/misc/ecs.lua @@ -58,6 +58,32 @@ local function cache_world(obj, k) return desc end + local function gen_all_pat() + local desc = {} + local i = 1 + for name,t in pairs(c.typenames) do + local a = { + name = t.name, + id = t.id, + type = t.type, + opt = true, + r = true, + } + table.move(t, 1, #t, 1, a) + desc[i] = a + i = i + 1 + end + return desc + end + + setmetatable(c, { __index = function(_, key) + if key == "all" then + local all = k:_groupiter(gen_all_pat()) + c.all = all + return all + end + end }) + local function gen_select_pat(pat) local typenames = c.typenames local desc = {} @@ -190,6 +216,7 @@ do -- newtype function M:register(typeclass) local name = assert(typeclass.name) local ctx = context[self] + ctx.all = nil -- clear all pattern local typenames = ctx.typenames local id = ctx.id + 1 assert(typenames[name] == nil and id <= ecs._MAXTYPE) @@ -314,6 +341,12 @@ function M:sync(pat, iter) return iter end +function M:readall(iter) + local p = context[self].all + self:_sync(p, iter) + return iter +end + function M:clear(name) local id = assert(context[self].typenames[name].id) self:_clear(id) diff --git a/framework/shells/misc_scripts/lnx_fwk.sh b/framework/shells/misc_scripts/lnx_fwk.sh new file mode 100644 index 0000000..7f8cfb8 --- /dev/null +++ b/framework/shells/misc_scripts/lnx_fwk.sh @@ -0,0 +1,12 @@ +# linux script framework header file +# Longwei Lai +################################################################### + +____FWK_SCRIPT_PATH="$(cd "${MISC_SCRIPTS_PATH}" && pwd)" + +source "${____FWK_SCRIPT_PATH}/lnx_fwk_log.sh" +source "${____FWK_SCRIPT_PATH}/lnx_fwk_path.sh" +source "${____FWK_SCRIPT_PATH}/lnx_fwk_progress.sh" +source "${____FWK_SCRIPT_PATH}/lnx_fwk_faketime.sh" +source "${____FWK_SCRIPT_PATH}/lnx_fwk_uwsgi.sh" + diff --git a/framework/shells/misc_scripts/lnx_fwk_defs.sh b/framework/shells/misc_scripts/lnx_fwk_defs.sh new file mode 100644 index 0000000..970cf33 --- /dev/null +++ b/framework/shells/misc_scripts/lnx_fwk_defs.sh @@ -0,0 +1,12 @@ +# linux script framework some common macros define +# Longwei Lai +################################################################### + +# server options path, all server options save in here +____FWK_SVR_OPTIONS_PATH="${SVR_PATH}/.options" + +# all server option files path define +____FWK_SVR_DEBUG_OPTION_FILE="${____FWK_SVR_OPTIONS_PATH}/debug_option" +____FWK_SVR_FAKETIME_OPTION_FILE="${____FWK_SVR_OPTIONS_PATH}/faketime_option" +____FWK_SVR_FAKETIME_BEGIN_OPTION_FILE="${____FWK_SVR_OPTIONS_PATH}/faketime_begin_option" + diff --git a/framework/shells/misc_scripts/lnx_fwk_faketime.sh b/framework/shells/misc_scripts/lnx_fwk_faketime.sh new file mode 100644 index 0000000..87d1837 --- /dev/null +++ b/framework/shells/misc_scripts/lnx_fwk_faketime.sh @@ -0,0 +1,60 @@ +# fake time about functions encapsulation +# Longwei Lai +################################################################### + +source "${____FWK_SCRIPT_PATH}/lnx_fwk_defs.sh" + +# read current fake time setting +# arguments: +# N/A +# returns: +# echo style: +# faketime or "no faketime" +# return style: +# N/A +function read_faketime() +{ + local faketime= + if [ -f "${____FWK_SVR_FAKETIME_OPTION_FILE}" ]; then + faketime="`cat "${____FWK_SVR_FAKETIME_OPTION_FILE}"`" + fi + + if [ -z "${faketime}" ]; then + echo -n "no faketime" + return + fi + + local now_time="`date "+%Y-%m-%d %H:%M:%S"`" + local faketime_begin= + if [ -f "${____FWK_SVR_FAKETIME_BEGIN_OPTION_FILE}" ]; then + faketime_begin="`cat "${____FWK_SVR_FAKETIME_BEGIN_OPTION_FILE}"`" + else + faketime_begin="${now_time}" + echo -n "${faketime_begin}" > "${____FWK_SVR_FAKETIME_BEGIN_OPTION_FILE}" + fi + + local now_ts="`date -d "${now_time}" '+%s'`" + local faketime_begin_ts="`date -d "${faketime_begin}" '+%s'`" + local diff_time=$((${now_ts} - ${faketime_begin_ts})) + + local faketime_ts="`date -d "${faketime}" '+%s'`" + local now_faketime_ts=$((${faketime_ts} + ${diff_time})) + local real_faketime="`date -d "@${now_faketime_ts}" '+%Y-%m-%d %H:%M:%S'`" + + echo -n "${real_faketime}" +} + +# clear fake time setting +# arguments: +# N/A +# returns: +# echo style: +# N/A +# return style: +# N/A +function clear_faketime() +{ + if [ -f "${____FWK_SVR_FAKETIME_OPTION_FILE}" ]; then + echo -n "" > "${____FWK_SVR_FAKETIME_OPTION_FILE}" + fi +} diff --git a/framework/shells/misc_scripts/lnx_fwk_log.sh b/framework/shells/misc_scripts/lnx_fwk_log.sh new file mode 100644 index 0000000..352ee09 --- /dev/null +++ b/framework/shells/misc_scripts/lnx_fwk_log.sh @@ -0,0 +1,18 @@ +# log about function encapsulation +# Longwei Lai +################################################################### + +function log_info() +{ + echo "$@" +} + +function log_warn() +{ + echo -e "\e[1;33m$@\e[0m" +} + +function log_err() +{ + echo -e "\e[1;31m$@\e[0m" +} diff --git a/framework/shells/misc_scripts/lnx_fwk_path.sh b/framework/shells/misc_scripts/lnx_fwk_path.sh new file mode 100644 index 0000000..c0ab8f2 --- /dev/null +++ b/framework/shells/misc_scripts/lnx_fwk_path.sh @@ -0,0 +1,27 @@ +# linux script framework path about function encapsulation +# Longwei Lai +################################################################### + +source "${____FWK_SCRIPT_PATH}/lnx_fwk_log.sh" + +if [ -z "${SVR_PATH}" ]; then + log_err "please define SVR_PATH first" + exit 1 +fi + +____FWK_PID_SUFFIX=pid +____FWK_NOHUP_SUFFIX=nohup + +# make server pid file path +function make_server_pid_file_path() +{ + local svr_name_or_path="$1" + local path_part="`dirname "${svr_name_or_path}"`" + local name_part="`basename "${svr_name_or_path}"`" + if [ "${path_part}" = "." ]; then + path_part="${SVR_PATH}" + fi + + echo -n "${path_part}/.${name_part}.${____FWK_PID_SUFFIX}" +} + diff --git a/framework/shells/misc_scripts/lnx_fwk_progress.sh b/framework/shells/misc_scripts/lnx_fwk_progress.sh new file mode 100644 index 0000000..6b8aca9 --- /dev/null +++ b/framework/shells/misc_scripts/lnx_fwk_progress.sh @@ -0,0 +1,71 @@ +# linux script framework progress about functions encapsulation +# Longwei Lai +################################################################### + +source "${____FWK_SCRIPT_PATH}/lnx_fwk_log.sh" + +# check specific progress is running or not +function is_progress_running() +{ + local pid="$1" + local pids="`top -b -n1 | sed -e 's/\s*\(.*\)/\1/; s/\([0-9]*\).*/\1/; /^$/d' | tr -s '\n' " "`" + for now_pid in $pids; do + if [ "$now_pid" -eq "${pid}" ]; then + echo -n "TRUE" + return + fi + done + + echo -n "FALSE" +} + +# kill specific progress(included child progresses) +function kill_progress() +{ + # kill child processes + local will_kill_pid="$1" + for child in $(ps -o pid --no-headers --ppid ${will_kill_pid}); do + kill -9 "${child}" 1>/dev/null 2>&1 + done + + # kill self + kill -9 "${will_kill_pid}" 1>/dev/null 2>&1 +} + +# check specific pidfile's progress is running or not +function is_pidfile_running() +{ + local pid_file="$1" + if [ ! -e "${pid_file}" ]; then + echo -n "FALSE" + return + fi + + local pid="`cat "${pid_file}"`" + if [ $(is_progress_running "${pid}") = "FALSE" ]; then + rm -rf "${pid_file}" 1>/dev/null 2>&1 + echo -n "FALSE" + return + fi + + echo -n "TRUE" +} + +# kill specific pidfile's progress(included child progresses) +function stop_pidfile() +{ + local pid_file="$1" + if [ "$(is_pidfile_running "${pid_file}")" = "TRUE" ]; then + pid="`cat "${pid_file}"`" + kill_progress "$pid" + sleep 1.618 + fi + + if [ "$(is_pidfile_running "${pid_file}")" = "TRUE" ]; then + echo "stop pidfile failed" + stop_pidfile "${pid_file}" + else + rm -rf "${pid_file}" + fi +} + diff --git a/framework/shells/misc_scripts/lnx_fwk_server.sh b/framework/shells/misc_scripts/lnx_fwk_server.sh new file mode 100644 index 0000000..fb39e0a --- /dev/null +++ b/framework/shells/misc_scripts/lnx_fwk_server.sh @@ -0,0 +1,230 @@ +# linux script framework server about functions encapsulation +# Longwei Lai +################################################################### + +source "${____FWK_SCRIPT_PATH}/lnx_fwk_log.sh" +source "${____FWK_SCRIPT_PATH}/lnx_fwk_defs.sh" +source "${____FWK_SCRIPT_PATH}/lnx_fwk_path.sh" +source "${____FWK_SCRIPT_PATH}/lnx_fwk_progress.sh" + +# control commands define +____FWK_SERVER_CTRL_START=start +____FWK_SERVER_CTRL_STOP=stop +____FWK_SERVER_CTRL_RESTART=restart +____FWK_SERVER_CTRL_IS_RUNNING=is_running +____FWK_SERVER_CTRL_LIST_STATUS=list_status + +# ctrl specific server +# arguments: +# arg1: server name +# arg2: server control cmd +# returns: +# echo style: +# N/A +# return style: +# 0: success +# 1: failed +function ctrl_server() +{ + local svr_name="$1" + local ctrl_cmd="$2" + + local args=($@) + local svr_args=${args[@]:2} + + if [ "${ctrl_cmd}" = "${____FWK_SERVER_CTRL_START}" ]; then + start_server "${svr_name}" ${svr_args} + elif [ "${ctrl_cmd}" = "${____FWK_SERVER_CTRL_STOP}" ]; then + stop_server "${svr_name}" ${svr_args} + elif [ "${ctrl_cmd}" = "${____FWK_SERVER_CTRL_RESTART}" ]; then + restart_server "${svr_name}" ${svr_args} + elif [ "${ctrl_cmd}" = "${____FWK_SERVER_CTRL_IS_RUNNING}" ]; then + is_server_running "${svr_name}" ${svr_args} + elif [ "${ctrl_cmd}" = "${____FWK_SERVER_CTRL_LIST_STATUS}" ]; then + list_server_status "${svr_name}" ${svr_args} + else + log_err "unknown ctrl command: ${ctrl_cmd}" + return 1 + fi +} + +# start specific server +# arguments: +# arg1: server name +# arg2: server entry +# arg3-n: server start arguments +# returns: +# echo style: +# N/A +# return style: +# 0: success +# 1: failed +# 2: already start, don't need start again +function start_server() +{ + local svr_name="$1" + local svr_entry="$2" + local show_name="$(get_server_show_name "${svr_name}")" + local pid_file="$(make_server_pid_file_path "${svr_name}")" + + local args=($@) + local svr_args="${args[@]:2}" + + # switch current directory to server path + local old_path="`pwd`" + cd "`dirname "${svr_path}"`" + + # makesure just running one instance + if [ "$(is_server_running "${svr_name}")" = TRUE ]; then + log_warn "${show_name} is running(pid: `cat "${pid_file}"`), don't need start!" + cd "${old_path}" + return 2 + fi + + # start server + local nohup_file=""`dirname "${svr_path}"`"/."${svr_name}".${____FWK_NOHUP_SUFFIX}" + nohup ${svr_entry} ${svr_args} 1>"${nohup_file}" 2>&1 & + local svr_pid=$! + if [ $? -ne 0 ]; then + log_err "start ${show_name} failed, return code: $?, timeout?: ${check_timeout}" + return 1 + fi + echo -n "${svr_pid}" > "${pid_file}" + + # back to old path + cd "${old_path}" + + log_info "start ${show_name} success, pid: ${svr_pid}" +} + +# stop specific server +# arguments: +# arg1: server name +# returns: +# echo style: +# N/A +# return style: +# N/A +function stop_server() +{ + local svr_name="$1" + local pid_file="$(make_server_pid_file_path "${svr_name}")" + + local server_running=`is_server_running "${svr_name}"` + if [ "${server_running}" = TRUE ]; then + stop_pidfile "${pid_file}" + fi + + if [ "${server_running}" = TRUE ]; then + local nohup_file=""`dirname "${pid_file}"`"/."${svr_name}".${____FWK_NOHUP_SUFFIX}" + if [ -f "${nohup_file}" ]; then + log_info "Found ${svr_name} nohup file, backup it" + local backup_nohup_file="${nohup_file}.`date "+%m%d%H%M%S"`" + \cp -f "${nohup_file}" "${backup_nohup_file}" + fi + fi +} + +# restart specific server +# arguments: +# arg1: server name +# returns: +# echo style: +# N/A +# return style: +# N/A +function restart_server() +{ + stop_server $@ + start_server $@ +} + +# check specific server is running or not +# arguments: +# arg1: server name +# returns: +# echo style: +# TRUE: server running +# FALSE: server not running +# return style: +# N/A +function is_server_running() +{ + local svr_name="$1" + local pid_file="$(make_server_pid_file_path "${svr_name}")" + echo -n "$(is_pidfile_running "${pid_file}")" +} + +# list specific server status +# arguments: +# arg1: server name +# returns: +# echo style: +# N/A +# return style: +# N/A +function list_server_status() +{ + local svr_name="$1" + + echo -n -e "${svr_name} status: " + if [ "$(is_server_running "${svr_name}")" = "TRUE" ]; then + local pid_file="$(make_server_pid_file_path "${svr_name}")" + echo -n "Running(pid: " + echo "`cat "${pid_file}"`)" + else + echo "Stopped" + fi +} + +# get server debug option +# arguments: +# arg1: server name +# returns: +# echo style: +# TRUE: build with debug +# FALSE: build with release +# return style: +# N/A +function get_server_debug_option() +{ + # get debug option(from option directory) + local dbg_opt_file="${____FWK_SVR_DEBUG_OPTION_FILE}" + if [ ! -e "${dbg_opt_file}" ]; then + echo -n FALSE + return + fi + + local dbg_opt="`cat "${dbg_opt_file}"`" + if [ "${dbg_opt}" != TRUE ]; then + echo -n FALSE + else + echo -n TRUE + fi +} + +# get server show name(appended all server options) +# arguments: +# arg1: server name +# returns: +# echo style: +# : the server show name, appended all server options +# return style: +# N/A +function get_server_show_name() +{ + local svr_name="$1" + local show_name="${svr_name}(" + + # append debug/release option + local dbg_opt="$(get_server_debug_option "${svr_name}")" + if [ "${dbg_opt}" = TRUE ]; then + show_name+="debug" + else + show_name+="release" + fi + + show_name+=")" + echo -n "${show_name}" +} + diff --git a/framework/shells/misc_scripts/lnx_fwk_uwsgi.sh b/framework/shells/misc_scripts/lnx_fwk_uwsgi.sh new file mode 100644 index 0000000..c92bdd0 --- /dev/null +++ b/framework/shells/misc_scripts/lnx_fwk_uwsgi.sh @@ -0,0 +1,139 @@ +# linux script framework uwsgi about function encapsulation +# Longwei Lai +################################################################### + +source "${____FWK_SCRIPT_PATH}/lnx_fwk_log.sh" +source "${____FWK_SCRIPT_PATH}/lnx_fwk_progress.sh" + +____UWSGI_PID_FILE=${WEBSVR_PATH}/.uwsgi.pid +____UWSGI_FIFO_FILE=${WEBSVR_PATH}/.uwsgi_master_fifo +____UWSGI_CFG_FILE="${WEBSVR_PATH}/uwsgi.ini" + +____UWSGI_LOG_PATH="${WEBSVR_PATH}/logs" +____UWSGI_LOG_FILE_NAME="${____UWSGI_LOG_PATH}/.webserver_`date "+%m_%d_%H_%M_%S.nohuplog"`" + +# makesure log path created +# !!! comment for onemt +# mkdir -p "${____UWSGI_LOG_PATH}" + +# start uwsgi +function start_uwsgi() +{ + if [ `is_uwsgi_running` = TRUE ]; then + log_err "uwsgi running(pid:`get_uwsgi_pid`), please stop first" + return 1 + fi + + # enter working directory + local old_pwd="`pwd`" + if [ "${old_pwd}" != "${WEBSVR_PATH}" ]; then + cd "${WEBSVR_PATH}" + fi + + # startup uwsgi + nohup uwsgi "${____UWSGI_CFG_FILE}" >>"${____UWSGI_LOG_FILE_NAME}" 2>&1 & + local uwsgi_pid=$! + + # check startup or not + local ret_code=$? + if [ ${ret_code} -ne 0 ]; then + log_err "start uwsgi failed, return code: ${ret_code}" + cd "${old_pwd}" + return 1 + fi + + # store uwsgi parent process pid to pid file. + echo -n ${uwsgi_pid} > "${____UWSGI_PID_FILE}" + + cd "${old_pwd}" + log_info "uwsgi started, master progress pid: ${uwsgi_pid}" +} + +# stop uwsgi +function stop_uwsgi() +{ + if [ `is_uwsgi_running` = FALSE ]; then + log_err 'uwsgi not running, stop failed!' + return 1 + fi + + local uwsgi_pid=`get_uwsgi_pid` + stop_pidfile "${____UWSGI_PID_FILE}" + + log_info "uwsgi(pid:${uwsgi_pid}) stopped!" +} + +# 重新载入py代码(在代码有修改后调用,将完成所有workers的优雅重启) +function reload_uwsgi() +{ + reload_uwsgi_check + if [ $? != 0 ]; then + return 1 + fi + + # Send SIGHUP to reload. + # local uwsgi_pid=`get_uwsgi_pid` + # kill -SIGHUP ${uwsgi_pid} + + # Write 'r' command to fifo file + echo 'r' > "${____UWSGI_FIFO_FILE}" + + log_info 'all uwsgi workers reloaded!' +} + +function chain_reload_uwsgi() +{ + reload_uwsgi_check + if [ $? != 0 ]; then + return 1 + fi + + # Write 'c' command to fifo file + echo 'c' > "${____UWSGI_FIFO_FILE}" + + sleep 6.18 + log_info 'all uwsgi workers reloaded!' +} + +function reload_uwsgi_check() +{ + if [ `is_uwsgi_running` = FALSE ]; then + log_warn "uwsgi not start, will startup first" + start_uwsgi + if [ $? != 0 ]; then + return 1 + fi + + log_info 'wait 5 seconds to wait all uwsgi workers startup done...' + sleep 5 + fi + + return 0 +} + +function show_uwsgi_status() +{ + if [ `is_uwsgi_running` = TRUE ]; then + log_info "uwsgi running, pid:`get_uwsgi_pid`" + echo 's' > "${____UWSGI_FIFO_FILE}" + else + log_info "uwsgi stopped!" + fi +} + +# 确认uwsgi是否在运行中 +function is_uwsgi_running() +{ + is_pidfile_running "${____UWSGI_PID_FILE}" +} + +# 取得uwsgi pid +function get_uwsgi_pid() +{ + if [ `is_uwsgi_running` != "TRUE" ]; then + echo -n "0" + else + echo -n "`cat "${____UWSGI_PID_FILE}"`" + fi +} + diff --git a/framework/shells/misc_scripts/lnx_logic.sh b/framework/shells/misc_scripts/lnx_logic.sh new file mode 100644 index 0000000..687c87d --- /dev/null +++ b/framework/shells/misc_scripts/lnx_logic.sh @@ -0,0 +1,16 @@ +# linux script logic header file +# Longwei Lai +################################################################### + +____LOGIC_SCRIPT_PATH="$(cd "${MISC_SCRIPTS_PATH}" && pwd)" + +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_misc.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_defs.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_serverupdate.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_serverbuild.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_serverctrl.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_servertar.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_serverhotfix.sh" + +# source "${____LOGIC_SCRIPT_PATH}/lnx_logic_mcctrl.sh" + diff --git a/framework/shells/misc_scripts/lnx_logic_defs.sh b/framework/shells/misc_scripts/lnx_logic_defs.sh new file mode 100644 index 0000000..420418d --- /dev/null +++ b/framework/shells/misc_scripts/lnx_logic_defs.sh @@ -0,0 +1,31 @@ +# linux script logic macros define +# Longwei Lai +################################################################### + +# server names define +____LOGIC_LOGIN=loginserver +____LOGIC_GLOBAL=globalserver +____LOGIC_GAME=gameserver +____LOGIC_WEB=webserver + +# server start files define +____START_FILE_LOGIN=.startsuccess_login +____START_FILE_GLOBAL=.startsuccess_global +____START_FILE_GAME=.startsuccess_game +____START_FILE_WEB=.startsuccess_web + +# server start/stop result files define +____START_RESULT_FILE_LOGIN=.startresult_login +____START_RESULT_FILE_GLOBAL=.startresult_global +____START_RESULT_FILE_GAME=.startresult_game +____START_RESULT_FILE_WEB=.startresult_web + +# game server some max-wait time define +____GAME_SERVER_START_MAX_WAIT=7200 +____GAME_SERVER_STOP_MAX_WAIT=7200 + +# server http key, use to sign message +____SERVER_HTTP_KEY="cGyZCP2QXgG#" + +# server start config file name +____APP_START_CONF_NAME=.app.lnx.cfg diff --git a/framework/shells/misc_scripts/lnx_logic_misc.sh b/framework/shells/misc_scripts/lnx_logic_misc.sh new file mode 100644 index 0000000..b900fa7 --- /dev/null +++ b/framework/shells/misc_scripts/lnx_logic_misc.sh @@ -0,0 +1,147 @@ +# linux script logic misc functions encapsulation +# Longwei Lai +################################################################### + +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_defs.sh" + +function list_help() +{ + # foreground/background: 3:foreground 4:background + # colors comments: 0:black 1:red 2:green 3:yellow 4:blue 5:purple 6:sky-blue 7:white + # reset all: \e[0m + + clear + echo -e "\e[1;35musage: ./run [command-options...]\e[0m" + echo -e "\e[1;37m-----------------------------------------------------------------------\e[0m" + echo -e " \e[1;32mserver build/tarball commands:\e[0m" + echo -e " \e[1;33mup\e[0m: update from git" + echo -e " \e[1;33mver_info\e[0m: show version info" + echo -e " \e[1;33mswitch_branch\e[0m: <\e[1;37mthe_new_branch\e[0m>: switch branch" + echo -e " \e[1;33mmake\e[0m/\e[1;33mbuild\e[0m <\e[1;37mdebug\e[0m/\e[1;37mrelease\e[0m> <\e[1;37mproduct\e[0m/\e[1;37minternal\e[0m/\e[1;37mpressure_test\e[0m\e[0m>: " + echo " build servers, default build release version servers" + echo " -- default use release setting to build" + echo " -- default use product setting to build" + echo -e " \e[1;33mtar\e[0m <\e[1;37mdebug\e[0m/\e[1;37mrelease\e[0m> <\e[1;37mproduct\e[0m/\e[1;37minternal\e[0m/\e[1;37mpressure_test\e[0m> <\e[1;37mfull\e[0m/\e[1;37mpure\e[0m>: tarball servers" + echo " tarball servers default build all versions servers" + echo " -- default use release setting to build" + echo " -- default use product setting to build" + echo " -- default use pure setting to puild" + echo -e "\e[1;37m-----------------------------------------------------------------------\e[0m" + echo -e " \e[1;32mserver control commands:\e[0m" + echo -e " \e[1;33mstart_xxx\e[0m: start server" + echo -e " start specific server:" + echo -e " \e[1;33mstop_xxx\e[0m: stop server" + echo -e " \e[1;33msafestop_xxx\e[0m: safe stop server" + echo -e " \e[1;33mrestart_xxx\e[0m: restart server" + echo -e " \e[1;33mset_xxx_logic_status\e[0m: set server logic status(progress independent)" + echo -e " \e[1;32mserver types:\e[0m" + echo -e " * \e[1;36mgameserver:\e[0m game server, for get more informations about gameserver, run \e[1;37m./run help_gameserver\e[0m" + echo -e " * \e[1;36mloginserver:\e[0m login server, for get more informations about loginserver, run \e[1;37m./run help_loginserver\e[0m" + echo -e "\e[1;37m-----------------------------------------------------------------------\e[0m" + echo -e " \e[1;32mfaketime commands:\e[0m" + echo -e " \e[1;33mread_faketime\e[0m: read now fake time setting" + echo -e " \e[1;33mclear_faketime\e[0m: clear fake time setting" + echo -e "\e[1;37m-----------------------------------------------------------------------\e[0m" + echo -e " \e[1;32mtest commands:\e[0m" + echo " ...." +} + +function list_help_gameserver() +{ + clear + echo -e "\e[1;35mgameserver manaual page:\e[0m" + echo -e "\e[1;37m-----------------------------------------------------------------------\e[0m" + echo -e " \e[1;33mstart_gameserver\e[0m: <\e[1;37mfakedate=xxxx-xx-xx\e[0\e[0m> <\e[1;37mfaketime=xx:xx:xx\e[0m> <\e[1;37mdontfaketime=YES/NO\e[0m>" + echo -e " \e[0;32mstart gameserver\e[0m" + echo -e " \e[1;37mfakedate=xxxx-xx-xx\e[0m: fake date start, eg: 2017-08-08" + echo -e " \e[1;37mfaketime=xx:xx:xx\e[0m: fake time start, eg: 08-30-59" + echo -e " \e[1;37mdontfaketime=YES/NO\e[0m: use fake time setting or not, default is NO" + echo -e " \e[1;33mstop_gameserver\e[0m:" + echo -e " \e[0;32mstop gameserver, for now, don't need any arguments to specific\e[0m" + echo -e " \e[1;33mrestart_gameserver\e[0m: <\e[1;37mfakedate=xxxx-xx-xx\e[0\e[0m> <\e[1;37mfaketime=xx:xx:xx\e[0m> <\e[1;37mdontfaketime=YES/NO\e[0m>" + echo -e " \e[0;32mrestart gameserver\e[0m" + echo -e " \e[1;37mfakedate=xxxx-xx-xx\e[0m: fake date start, eg: 2017-08-08" + echo -e " \e[1;37mfaketime=xx:xx:xx\e[0m: fake time start, eg: 08-30-59" + echo -e " \e[1;37mdontfaketime=YES/NO\e[0m: use fake time setting or not, default is NO" + echo -e " \e[1;33msafestop_gameserver\e[0m:" + echo -e " \e[0;32mlike stop_gameserver, but this command will safety stop gameserver(all game data will be saved before gameservers killed)\e[0m" + echo -e " \e[1;33mgameserver_status\e[0m:" + echo -e " \e[0;32mlist gameserver status\e[0m" + echo -e " \e[1;33mgameserver_logic_status\e[0m:" + echo -e " \e[0;32mlist gameserver logic status(progress independent)\e[0m" + echo -e " \e[1;33mset_gameserver_logic_status\e[0m:" + echo -e " \e[0;32mset gameserver logic status(progress independent)\e[0m" +} + +function list_help_loginserver() +{ + clear + echo -e "\e[1;35mloginserver manaual page:\e[0m" + echo -e "\e[1;37m-----------------------------------------------------------------------\e[0m" + echo -e " \e[1;33mstart_loginserver\e[0m: <\e[1;37mfakedate=xxxx-xx-xx\e[0\e[0m> <\e[1;37mfaketime=xx:xx:xx\e[0m> <\e[1;37mdontfaketime=YES/NO\e[0m>" + echo -e " \e[0;32mstart loginserver\e[0m" + echo -e " \e[1;37mfakedate=xxxx-xx-xx\e[0m: fake date start, eg: 2017-08-08" + echo -e " \e[1;37mfaketime=xx:xx:xx\e[0m: fake time start, eg: 08-30-59" + echo -e " \e[1;37mdontfaketime=YES/NO\e[0m: use fake time setting or not, default is NO" + echo -e " \e[1;33mstop_loginserver\e[0m:" + echo -e " \e[0;32mstop loginserver, for now, don't need any arguments to specific\e[0m" + echo -e " \e[1;33mrestart_loginserver\e[0m: <\e[1;37mfakedate=xxxx-xx-xx\e[0\e[0m> <\e[1;37mfaketime=xx:xx:xx\e[0m> <\e[1;37mdontfaketime=YES/NO\e[0m>" + echo -e " \e[0;32mrestart loginserver\e[0m" + echo -e " \e[1;37mfakedate=xxxx-xx-xx\e[0m: fake date start, eg: 2017-08-08" + echo -e " \e[1;37mfaketime=xx:xx:xx\e[0m: fake time start, eg: 08-30-59" + echo -e " \e[1;37mdontfaketime=YES/NO\e[0m: use fake time setting or not, default is NO" + echo -e " \e[1;33msafestop_loginserver\e[0m:" + echo -e " \e[0;32mlike stop_loginserver, but this command will safety stop loginserver(all game data will be saved before loginservers killed)\e[0m" + echo -e " \e[1;33mloginserver_status\e[0m:" + echo -e " \e[0;32mlist loginserver status\e[0m" + echo -e " \e[1;33mloginserver_logic_status\e[0m:" + echo -e " \e[0;32mlist loginserver logic status(progress independent)\e[0m" + echo -e " \e[1;33mset_loginserver_logic_status\e[0m:" + echo -e " \e[0;32mset loginserver logic status(progress independent)\e[0m" +} + + +function env_ensure() +{ + # force source /etc/profile + source /etc/profile + + # add SVR_PATH to LD_LIBRARY_PATH variable and export it + export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${SVR_PATH}" + + # setup linux sys listen backlog size(dynamic change) to ${willsetbacklog} + local willsetbacklog=4096 + local smcfile=/proc/sys/net/core/somaxconn + if [ -w "${smcfile}" ]; then + local oldbacklog=`cat "${smcfile}"` + if [ "$oldbacklog" -lt "${willsetbacklog}" ]; then + log_warn "system listen backlog config[${oldbacklog}] too small, try to config to 4096..." + echo -n ${willsetbacklog} > "${smcfile}" + local retcode=$? + if [ "${retcode}" -ne 0 ]; then + log_err "modify listen backklog size failed, permission deny? return code: ${retcode}" + exit $retcode + fi + fi + fi + + # setup max open file limits to 65535 + ulimit -n 65535 + retcode=$? + if [ "${retcode}" -ne 0 ]; then + echo "mofify max open file size failed, return code: ${retcode}" + exit $retcode + fi + + # setup coredump file limits to ulimited + ulimit -c unlimited + retcode=$? + if [ "${retcode}" -ne 0 ]; then + echo "mofify coredump file limits to ulimited failed, return code: ${retcode}" + exit $retcode + fi + + export SKYNET_THREAD=`cat /proc/cpuinfo | grep "processor" |wc -l` + echo "SKYNET_THREAD is" ${SKYNET_THREAD} +} + diff --git a/framework/shells/misc_scripts/lnx_logic_serverbuild.sh b/framework/shells/misc_scripts/lnx_logic_serverbuild.sh new file mode 100644 index 0000000..b3647ae --- /dev/null +++ b/framework/shells/misc_scripts/lnx_logic_serverbuild.sh @@ -0,0 +1,65 @@ +# linux script logic server build(included tarball) about functions encapsulation +# Longwei Lai +################################################################### + +source "${____LOGIC_SCRIPT_PATH}/lnx_fwk_defs.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_defs.sh" + +# return make server +function make_server() +{ + local debug_opt=FALSE + local isdebug_prompt=release + local make_flags="config=release" + local debug_flag="`echo -n "$1" | tr [:lower:] [:upper:]`" + if [ "${debug_flag}" = "DEBUG" -o "${debug_flag}" = "DBG" -o "${debug_flag}" = "D" ]; then + debug_opt=TRUE + isdebug_prompt=debug + make_flags="config=debug" + fi + + local product_opt="product" + local premake_path="${____LOGIC_SCRIPT_PATH}/" + local internal_flag="`echo -n "$2" | tr [:lower:] [:upper:]`" + if [ "${internal_flag}" = "INTERNAL" -o "${internal_flag}" = "INTERNAL_ENV" -o "${internal_flag}" = "INL" ]; then + product_opt="internal" + elif [ "${internal_flag}" = "PRESSURE_TEST" -o "${internal_flag}" = "PRESSURE_TEST_ENV" -o "${internal_flag}" = "PT" ]; then + product_opt="pressure_test" + fi + + # stop all servers + log_info "stop all servers..." + stop_all_servers 1>/dev/null 2>&1 + + # make + log_info "making(${isdebug_prompt}, ${product_opt})..." + + local make_log_path="${SVR_PATH}/.make_logs" + mkdir -p "${make_log_path}" + + local make_log_file="${make_log_path}/make_log.`date '+%Y%m%d_%H%M%S_%N'`" + log_info "make log redirect to [${make_log_file}]..." + ( cd "${SVR_PATH}" && make linux "${make_flags}" 2>&1 | tee "${make_log_file}" | grep -e '\(error\)\|\(warning\)'; exit ${PIPESTATUS[0]} ) + if [ $? -ne 0 ]; then + log_err "make server failed" + return 1 + fi + + # generate option file + log_info "generate server options..." + mkdir -p "${____FWK_SVR_OPTIONS_PATH}" + echo -n ${debug_opt} > "${____FWK_SVR_DEBUG_OPTION_FILE}" +} + +function make_project() +{ + local projname="$1" + local make_flags="$2" + local make_log_file="$3" + log_info "building ${projname} project..." + ( cd "${make_path}" && make -f "${projname}.make" "${make_flags}" 2>&1 | tee "${make_log_file}" | grep -v '): warning CS.*'; exit ${PIPESTATUS[0]} ) + if [ $? -ne 0 ]; then + log_err "make failed(build ${projname} project failed)" + return 1 + fi +} diff --git a/framework/shells/misc_scripts/lnx_logic_serverconf.sh b/framework/shells/misc_scripts/lnx_logic_serverconf.sh new file mode 100644 index 0000000..dd54aa6 --- /dev/null +++ b/framework/shells/misc_scripts/lnx_logic_serverconf.sh @@ -0,0 +1,37 @@ +# linux script server config file about functions encapsulation. +# Longwei Lai +################################################################### + +source "${____FWK_SCRIPT_PATH}/lnx_fwk_path.sh" +source "${____FWK_SCRIPT_PATH}/lnx_fwk_server.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_defs.sh" + +# get game node id config from server config file +function get_serverconf_gamenode_id() +{ + local dbconf_file="${SVR_PATH}/dbconf.lua" + echo -n "`sed -n -r 's/dbconf\.gamenodeid\s*=\s*([0-9]+).*/\1/p' "${dbconf_file}"`" +} + +# get login node id config from server config file +function get_serverconf_loginnode_id() +{ + local dbconf_file="${SVR_PATH}/dbconf.lua" + echo -n "`sed -n -r 's/dbconf\.loginnodeid\s*=\s*([0-9]+).*/\1/p' "${dbconf_file}"`" +} + +# get login node id config from server config file +function get_serverconf_globalnode_id() +{ + local dbconf_file="${SVR_PATH}/dbconf.lua" + echo -n "`sed -n -r 's/dbconf\.globalnodeid\s*=\s*([0-9]+).*/\1/p' "${dbconf_file}"`" +} + +# get login node id config from server config file +function get_serverconf_webnode_id() +{ + local dbconf_file="${SVR_PATH}/dbconf.lua" + echo -n "`sed -n -r 's/dbconf\.webnodeid\s*=\s*([0-9]+).*/\1/p' "${dbconf_file}"`" +} + + diff --git a/framework/shells/misc_scripts/lnx_logic_serverctrl.sh b/framework/shells/misc_scripts/lnx_logic_serverctrl.sh new file mode 100644 index 0000000..29695de --- /dev/null +++ b/framework/shells/misc_scripts/lnx_logic_serverctrl.sh @@ -0,0 +1,699 @@ +# linux script server control logic functions encapsulation. +# Longwei Lai +################################################################### + +source "${____FWK_SCRIPT_PATH}/lnx_fwk_path.sh" +source "${____FWK_SCRIPT_PATH}/lnx_fwk_server.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_defs.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_serverhttp.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_serversql.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_serverconf.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_serverstatus.sh" + +function server_ctrl_cmd_adapt() +{ + local args=($@) + + local cmd=${args[0]} + local cmd_type=${cmd%%_*} + local server_type=${cmd#*_} + local ctrl_args=${args[@]:1} + + if [ "${server_type}" = "status" -o "${server_type}" = "st" -o\ + "${server_type}" = "logic_status" -o "${server_type}" = "logic_st" ]; then + local tmp=${cmd_type} + cmd_type=${server_type} + server_type=${tmp} + fi + + case "${cmd_type}" in + start) + server_ctrl_start ${server_type} ${ctrl_args} + ;; + stop) + server_ctrl_stop ${server_type} ${ctrl_args} + ;; + safestop) + server_ctrl_safestop ${server_type} ${ctrl_args} + ;; + restart) + server_ctrl_restart ${server_type} ${ctrl_args} + ;; + status) + server_ctrl_status ${server_type} ${ctrl_args} + ;; + logic_status) + server_ctrl_logic_status ${server_type} ${ctrl_args} + ;; + set) + server_ctrl_set ${server_type} ${ctrl_args} + ;; + + *) + list_help + return 0 + ;; + esac +} + +function server_ctrl_start() +{ + local args=($@) + + local server_type=${args[0]} + local start_args=${args[@]:1} + + case "${server_type}" in + devilserver) + start_gameserver ${start_args} + ;; + ruinsserver) + start_gameserver ${start_args} + ;; + gameserver) + start_gameserver ${start_args} + ;; + pyramidserver) + start_gameserver ${start_args} + ;; + legendserver) + start_gameserver ${start_args} + ;; + cloudserver) + start_gameserver ${start_args} + ;; + loginserver) + start_loginserver ${start_args} + ;; + globalserver) + start_globalserver ${start_args} + ;; + webserver) + start_webserver ${start_args} + ;; + *) + log_err "unknown server[${server_type}], start server failed" + return 1 + ;; + esac +} + +function server_ctrl_stop() +{ + local args=($@) + + local server_type=${args[0]} + local stop_args=${args[@]:1} + + case "${server_type}" in + devilserver) + stop_gameserver ${stop_args} + ;; + ruinsserver) + stop_gameserver ${stop_args} + ;; + gameserver) + stop_gameserver ${stop_args} + ;; + pyramidserver) + stop_gameserver ${stop_args} + ;; + legendserver) + stop_gameserver ${stop_args} + ;; + cloudserver) + stop_gameserver ${stop_args} + ;; + loginserver) + stop_loginserver ${stop_args} + ;; + globalserver) + stop_globalserver ${stop_args} + ;; + webserver) + stop_webserver ${stop_args} + ;; + *) + log_err "unknown server[${server_type}], stop server failed" + return 1 + ;; + esac +} + +function server_ctrl_safestop() +{ + local args=($@) + + local server_type=${args[0]} + local stop_args=${args[@]:1} + + case "${server_type}" in + devilserver) + safestop_gameserver ${stop_args} + ;; + ruinsserver) + safestop_gameserver ${stop_args} + ;; + gameserver) + safestop_gameserver ${stop_args} + ;; + pyramidserver) + safestop_gameserver ${stop_args} + ;; + legendserver) + safestop_gameserver ${stop_args} + ;; + cloudserver) + safestop_gameserver ${stop_args} + ;; + loginserver) + safestop_loginserver ${stop_args} + ;; + globalserver) + safestop_globalserver ${stop_args} + ;; + webserver) + safestop_webserver ${stop_args} + ;; + *) + log_err "unknown server[${server_type}], safestop server failed" + return 1 + ;; + esac +} + +function server_ctrl_restart() +{ + local args=($@) + + local server_type=${args[0]} + local restart_args=${args[@]:1} + + case "${server_type}" in + devilserver) + restart_gameserver ${restart_args} + ;; + ruinsserver) + restart_gameserver ${restart_args} + ;; + gameserver) + restart_gameserver ${restart_args} + ;; + pyramidserver) + restart_gameserver ${restart_args} + ;; + legendserver) + restart_gameserver ${restart_args} + ;; + cloudserver) + restart_gameserver ${restart_args} + ;; + loginserver) + restart_loginserver ${restart_args} + ;; + globalserver) + restart_globalserver ${restart_args} + ;; + webserver) + restart_webserver ${restart_args} + ;; + *) + log_err "unknown server[${server_type}], restart server failed" + return 1 + ;; + esac +} + +function server_ctrl_status() +{ + local args=($@) + + local server_type=${args[0]} + local list_args=${args[@]:1} + + case "${server_type}" in + devilserver) + list_gameserver_status ${list_args} + ;; + ruinsserver) + list_gameserver_status ${list_args} + ;; + gameserver) + list_gameserver_status ${list_args} + ;; + pyramidserver) + list_gameserver_status ${list_args} + ;; + legendserver) + list_gameserver_status ${list_args} + ;; + cloudserver) + list_gameserver_status ${list_args} + ;; + loginserver) + list_loginserver_status ${list_args} + ;; + globalserver) + list_globalserver_status ${list_args} + ;; + webserver) + list_webserver_status ${list_args} + ;; + *) + log_err "unknown server[${server_type}], restart server failed" + return 1 + ;; + esac +} + +function server_ctrl_logic_status() +{ + local args=($@) + + local server_type=${args[0]} + local list_args=${args[@]:1} + + case "${server_type}" in + gameserver) + echo "gameserver logic status:" + echo "`get_gameserver_logic_status`" + ;; + pyramidserver) + echo "pyramidserver logic status:" + echo "`get_gameserver_logic_status`" + ;; + legendserver) + echo "legendserver logic status:" + echo "`get_gameserver_logic_status`" + ;; + devilserver) + echo "devilserver logic status:" + echo "`get_gameserver_logic_status`" + ;; + cloudserver) + echo "cloudserver logic status:" + echo "`get_gameserver_logic_status`" + ;; + *) + echo "${server_type} logic status: Unknown" + ;; + esac +} + +function server_ctrl_set() +{ + local args=($@) + + local server_type_and_set_type=${args[0]} + local list_args=${args[@]:1} + + case "${server_type_and_set_type}" in + gameserver_logic_status|pyramidserver_logic_status|legendserver_logic_status|devilserver_logic_status) + set_gameserver_logic_status ${list_args} + ;; + *) + log_info "for now, not support <${server_type_and_set_type}> command!" + ;; + esac +} + +function start_gameserver() +{ + echo "game start config"; + check_app_type "gs" + + local args=$@ + local server_entry="${FRAMEWORK_PATH}/skynet/skynet" + local start_file="${SVR_PATH}/${____START_FILE_GAME}" + local result_file="${SVR_PATH}/${____START_RESULT_FILE_GAME}" + + start_server_and_wait "${____LOGIC_GAME}" "${server_entry}" "${____APP_START_CONF_NAME}" "${start_file}" "${result_file}" ${args} +} + +function stop_gameserver() +{ + local args=$@ + stop_server "${____LOGIC_GAME}" ${args} + + local start_file="${SVR_PATH}/${____START_FILE_GAME}" + remove_server_start_file "${start_file}" +} + +function restart_gameserver() +{ + local args=$@ + stop_gameserver ${args} + echo "Sleep 5 second..." + sleep 5 + + start_gameserver ${args} +} + +function list_gameserver_status() +{ + local args=$@ + + list_server_status "${____LOGIC_GAME}" ${args} +} + +function start_loginserver() +{ + echo "login start config"; + check_app_type "ls" + + local args=$@ + local server_entry="${FRAMEWORK_PATH}/skynet/skynet" + local start_file="${SVR_PATH}/${____START_FILE_LOGIN}" + local result_file="${SVR_PATH}/${____START_RESULT_FILE_LOGIN}" + + start_server_and_wait "${____LOGIC_LOGIN}" "${server_entry}" "${____APP_START_CONF_NAME}" "${start_file}" "${result_file}" ${args} +} + +function stop_loginserver() +{ + local args=$@ + stop_server "${____LOGIC_LOGIN}" ${args} + + local start_file="${SVR_PATH}/${____START_FILE_LOGIN}" + remove_server_start_file "${start_file}" +} + +function restart_loginserver() +{ + local args=$@ + + stop_loginserver $args + echo "Sleep 5 second..." + sleep 5 + + start_loginserver $args +} + +function list_loginserver_status() +{ + local args=$@ + list_server_status "${____LOGIC_LOGIN}" ${args} +} + +function auto_update_db() +{ + local arg_env=`cat ${____APP_START_CONF_NAME} | grep app_env | awk -F\" '{print $2}'` + if [[ ${arg_env} == "dev" ]] || [[ ${arg_env} == "sit" ]]; then + local dbconf_name=dbconf.lua + sh ${MISC_SCRIPTS_PATH}/lnx_logic_updatedb.sh ${dbconf_name} + fi +} + +function start_globalserver() +{ + echo "global start config"; + check_app_type "ms" + + auto_update_db + + local args=$@ + local server_entry="${FRAMEWORK_PATH}/skynet/skynet" + local start_file="${SVR_PATH}/${____START_FILE_GLOBAL}" + local result_file="${SVR_PATH}/${____START_RESULT_FILE_GLOBAL}" + start_server_and_wait "${____LOGIC_GLOBAL}" "${server_entry}" "${____APP_START_CONF_NAME}" "${start_file}" "${result_file}" ${args} +} + +function stop_globalserver() +{ + local args=$@ + stop_server "${____LOGIC_GLOBAL}" ${args} + + local start_file="${SVR_PATH}/${____START_FILE_GLOBAL}" + remove_server_start_file "${start_file}" +} + +function restart_globalserver() +{ + local args=$@ + + stop_globalserver $args + echo "Sleep 5 second..." + sleep 5 + + start_globalserver $args +} + +function list_globalserver_status() +{ + local args=$@ + list_server_status "${____LOGIC_GLOBAL}" ${args} +} + +function check_app_type() { + cat ${____APP_START_CONF_NAME}; + + local tar_type=${1} + local app_type=`cat ${____APP_START_CONF_NAME} | grep app_type | awk -F\" '{print $2}'` + if [[ "${app_type}" != "${tar_type}" ]]; then + echo "app_type[${app_type}] error" + exit 1 + fi +} + +function start_webserver() +{ + echo "web start config"; + check_app_type "ws" + + local args=$@ + local server_entry="${FRAMEWORK_PATH}/skynet/skynet" + local start_file="${SVR_PATH}/${____START_FILE_WEB}" + local result_file="${SVR_PATH}/${____START_RESULT_FILE_WEB}" + + start_server_and_wait "${____LOGIC_WEB}" "${server_entry}" "${____APP_START_CONF_NAME}" "${start_file}" "${result_file}" ${args} +} + +function stop_webserver() +{ + local args=$@ + stop_server "${____LOGIC_WEB}" ${args} + + local start_file="${SVR_PATH}/${____START_FILE_WEB}" + remove_server_start_file "${start_file}" +} + +function restart_webserver() +{ + local args=$@ + + stop_webserver $args + echo "Sleep 5 second..." + sleep 5 + + start_webserver $args +} + +function list_webserver_status() +{ + local args=$@ + list_server_status "${____LOGIC_WEB}" ${args} +} + +function start_server_and_wait() +{ + local server_name="$1" + local server_entry="$2" + local server_config_file="$3" + local start_file="$4" + local result_file="$5" + + # parse arguments and get server start args + local args=($@) + local start_args="${args[@]:5}" + + # remove start result file first + \rm -rf "${result_file}" + + \rm -rf "${start_file}" + + # start loginserver + start_server "${server_name}" "${server_entry}" ${server_config_file} ${start_args} + local start_ret=$? + if [ ${start_ret} -ne 0 ]; then + echo -n "Start ${server_name} failed(error code: 1)" > "${result_file}" + return 1 + fi + + # sleep 0.618 seconds, and then wait server login success + sleep 0.618 + + # wait loginserver start file + wait_server_start_file ${server_name} "${start_file}" + if [ $? -ne 0 ]; then + log_err "Wait server start file failed, start ${server_name} failed!" + \rm -rf "${start_file}" # force cleanup start file again + echo -n "Start ${server_name} failed(error code: 2)" > "${result_file}" + return 2 + fi + + echo -n "Start ${server_name} success" > "${result_file}" + + log_info "done!" +} + +function wait_server_start_file() +{ + local server_name="$1" + local start_file="$2" + local begin_wait=`date "+%s"` + + local wait_time=0 + local temp_file=".${server_name}.temp" + + log_info "wait for the ${server_name} to generate the startup successfully file..." + while [ ! -f "${start_file}" ]; do + echo -n "`list_server_status "${server_name}"`" > "${temp_file}" + if egrep "Stopped" "${temp_file}" > /dev/null; then + log_err "${server_name} stopped, failed to start server!" + + \rm -rf "${temp_file}" + return 1 + fi + + sleep 10 + wait_time=$((${wait_time} + 10)) + log_info "wait for the ${server_name} to generate the startup successfully file(${wait_time} seconds)..." + if [ ${wait_time} -ge ${____GAME_SERVER_START_MAX_WAIT} ]; then + log_err "wait for the ${server_name} startup time too long, kill it!" + + \rm -rf "${temp_file}" + stop_server "${____LOGIC_GAME}" + + \rm -rf "${start_file}" + + return 2 + fi + done + + \rm -rf "${temp_file}" + \rm -rf "${start_file}" + + return 0 +} + +function remove_server_start_file() +{ + local start_file="$1" + \rm -rf "${start_file}" +} + +function debugcontrol() +{ + local port=${1} + local commond=${2} + + exec 3<>/dev/tcp/127.0.0.1/${port} + if [ $? -ne 0 ]; then + log_err ${commond} "failed" + return 1 + fi + + echo "${commond}" >&3 + while read -r line + do if [[ "$line" == "" || "$line" == "" ]]; then + log_info "send ${commond}" $line; + break; + fi done <&3;exec 3<&-;exec 3>&-; + + return 0 +} + +function safestop_loginserver() +{ + local nodeid="`get_serverconf_loginnode_id`" + local port="`exec_sql_cmd_on_confdb "SELECT port FROM conf_debug WHERE nodeid = ${nodeid}"`" + debugcontrol ${port} "safestop exitlog" + + local args=$@ + stop_loginserver ${args} +} + +function safestop_globalserver() +{ + local nodeid="`get_serverconf_globalnode_id`" + local port="`exec_sql_cmd_on_confdb "SELECT port FROM conf_debug WHERE nodeid = ${nodeid}"`" + debugcontrol ${port} "safestop exitlog" + + local args=$@ + stop_globalserver ${args} +} + +function safestop_webserver() +{ + local nodeid="`get_serverconf_webnode_id`" + local port="`exec_sql_cmd_on_confdb "SELECT port FROM conf_debug WHERE nodeid = ${nodeid}"`" + debugcontrol ${port} "safestop exitlog" + + local args=$@ + stop_webserver ${args} +} + +function safestop_gameserver() +{ + # check running or not + local gs_st="`list_gameserver_status`" + if `list_gameserver_status | egrep 'Stopped' > /dev/null`; then + log_warn "gameserver not running" + return 0 + fi + + # set gameserver status to maintain + set_gameserver_logic_status 2 + if [ $? -ne 0 ]; then + log_err "send gameserver status to failed!" + log_err "safe stop gameserver failed!" + return 1 + fi + + # get telnet port + log_info "get telnet port..." + local gamenode_id="`get_serverconf_gamenode_id`" + local telnet_port="`exec_sql_cmd_on_confdb "SELECT port FROM conf_debug WHERE nodeid = ${gamenode_id}"`" + log_info "telnet port got: ${telnet_port}" + + # wait all agentlt services count down to 10 or less(we use python telnetlib standard lib to implement telnet access) + local max_agentlt_count=20 + local temp_pycode_file=".safestop_temp_pycode_file" + echo -e "from telnetlib import Telnet\nt = Telnet('127.0.0.1', ${telnet_port})\nt.write('list\\\n')\nagentlt_svcs_count = len([agentlt_svc for agentlt_svc in t.read_until('OK', 120).split('\\\n') if 'agentlt' in agentlt_svc])\nprint agentlt_svcs_count" > "${temp_pycode_file}" + local check_agentlt_ok=FALSE + local begin_check_time=`date "+%s"` + local max_check_time=${____GAME_SERVER_STOP_MAX_WAIT} + while [ "${check_agentlt_ok}" != "TRUE" ]; do + local used_time=$((`date "+%s"` - ${begin_check_time})) + if [ ${used_time} -ge ${max_check_time} ]; then + log_err "wait down to ${max_agentlt_count} or less timeout(used: ${used_time})!" + log_err "safe stop gameserver failed!" + return 1 + fi + + local now_agentlt_svcs_count="`python "${temp_pycode_file}"`" + if [ $? -ne 0 ]; then + log_err "failed to get server count, telnet error!" + log_err "safe stop gameserver failed!" + return 2 + fi + + log_info "wait for count down to ${max_agentlt_count} or less(now: ${now_agentlt_svcs_count}, used ${used_time} seconds)..." + if [ "${now_agentlt_svcs_count}" -lt 10 ]; then + check_agentlt_ok=TRUE + else + sleep 3 + fi + done + + rm -rf "${temp_pycode_file}" + + # wait 5 seconds, and then execute kill + log_info "wait 5 seconds, and then execute force stop..." + sleep 5 + + debugcontrol ${telnet_port} "safestop exitlog" + + local args=$@ + stop_gameserver ${args} +} diff --git a/framework/shells/misc_scripts/lnx_logic_serverhotfix.sh b/framework/shells/misc_scripts/lnx_logic_serverhotfix.sh new file mode 100644 index 0000000..e0ad7d8 --- /dev/null +++ b/framework/shells/misc_scripts/lnx_logic_serverhotfix.sh @@ -0,0 +1,88 @@ +# linux script logic server build(included tarball) about functions encapsulation +# HongWei Zheng +################################################################### + +source "${____LOGIC_SCRIPT_PATH}/lnx_fwk_defs.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_defs.sh" + + +############################################################ +# 解压热更包 +function _unzip_hotfix() +{ + echo "解压热更包... [${SVR_PATH}/${HF_ZIP_NAME}]" + cd ${SVR_PATH} && unzip -d ./ -o ./${HF_ZIP_NAME} +} + +function _exec_debug_cmd() +{ + local url='http://localhost:3001/' + local cmd=${1} + local ret=`curl ${url}${cmd}|grep 'CMD OK'` + if [ "${ret}" = "" ]; then + return 0 + fi + return 1 +} + +# 清除代码缓存 +function _clear_code_cache() +{ + echo "清理代码缓存..." + _exec_debug_cmd 'clearcache' + return $? +} + +# 检查外部参数 +function _check_hotfix_args() +{ + if [ -z "${HF_ZIP_NAME}" ]; then + echo "热更包名错误 [${HF_ZIP_NAME}]" + return 1 + fi + + local game=`ls ${SVR_PATH}|grep 'game'` + if [ "${game}" != "game" ]; then + echo "源码目录错误 [${SVR_PATH} ${game}]" + return 1 + fi + + if [ ! -s "${SVR_PATH}/${HF_ZIP_NAME}" ]; then + echo "热更包不存在 [${SVR_PATH}/${HF_ZIP_NAME}]" + return 1 + fi + + return 0 +} + +function hotfix_zip_gs() +{ + HF_ZIP_NAME=${1} #热更包名 + + _check_hotfix_args + if [ $? -ne 0 ]; then + echo "参数错误" + exit 1 + fi + + _exec_debug_cmd 'help' + if [ $? -ne 0 ]; then + echo "连接debug_control失败" + exit 1 + fi + + _unzip_hotfix + if [ $? -ne 0 ]; then + echo "解压热更包失败" + exit 1 + fi + + _clear_code_cache + if [ $? -ne 0 ]; then + echo "清除代码缓存失败" + exit 1 + fi + + echo "热更完成" ${HF_ZIP_NAME} + echo "热更完成" ${HF_ZIP_NAME} >> ./hotfixPkgLog.txt +} diff --git a/framework/shells/misc_scripts/lnx_logic_serverhttp.sh b/framework/shells/misc_scripts/lnx_logic_serverhttp.sh new file mode 100644 index 0000000..56700c7 --- /dev/null +++ b/framework/shells/misc_scripts/lnx_logic_serverhttp.sh @@ -0,0 +1,56 @@ +# linux script server http interactive logic functions encapsulation. +# Longwei Lai +################################################################### + +source "${____FWK_SCRIPT_PATH}/lnx_fwk_path.sh" +source "${____FWK_SCRIPT_PATH}/lnx_fwk_server.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_defs.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_serversql.sh" + +# send http message to specific node +# arguments: +# arg1: node +# arg2: message cmd(integer) +# arg3: sub message cmd(integer) +# arg4: http message(what message do you want to send?) +# returns: +# echo style: +# server return message +# return style: +# 0: success +# : failed +function send_httpmsg_to_node() +{ + local node="$1" + + local msg_cmd="$2" + local msg_subcmd="$3" + local http_msg="$4" + + local verify_key="${____SERVER_HTTP_KEY}" + local now_time=`date "+%s"` + + # calculate sign + local verify_sign="`echo -n "${now_time}${verify_key}" | md5sum | cut -d ' ' -f 1`" + + # get http access info + local http_info=(`exec_sql_cmd_on_confdb "SELECT web, port FROM conf_http WHERE nodeid = ${node}"`) + if [ $? -ne 0 ]; then + log_err "failed to get http access port from mysql!" + log_err "failed to send http message[${http_msg}] to node: ${node}!" + return 1 + fi + local http_addr="http://${http_info[0]}:${http_info[1]}" + + local http_packet="{\"cmd\":${msg_cmd}, \"subcmd\": ${msg_subcmd}, \"data\": ${http_msg}, \"sign\": \"${verify_sign}\", \"time\":${now_time}}" + local curl_ret="`curl --max-time 8 -s -d "${http_packet}" "${http_addr}"`" + local curl_ret_code=$? + if [ ${curl_ret_code} -ne 0 ]; then + echo -n "${curl_ret}" + return ${curl_ret_code} + else + echo -n "${curl_ret}" + return 0 + fi +} + diff --git a/framework/shells/misc_scripts/lnx_logic_serversql.sh b/framework/shells/misc_scripts/lnx_logic_serversql.sh new file mode 100644 index 0000000..214c75f --- /dev/null +++ b/framework/shells/misc_scripts/lnx_logic_serversql.sh @@ -0,0 +1,122 @@ +# linux script server sql about functions encapsulation. +# Longwei Lai +################################################################### + +source "${____FWK_SCRIPT_PATH}/lnx_fwk_path.sh" +source "${____FWK_SCRIPT_PATH}/lnx_fwk_server.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_defs.sh" + +# execute sql command on config database +# arguments: +# arg1: sql command +# returns: +# echo style: +# sql query return +# return style: +# 0: success +# : failed +function exec_sql_cmd_on_confdb() +{ + exec_sql_cmd mysql_confdb "$1" +} + +# execute sql command on game database +# arguments: +# arg1: sql command +# returns: +# echo style: +# sql query return +# return style: +# 0: success +# : failed +function exec_sql_cmd_on_gamedb() +{ + exec_sql_cmd mysql_gamedb "$1" +} + +# execute sql command on specific config database(in dbconf.lua) +# arguments: +# arg1: config name +# arg2: sql command +# returns: +# echo style: +# sql query return +# return style: +# 0: success +# : failed +function exec_sql_cmd() +{ + local dbcfg_name="$1" + local sql_stmt="$2" + local access_info=(`_get_db_access_info "${dbcfg_name}"`) + local sql_ret="`python2.6 ${____FWK_SCRIPT_PATH}/mysql_handler.py ${access_info[0]} ${access_info[1]} "${access_info[2]}" "${access_info[3]}" "${access_info[4]}" "${sql_stmt}"`" + if [ $? -ne 0 ]; then + echo -n "${sql_ret}" + return 1 + fi + + echo -n "${sql_ret}" +} + +# -------------------- internal implementation ---------------------- +function _get_db_access_info() +{ + local dbcfg_name="$1" + + local dbconf_file="${SVR_PATH}/dbconf.lua" + if [ ! -f "${dbconf_file}" ]; then + log_err "not found db config file: ${dbconf_file}" + return 1 + fi + + local can_grep=FALSE + local sql_host= + local sql_port= + local sql_user= + local sql_passwd= + local sql_db= + while read line; do + if [ ! -z "`echo -n "${line}" | grep "dbconf.${dbcfg_name}"`" ]; then + can_grep=TRUE + continue + fi + + if [ "${can_grep}" != TRUE ]; then + continue + fi + + local isend="`echo -n "${line}" | sed -n -r 's/\s*(})\s*/\1/p'`" + if [ ! -z "${isend}" ]; then + break + fi + + local gethost="`echo -n "${line}" | sed -n -r 's/\s*host\s*=\s*"(.*)"\s*,/\1/p'`" + if [ ! -z "${gethost}" ]; then + sql_host="${gethost}" + fi + + local getport="`echo -n "${line}" | sed -n -r 's/\s*port\s*=\s*(.*)\s*,/\1/p'`" + if [ ! -z "${getport}" ]; then + sql_port="${getport}" + fi + + local getuser="`echo -n "${line}" | sed -n -r 's/\s*user\s*=\s*"([^"]*)"\s*,/\1/p'`" + if [ ! -z "${getuser}" ]; then + sql_user="${getuser}" + fi + + local getpasswd="`echo -n "${line}" | sed -n -r 's/\s*password\s*=\s*"([^"]*)"\s*,/\1/p'`" + if [ ! -z "${getpasswd}" ]; then + sql_passwd="${getpasswd}" + fi + + local getdb="`echo -n "${line}" | sed -n -r 's/\s*database\s*=\s*"([^"]*)"\s*,/\1/p'`" + if [ ! -z "${getdb}" ]; then + sql_db="${getdb}" + fi + done < "${dbconf_file}" + + echo ${sql_host} ${sql_port} ${sql_user} ${sql_passwd} ${sql_db} +} + + diff --git a/framework/shells/misc_scripts/lnx_logic_serverstatus.sh b/framework/shells/misc_scripts/lnx_logic_serverstatus.sh new file mode 100644 index 0000000..a433893 --- /dev/null +++ b/framework/shells/misc_scripts/lnx_logic_serverstatus.sh @@ -0,0 +1,130 @@ +# linux script server logic status about functions encapsulation. +# Longwei Lai +################################################################### + +source "${____FWK_SCRIPT_PATH}/lnx_fwk_path.sh" +source "${____FWK_SCRIPT_PATH}/lnx_fwk_server.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_defs.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_serverhttp.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_serversql.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_serverconf.sh" + +# get gameserver logic status describe +# arguments: +# arg1: logic status(1,2,...) +# returns: +# echo style: +# the logic status describe. +# return style: +# N/A +function get_gameserver_logic_status_desc() +{ + local logic_status="$1" + if [ "${logic_status}" = 1 ]; then + echo -n "Normal(正常)" + elif [ "${logic_status}" = 2 ]; then + echo -n "Maintain(维护)" + elif [ "${logic_status}" = 3 ]; then + echo -n "New(新服)" + else + echo -n "Unknown(未知):${logic_status}" + fi +} + +# get gameserver logic status +# arguments: +# N/A +# returns: +# echo style: +# the gameserver all kingdoms status describe. +# return style: +# N/A +function get_gameserver_logic_status() +{ + # get all kingdom ids + local gamenode_id="`get_serverconf_gamenode_id`" + local kingdom_ids="`exec_sql_cmd_on_confdb "SELECT kid from conf_kingdom WHERE nodeid = ${gamenode_id}"`" + + for kingdom_id in ${kingdom_ids}; do + local sql_cmd="SELECT status FROM conf_kingdom WHERE kid = ${kingdom_id}" + local kingdom_status="`exec_sql_cmd_on_confdb "${sql_cmd}"`" + echo "kingdom ${kingdom_id} status: `get_gameserver_logic_status_desc "${kingdom_status}"`" + done +} + +# set gameserver logic status +# arguments: +# arg1: the new gameserver logic status(1, 2, ...) +# returns: +# echo style: +# N/A +# return style: +# 0: success +# : failed +function set_gameserver_logic_status() +{ + # get gameserver all kingdom ids + local gamenode_id="`get_serverconf_gamenode_id`" + local kingdom_ids="`exec_sql_cmd_on_confdb "SELECT kid from conf_kingdom WHERE nodeid = ${gamenode_id}"`" + + local new_status="$1" + log_info "set gameserver status to ${new_status}..." + + # if set status to maintain then enable ipwhitelist, otherwise disable ipwhitelist + local new_ipwhitelist_status=0 + if [ "${new_status}" = 2 ]; then + new_ipwhitelist_status=1 + fi + log_info "set gameserver ipwhitelist status to ${new_ipwhitelist_status}..." + + local send_http_msg_failed=FALSE + local loginnode_id="`get_serverconf_loginnode_id`" + log_info "call HTTP API to set status(send to login node:${loginnode_id})..." + for kingdom_id in ${kingdom_ids}; do + # modify conf_kingdom table + log_info "set kingdom ${kingdom_id} status to: ${new_status}..." + local msg="{\"kingdoms\": [${kingdom_id}], \"status\": ${new_status}}" + local res_msg="`send_httpmsg_to_node ${loginnode_id} 1 2 "${msg}"`" + local send_succ=$? + if [ ${send_succ} -ne 0 -o -z "`echo "${res_msg}" | egrep '"result"\s*:\s*1\s*,'`" ]; then + log_warn "failed to set kingdom ${kingdom_id} status, http res:${res_msg}, skip..." + send_http_msg_failed=TRUE + else + log_info "set kingdom ${kingdom_id} status to ${new_status} success, return: ${res_msg}" + fi + + # modify conf_ipwhitelist table + log_info "set kingdom ${kingdom_id} ipwhitelist status to: ${new_ipwhitelist_status}..." + local msg="{\"kid\": ${kingdom_id}, \"status\": ${new_ipwhitelist_status}}" + local res_msg="`send_httpmsg_to_node ${loginnode_id} 1 17 "${msg}"`" + local send_succ=$? + if [ ${send_succ} -ne 0 -o -z "`echo "${res_msg}" | egrep '"result"\s*:\s*true\s*,'`" ]; then + log_warn "failed to set kingdom ${kingdom_id} ipwhitelist status, res:${res_msg}!, skip..." + send_http_msg_failed=TRUE + fi + done + + if [ ${send_http_msg_failed} = TRUE ]; then + log_info "set some kingdom status failed, try update sql some tables to set status..." + for kingdom_id in ${kingdom_ids}; do + log_info "set kingdom ${kingdom_id} status to: ${new_status}..." + + local sql_cmd="UPDATE conf_kingdom SET status = ${new_status} WHERE kid = ${kingdom_id}" + exec_sql_cmd_on_confdb "${sql_cmd}" + local sql_succ=$? + if [ ${sql_succ} -ne 0 ]; then + log_err "failed to set kingdom ${kingdom_id} status, res:${res_msg}!" + return ${sql_succ} + fi + + sql_cmd="UPDATE conf_ipwhitelist SET status = ${new_ipwhitelist_status} WHERE kid = ${kingdom_id}" + exec_sql_cmd_on_confdb "${sql_cmd}" + sql_succ=$? + if [ ${sql_succ} -ne 0 ]; then + log_err "failed to set kingdom ${kingdom_id} ipwhitelist status, res:${res_msg}!" + return ${sql_succ} + fi + done + fi +} + diff --git a/framework/shells/misc_scripts/lnx_logic_servertar.sh b/framework/shells/misc_scripts/lnx_logic_servertar.sh new file mode 100644 index 0000000..0b905ae --- /dev/null +++ b/framework/shells/misc_scripts/lnx_logic_servertar.sh @@ -0,0 +1,314 @@ +# linux script server tarball functions encapsulation +# Longwei Lai +################################################################### + +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_defs.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_serverbuild.sh" + +# 创建启动配置 +function _create_start_file() { + log_info "create start config ${____APP_START_CONF_NAME} ..." + local server_name=${1} + local proj_name=${2} + local product_opt=${3} + local working_dir=${4} + local cfg_file="${SVR_PATH}/${____APP_START_CONF_NAME}" + + function _append_line() { + echo "${1}=\"${2}\"" >> ${cfg_file} + } + + function _append_include() { + echo "include \"${1}\"" >> ${cfg_file} + } + + rm -rf ${cfg_file} + + _append_include "framework/snconfig" + + case ${server_name} in + loginserver) + _append_include "login/snconfig" + _append_line "app_type" "ls" + ;; + globalserver) + _append_include "global/snconfig" + _append_line "app_type" "ms" + ;; + webserver) + _append_include "web/snconfig" + _append_line "app_type" "ws" + ;; + gameserver) + _append_include "game/snconfig" + _append_line "app_type" "gs" + ;; + ruinsserver) + _append_include "game/snconfig" + _append_line "app_type" "rs" + ;; + devilserver) + _append_include "game/snconfig" + _append_line "app_type" "ds" + ;; + pyramidserver) + _append_include "game/snconfig" + _append_line "app_type" "ps" + ;; + cloudserver) + _append_include "game/snconfig" + _append_line "app_type" "cp" + ;; + *) + log_info "未知[应用类型]参数" + return 1 + ;; + esac + + case ${proj_name} in + boe|ww) + _append_line "app_project" ${proj_name} + ;; + *) + log_info "未知[应用项目]参数" + return 1 + ;; + esac + + case ${product_opt} in + product) + _append_line "app_env" "pro" + ;; + internal) + _append_line "app_env" "sit" + ;; + *) + log_info "未知[应用环境]参数" + return 1 + ;; + esac + + \cp -rf "${cfg_file}" "${working_dir}/" + log_info "`cat ${cfg_file}`" + + return 0 +} + +function _copy_skynet() { + log_info "copy skynet ..." + + local super_dir=${1} + local skynet_from="${FRAMEWORK_PATH}/skynet" + local skynet_to="${super_dir}/skynet" + mkdir -p "${skynet_to}" + + local skynet_copy_files=(service cservice lualib luaclib skynet LICENSE HISTORY.md README.md) + for item in ${skynet_copy_files[@]}; do + \cp -rf "${skynet_from}/${item}" "${skynet_to}/${item}" + done +} + +function _copy_3rd() { + log_info "copy 3rd ..." + + local super_dir=${1} + local thirdrd_from="${FRAMEWORK_PATH}/3rd" + local thirdrd_to="${super_dir}/3rd" + + mkdir -p ${thirdrd_to} + + for item in `ls ${thirdrd_from}`; do + local item_from=${thirdrd_from}/${item} + local item_to=${thirdrd_to}/${item} + if [ -d "${item_from}" ]; then + mkdir -p ${item_to} + + if [ ! -z "`find "${item_from}" -type f -name '*.so'`" ]; then + \cp -rf "${item_from}/"*.so "${item_to}/" + fi + + if [ ! -z "`find "${item_from}" -type f -name '*.lua'`" ]; then + \cp -rf "${item_from}/"*.lua "${item_to}/" + fi + else + \cp -rf "${item_from}" "${item_to}" + fi + done +} + +function _copy_luasrc() { + local working_dir=${1} + local server_name=${2} + function _copy_dir() { + local name=${1} + log_info "copy ${name} lua src ..." + \cp -rf "${SVR_PATH}/${name}" "${working_dir}/" + } + + case ${server_name} in + loginserver) _copy_dir "login";; + globalserver) _copy_dir "global";; + webserver) _copy_dir "web";; + gameserver|ruinsserver|devilserver|pyramidserver|cloudserver) _copy_dir "game";; + *) + log_info "Unknown application type ${server_name}" + exit 1 + ;; + esac +} + +function _copy_others() { + log_info "copy others ..." + + local working_dir=${1} + \cp -rf "${____FWK_SVR_OPTIONS_PATH}" "${working_dir}/" + \cp -rf "${SVR_PATH}/run" "${working_dir}/run" +} + +function _create_version_file() { + log_info "creating server version file[SERVER_VERSION.txt] ..." + + local working_dir=${1} + local isdebug_prompt=${2} + local product_opt=${3} + local proj_name=${4} + local server_name=${5} + local svr_desc_file="${working_dir}/SERVER_VERSION.txt" + + echo -e "SERVER BUILD OPTION: ${isdebug_prompt}, ${product_opt}\n" > "${svr_desc_file}" + echo -e "--------------------------------------------------------------------------\n" >> "${svr_desc_file}" + echo -e "PROJECT NAME: ${proj_name}" >> "${svr_desc_file}" + echo -e "SERVER NAME: ${server_name}" >> "${svr_desc_file}" + echo -e "BUILD NUMBER: ${BUILD_NUMBER}" >> "${svr_desc_file}" + echo -e "WHY BUILD: ${WHY_BUILD}" >> "${svr_desc_file}" + echo -e "" >> "${svr_desc_file}" + echo -e "GIT REMOTE INFO:\n`git remote -v`" >> "${svr_desc_file}" + echo -e "GIT BRANCH: `git branch -v | grep -e '\*\s\+' | cut -d' ' -f2-`" >> "${svr_desc_file}" + echo -e "LAST 10 COMMITS:\n`git --no-pager log -n 10`" >> "${svr_desc_file}" + echo -e "BUILD TIME: `date "+%Y-%m-%d %H:%M:%S"`" >> "${svr_desc_file}" +} + +function _create_md5() { + local package_name=${1} + local checksum_file="${package_name}.md5" + md5sum "${package_name}" | cut -d' ' -f1 > "${checksum_file}" + + log_info "file checksum: `cat "${checksum_file}"`" +} + +function _clean_tmp_files() { + log_info "clean tmp files ..." + + local working_dir=${1} + find "${working_dir}" -type f -name '.*.pid' -print0 |xargs -0 rm -rf # .*.pid: pid files + find "${working_dir}" -type f -name '*.log.*' -print0 |xargs -0 rm -rf # *.log.xxxx-xx-xx: log files + find "${working_dir}" -type f -name '*.nohup' -print0 |xargs -0 rm -rf # .*.nohup: nohup files + find "${working_dir}" -type f -name '.*.nohup' -print0 |xargs -0 rm -rf # .*.nohup: nohup files +} + +function _tarball_files() { + log_info "tarball files ..." + + local working_dir=${1} + local package_name=${2} + + log_info "build reason: ${WHY_BUILD}" + log_info "tarball: ${package_name}" + + ( cd "${working_dir}" && tar -jcvf "${package_name}" ./ > /dev/null ) + if [ $? -ne 0 ]; then + log_err " build server tarball file failed" + else + \rm -rf "${working_dir}" + fi +} + +function _copy_framework() { + local working_dir=${1} + local item_to="${working_dir}/framework" + for item in `ls ${FRAMEWORK_PATH}`; do + case "${item}" in + skynet) _copy_skynet ${item_to};; + 3rd) _copy_3rd ${item_to};; + *) + echo "copy ${item} ..." + \cp -rf "${FRAMEWORK_PATH}/${item}" "${item_to}/" + ;; + esac + done +} + +function tar_server() { + echo tar_server $@ + + local dbg_flag="`echo -n "$1" | tr [:lower:] [:upper:]`" + local internal_flag="`echo -n "$2" | tr [:lower:] [:upper:]`" + local pure_opt="`echo -n "$3" | tr [:upper:] [:lower:]`" + local is_make="`echo -n "$4" | tr [:upper:] [:lower:]`" + + local dbg_opt=FALSE + local isdebug_prompt=release + if [ "${dbg_flag}" = "DEBUG" -o "${dbg_flag}" = "DBG" -o "${dbg_flag}" = "D" ]; then + dbg_opt=TRUE + isdebug_prompt=debug + fi + + # if not specific compile environment, compile all environments packages + if [ -z "${internal_flag}" ]; then # TODO: all environments server tarball files build will open at later + tar_server "${dbg_flag}" product + test $? -eq 0 || return 1 + return + fi + + local begin_tarball_time=`date +%s` + local product_opt="product" + if [ "${internal_flag}" = "INTERNAL" -o "${internal_flag}" = "INTERNAL_ENV" -o "${internal_flag}" = "INL" ]; then + product_opt=internal + elif [ "${internal_flag}" = "PRESSURE_TEST" -o "${internal_flag}" = "PRESSURE_TEST_ENV" -o "${internal_flag}" = "PT" ]; then + product_opt=pressure_test + fi + + local proj_name="${PROJECT_NAME}" + if [ -z "${proj_name}" ]; then + proj_name='ukn_proj' + fi + + local server_name="${SERVER_NAME}" + if [ -z "${server_name}" ]; then + server_name='ukn_svr' + fi + + # is need make + if [ "${is_make}" != "nomake" ]; then + log_info "making ..." + + make_server "${dbg_flag}" $2 + if [ $? -ne 0 ]; then + log_err "build server failed" + return 1 + fi + else + log_info "no make ..." + fi + + local tarball_dir="${SVR_PATH}/tarballs" + local working_dir="${tarball_dir}/onemt.server" + local package_name="${tarball_dir}/onemt.${proj_name}.server.${server_name}.${product_opt}.${pure_opt}.tar.bz2" + + mkdir -p "${tarball_dir}" + \rm -rf "${working_dir}" + mkdir -p "${working_dir}" + + _copy_framework ${working_dir} + _copy_others ${working_dir} + _copy_luasrc ${working_dir} ${server_name} + + _create_start_file ${server_name} ${proj_name} ${product_opt} ${working_dir} + _create_version_file ${working_dir} ${isdebug_prompt} ${product_opt} ${proj_name} ${server_name} + _clean_tmp_files ${working_dir} + _tarball_files ${working_dir} ${package_name} + _create_md5 ${package_name} + + local tarball_used=$((`date +%s` - ${begin_tarball_time})) + log_info "done! used time: ${tarball_used} seconds" +} diff --git a/framework/shells/misc_scripts/lnx_logic_serverupdate.sh b/framework/shells/misc_scripts/lnx_logic_serverupdate.sh new file mode 100644 index 0000000..3ca91bd --- /dev/null +++ b/framework/shells/misc_scripts/lnx_logic_serverupdate.sh @@ -0,0 +1,77 @@ +# linux script logic server update about functions encapsulation +# Longwei Lai +################################################################### + +source "${____LOGIC_SCRIPT_PATH}/lnx_fwk_defs.sh" +source "${____LOGIC_SCRIPT_PATH}/lnx_logic_defs.sh" + +# get version info(from git) +function get_server_version_info() +{ + log_info "Local Branch: `git branch -v | grep -e '\*\s\+' | cut -d ' ' -f 2`" + log_info "Remote Branch: `git remote`/`git branch -v | grep -e '\*\s\+' | cut -d ' ' -f 2`" + log_info "Last Commit Info:" + log_info "`git log -1 $(get_now_git_version_no)`" +} + +# update server(from git) +function update_server() +{ + local git_path="${SVR_PATH}" + log_info "now branch:$(get_now_branch)" + log_info "pull all updates from remote..." + ( cd ${git_path} && git pull origin ) + if [ $? -ne 0 ]; then + log_err "update server from git failed" + return 1 + fi + + log_info "update all submodules..." + ( cd ${git_path} && git submodule update --init --recursive ) + if [ $? -ne 0 ]; then + log_err "update server submodules failed" + return 1 + fi + + log_info "update success, update to: $(get_now_git_version_no)" +} + +# switch branch +function switch_branch() +{ + local new_branch="$1" + local old_branch="$(get_now_branch)" + if [ "${new_branch}" = "${old_branch}" ]; then + log_warn "same branch[${new_branch}], don't need switch" + return 0 + fi + + git checkout -b "${new_branch}" "origin/${new_branch}" + local co_ret=$? + if [ ${co_ret} -ne 0 ]; then + log_err "switch [${old_branch}] --> [${new_branch}] failed, checkout failed, return: ${co_ret}" + return 1 + fi + + git submodule update --init --recursive + local submodule_up_ret=$? + if [ ${submodule_up_ret} -ne 0 ]; then + log_err "switch [${old_branch}] --> [${new_branch}] failed, submodule update failed, return: ${co_ret}" + return 1 + fi + + log_info "switch [${old_branch}] --> [${new_branch}] success!" +} + +# get current branch +function get_now_branch() +{ + echo -n "`git branch | grep -e '\*\s\+' | cut -d ' ' -f 2-`" +} + +# get current now git version no +function get_now_git_version_no() +{ + echo -n "`git branch -v | grep -e '\*\s\+' | cut -d ' ' -f3`" +} + diff --git a/framework/shells/misc_scripts/lnx_logic_updatedb.sh b/framework/shells/misc_scripts/lnx_logic_updatedb.sh new file mode 100644 index 0000000..f8b153c --- /dev/null +++ b/framework/shells/misc_scripts/lnx_logic_updatedb.sh @@ -0,0 +1,30 @@ +function _log() { + echo "[Log] $@" +} + +root_path=`pwd` +file_name=$1 + +version_sql_path=$root_path/global/sql +if [ ! -d $version_sql_path ]; +then + mkdir -p $version_sql_path +fi + +db_gamedata=`grep dbconf.mysql_gamedb $file_name -A 9 | grep database | awk -F\" '{print $2}'` +db_gameconf=`grep dbconf.mysql_confdb $file_name -A 9 | grep database | awk -F\" '{print $2}'` +user=`grep dbconf.mysql_gamedb $file_name -A 9 | grep user | awk -F\" '{print $2}'` +host=`grep dbconf.mysql_gamedb $file_name -A 9 | grep host | awk -F\" '{print $2}'` +port=`grep dbconf.mysql_gamedb $file_name -A 9 | grep port | awk -F\= '{print $2}' | awk -F, '{print $1}'` +password=`grep dbconf.mysql_gamedb $file_name -A 9 | grep password | awk -F\" '{print $2}'` +_log $db_gamedata $user $host $port $password + +MYSQL="mysql -u$user -p$password -h$host -P$port" + +cd ${version_sql_path} +files=$(ls) +for file in ${files[@]} +do + _log "执行 sql文件:" $file + ${MYSQL} --force $db_gamedata < $file 2>/dev/null +done diff --git a/framework/shells/misc_scripts/mac_logic_updatedb.sh b/framework/shells/misc_scripts/mac_logic_updatedb.sh new file mode 100644 index 0000000..9b7cd70 --- /dev/null +++ b/framework/shells/misc_scripts/mac_logic_updatedb.sh @@ -0,0 +1,53 @@ +function _log() { + echo "[Log] $@" +} + +root_path=`pwd` +file_name=${1} +branch_name=`git branch | awk '$1 == "*"{print}' | awk -F/ '{print $2}' | awk -F')' '{print $1}'` + +version_sql_path=$root_path/global/sql +if [ ! -d $version_sql_path ]; +then + mkdir -p $version_sql_path +fi + +cat >> test.lua << EOF +local dbconf = require("$file_name") +print("database", dbconf.mysql_gamedb.database) +print("confdatabase", dbconf.mysql_confdb.database) +print("host", dbconf.mysql_gamedb.host) +print("port", dbconf.mysql_gamedb.port) +print("user", dbconf.mysql_gamedb.user) +print("password", dbconf.mysql_gamedb.password) +EOF + +ret=(`lua test.lua | awk -F" " '{print $2}'`) +db_gamedata=${ret[0]} +db_gameconf=${ret[1]} +host=${ret[2]} +port=${ret[3]} +user=${ret[4]} +password=${ret[5]} + +MYSQL="mysql -u$user -p$password -h$host -P$port" + +notchange="nothing to commit, working tree clean" +rm -rf test.lua + +cd ${version_sql_path} +files=$(ls) +sql_name=$version_sql_path/${branch_name}.sql + +if [ ! -f "$sql_name" ]; then + echo "USE \`${db_gamedata}\`;" > ${sql_name} +fi + +#排序后遍历文件 +for file in ${files[@]} +do + _log "执行 sql文件:" $file + ${MYSQL} --force $db_gamedata < $file 2>/dev/null +done + +rm -rf out.txt diff --git a/framework/shells/misc_scripts/mysql_handler.py b/framework/shells/misc_scripts/mysql_handler.py new file mode 100644 index 0000000..5a0e6f6 --- /dev/null +++ b/framework/shells/misc_scripts/mysql_handler.py @@ -0,0 +1,24 @@ +#-*- coding:utf-8 -*- + +try: + import MySQLdb as mdb +except: + import pymysql as mdb + mdb.install_as_MySQLdb() + +from sys import argv + +script, sql_host, sql_port, sql_user, sql_passwd, sql_db, sql_stmt = argv + +connection = mdb.connect(host = sql_host, port = int(sql_port), user = sql_user, passwd = sql_passwd, db = sql_db) +cursor = connection.cursor() +try: + cursor.execute(sql_stmt) + records = cursor.fetchall() + fmttedRecords = '\n'.join(' '.join(str(col) for col in record) for record in records) + print fmttedRecords + +finally: + cursor.close() + connection.close() + diff --git a/framework/shells/run_linux b/framework/shells/run_linux new file mode 100755 index 0000000..6285f9b --- /dev/null +++ b/framework/shells/run_linux @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# game server control(build, tar, start, stop, ...) script file +# Longwei Lai +################################################################### + +# define current path and script path +CUR_PATH="`pwd`" +SCRIPT_PATH="$(cd "`dirname "$0"`" && pwd)" # .../shells +FRAMEWORK_PATH="`dirname "${SCRIPT_PATH}"`" # .../framework +MISC_SCRIPTS_PATH="${SCRIPT_PATH}/misc_scripts" # .../misc_scripts +SVR_PATH="$(dirname "${FRAMEWORK_PATH}")" # .../*-server + +# include framework header file and logic header file +cd "${MISC_SCRIPTS_PATH}" +source "${MISC_SCRIPTS_PATH}/lnx_fwk.sh" +source "${MISC_SCRIPTS_PATH}/lnx_logic.sh" +cd "${CUR_PATH}" + +# main function(implemented command dispatch logic) +function main() +{ + # firstly, ensure environment + env_ensure + + # do command dispatch + local args=($@) + local cmd=${args[0]} + local cmd_args=${args[@]:1} + case "${cmd}" in + # server build/tarball commands + up|update) + update_server ${cmd_args} + ;; + ver_info) + get_server_version_info ${cmd_args} + ;; + switch_branch) + switch_branch ${cmd_args} + ;; + make|build) + make_server ${cmd_args} + ;; + tar|compress) + tar_server ${cmd_args} + ;; + hfg|hotfix_gs) + hotfix_zip_gs ${cmd_args} + ;; + + # fake time about commands + read_faketime) + log_info $(read_faketime) + ;; + clear_faketime) + clear_faketime + log_info "fake time setting cleared" + ;; + + # special command help pages show commands + help_gameserver|help_gs) + list_help_gameserver ${cmd_args} + ;; + help_loginserver|help_ls) + list_help_loginserver ${cmd_args} + ;; + + # test commands + # ... + + # other server control commands + *) + server_ctrl_cmd_adapt ${args[@]} + ;; + esac +} + +main $@ + diff --git a/framework/shells/run_mac b/framework/shells/run_mac new file mode 100755 index 0000000..438a521 --- /dev/null +++ b/framework/shells/run_mac @@ -0,0 +1,163 @@ +#!/usr/bin/env bash +# @author 郑宏伟 +# @brief MACOS服务器启动脚本 + +____APP_START_CONF_NAME=".app.mac.cfg" + +SCRIPT_PATH="$(cd "`dirname "$0"`" && pwd)" +FRAMEWORK_PATH="`dirname "${SCRIPT_PATH}"`" # .../framework +MISC_SCRIPTS_PATH="${SCRIPT_PATH}/misc_scripts" # .../misc_scripts + +function _log() +{ + echo "[Log] $@" +} + +function EnvEnsure() +{ + local os_type=`uname` + if [ $os_type != "Darwin" ];then + _log "illegal os: $os_type" + exit 1 + fi + + local cpu=`sysctl -n machdep.cpu.core_count` + export SKYNET_THREAD=`expr ${cpu} \* 2` +} + +function Main() +{ + ParseAgrs $@ + CreateConfigFile ${____APP_START_CONF_NAME} + EnvEnsure + ${FRAMEWORK_PATH}/skynet/skynet ${____APP_START_CONF_NAME} "${ARG_TYPE}-${ARG_PRO}-${ARG_ENV}" +} + +function Help() +{ + echo 'Usage:' + echo 'start_server.sh -p 应用项目 -e 应用环境 -t 应用类型 [-d DBCONF名]' + echo 'Options:' + echo ' -p 必需 应用项目' + echo ' 奥斯曼大帝国:boe' + echo ' 西部:ww' + echo ' -e 必需 应用环境' + echo ' 开发环境:dev' + echo ' 集成测试:sit' + echo ' 产品环境:pro' + echo ' -t 必需 应用类型' + echo ' 游戏服:gs' + echo ' 登录服:ls' + echo ' 全局服:ms' + echo ' WEB服:ws' + echo ' -d 可选 DBCONF名' +} + +function ProjectAdapter() +{ + case ${1} in + boe|ww) ARG_PRO="$OPTARG";; + ?) + _log "未知[应用项目]参数" + exit 1 + ;; + esac +} + +function EnvAdapter() +{ + case ${1} in + dev|sit|pro) ARG_ENV="$OPTARG";; + ?) + _log "未知[应用环境]参数" + exit 1 + ;; + esac +} + +function TypeAdapter() +{ + case ${1} in + ls) + ARG_TYPE="$OPTARG" + ARG_START_CONF=login/snconfig + ;; + ms) + ARG_TYPE="$OPTARG" + ARG_START_CONF=global/snconfig + ;; + ws) + ARG_TYPE="$OPTARG" + ARG_START_CONF=web/webconf + ;; + gs|rw|cp) + ARG_TYPE="$OPTARG" + ARG_START_CONF=game/snconfig + ;; + ?) + _log "未知[应用类型]参数" + exit 1 + ;; + esac +} + +function ParseAgrs() +{ + while getopts :p:e:t:hd: OPT + do + case $OPT in + p) ProjectAdapter $OPTARG;; + e) EnvAdapter $OPTARG;; + t) TypeAdapter $OPTARG;; + d) DBCONF_NAME="$OPTARG";; + h) Help;exit 0;; + ?) + _log "未知参数" + exit 1 + ;; + esac + done +} + +function auto_update_db() +{ + local dbconf="dbconf" + if [[ ! -z ${DBCONF_NAME} ]]; then + dbconf="${DBCONF_NAME}" + fi + + if [[ -z ${dbconf} ]]; then + _log "dbconf name err" + exit 1 + fi + + if [[ ${ARG_ENV} == "dev" && ${ARG_TYPE} == "ms" ]]; then + sh ${MISC_SCRIPTS_PATH}/mac_logic_updatedb.sh ${dbconf} + fi +} + +function CreateConfigFile() +{ + local STARTFILE=${1} + if [[ -z ${ARG_PRO} || -z ${ARG_ENV} || -z ${ARG_TYPE} ]]; then + _log "缺少参数";exit 2; + fi + + rm -rf ${STARTFILE} + cat >> ./${STARTFILE} << EOF +include "framework/snconfig" +include "${ARG_START_CONF}" +app_type="${ARG_TYPE}" +app_project="${ARG_PRO}" +app_env="${ARG_ENV}" +EOF + + if [[ ! -z ${DBCONF_NAME} ]]; then + echo "dbconf=\"${DBCONF_NAME}\"" >> ./${STARTFILE} + fi + + # 自动更新数据库文件 + auto_update_db +} + +Main $@ diff --git a/logs/.gitkeep b/logs/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/server/run b/server/run new file mode 100755 index 0000000..8f10727 --- /dev/null +++ b/server/run @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +RUN_PATH="$(cd "`dirname "$0"`" && pwd)/../framework/shells" +os_type=`uname` +if [ $os_type == "Darwin" ]; then + case ${1} in + boels) ${RUN_PATH}/run_mac -p boe -e dev -t ls -d login.dbconf;; + boems) ${RUN_PATH}/run_mac -p boe -e dev -t ms -d global.dbconf;; + boews) ${RUN_PATH}/run_mac -p boe -e dev -t ws -d web.dbconf;; + boegs) ${RUN_PATH}/run_mac -p boe -e dev -t gs -d game.dbconf;; + boerw) ${RUN_PATH}/run_mac -p boe -e dev -t gs -d game.dbconf_rw;; + boecp) ${RUN_PATH}/run_mac -p boe -e dev -t gs -d game.dbconf_cp;; + boede) ${RUN_PATH}/run_mac -p boe -e dev -t gs -d game.dbconf_de;; + *) echo "param error";exit 1;; + esac +elif [ $os_type == "Linux" ];then + ${RUN_PATH}/run_linux $@ +else + echo "env error" + exit 1 +fi diff --git a/shell/.gitkeep b/shell/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tools/lqc/cli/app.lua b/tools/lqc/cli/app.lua deleted file mode 100644 index 21df7dd..0000000 --- a/tools/lqc/cli/app.lua +++ /dev/null @@ -1,127 +0,0 @@ ---- Module which contains the main logic of the CLI application --- @module lqc.cli.app --- @alias app -local Vector = require 'lqc.helpers.vector' -local reduce = require 'lqc.helpers.reduce' -local filter = require 'lqc.helpers.filter' -local fs = require 'lqc.helpers.fs' -local random = require 'lqc.random' -local lqc = require 'lqc.quickcheck' -local loader = require 'lqc.cli.loader' -local arg_parser = require 'lqc.cli.arg_parser' -local report = require 'lqc.report' - --- File used for remembering last used seed for generating test cases. -local check_file = '.lqc' - ---- Tries to read the last quickcheck seed. --- @return the last used seed (or nil on error). -local function read_from_check_file() - return fs.read_file(check_file) -end - ---- Writes the seed to the check file (.lqc.lua). --- @param seed Seed to write to the file -local function write_to_check_file(seed) - fs.write_file(check_file, seed) -end - ---- Initializes the random seed; either with last used value (--check) or a --- specific seed (-s, --seed) or default (current timestamp) --- @param config Config which specifies how seed initialization should be handled -local function initialize_random_seed(config) - local seed = config.seed - if config.check then -- redo last generated test run (if --check specified) - seed = read_from_check_file() - end - local actual_used_seed = random.seed(seed) - write_to_check_file(actual_used_seed) - report.report_seed(actual_used_seed) -end - ---- Depending on the config, returns a list of files that should be executed. --- @return List of script files that should be executed. -local function find_files(files_or_dirs) - return reduce(files_or_dirs, Vector.new(), function(file_or_dir, acc) - if fs.is_file(file_or_dir) then - return acc:push_back(file_or_dir) - end - - -- directory - return acc:append(Vector.new(fs.find_files(file_or_dir))) - end):to_table() -end - ---- Checks if a file is a file ending in .lua or .moon (if moonscript available) --- @param file Path to a file --- @return true if it has the correct extension; otherwise false. -local function is_script_file(file) - return fs.is_lua_file(file) or fs.is_moonscript_file(file) -end - ---- Filters out all files not ending in .lua or .moon --- @param files list of all files --- @return filtered list of files, containing only the Lua and Moonscript scripts -local function find_script_files(files) - return filter(files, is_script_file) -end - ---- Executes all scripts, specified by a table of file paths. --- @param files List of scripts to execute. -local function execute_scripts(files) - for _, file in pairs(files) do - -- TODO clear environment each time? - local script = loader.load_script(file) - script() - end -end - ---- Verifies all properties, with the work divided over X number of threads. --- @param numthreads Number of threads to divide the work over. -local function verify_properties(numthreads) - if numthreads == 1 then - lqc.check() - return - end - - lqc.check_mt(numthreads) -end - ---- Shows the test output (statistics) -local function show_output() - report.report_errors() - report.report_summary() -end - -local app = {} - --- Exits the application -function app.exit() - os.exit(lqc.failed and 1 or 0) -end - ---- Main function of the CLI application --- 1. parse arguments --- 2. find all files needed to run --- 3. initialize random seed --- 4. run 1 file, or all files in a directory (depending on args) --- 5. execute properties (lqc.check) --- 6. show output --- @param cli_args Table containing the commandline arguments -function app.main(cli_args) - local config = arg_parser.parse(cli_args or {}) - local files = find_files(config.files_or_dirs) - local script_files = find_script_files(files) - - initialize_random_seed(config) - lqc.init(config.numtests, config.numshrinks) - report.configure(config.colors) - execute_scripts(script_files) - verify_properties(config.threads) - show_output() - - app.exit() -end - -return app - diff --git a/tools/lqc/cli/arg_parser.lua b/tools/lqc/cli/arg_parser.lua deleted file mode 100644 index 048a3e0..0000000 --- a/tools/lqc/cli/arg_parser.lua +++ /dev/null @@ -1,43 +0,0 @@ ---- Module for parsing of commandline arguments --- @module lqc.cli.arg_parser --- @alias lib -local argparse = require 'argparse' -local config = require 'lqc.config' - --- Module for easily parsing list of command line arguments - -local name_of_executable = 'lqc' -local help_info = 'Property based testing tool written in Lua' -local parser = argparse(name_of_executable, help_info) -parser.error = function(msg) - error(msg) -end - --- Converts a string to an integer --- Returns an integer representation of the input string or raises an error on failure. -local function str_to_int(x) - return tonumber(x) -end - -parser:argument('files_or_dirs', - 'List of input files or directories (recursive search) used for testing, default = "."', nil, nil, '*') -parser:mutex(parser:flag('--check', 'Re-checks the set of tests generated by the last seed'), parser:option('--seed -s', - 'Value of the random seed to use, default = seed based on current time', nil, str_to_int)) -parser:option('--numtests', 'Number of iterations per property, default = 100', nil, str_to_int) -parser:option('--numshrinks', 'Number of shrinks per failing property, default = 100', nil, str_to_int) -parser:flag('--colors -c', "Enable coloring of test output, default = disabled (doesn't work on Windows!).") -parser:option('--threads -t', "Executes properties in parallel, default = single-threaded (requires Lua Lanes!).", nil, - str_to_int) - -local lib = {} - ---- Parses the arguments --- @return a table containing the config specified by the user; --- raises an error if parsing failed. -function lib.parse(args) - local parsed_values = parser:parse(args) - return config.resolve(parsed_values) -end - -return lib - diff --git a/tools/lqc/cli/loader.lua b/tools/lqc/cli/loader.lua deleted file mode 100644 index a49b368..0000000 --- a/tools/lqc/cli/loader.lua +++ /dev/null @@ -1,77 +0,0 @@ ---- Module for pre-loading all lqc modules so the user does not have to do this. --- @module lqc.cli.loader --- @alias lib -local deep_copy = require 'lqc.helpers.deep_copy' -local fs = require 'lqc.helpers.fs' -local has_moonscript, moonscript = pcall(require, 'moonscript') - -local lib = {} - --- Prepare new global env for easier use of property based testing library -local new_global_env = deep_copy(_G) -new_global_env.Generator = require 'lqc.generator' -new_global_env.any = require 'lqc.generators.any' -new_global_env.bool = require 'lqc.generators.bool' -new_global_env.byte = require 'lqc.generators.byte' -new_global_env.char = require 'lqc.generators.char' -new_global_env.float = require 'lqc.generators.float' -new_global_env.int = require 'lqc.generators.int' -new_global_env.str = require 'lqc.generators.string' -new_global_env.tbl = require 'lqc.generators.table' -new_global_env.random = require 'lqc.random' -new_global_env.property = require 'lqc.property' -new_global_env.fsm = require 'lqc.fsm' -new_global_env.state = require 'lqc.fsm.state' -new_global_env.command = require 'lqc.fsm.command' -do - local lqc_gen = require 'lqc.lqc_gen' - new_global_env.choose = lqc_gen.choose - new_global_env.frequency = lqc_gen.frequency - new_global_env.elements = lqc_gen.elements - new_global_env.oneof = lqc_gen.oneof -end - ---- Compatibility workaround: setfenv is removed from Lua for versions > 5.1. --- This function aims to provide same functionality. --- Based mostly on http://leafo.net/guides/setfenv-in-lua52-and-above.html --- @param func function for which the environment should be changed --- @param new_env table containing the new environment to be set -local function setfenv_compat(func, new_env) - local idx = 1 - repeat - local name = debug.getupvalue(func, idx) - if name == '_ENV' then - debug.upvaluejoin(func, idx, function() - return new_env - end, 1) - end - idx = idx + 1 - until name == '_ENV' or name == nil - - return func -end - -local setfenv = setfenv or setfenv_compat - ---- Loads a script, sets a new environment (for easier property based testing), --- @param file_path Path to the file containing a Lua/Moonscript script --- @return the modified script which can be called as a function. -function lib.load_script(file_path) - -- Check if Moonscript file and if Moonscript available - if fs.is_moonscript_file(file_path) then - if not has_moonscript then - return function() - end - end -- return empty 'script' - - local script = moonscript.loadfile(file_path) - return setfenv(script, new_global_env) - end - - -- Lua file - local script = loadfile(file_path) - return setfenv(script, new_global_env) -end - -return lib - diff --git a/tools/lqc/config.lua b/tools/lqc/config.lua deleted file mode 100644 index dcdf222..0000000 --- a/tools/lqc/config.lua +++ /dev/null @@ -1,46 +0,0 @@ ---- Helper module for managing the config in the application. --- @module lqc.config --- @alias config -local config = {} - ---- Helper function for getting the default random seed. --- --- @return default seed used by the application (the current timestamp). -function config.default_seed() - return os.time() -end - -local default_config = { - files_or_dirs = {'.'}, - seed = config.default_seed(), - numtests = 100, - numshrinks = 100, - colors = false, - threads = 1, - check = false, -} - ---- Checks if the argument is empty (nil or {}). --- --- @param x Argument to check --- @return true if arg is empty; otherwise false. -local function is_empty_arg(x) - return x == nil or (type(x) == 'table' and #x == 0) -end - ---- Determines the config to use based on the table of supplied values. --- If no value is supplied for a specific setting, a default value is used --- (see top of file). --- --- @return the updated config -function config.resolve(values) - for _, arg_name in ipairs {'files_or_dirs', 'seed', 'numtests', 'numshrinks', 'colors', 'threads', 'check'} do - if is_empty_arg(values[arg_name]) then - values[arg_name] = default_config[arg_name] - end - end - return values -end - -return config - diff --git a/tools/lqc/fsm.lua b/tools/lqc/fsm.lua deleted file mode 100644 index 4981f9d..0000000 --- a/tools/lqc/fsm.lua +++ /dev/null @@ -1,102 +0,0 @@ ---- Helper module for specifying finite state machines (FSMs) with. Provides a DSL. --- @module lqc.fsm --- @alias fsm -local algorithm = require 'lqc.fsm.algorithm' -local state = require 'lqc.fsm.state' -local lqc = require 'lqc.quickcheck' - ---- Adds a stop state to the list of states. --- This is a special predefined state that will stop the FSM from generating --- more state transitions. --- @param state_list List of states in the FSM (not including stop state) --- @return the updated state list (variable modified in place). -local function add_stop_state(state_list) - table.insert(state_list, state 'stop' { - precondition = function() - return true - end, -- always succeeds - next_state = function() - return nil - end, -- not used - postcondition = function() - return true - end, -- always succeeds - }) - return state_list -end - ---- Checks if an object is callable (function or functable): --- @param obj Object to be checked if it is callable --- @return true if it is callable; otherwise false -local function is_callable(obj) - local type_obj = type(obj) - return type_obj == 'function' or type_obj == 'table' -end - ---- Checks if the FSM table contains a valid specification of a state machine --- @param fsm_table Table containing FSM information/description --- @return nil; raises an error message if specification is not valid -local function check_valid_fsm_spec(fsm_table) - if not is_callable(fsm_table.commands) then - error 'Need to provide list of commands to FSM!' - end - if not is_callable(fsm_table.initial_state) then - error 'Need to provide initial state function to FSM!' - end - - local states = fsm_table.states - if type(states) ~= 'table' then - error 'Need to provide a table of possible states of the FSM!' - end - - -- States are already checked in state.lua -end - -local function default_cleanup() -end -local function default_when_fail() -end - ---- Constructs a new FSM --- @param description text description of the FSM --- @param fsm_table table containing FSM info --- @return FSM object -local function new(description, fsm_table) - local FSM = {} - - function FSM.check(_) - return algorithm.check(description, fsm_table) - end - - return FSM -end - ---- Creates a new FSM and inserts it into the list of properties. --- @param descr Text description of the FSM --- @param fsm_info_table Table containing information of the FSM -local function fsm(descr, fsm_info_table) - local function fsm_func(fsm_table) - fsm_table.states = add_stop_state(fsm_table.states) - fsm_table.cleanup = fsm_table.cleanup or default_cleanup - fsm_table.when_fail = fsm_table.when_fail or default_when_fail - fsm_table.numtests = fsm_table.numtests or lqc.numtests - fsm_table.numshrinks = fsm_table.numshrinks or lqc.numshrinks - - check_valid_fsm_spec(fsm_table) - local new_fsm = new(descr, fsm_table) - table.insert(lqc.properties, new_fsm) - end - - if fsm_info_table then - -- Called normally (most likely from Moonscript) - fsm_func(fsm_info_table) - return function() - end - end - - -- Called with DSL syntax - return fsm_func -end - -return fsm - diff --git a/tools/lqc/fsm/action.lua b/tools/lqc/fsm/action.lua deleted file mode 100644 index eb34db6..0000000 --- a/tools/lqc/fsm/action.lua +++ /dev/null @@ -1,37 +0,0 @@ ---- Module for describing an action in a 'declarative' way --- @classmod lqc.fsm.action --- @alias Action -local Action = {} -local Action_mt = { - __index = Action, -} - ---- Creates a new action. --- @param var (Symbolic) variable to store the result of the action in --- @param cmd Command that was called during this action --- @param command_generator Generator that generated the command, used for shrinking the command --- @return new action object -function Action.new(var, cmd, command_generator) -- TODO rename to args_generators - if var == nil then - error 'Need to provide variable to action object!' - end - if cmd == nil then - error 'Need to provide command to action object!' - end - - local action = { - variable = var, - command = cmd, - cmd_gen = command_generator, - } - return setmetatable(action, Action_mt) -end - ---- Returns a string representation of the action --- @return string representation of the action -function Action:to_string() - return '{ set, ' .. self.variable:to_string() .. ', ' .. self.command:to_string() .. ' }' -end - -return Action - diff --git a/tools/lqc/fsm/algorithm.lua b/tools/lqc/fsm/algorithm.lua deleted file mode 100644 index 989f605..0000000 --- a/tools/lqc/fsm/algorithm.lua +++ /dev/null @@ -1,471 +0,0 @@ ---- Module describing the algorithm used by the FSM properties. --- @module lqc.fsm.algorithm --- @alias lib -local Vector = require 'lqc.helpers.vector' -local Var = require 'lqc.fsm.var' -local Command = require 'lqc.fsm.command' -local Action = require 'lqc.fsm.action' -local random = require 'lqc.random' -local deep_copy = require 'lqc.helpers.deep_copy' -local report = require 'lqc.report' -local unpack = unpack or table.unpack -- for compatibility reasons - - -local lib = {} - ---- Creates a small helper object that keeps track of a counter --- @return counter object -function lib.make_counter() - local Counter = { - val = 1, - increase = function(self) - self.val = self.val + 1 - end, - value = function(self) - return self.val - end, - } - return setmetatable(Counter, { - __index = Counter, - }) -end - ---- Checks if x lies between min and max --- @param x A number --- @param min Minimum value --- @param max Maximum value --- @return true if min <= x and x <= max; otherwise false. -local function is_between(x, min, max) - return min <= x and x <= max -end - ---- Determines how many items should be shrunk --- @param max_amount Maximum amount of items that are allowed to be shrunk down --- @return random number between 1 and max_amount (inclusive) -local function shrink_how_many(max_amount) - if max_amount <= 1 then - return 1 - end - return random.between(1, max_amount) -end - ---- Determines if an an action should be marked for deletion --- @return true if it should be deleted; otherwise false -local function should_select_action() - return random.between(1, 4) == 1 -- 25% chance -end - ---- Finds a specific state in the list of states based on name of the state. --- @param states List of states to search in --- @param state_name Name of the state to find --- @return the state with that name; --- raises an error if no state is present with the specified name -function lib.find_state(states, state_name) - for i = 1, #states do - local state = states[i] - if state.name == state_name then - return state - end - end - error('State "' .. state_name .. '" not found in list of FSM states!') -end - ---- Finds the next command based on the FSM model and the current state. --- @param fsm table describing the FSM property --- @param current_state Variable containing the current state of the FSM --- @param var_counter Number indicating how many variables have already been used --- @return 3 values: chosen_command, cmd_generator, updated_current_state -function lib.find_next_action(fsm, current_state, var_counter) - local numtests, commands, states = fsm.numtests, fsm.commands, fsm.states - local cmd_gen = commands(current_state) - - for _ = 1, 100 do -- TODO make configurable? - local cmd = cmd_gen:pick(numtests) - local selected_state = lib.find_state(states, cmd.state_name) - - if selected_state.precondition(current_state, cmd.args) then -- valid command - local variable = Var.new(var_counter:value()) - var_counter:increase() - current_state = selected_state.next_state(current_state, variable, cmd.args) - return Action.new(variable, cmd, cmd_gen), current_state - end - end - - -- Could not find a next action -> stop generating further actions - return Action.new(Var.new(var_counter:value()), Command.stop, nil), current_state -end - ---- Generates a list of steps for a FSM specification --- @param fsm_table table containing description of a FSM property --- @return list of generated actions -function lib.generate_actions(fsm_table) - local generated_actions = Vector.new() - local counter = lib.make_counter() - local state = fsm_table.initial_state() - - repeat - local action, next_state = lib.find_next_action(fsm_table, state, counter) - state = next_state - generated_actions:push_back(action) - until action.command.state_name == 'stop' - - return generated_actions -end - ---- Slices of the last actions past index --- @param action_vector Vector of actions --- @param index Last position in the vector that should not be removed --- @return the action vector (modified in place) -function lib.slice_last_actions(action_vector, index) - local action_vector_copy = deep_copy(action_vector) - local last_pos = action_vector_copy:size() - 1 -- -1 since we want to keep stop action! - for i = last_pos, index + 1, -1 do - action_vector_copy:remove_index(i) - end - return action_vector_copy -end - ---- Selects at most 'how_many' amount of actions from the vector of actions --- to be marked for deletion. --- @param action_vector Vector containing list of actions --- @return vector of actions which should be deleted. -function lib.select_actions(action_vector) - local selected_actions, idx_vector = Vector.new(), Vector.new() - if action_vector:size() <= 2 then - return selected_actions, idx_vector - end - local amount = 0 - local size = action_vector:size() - 2 -- don't remove stop action, keep atleast 1 other action - local how_many = shrink_how_many(size) - - for i = 1, size do - if amount >= how_many then - break - end - if should_select_action() then -- TODO make this a variable function and use a while loop? - idx_vector:push_back(i) - amount = amount + 1 - end - end - - for i = 1, amount do - selected_actions:push_back(action_vector:get(idx_vector:get(i))) - end - - return selected_actions, idx_vector -end - ---- Removes all elements of 'which_actions' from 'action_vector' --- @param action_vector Vector of actions from which actions will be removed --- @param which_actions actions to be removed --- @return an updated vector -function lib.delete_actions(action_vector, which_actions) - local action_vector_copy = deep_copy(action_vector) - for i = 1, which_actions:size() do - action_vector_copy:remove(which_actions:get(i)) - end - return action_vector_copy -end - ---- Does the actual execution of the FSM by executing the list of actions --- If one of the postconditions fail after an action is applied, then the --- actions will be shrunk down to a simpler scenario. --- At the end, the state is cleaned up by the cleanup-callback. --- Returns 4 values: --- 1) true if the FSM property succeeded for these actions; false otherwise. --- 2) index of last successful step (1-based) --- 3) state of the model right before the failing action --- 4) result of the last failing action --- @param fsm_table table containing description of a FSM property --- @param generated_actions list of actions to be executed for this FSM --- @return 4 values: --- 1. bool: true = FSM succeeded; false = FSM failed --- 2. Amount of actions executed --- 3. Last state of the FSM --- 4. Last result (return value of last command) -function lib.execute_fsm(fsm_table, generated_actions) - local state = fsm_table.initial_state() - local last_state, last_result = state, nil - - for i = 1, generated_actions:size() do - local command = generated_actions:get(i).command - local selected_state = lib.find_state(fsm_table.states, command.state_name) - local result = command.func(unpack(command.args)) -- side effects happen here - local updated_state = selected_state.next_state(state, result, command.args) - - last_state, last_result = state, result - - -- and verify the model matches the actual system - -- NOTE: the state passed in is the state that the system had BEFORE - -- executing this specific action! - if not selected_state.postcondition(state, result, command.args) then - fsm_table.cleanup(state) - return false, i, last_state, last_result - end - - state = updated_state -- update model - - end - - fsm_table.cleanup(state) - return true, generated_actions:size() - 1, last_state, last_result -end - ---- Is the list of actions valid to execute on this FSM? --- Replays sequence symbolically to verify if it is indeed valid. --- @param fsm_table table containing description of a FSM property --- @param action_vector list of actions to be checked --- @return true if list of actions valid; otherwise false. -function lib.is_action_sequence_valid(fsm_table, action_vector) - local states = fsm_table.states - local state = fsm_table.initial_state() - - for i = 1, action_vector:size() do - local action = action_vector:get(i) - local selected_state = lib.find_state(states, action.command.state_name) - - if not selected_state.precondition(state, action.command.args) then - return false - end - - state = selected_state.next_state(state, action.variable, action.command.args) - end - return true -end - ---- Tries to shrink the list of actions to a simpler form by removing steps of --- the sequence and checking if it is still valid. --- The function is recursive and will loop until tries_left is 0. --- @param fsm_table table containing description of a FSM property --- @param action_list list of actions to be shrunk down --- @return 2 values: --- 1) action_list if shrinking was not possible after X amount of tries; --- otherwise it will return a shrunk list of actions. --- 2) list of deleted actions (empty if shrinking failed after X tries) -local function do_shrink_actions(fsm_table, action_list) - local which_actions = lib.select_actions(action_list) - local shrunk_actions = lib.delete_actions(action_list, which_actions) - - if not lib.is_action_sequence_valid(fsm_table, shrunk_actions) then - return action_list, Vector.new() - end - - return shrunk_actions, which_actions -end - ---- Does the shrinking of the FSM actions --- choose 1 - N steps and delete them from list of actions --- repeat X amount of times (recursively) --- @param fsm_table table containing description of a FSM property --- @param generated_actions List of actions to be shrunk down --- @param removed_actions Already removed actions --- @param tries Remaining tries to recusively try shrinking the actions --- @return the shrunk down list of actions -local function shrink_actions(fsm_table, generated_actions, removed_actions, tries) - if tries == 1 then - return generated_actions, removed_actions - end - - local shrunk_actions, deleted_actions = do_shrink_actions(fsm_table, generated_actions) - local total_removed_actions = removed_actions:append(deleted_actions) - - -- TODO add execute fsm here and shrink deleted actions? - return shrink_actions(fsm_table, shrunk_actions, total_removed_actions, tries - 1) -end - ---- Tries to shrink the list of failing actions by selecting a subset and --- checking if the combination is now valid and if the FSM still fails or not --- This is a recursive function. --- @param fsm_table table containing description of a FSM property --- @param generated_actions List of actions to be shrunk down --- @param deleted_actions Already deleted actions --- @param tries Remaining tries to recusively try shrinking the actions --- @return 3 things: --- 1. the shrunk list of actions (or the original list if no better solution was found) --- 2. the end state of the fsm after these actions --- 3. result of the last action -local function shrink_deleted_actions(fsm_table, generated_actions, deleted_actions, tries) - if tries == 1 then - return generated_actions - end - - local which_actions = lib.select_actions(deleted_actions) - local shrunk_actions = lib.delete_actions(generated_actions, which_actions) - - -- Retry if invalid sequence - local is_valid = lib.is_action_sequence_valid(fsm_table, shrunk_actions) - if not is_valid then - return shrink_deleted_actions(fsm_table, generated_actions, deleted_actions, tries - 1) - end - - -- Check FSM again - -- if FSM succeeds now, (one or more of) the failing actions have been deleted - -- -> simply retry TODO there is an optimisation possible here.. - -- else FSM still failed -> chosen actions did not matter, ignore them and - -- further try shrinking - local is_successful = lib.execute_fsm(fsm_table, shrunk_actions) - if is_successful then - deleted_actions = lib.delete_actions(deleted_actions, which_actions) - end - return shrink_deleted_actions(fsm_table, shrunk_actions, deleted_actions, tries - 1) -end - ---- Tries to shrink down the list of FSM actions. This function is called --- recursively until all tries are used. --- @param fsm_table table containing description of a FSM property --- @param generated_actions List of actions to be shrunk down --- @param step last step in the series of actions that succeeded while testing --- the FSM property --- @param tries Remaining tries to recusively try shrinking the actions --- @return list of shrunk down actions -function lib.shrink_fsm_actions(fsm_table, generated_actions, step, tries) - if tries == 1 then - return generated_actions - end - - local sliced_actions = lib.slice_last_actions(generated_actions, step) -- cut off actions after failure.. - local shrunk_actions, deleted_actions = - shrink_actions(fsm_table, sliced_actions, Vector.new(), fsm_table.numshrinks) - if deleted_actions:size() == 0 then - -- shrinking did not help, try again - return lib.shrink_fsm_actions(fsm_table, sliced_actions, step, tries - 1) - end - - -- shrinking did help, retry FSM: - local is_successful1, new_step1 = lib.execute_fsm(fsm_table, shrunk_actions) - if not is_successful1 then - -- FSM still fails, deleted actions can be ignored, try further shrinking - return lib.shrink_fsm_actions(fsm_table, shrunk_actions, new_step1, tries - 1) - end - - -- now FSM works -> faulty action is in the just deleted actions - local shrunk_down_actions = shrink_deleted_actions(fsm_table, sliced_actions, deleted_actions, fsm_table.numshrinks) - -- retry fsm: - -- if a solution could not be found by shrinking down the deleted actions - -- -> sliced actions is smallest solution found - -- else if FSM still fails after shrinking deleted_actions - -- -> try further shrinking - local is_successful2, new_step2 = lib.execute_fsm(fsm_table, shrunk_down_actions) - local minimal_actions = is_successful2 and sliced_actions or shrunk_down_actions - return lib.shrink_fsm_actions(fsm_table, minimal_actions, new_step2, tries - 1) -end - ---- Select a group of actions to be marked for shrinking down. --- @param action_list List of actions to be shrunk down. --- @return vector of indices for the actions which should be removed -local function select_actions_for_arg_shrinking(action_list) - local _, idx_vector = lib.select_actions(action_list) - if idx_vector:size() == 0 and is_between(action_list:size(), 2, 10) then - -- try shrinking 1 action anyway - local idx = random.between(1, action_list:size() - 1) -- don't shrink stop action! - idx_vector:push_back(idx) - end - return idx_vector -end - ---- Does the actual shrinking of the command arguments of a sequence of actions. --- @param fsm_table table containing description of a FSM property --- @param action_list List of actions to be shrunk down --- @return an updated sequence of the action list (original is modified!) with --- shrunk arguments. -local function shrink_args(fsm_table, action_list) - local idx_vector = select_actions_for_arg_shrinking(action_list) - - for i = idx_vector:size(), 1, -1 do -- shrunk from end to beginning (most likely to succeed) - local idx = idx_vector:get(i) - local action = action_list:get(idx) - - for _ = 1, fsm_table.numshrinks do - local command_copy = action.command -- shallow copy (reference only) - - action.command = action.cmd_gen:shrink(action.command) - -- revert if shrink is not valid - local is_valid = lib.is_action_sequence_valid(fsm_table, action_list) - if not is_valid then - action.command = command_copy; - break - end - end - end - - return action_list -end - ---- Shrinks down the arguments provided to the sequence of actions --- This function is called recursively until all tries are used. --- @param fsm_table table containing description of a FSM property --- @param generated_actions List of actions to be shrunk down --- @param tries Remaining tries to recusively try shrinking the actions --- @return -local function shrink_fsm_args(fsm_table, generated_actions, tries) - if tries == 1 then - return generated_actions - end - - local shrunk_actions = shrink_args(fsm_table, deep_copy(generated_actions)) -- , fsm_table.numshrinks) - - -- retry FSM - local is_successful = lib.execute_fsm(fsm_table, shrunk_actions) - if not is_successful then - -- FSM still fails, shrinking of args was successful, try further shrinking - return shrink_fsm_args(fsm_table, shrunk_actions, tries - 1) - end - - -- FSM works now, shrinking of args unsuccessful -> retry - return shrink_fsm_args(fsm_table, generated_actions, tries - 1) -end - ---- Replays the FSM property. --- @param fsm_table table containing description of a FSM property --- @param action_vector List of actions to replay --- @return last state and result while executing the FSM. -local function replay_fsm(fsm_table, action_vector) - local _, _, last_state, last_result = lib.execute_fsm(fsm_table, action_vector) - return last_state, last_result -end - ---- Shrinks the list of generated actions for a given FSM. --- This is a recursive function which keeps trying for X amount of times. --- @param fsm_table table containing description of a FSM property --- @param generated_actions List of actions to be shrunk down --- @param step Last successful step that was executed before FSM failed --- @return shrunk list of actions or original action list if shrinking did not help. -local function shrink_fsm(fsm_table, generated_actions, step) - local fsm_shrink_amount = fsm_table.numshrinks - local shrunk_actions = lib.shrink_fsm_actions(fsm_table, generated_actions, step, fsm_shrink_amount) - local shrunk_actions_and_args = shrink_fsm_args(fsm_table, shrunk_actions, fsm_shrink_amount) - local lst_state, lst_result = replay_fsm(fsm_table, shrunk_actions_and_args) - return shrunk_actions_and_args, lst_state, lst_result -end - ---- The main checking function for FSM specifications. --- Checks a number of times (according to FSM spec) if property is true. --- If the specification failed, then the result will be shrunk down to a --- simpler case. --- @param description string description of the FSM property --- @param fsm_table table containing description of a FSM property --- @return nil on success; otherwise a table containing info related to FSM error. -function lib.check(description, fsm_table) - for _ = 1, fsm_table.numtests do - local generated_actions = lib.generate_actions(fsm_table) - - local is_successful, last_step = lib.execute_fsm(fsm_table, generated_actions) - if not is_successful then - report.report_failed() - local shrunk_actions, last_state, last_result = shrink_fsm(fsm_table, generated_actions, last_step) - fsm_table.when_fail(shrunk_actions:to_table(), last_state, last_result) - return { - description = description, - generated_actions = generated_actions, - shrunk_actions = shrunk_actions, - } - end - - report.report_success() - end - - return nil -end - -return lib - diff --git a/tools/lqc/fsm/command.lua b/tools/lqc/fsm/command.lua deleted file mode 100644 index 1662028..0000000 --- a/tools/lqc/fsm/command.lua +++ /dev/null @@ -1,104 +0,0 @@ ---- Module for representing a command internally in a 'declarative' way --- @module lqc.fsm.command --- @alias command -local Gen = require 'lqc.generator' -local random = require 'lqc.random' -local deep_copy = require 'lqc.helpers.deep_copy' - ---- Returns a string representation of the command. --- @param cmd Command to transform into a string --- @return string representation of the command -local function stringify(cmd) - local result = {'{ call, ', cmd.state_name} - - local size_result = #result - for i = 1, #cmd.args do - -- TODO allow printing of table etc.. - result[i + size_result] = ', ' .. cmd.args[i] - end - - result[#result + 1] = ' }' - return table.concat(result) -end - ---- Creates a function that picks a random value for each of the generators --- specified in the argument list. --- @param state_name Name of the state this command is associated with --- @param command_func Function to be called when this command is selected --- @param args_generators List of generators that generate the arguments for command_func --- @return table with keys { state_name, func, args } -local function pick(state_name, command_func, args_generators) - local function do_pick(num_tests) - local args = {} - for i = 1, #args_generators do - args[i] = args_generators[i]:pick(num_tests) - end - return { - state_name = state_name, - func = command_func, - args = args, - to_string = stringify, - } - end - return do_pick -end - ---- Does the actual shrinking of the args --- Randomly picks 1 of the arguments and shrinks it, rest stays the same --- @param prev_args Previously generated args for the command --- @param args_generators Generators used to generate and shrink the args --- @return shrunk down argument list -local function shrink_args(prev_args, args_generators) - local idx = random.between(1, #prev_args) - local shrunk_arg = args_generators[idx]:shrink(prev_args[idx]) - local args_copy = deep_copy(prev_args) - args_copy[idx] = shrunk_arg - return args_copy -end - ---- Shrinks the command to a simpler form. --- Only args are shrunk, state_name and func are unmodified. --- @param state_name Name of the state this command is associated with --- @param command_func Function to be called when this command is selected --- @param args_generators Generators used to generate and shrink the args --- @return function that does the actual shrinking for this command -local function shrink(state_name, command_func, args_generators) - local function do_shrink(previous) - if #previous.args == 0 then - return previous - end - return { - state_name = state_name, - func = command_func, - args = shrink_args(previous.args, args_generators), - to_string = stringify, - } - end - return do_shrink -end - ---- Creates a new command generator with a state_name, command function --- and a list of generators (args will be passed into the command_func in the FSM) --- @param state_name Name of the state this command is associated with --- @param command_func Function to be called when this command is selected --- @param args_generators Generators used to generate and shrink the args --- @return generator for randomly creating a command -local function new(state_name, command_func, args_generators) - local generator = Gen.new(pick(state_name, command_func, args_generators), - shrink(state_name, command_func, args_generators)) - generator.state_name = state_name - return generator -end - -local command = {} -local command_mt = { - __call = function(_, command_tbl) - return new(command_tbl[1], command_tbl[2], command_tbl[3]) - end, -} - -command.stop = new('stop', function() -end, {}) - -return setmetatable(command, command_mt) - diff --git a/tools/lqc/fsm/state.lua b/tools/lqc/fsm/state.lua deleted file mode 100644 index 87ec1b9..0000000 --- a/tools/lqc/fsm/state.lua +++ /dev/null @@ -1,59 +0,0 @@ ---- Module that provides a DSL for specifying states in the FSM DSL framework. --- @module lqc.fsm.state --- @alias make_state ---- Checks if object is callable (a function or a functable) --- @param obj Object to check --- @return true if obj is callable; otherwise false. -local function is_callable(obj) - local type_obj = type(obj) - return type_obj == 'function' or type_obj == 'table' -end - ---- Checks if the state contains all the necessary info for a valid state specification --- @param state table containing precondition, next_state and postcondition --- @param state_name name of the state --- @return nil; raises an error if state contains wrong or missing information -local function check_valid_state(state, state_name) - if type(state_name) ~= 'string' then - error 'Missing state name!' - end - if type(state) ~= 'table' then - error 'State should be specified as a table!' - end - if not is_callable(state.precondition) then - error('Need to provide a precondition function to state ' .. state_name .. '!') - end - if not is_callable(state.next_state) then - error('Need to provide a next_state function to state ' .. state_name .. '!') - end - if not is_callable(state.postcondition) then - error('Need to provide a postcondition function to state ' .. state_name .. '!') - end -end - ---- Helper function for specifying a state in the FSM --- @param state_name Name to assign to the state --- @param state_information Table containing precondition, next_state, postcondition functions --- @return new table containing state information, ready to be added to the FSM -local function make_state(state_name, state_information) - local function make_state_helper(state_info) - check_valid_state(state_info, state_name) - return { - name = state_name, - precondition = state_info.precondition, - next_state = state_info.next_state, - postcondition = state_info.postcondition, - } - end - - if state_information ~= nil then - -- Called with normal syntax, directly return result - return make_state_helper(state_information) - end - - -- called with Lua DSL syntax, return closue which returns result - return make_state_helper -end - -return make_state - diff --git a/tools/lqc/fsm/var.lua b/tools/lqc/fsm/var.lua deleted file mode 100644 index 739ab46..0000000 --- a/tools/lqc/fsm/var.lua +++ /dev/null @@ -1,29 +0,0 @@ ---- Helper module for symbolically representing a variable. --- @classmod lqc.fsm.var --- @alias var -local Var = {} -local Var_mt = { - __index = Var, -} - ---- Creates a symbolic representation of a variable. --- @param value Value of the variable --- @return a new variable object -function Var.new(value) - if value == nil then - error 'Need to provide a value to Var!' - end - local var = { - value = value, - } - return setmetatable(var, Var_mt) -end - ---- Returns a string representation of the variable --- @return string representation of the variable -function Var:to_string() - return '{ var, ' .. self.value .. ' }' -end - -return Var - diff --git a/tools/lqc/generator.lua b/tools/lqc/generator.lua deleted file mode 100644 index 698a2d2..0000000 --- a/tools/lqc/generator.lua +++ /dev/null @@ -1,43 +0,0 @@ ---- Class for generating (custom) generators. --- @classmod lqc.generator --- @alias Gen -local Gen = {} -local Gen_mt = { - __index = Gen, -} - ---- Creates a new generator for generating random values. --- @param pick_func a function that randomly creates a certain datatype. --- @param shrink_func a function that shrinks (simplifies) a given input based --- on last input. --- @return a generator object --- @see pick --- @see shrink -function Gen.new(pick_func, shrink_func) - local Generator = { - pick_func = pick_func, - shrink_func = shrink_func, - } - - return setmetatable(Generator, Gen_mt) -end - ---- Generates a new random value based on this generator's pick value. --- --- @param numtests amount of times a property will be run, can be used to guide --- the choosing process. --- @return a new randomly chosen value -function Gen:pick(numtests) - return self.pick_func(numtests) -end - ---- Shrinks a generated value to a simpler value. --- --- @param prev The previously generated value. --- @return A newly generated value, simplified from the previous value. -function Gen:shrink(prev) - return self.shrink_func(prev) -end - -return Gen - diff --git a/tools/lqc/generators/any.lua b/tools/lqc/generators/any.lua deleted file mode 100644 index 05ff896..0000000 --- a/tools/lqc/generators/any.lua +++ /dev/null @@ -1,30 +0,0 @@ - ---- Module for generating 'any' random value. --- @lqc.generators.any --- @alias new - -local lqc_gen = require 'lqc.lqc_gen' -local tbl = require 'lqc.generators.table' -local int = require 'lqc.generators.int' -local float = require 'lqc.generators.float' -local str = require 'lqc.generators.string' -local bool = require 'lqc.generators.bool' - - ---- Creates a new generator that can generate a table, string, int, float or bool. --- @param optional_samplesize Amount of times the property is tested, used to guide --- the randomization process. --- @return generator that can generate 1 of the previously mentioned types in --- the description -local function new(optional_samplesize) - return lqc_gen.oneof { - tbl(optional_samplesize), - str(optional_samplesize), - int(optional_samplesize), - float(), - bool() - } -end - -return new - diff --git a/tools/lqc/generators/bool.lua b/tools/lqc/generators/bool.lua deleted file mode 100644 index 80238d5..0000000 --- a/tools/lqc/generators/bool.lua +++ /dev/null @@ -1,25 +0,0 @@ ---- Module for generating a bool randomly. --- @classmod lqc.generators.bool --- @alias new -local Gen = require 'lqc.generator' -local random = require 'lqc.random' - ---- Picks a random bool --- @return true or false -local function pick() - return random.between(0, 1) == 0 -end - ---- Shrinks down a bool (always shrinks to false) -local function shrink(_) - return false -end - ---- Creates a new bool generator --- @return A generator object for randomly generating bools. -local function new() - return Gen.new(pick, shrink) -end - -return new - diff --git a/tools/lqc/generators/byte.lua b/tools/lqc/generators/byte.lua deleted file mode 100644 index 4058996..0000000 --- a/tools/lqc/generators/byte.lua +++ /dev/null @@ -1,14 +0,0 @@ ---- Module for generating bytes randomly. --- A byte is an integer with value between 0 - 255 (inclusive) --- @classmod lqc.generators.byte --- @alias byte -local int = require 'lqc.generators.int' - --- Creates a new byte generator --- @return generator for generating random byte values -local function byte() - return int(0, 255) -end - -return byte - diff --git a/tools/lqc/generators/char.lua b/tools/lqc/generators/char.lua deleted file mode 100644 index a072160..0000000 --- a/tools/lqc/generators/char.lua +++ /dev/null @@ -1,36 +0,0 @@ ---- Module for generating a random (ASCII) char (no 'special' characters such as NUL, NAK, ...) --- @lqc.generators.char --- @alias char -local Gen = require 'lqc.generator' -local int = require 'lqc.generators.int' - -local lowest_ascii_value = 32 -- 'space' -local highest_ascii_value = 126 -- '~' -local int_gen = int(lowest_ascii_value, highest_ascii_value) -local space = string.char(lowest_ascii_value) - --- Generates a random character (ASCII value between 'space' and '~' --- @return randomly chosen char (string of length 1) -local function char_pick() - return string.char(int_gen:pick_func()) -end - ---- Shrinks down a previously generated char to a simpler value. Shrinks --- towards the 'space' ASCII character. --- @param prev previously generated char value --- @return shrunk down char value -local function char_shrink(prev) - if string.byte(prev) <= lowest_ascii_value then - return space - end - return string.char(string.byte(prev) - 1) -end - ---- Creates a generator for ASCII-chars --- @return generator that can randomly create ASCII values -local function char() - return Gen.new(char_pick, char_shrink) -end - -return char - diff --git a/tools/lqc/generators/float.lua b/tools/lqc/generators/float.lua deleted file mode 100644 index be941cd..0000000 --- a/tools/lqc/generators/float.lua +++ /dev/null @@ -1,30 +0,0 @@ ---- Module for generating float values. --- @lqc.generators.float --- @alias new -local Gen = require 'lqc.generator' - --- Generates a random float. --- @param numtests Number of times this generator is called in a test; used to --- guide the randomization process. --- @return random float (between - numtests / 2 and numtests / 2). -local function float_pick(numtests) - local lower_bound = -numtests / 2 - local upper_bound = numtests / 2 - return lower_bound + math.random() * (upper_bound - lower_bound) -end - ---- Shrinks a float to a simpler value --- @param prev a previously generated float value --- @return shrunk down float value -local function float_shrink(prev) - return prev / 2 -end - ---- Creates a generator for float values --- @return a generator that can generate float values. -local function new() - return Gen.new(float_pick, float_shrink) -end - -return new - diff --git a/tools/lqc/generators/int.lua b/tools/lqc/generators/int.lua deleted file mode 100644 index 48d7783..0000000 --- a/tools/lqc/generators/int.lua +++ /dev/null @@ -1,108 +0,0 @@ ---- Module for generating integer values --- @module lqc.generators.int --- @alias new -local Gen = require 'lqc.generator' -local random = require 'lqc.random' -local abs = math.abs - ---- Helper function for picking a random integer, bounded by min and max. --- @param min minimum value --- @param max maximum value --- @return function that can generate an integer (min <= int <= max) -local function pick_bounded(min, max) - local function do_pick() - return random.between(min, max) - end - return do_pick -end - ---- Helper function for finding number closest to 0. --- @param a number 1 --- @param b number 2 --- @return number closest to 0 -local function find_closest_to_zero(a, b) - return (abs(a) < abs(b)) and a or b -end - ---- Helper function for shrinking integer, bounded by min and max. (min <= int <= max) --- @param min minimum value --- @param max maximum value --- @return shrunk integer (shrinks towards 0 / closest value to 0 determined --- by min and max) -local function shrink_bounded(min, max) - local bound_limit = find_closest_to_zero(min, max) - local function do_shrink(previous) - if previous == 0 or previous == bound_limit then - return previous - end - if previous > 0 then - return math.floor(previous / 2) - end - return math.ceil(previous / 2) - end - return do_shrink -end - ---- Picks a random integer, uniformy spread between +- sample_size / 2. --- @param sample_size Number of times this generator is used in a property; --- used to guide the optimatization process. --- @return random integer -local function pick_uniform(sample_size) - local value = sample_size / 2 - return random.between(value - sample_size, value) -end - ---- Shrinks an integer by dividing it by 2 and rounding towards 0. --- @param previous previously generated integer value --- @return shrunk down integer value -local function shrink(previous) - if previous == 0 then - return 0 - end - if previous > 0 then - return math.floor(previous / 2) - end - return math.ceil(previous / 2) -end - ---- Creates a generator for generating an integer between min and max. --- @param min minimum value --- @param max maximum value --- @return generator that generates integers between min and max. -local function integer_between(min, max) - return Gen.new(pick_bounded(min, max), shrink_bounded(min, max)) -end - ---- Creates a generator for generating a positive integer between 0 and max. --- @param max maximum value --- @return generator that generates integer between 0 and max. -local function positive_integer(max) - return Gen.new(pick_bounded(0, max), shrink) -end - ---- Creates a generator for generating an integer uniformly chosen --- between +- sample_size / 2. --- @return generator that can generate an integer -local function integer() - return Gen.new(pick_uniform, shrink) -end - ---- Creates a new integer generator. --- @param nr1 number containing first bound --- @param nr2 number containing second bound --- @return generator that can generate integers according to the following strategy: --- - nr1 and nr2 provided: nr1 <= int <= nr2 --- - only nr1 provided: 0 <= int <= max --- - no bounds provided: -numtests/2 <= int <= numtests/2 -local function new(nr1, nr2) - if nr1 and nr2 then - return integer_between(nr1, nr2) - end - if nr1 then - return positive_integer(nr1) - end - return integer() -end - -return new - diff --git a/tools/lqc/generators/string.lua b/tools/lqc/generators/string.lua deleted file mode 100644 index 01226d0..0000000 --- a/tools/lqc/generators/string.lua +++ /dev/null @@ -1,200 +0,0 @@ ---- Module for generating random strings --- @module lqc.generators.string --- @alias new -local random = require 'lqc.random' -local lqc = require 'lqc.quickcheck' -local Gen = require 'lqc.generator' -local char = require 'lqc.generators.char' -local char_gen = char() - --- NOTE: The shrink algorithms are *heavily* based on triq --- https://github.com/krestenkrab/triq - ---- Determines how many items to shrink. --- @param length size of the string --- @return integer indicating how many items to shrink -local function shrink_how_many(length) - -- 20% chance more than 1 member is shrunk - if random.between(1, 5) == 1 then - return random.between(1, length) - end - - return 1 -end - ---- Replaces a character in the string 'str' at index 'idx' with 'new_char'. --- @param str string to be modified --- @param new_char value that will replace the old character in the string --- @param idx position in the string where the character should be replaced --- @return updated string -local function string_replace_char(str, new_char, idx) - local result = {} - result[1] = string.sub(str, 1, idx - 1) - result[2] = new_char - result[3] = string.sub(str, idx + 1) - return table.concat(result) -end - ---- Replaces 1 character at a random location in the string, tries up to 100 --- times if shrink gave back same result. --- @param prev previously generated string value --- @param length size of the string --- @param iterations_count remaining tries to shrink down this string --- @return shrunk down string value -local function do_shrink_generic(prev, length, iterations_count) - local idx = random.between(1, length) - local old_char = string.sub(prev, idx, idx) - local new_char = char_gen:shrink(old_char) - - if new_char == old_char and iterations_count ~= 0 then - -- Shrink introduced no simpler result, retry at other index. - return do_shrink_generic(prev, length, iterations_count - 1) - end - - return string_replace_char(prev, new_char, idx) -end - ---- Shrinks an amount of characters in the string. --- @param str string to be shrunk down --- @param length size of the string --- @param how_many amount of characters to shrink --- @return shrunk down string value -local function shrink_generic(str, length, how_many) - if how_many ~= 0 then - local new_str = do_shrink_generic(str, length, lqc.numshrinks) - return shrink_generic(new_str, length, how_many - 1) - end - - return str -end - ---- Determines if the string should be shrunk down to a shorter string --- @param str_len size of the string --- @return true: string should be made shorter; false: string should remain --- size during shrinking -local function should_shrink_smaller(str_len) - if str_len == 0 then - return false - end - return random.between(1, 5) == 1 -end - ---- Shrinks the string by removing 1 character --- @param str string to be shrunk down --- @param str_len size of the string --- @return new string with 1 random character removed -local function shrink_smaller(str, str_len) - local idx = random.between(1, str_len) - - -- Handle edge cases (first or last char) - if idx == 1 then - return string.sub(str, 2) - end - if idx == str_len then - return string.sub(str, 1, idx - 1) - end - - local new_str = {string.sub(str, 1, idx - 1), string.sub(str, idx + 1)} - return table.concat(new_str) -end - ---- Generates a string with a specific size. --- @param size size of the string to generate --- @return string of a specific size -local function do_generic_pick(size) - local result = {} - for _ = 1, size do - result[#result + 1] = char_gen:pick() - end - return table.concat(result) -end - ---- Generates a string with arbitrary length (0 <= size <= numtests). --- @param numtests Number of times the property uses this generator; used to --- guide the optimization process. --- @return string of an arbitrary size -local function arbitrary_length_pick(numtests) - local size = random.between(0, numtests) - return do_generic_pick(size) -end - ---- Shrinks a string to a simpler form (smaller / different chars). --- 1. Returns empty strings instantly --- 2. Determine if string should be made shorter --- 2.1 if true: remove a char --- 2.2 otherwise: --- * simplify a random amount of characters --- * remove a char if simplify did not help --- * otherwise return the simplified string --- @param prev previously generated string value --- @return shrunk down string value -local function arbitrary_length_shrink(prev) - local length = #prev - if length == 0 then - return prev - end -- handle empty strings - - if should_shrink_smaller(length) then - return shrink_smaller(prev, length) - end - - local new_str = shrink_generic(prev, length, shrink_how_many(length)) - if new_str == prev then - -- shrinking didn't help, remove an element - return shrink_smaller(new_str, length) - end - - -- string shrunk succesfully! - return new_str -end - ---- Helper function for generating a string with a specific size --- @param size size of the string to generate --- @return function that can generate strings of a specific size -local function specific_length_pick(size) - local function do_specific_pick() - return do_generic_pick(size) - end - return do_specific_pick -end - ---- Shrinks a string to a simpler form (only different chars since length is fixed). --- * "" -> "" --- * non-empty string, shrinks upto max 5 chars of the string --- @param prev previously generated string value --- @return shrunk down string value -local function specific_length_shrink(prev) - local length = #prev - if length == 0 then - return prev - end -- handle empty strings - return shrink_generic(prev, length, shrink_how_many(length)) -end - ---- Generator for a string with an arbitrary size --- @return generator for a string of arbitrary size -local function arbitrary_length_string() - return Gen.new(arbitrary_length_pick, arbitrary_length_shrink) -end - ---- Creates a generator for a string of a specific size --- @param size size of the string to generate --- @return generator for a string of a specific size -local function specific_length_string(size) - return Gen.new(specific_length_pick(size), specific_length_shrink) -end - ---- Creates a new ASCII string generator --- @param size size of the string --- @return generator that can generate the following: --- 1. size provided: a string of a specific size --- 2. no size provided: string of an arbitrary size -local function new(size) - if size then - return specific_length_string(size) - end - return arbitrary_length_string() -end - -return new - diff --git a/tools/lqc/generators/table.lua b/tools/lqc/generators/table.lua deleted file mode 100644 index b85d418..0000000 --- a/tools/lqc/generators/table.lua +++ /dev/null @@ -1,184 +0,0 @@ ---- Module for generating tables of varying sizes and types --- @module lqc.generators.table --- @alias new_table -local Gen = require 'lqc.generator' -local random = require 'lqc.random' -local bool = require 'lqc.generators.bool' -local int = require 'lqc.generators.int' -local float = require 'lqc.generators.float' -local string = require 'lqc.generators.string' -local lqc = require 'lqc.quickcheck' -local lqc_gen = require 'lqc.lqc_gen' -local oneof = lqc_gen.oneof -local frequency = lqc_gen.frequency -local deep_equals = require 'lqc.helpers.deep_equals' -local deep_copy = require 'lqc.helpers.deep_copy' - ---- Checks if an object is equal to another (shallow equals) --- @param a first value --- @param b second value --- @return true if a equals b; otherwise false -local function normal_equals(a, b) - return a == b -end - ---- Determines if the shrink mechanism should try to reduce the table in size. --- @param size size of the table to shrink down --- @return true if size should be reduced; otherwise false -local function should_shrink_smaller(size) - if size == 0 then - return false - end - return random.between(1, 5) == 1 -end - ---- Determines how many items in the table should be shrunk down --- @param size size of the table --- @return amount of values in the table that should be shrunk down -local function shrink_how_many(size) - -- 20% chance between 1 and size, 80% 1 element. - if random.between(1, 5) == 1 then - return random.between(1, size) - end - return 1 -end - ---- Creates a generator for a table of arbitrary or specific size --- @param table_size size of the table to be generated --- @return generator that can generate tables -local function new_table(table_size) - -- Keep a list of generators used in this new table - -- This variable is needed to share state (which generators) between - -- the pick and shrink function - local generators = {} - - --- Shrinks the table by 1 randomly chosen element - -- @param tbl previously generated table value - -- @param size size of the table - -- @return shrunk down table - local function shrink_smaller(tbl, size) - local idx = random.between(1, size) - table.remove(tbl, idx) - table.remove(generators, idx) -- also update the generators for this table!! - return tbl - end - - --- Shrink a value in the table. - -- @param tbl table to shrink down - -- @param size size of the table - -- @param iterations_count remaining amount of times the shrinking should be retried - -- @return shrunk down table - local function do_shrink_values(tbl, size, iterations_count) - local idx = random.between(1, size) - local old_value = tbl[idx] - local new_value = generators[idx]:shrink(old_value) - - -- Check if we should retry shrinking: - if iterations_count ~= 0 then - local check_equality = (type(new_value) == 'table') and deep_equals or normal_equals - if check_equality(new_value, old_value) then - -- Shrink introduced no simpler result, retry at other index. - return do_shrink_values(tbl, size, iterations_count - 1) - end - end - - tbl[idx] = new_value - return tbl - end - - --- Shrinks an amount of values in the table - -- @param tbl table to shrink down - -- @param size size of the table - -- @param how_many amount of values to shrink down - -- @return shrunk down table - local function shrink_values(tbl, size, how_many) - if how_many ~= 0 then - local new_tbl = do_shrink_values(tbl, size, lqc.numshrinks) - return shrink_values(new_tbl, size, how_many - 1) - end - return tbl - end - - --- Generates a random table with a certain size - -- @param size size of the table to generate - -- @return tableof a specific size with random values - local function do_generic_pick(size) - local result = {} - for idx = 1, size do - -- TODO: Figure out a better way to decrease table size rapidly, maybe use - -- math.log (e.g. ln(size + 1) ? - local subtable_size = math.floor(size * 0.01) - local generator = frequency {{10, new_table(subtable_size)}, - {90, oneof {bool(), int(size), float(), string(size)}}} - generators[idx] = generator - result[idx] = generator:pick(size) - end - return result - end - - -- Now actually generate a table: - if table_size then -- Specific size - --- Helper function for generating a table of a specific size - -- @param size size of the table to generate - -- @return function that can generate a table of a specific size - local function specific_size_pick(size) - local function do_pick() - return do_generic_pick(size) - end - return do_pick - end - - --- Shrinks a table without removing any elements. - -- @param prev previously generated table value - -- @return shrunk down table - local function specific_size_shrink(prev) - local size = #prev - if size == 0 then - return prev - end -- handle empty tables - return shrink_values(prev, size, shrink_how_many(size)) - end - - return Gen.new(specific_size_pick(table_size), specific_size_shrink) - end - - -- Arbitrary size - - --- Generate a (nested / empty) table of an arbitrary size. - -- @param numtests Amount of times the property calls this generator; used to - -- guide the optimatization process. - -- @return table of an arbitrary size - local function arbitrary_size_pick(numtests) - local size = random.between(0, numtests) - return do_generic_pick(size) - end - - --- Shrinks a table by removing elements or shrinking values in the table. - -- @param prev previously generated value - -- @return shrunk down table value - local function arbitrary_size_shrink(prev) - local size = #prev - if size == 0 then - return prev - end -- handle empty tables - - if should_shrink_smaller(size) then - return shrink_smaller(prev, size) - end - - local tbl_copy = deep_copy(prev) - local new_tbl = shrink_values(tbl_copy, size, shrink_how_many(size)) - if deep_equals(prev, new_tbl) then - -- shrinking didn't help, remove an element - return shrink_smaller(prev, size) - end - - -- table shrunk successfully! - return new_tbl - end - - return Gen.new(arbitrary_size_pick, arbitrary_size_shrink) -end - -return new_table - diff --git a/tools/lqc/helpers/deep_copy.lua b/tools/lqc/helpers/deep_copy.lua deleted file mode 100644 index c884336..0000000 --- a/tools/lqc/helpers/deep_copy.lua +++ /dev/null @@ -1,39 +0,0 @@ ---- Helper module for performing a deep copy. --- @module lqc.helpers.deep_copy --- @alias deep_copy -local pairs = pairs -local type = type -local setmetatable = setmetatable -local getmetatable = getmetatable - ---- Deep copies an object recursively (including (nested) tables, metatables, --- circular references, ...) --- Heavily based on http://stackoverflow.com/questions/640642/how-do-you-copy-a-lua-table-by-value --- @param obj Object to be copied --- @param seen Table of previously seen objects (for handling circular references), default nil --- @return deep copy of obj -local function deep_copy(obj, seen) - -- handle number, string, boolean, ... - if type(obj) ~= 'table' then - return obj - end - - seen = seen or {} - if seen[obj] then - return seen[obj] - end -- handle circular references - - -- handle table - local result = {} - seen[obj] = result - - for key, value in pairs(obj) do - result[deep_copy(key, seen)] = deep_copy(value, seen) - end - - -- handle metatable - return setmetatable(result, deep_copy(getmetatable(obj), seen)) -end - -return deep_copy - diff --git a/tools/lqc/helpers/deep_equals.lua b/tools/lqc/helpers/deep_equals.lua deleted file mode 100644 index 1d28421..0000000 --- a/tools/lqc/helpers/deep_equals.lua +++ /dev/null @@ -1,37 +0,0 @@ ---- Helper module for checking if 2 values are equal by value. --- @module lqc.helpers.deep_equals --- @alias deep_equals -local pairs = pairs -local type = type - ---- Checks 1 value is equal to another. Also works for nested structures. --- @param a value a --- @param b value b --- @return true if objects are equal; otherwise false -local function deep_equals(a, b) - local type_a = type(a) - if type_a ~= type(b) then - return false - end - if type_a ~= 'table' then - return a == b - end - - if #a ~= #b then - return false - end - for k, v1 in pairs(a) do - local v2 = b[k] - if type(v1) == 'table' then - return deep_equals(v1, v2) - end - if v1 ~= v2 then - return false - end - end - - return true -end - -return deep_equals - diff --git a/tools/lqc/helpers/filter.lua b/tools/lqc/helpers/filter.lua deleted file mode 100644 index a2c0f0c..0000000 --- a/tools/lqc/helpers/filter.lua +++ /dev/null @@ -1,23 +0,0 @@ ---- Helper module for filtering elements out of an array based on a predicate. --- @module lqc.helpers.filter --- @alias filter ---- Filters an array based on a predicate function --- @param array List of values in a table --- @param predicate Function taking 1 argument (element in the array), returns --- a bool indicating if element should be removed or not --- @return new array containing only the values for which the predicate is true -local function filter(array, predicate) - local result = {} - - for idx = 1, #array do - local value = array[idx] - if predicate(value) then - result[#result + 1] = value - end - end - - return result -end - -return filter - diff --git a/tools/lqc/helpers/fs.lua b/tools/lqc/helpers/fs.lua deleted file mode 100644 index e217ea1..0000000 --- a/tools/lqc/helpers/fs.lua +++ /dev/null @@ -1,109 +0,0 @@ ---- Helper module for everything filesystem related. --- @module lqc.helpers.fs --- @alias lib -local lfs = require 'lfs' -local Vector = require 'lqc.helpers.vector' - -local lib = {} - ---- Concatenates multiple strings together into 1 big string --- @param strings array of strings to be concatenated together --- @return the concatenated string -local function strcat(...) - return table.concat({...}) -end - ---- Checks if 'f' is a file? --- @param f string of a file path --- @return true if f is a file; otherwise false -function lib.is_file(f) - return lfs.attributes(f, 'mode') == 'file' -end - ---- Check if 'd' is a directory? --- @param d string of a directory path --- @return true if d is a directory; otherwise false -function lib.is_dir(d) - return lfs.attributes(d, 'mode') == 'directory' -end - ---- Is the file a Lua file? (=a file ending in .lua) --- @param file path to a file --- @return true if it is a Lua file; otherwise false. -function lib.is_lua_file(file) - return file:match('%.lua$') ~= nil -end - ---- Is the file a Moonscript file? (= a file ending in .moon) --- @param file path to a file --- @return true if it is a Moonscript file; otherwise false. -function lib.is_moonscript_file(file) - return file:match('%.moon$') ~= nil -end - ---- Removes a file from the filesystem. --- @param path Path to the file that should be removed -function lib.remove_file(path) - os.remove(path) -end - ---- Checks if a file exists. --- @param path path to a file --- @return true if the file does exist; otherwise false -function lib.file_exists(path) - return lfs.attributes(path) ~= nil -end - ---- Reads the entire contents from a (binary) file and returns it. --- @param path path to the file to read from --- @return the contents of the file as a string or nil on error -function lib.read_file(path) - local file = io.open(path, 'rb') - if not file then - return nil - end - local contents = file:read '*a' - file:close() - return contents -end - ---- Writes the 'new_contents' to the (binary) file specified by 'path' --- @param path path to file the contents should be written to --- @param new_contents the contents that will be written --- @return nil; raises an error if file could not be opened. -function lib.write_file(path, new_contents) - if not new_contents then - return - end - local file = io.open(path, 'wb') - if not file then - error('Could not write to ' .. path .. '!') - end - file:write(new_contents) - file:close() -end - ---- Finds all files in a directory. --- @param directory_path String of a directory path --- @return a table containing all files in this directory and it's --- subdirectories. Raises an error if dir is not a valid --- string to a directory path. -function lib.find_files(directory_path) - local result = Vector.new() - - for file_name in lfs.dir(directory_path) do - if file_name ~= '.' and file_name ~= '..' then - local file = strcat(directory_path, '/', file_name) - if lib.is_dir(file) then - result:append(Vector.new(lib.find_files(file))) - elseif lib.is_file(file) then - result:push_back(file) - end - end - end - - return result:to_table() -end - -return lib - diff --git a/tools/lqc/helpers/map.lua b/tools/lqc/helpers/map.lua deleted file mode 100644 index 0ac14f9..0000000 --- a/tools/lqc/helpers/map.lua +++ /dev/null @@ -1,19 +0,0 @@ ---- Helper moduile for performing a function on each element in an array. --- @module lqc.helpers.map --- @alias map ---- Maps a function over an array --- @param array List of elements on which a function will be applied --- @param func Function to be applied over the array. Takes 1 argument (element of the array); returns a result --- @return A new array with func applied to each element in the array -local function map(array, func) - local result = {} - - for idx = 1, #array do - result[#result + 1] = func(array[idx]) - end - - return result -end - -return map - diff --git a/tools/lqc/helpers/reduce.lua b/tools/lqc/helpers/reduce.lua deleted file mode 100644 index d3e52e6..0000000 --- a/tools/lqc/helpers/reduce.lua +++ /dev/null @@ -1,33 +0,0 @@ ---- Helper module for reducing an array of values into a single value. --- @module lqc.helpers.reduce --- @alias reeduce ---- Helper function that performs the actual reduce operation --- @param array List of elements to be reduced into 1 value --- @param acc Accumulator containing the temporary result --- @param func Function to be applied for each element in the array. Takes 2 --- arguments: element out of the array and the current state of the --- accumulator. Returns the updated accumulator state --- @param pos Position in the array to apply the reduce operation on --- @return the result obtained by reducing the array into 1 value -local function do_reduce(array, acc, func, pos) - if pos < #array then - local new_pos = pos + 1 - return do_reduce(array, func(array[new_pos], acc), func, new_pos) - end - - return acc -end - ---- Reduces an array of values into a single value --- @param array List of elements to be reduced into 1 value --- @param start Start value of the accumulator --- @param func Function to be applied for each element in the array. Takes 2 --- arguments: element out of the array and the current state of the --- accumulator. Returns the updated accumulator state --- @return the result obtained by reducing the array into 1 value -local function reduce(array, start, func) - return do_reduce(array, start, func, 0) -end - -return reduce - diff --git a/tools/lqc/helpers/vector.lua b/tools/lqc/helpers/vector.lua deleted file mode 100644 index c06394f..0000000 --- a/tools/lqc/helpers/vector.lua +++ /dev/null @@ -1,115 +0,0 @@ ---- Module for a data container that does not allow nil values. --- @classmod lqc.helpers.vector --- @alias Vector -local deep_equals = require 'lqc.helpers.deep_equals' - -local Vector = {} -local Vector_mt = { - __index = Vector, -} - ---- Constructs a new vector, possibly filled with data (a table value) --- @param data[opt={}] the data to be stored in the vector initially --- @return a new vector filled with the initial data if provided. -function Vector.new(data) - local begin_data = data or {} - local vector = { - data = begin_data, - } - return setmetatable(vector, Vector_mt) -end - ---- Adds an element to the back of the vector. --- @param obj a non-nil value --- @return self (for method chaining); raises an error if trying to add nil to the vector -function Vector:push_back(obj) - if obj == nil then - error 'nil is not allowed in vector datastructure!' - end - table.insert(self.data, obj) - return self -end - ---- Replaces an element in the vector. --- @param idx Index of the element in the vector to be replaced --- @param obj Object that the previous object should be replaced with --- @return self (for method chaining); raises an error if idx is an index --- not present in the vector -function Vector:replace(idx, obj) - local vec_size = self:size() - if idx < 1 or idx > vec_size then - error('Invalid index! Index should be between 1 and ' .. vec_size) - end - self.data[idx] = obj - return self -end - ---- Appends another vector to this vector --- @param other_vec Another vector object --- @return a vector containing the data of both vectors -function Vector:append(other_vec) - for i = 1, other_vec:size() do - self:push_back(other_vec:get(i)) - end - return self -end - ---- Gets the element at position 'index' in the vector --- @param index position of the value in the vector --- @return element at position 'index -function Vector:get(index) - return self.data[index] -end - ---- Checks if an element is contained in the vector --- @param element element to be checked if it is in the vector --- @return true if the element is already in the vector; otherwise false. -function Vector:contains(element) - for i = 1, #self.data do - if self.data[i] == element then - return true - end - end - return false -end - ---- Returns the size of the vector. --- @return length of the vector (0 if empty) -function Vector:size() - return #self.data -end - ---- Removes an element from the vector by value --- @param obj object to remove -function Vector:remove(obj) - -- Find element, then remove by index - local pos = -1 - for i = 1, #self.data do - if deep_equals(self.data[i], obj) then - pos = i - break - end - end - if pos == -1 then - return - end - table.remove(self.data, pos) -end - ---- Removes an element from the vector by index --- @param idx Position of the element you want to remove -function Vector:remove_index(idx) - if idx > self:size() then - return - end - table.remove(self.data, idx) -end - ---- Returns the vector, with the contents represented as a flat table --- @return Table with the contents of the vector -function Vector:to_table() - return self.data -end - -return Vector - diff --git a/tools/lqc/lqc_gen.lua b/tools/lqc/lqc_gen.lua deleted file mode 100644 index 78c7133..0000000 --- a/tools/lqc/lqc_gen.lua +++ /dev/null @@ -1,123 +0,0 @@ ---- Helper module providing various generators for generating data. --- @module lqc.lqc_gen --- @alias lib -local Gen = require 'lqc.generator' -local random = require 'lqc.random' -local reduce = require 'lqc.helpers.reduce' - -local lib = {} - ---- Picks a number randomly between min and max. --- @param min Minimum value to pick from --- @param max Maximum value to pick from --- @return a random value between min and max -local function choose_pick(min, max) - local function pick() - return random.between(min, max) - end - return pick -end - ---- Shrinks a value between min and max by dividing the sum of the closest --- number to 0 and the generated value with 2. --- This effectively reduces it to the value closest to 0 gradually in the --- chosen range. --- @param min Minimum value to pick from --- @param max Maximum value to pick from --- @return a shrunk value between min and max -local function choose_shrink(min, max) - local shrink_to = (math.abs(min) < math.abs(max)) and min or max - - local function shrink(value) - local shrunk_value = (shrink_to + value) / 2 - - if shrunk_value < 0 then - return math.ceil(shrunk_value) - else - return math.floor(shrunk_value) - end - end - - return shrink -end - ---- Creates a generator, chooses an integer between min and max (inclusive range). --- @param min Minimum value to pick from --- @param max Maximum value to pick from --- @return a random value between min and max -function lib.choose(min, max) - return Gen.new(choose_pick(min, max), choose_shrink(min, max)) -end - ---- Select a generator from a list of generators --- @param generators Table containing an array of generator objects. --- @return A new generator that randomly uses 1 of the generators in the list. -function lib.oneof(generators) - local which - local function oneof_pick(numtests) - which = random.between(1, #generators) - return generators[which]:pick(numtests) - end - local function oneof_shrink(prev) - return generators[which]:shrink(prev) - end - - return Gen.new(oneof_pick, oneof_shrink) -end - ---- Select a generator from a list of weighted generators ({{weight1, gen1}, ... }) --- @param generators A table containing an array of weighted generators. --- @return A new generator that randomly uses a generator from the list, taking the --- weights into account. -function lib.frequency(generators) - local which - - local function do_sum(generator, acc) - return generator[1] + acc - end - local function frequency_pick(numtests) - local sum = reduce(generators, 0, do_sum) - - local val = random.between(1, sum) - which = reduce(generators, {0, 1}, function(generator, acc) - local current_sum = acc[1] + generator[1] - if current_sum >= val then - return acc - else - return {current_sum, acc[2] + 1} - end - end)[2] - - return generators[which][2]:pick(numtests) - end - local function frequency_shrink(prev) - return generators[which][2]:shrink(prev) - end - - return Gen.new(frequency_pick, frequency_shrink) -end - ---- Create a generator that selects an element based on the input list. --- @param array an array of constant values --- @return Generator that can pick 1 of the values in the array, shrinks --- towards beginning of the list. -function lib.elements(array) - local last_idx - local function elements_pick() - local idx = random.between(1, #array) - last_idx = idx - return array[idx] - end - - local function elements_shrink(_) - if last_idx > 1 then - last_idx = last_idx - 1 - end - return array[last_idx] - end - - return Gen.new(elements_pick, elements_shrink) -end - -return lib - diff --git a/tools/lqc/property.lua b/tools/lqc/property.lua deleted file mode 100644 index 43ca4f2..0000000 --- a/tools/lqc/property.lua +++ /dev/null @@ -1,228 +0,0 @@ ---- Module for creating properties. Provides a small domain specific language --- for ease of use. --- @module lqc.property --- @alias property -local lqc = require 'lqc.quickcheck' -local report = require 'lqc.report' -local results = require 'lqc.property_result' -local unpack = unpack or table.unpack -- for compatibility reasons - - ---- Helper function, checks if x is an integer. --- @param x a value to be checked --- @return true if x is an integer; false otherwise. -local function is_integer(x) - return type(x) == 'number' and x % 1 == 0 -end - ---- Adds a small wrapper around the check function indicating success or failure --- @param prop_table Property to be wrapped. -local function add_check_wrapper(prop_table) - local check_func = prop_table.check - prop_table.check = function(...) - if check_func(...) then - return results.SUCCESS - else - return results.FAILURE - end - end -end - ---- Adds an 'implies' wrapper to the check function --- @param prop_table Property to be wrapped. -local function add_implies(prop_table) - local check_func = prop_table.check - prop_table.check = function(...) - if prop_table.implies(...) == false then - return results.SKIPPED - end - - return check_func(...) - end -end - ---- Adds a 'when_fail' wrapper to the check function --- @param prop_table Property to be wrapped. -local function add_when_fail(prop_table) - local check_func = prop_table.check - prop_table.check = function(...) - local result = check_func(...) - - if result == results.FAILURE then - prop_table.when_fail(...) - end - - return result - end -end - ---- Shrinks a property that failed with a certain set of inputs. --- This function is called recursively if a shrink fails. --- This function returns a simplified list or inputs. --- @param property the property that failed --- @param generated_values array of values to be shrunk down --- @param tries Amount of shrinking tries already done --- @return array of shrunk down values -local function do_shrink(property, generated_values, tries) - if not tries then - tries = 1 - end - local shrunk_values = property.shrink(unpack(generated_values)) - local result = property(unpack(shrunk_values)) - - if tries == property.numshrinks then - -- Maximum amount of shrink attempts exceeded. - return generated_values - end - - if result == results.FAILURE then - -- further try to shrink down - return do_shrink(property, shrunk_values, tries + 1) - elseif result == results.SKIPPED then - -- shrunk to invalid situation, retry - return do_shrink(property, generated_values, tries + 1) - end - - -- return generated values since they were last values for which property failed! - return generated_values -end - ---- Function that checks if the property is valid for a set amount of inputs. --- 1. check result of property X amount of times: --- - SUCCESS = OK, print '.' --- - SKIPPED = OK, print 'x' --- - FAILURE = NOT OK, see 2. --- 2. if FAILURE: --- 2.1 print property info, values for which it fails --- 2.2 do shrink to find minimal error case --- 2.3 when shrink stays the same or max amount exceeded -> print minimal example --- @param property Property to be checked X amount of times. --- @return nil on success; otherwise returns a table containing error info. -local function do_check(property) - for _ = 1, property.numtests do - local generated_values = property.pick() - local result = property(unpack(generated_values)) - - if result == results.SUCCESS then - report.report_success() - elseif result == results.SKIPPED then - report.report_skipped() - else - report.report_failed() - if #generated_values == 0 then - -- Empty list of generators -> no further shrinking possible! - return { - property = property, - generated_values = generated_values, - shrunk_values = generated_values, - } - end - - local shrunk_values = do_shrink(property, generated_values) - return { - property = property, - generated_values = generated_values, - shrunk_values = shrunk_values, - } - end - end - - return nil -end - ---- Creates a new property. --- NOTE: property is limited to 1 implies, for_all, when_fail --- more complex scenarios should be handled with state machine. --- @param descr String containing description of the property --- @param property_func Function to be checked X amount of times --- @param generators List of generators used to generate values supplied to 'property_func' --- @param numtests Number of times the property should be checked --- @param numshrinks Number of times a failing property should be shrunk down --- @return property object -local function new(descr, property_func, generators, numtests, numshrinks) - local prop = { - description = descr, - numtests = numtests, - numshrinks = numshrinks, - } - - -- Generates a new set of inputs for this property. - -- Returns the newly generated set of inputs as a table. - function prop.pick() - local generated_values = {} - for i = 1, #generators do - generated_values[i] = generators[i]:pick(numtests) - end - return generated_values - end - - -- Shrink 1 value randomly out of the given list of values. - function prop.shrink(...) - local values = {...} - local which = math.random(#values) - local shrunk_value = generators[which]:shrink(values[which]) - values[which] = shrunk_value - return values - end - - -- Function that checks if the property is valid for a set amount of inputs. - function prop:check() - return do_check(self) - end - - return setmetatable(prop, { - __call = function(_, ...) - return property_func(...) - end, - }) -end - ---- Inserts the property into the list of existing properties. --- @param descr String containing text description of the property --- @param prop_info_table Table containing information of the property --- @return nil; raises an error if prop_info_table is not in a valid format -local function property(descr, prop_info_table) - local function prop_func(prop_table) - local generators = prop_table.generators - if not generators or type(generators) ~= 'table' then - error('Need to supply generators in property!') - end - - local check_type = type(prop_table.check) - if check_type ~= 'function' and check_type ~= 'table' then - error('Need to provide a check function to property!') - end - - add_check_wrapper(prop_table) - - local implies_type = type(prop_table.implies) - if implies_type == 'function' or implies_type == 'table' then - add_implies(prop_table) - end - - local when_fail_type = type(prop_table.when_fail) - if when_fail_type == 'function' or when_fail_type == 'table' then - add_when_fail(prop_table) - end - - local it_amount = prop_table.numtests - local shrink_amount = prop_table.numshrinks - local numtests = is_integer(it_amount) and it_amount or lqc.numtests - local numshrinks = is_integer(shrink_amount) and shrink_amount or lqc.numshrinks - local new_prop = new(descr, prop_table.check, prop_table.generators, numtests, numshrinks) - table.insert(lqc.properties, new_prop) - end - - -- property called without DSL-like syntax - if prop_info_table then - prop_func(prop_info_table) - return function() - end - end - - -- property called with DSL syntax! - return prop_func -end - -return property - diff --git a/tools/lqc/property_result.lua b/tools/lqc/property_result.lua deleted file mode 100644 index 77086ab..0000000 --- a/tools/lqc/property_result.lua +++ /dev/null @@ -1,13 +0,0 @@ ---- Helper module containing enumeration of all possible results after --- evaluating a property. --- @module lqc.property_result ---- List of possible results after executing property --- @table result_enum --- @field SUCCESS property succeeded --- @field FAILURE property failed --- @field SKIPPED property skipped (implies predicate not met) -return { - SUCCESS = 1, - FAILURE = 2, - SKIPPED = 3, -} \ No newline at end of file diff --git a/tools/lqc/quickcheck.lua b/tools/lqc/quickcheck.lua deleted file mode 100644 index 7339ba1..0000000 --- a/tools/lqc/quickcheck.lua +++ /dev/null @@ -1,81 +0,0 @@ ---- Module which contains the core of the quickcheck engine. --- @module lqc.quickcheck --- @alias lib -local report = require 'lqc.report' -local map = require 'lqc.helpers.map' - -local shuffle = pairs -local lib = { - properties = {}, -- list of all properties - numtests = nil, -- Default amount of times a property should be tested - numshrinks = nil, -- Default amount of times a failing property should be shrunk down - failed = false, -} - ---- Checks if the quickcheck configuration is initialized --- @return true if it is initialized; otherwise false. -local function is_initialized() - return lib.numtests ~= nil and lib.numshrinks ~= nil -end - ---- Handles the result of a property. --- @param result table containing information of the property (or nil on success) --- @see lqc.property_result -local function handle_result(result) - if not result then - return - end -- successful - lib.failed = true - if type(result.property) == 'table' then -- property failed - report.report_failed_property(result.property, result.generated_values, result.shrunk_values) - return - end - - -- FSM failed - report.report_failed_fsm(result.description, result.generated_values, result.shrunk_values) -end - ---- Configures the amount of iterations and shrinks the check algorithm should perform. --- @param numtests Default number of tests per property --- @param numshrinks Default number of shrinks per property -function lib.init(numtests, numshrinks) - lib.numtests = numtests - lib.numshrinks = numshrinks -end - ---- Iterates over all properties in a random order and checks if the property --- holds true for each generated set of inputs. Raises an error if quickcheck --- engine is not initialized yet. -function lib.check() - if not is_initialized() then - error 'quickcheck.init() has to be called before quickcheck.check()!' - end - - for _, prop in shuffle(lib.properties) do - local result = prop:check() - handle_result(result) - end -end - ---- Multithreaded version of check(), uses a thread pool in the underlying --- implementation. Splits up the properties over different threads. --- @param numthreads Number of the threads to divide the properties over. -function lib.check_mt(numthreads) - local ThreadPool = require 'lqc.threading.thread_pool' - - if not is_initialized() then - error 'quickcheck.init() has to be called before quickcheck.check_mt()!' - end - - local pool = ThreadPool.new(numthreads) - for _, prop in shuffle(lib.properties) do - pool:schedule(function() - return prop:check() - end) - end - local results = pool:join() - map(results, handle_result) -end - -return lib - diff --git a/tools/lqc/random.lua b/tools/lqc/random.lua deleted file mode 100644 index 90745cd..0000000 --- a/tools/lqc/random.lua +++ /dev/null @@ -1,29 +0,0 @@ ---- Helper module for generating random numbers. --- @module lqc.random --- @alias lib -local time = os.time -local random_seed = math.randomseed -local random = math.random - -local lib = {} - ---- Seeds the random number generator --- @param seed Random seed (number) or nil for current timestamp --- @return The random seed used to initialize the random number generator with. -function lib.seed(seed) - if not seed then - seed = time() - end - random_seed(seed) - return seed -end - ---- Get random number between min and max --- @param min Minimum value to generate a random number in --- @param max Maximum value to generate a random number in (inclusive) -function lib.between(min, max) - return random(min, max) -end - -return lib - diff --git a/tools/lqc/report.lua b/tools/lqc/report.lua deleted file mode 100644 index c800385..0000000 --- a/tools/lqc/report.lua +++ /dev/null @@ -1,126 +0,0 @@ ---- Helper module for reporting test results to the user. --- @module lqc.report --- @alias lib -local map = require 'lqc.helpers.map' - -local write = io.write -local ipairs = ipairs - --- Variables for reporting statistics after test run is over. -local passed_amount = 0 -local failed_amount = 0 -local skipped_amount = 0 -local reported_errors = {} - -local lib = {} - ---- Formats a table to a human readable string --- @param t table to be formatted --- @return formatted table (as a string) -local function format_table(t) - local result = '{ ' - for _, v in ipairs(t) do - local type_v = type(v) - if type_v == 'table' then - result = result .. format_table(v) .. ' ' - elseif type_v == 'boolean' then - result = result .. (v and 'true ' or 'false ') - else - result = result .. v .. ' ' - end - end - return result .. '}' -end - ---- Writes a string to stdout (no newline at end). --- @param s string to be written to stdout -function lib.report(s) - write(s) -end - ---- Prints the used random seed to stdout. --- @param seed Random seed to be printed to stdout -function lib.report_seed(seed) - lib.report('Random seed = ' .. seed .. '\n') -end - ---- Prints a '.' to stdout -function lib.report_success() - passed_amount = passed_amount + 1 - lib.report '.' -end - ---- Prints a green '.' to stdout -local function report_success_colored() - passed_amount = passed_amount + 1 - lib.report '\27[32m.\27[0m' -end - ---- Prints a 'x' to stdout -function lib.report_skipped() - skipped_amount = skipped_amount + 1 - lib.report 'x' -end - ---- Prints a yellow 'x' to stdout -local function report_skipped_colored() - skipped_amount = skipped_amount + 1 - lib.report '\27[33mx\27[0m' -end - ---- Prints an 'F' to stdout -function lib.report_failed() - failed_amount = failed_amount + 1 - lib.report 'F' -end - ---- Prints a red 'F' to stdout -local function report_failed_colored() - failed_amount = failed_amount + 1 - lib.report '\27[31mF\27[0m' -end - ---- Saves an error to the list of errors. -function lib.save_error(failure_str) - table.insert(reported_errors, failure_str) -end - ---- Prints out information regarding the failed property -function lib.report_failed_property(property, generated_values, shrunk_values) - lib.save_error('\nProperty "' .. property.description .. '" failed!\n' .. 'Generated values = ' .. - format_table(generated_values) .. '\n' .. 'Simplified solution to = ' .. - format_table(shrunk_values) .. '\n') -end - ---- Prints out information regarding the failed FSM. -function lib.report_failed_fsm(description) - -- TODO output more information - lib.save_error('\nFSM ' .. description .. ' failed!\n') -end - ---- Reports all errors to stdout. -function lib.report_errors() - map(reported_errors, lib.report) - lib.report '\n' -- extra newline as separator between errors -end - ---- Prints a summary about certain statistics (test passed / failed, ...) -function lib.report_summary() - local total_tests = passed_amount + failed_amount + skipped_amount - lib.report('' .. total_tests .. ' tests, ' .. failed_amount .. ' failures, ' .. skipped_amount .. ' skipped.\n') -end - ---- Configures this module to use ANSI colors when printing to terminal or not. --- @param enable_colors true: colors will be used when printing to terminal; --- otherwise plain text will be printed. -function lib.configure(enable_colors) - if not enable_colors then - return - end - lib.report_success = report_success_colored - lib.report_skipped = report_skipped_colored - lib.report_failed = report_failed_colored -end - -return lib - diff --git a/tools/lqc/threading/msg_processor.lua b/tools/lqc/threading/msg_processor.lua deleted file mode 100644 index 8134c6e..0000000 --- a/tools/lqc/threading/msg_processor.lua +++ /dev/null @@ -1,44 +0,0 @@ ---- Helper module for a message processor that can process incoming messages --- from other threads. --- @classmod lqc.threading.msg_processor --- @alias MsgProcessor ---- Checks if 'x' is callable. --- @param x value to be checked --- @return true if callable; otherwise false. -local function is_callable(x) - local type_x = type(x) - return type_x == 'function' or type_x == 'table' -end - -local MsgProcessor = { - TASK_TAG = 'task', - RESULT_TAG = 'result', - STOP_VALUE = 'stop', - VOID_RESULT = '__VOID', -} - ---- Creates an object that can handle incoming messages. --- @param msg_box An object that can be used to send and receive incoming messages with --- @return a new MsgProcessor object -function MsgProcessor.new(msg_box) - local function main_loop_msg_processor() - -- TODO init random seed per thread? - while true do - local _, cmd = msg_box:receive(nil, MsgProcessor.TASK_TAG) - if cmd == MsgProcessor.STOP_VALUE then - return - elseif is_callable(cmd) then - -- NOTE: threadpool hangs if it returns nil.. - local result = cmd() or MsgProcessor.VOID_RESULT - msg_box:send(nil, MsgProcessor.RESULT_TAG, result) - else - return - end - end - end - - return main_loop_msg_processor -end - -return MsgProcessor - diff --git a/tools/lqc/threading/thread_pool.lua b/tools/lqc/threading/thread_pool.lua deleted file mode 100644 index f085e6c..0000000 --- a/tools/lqc/threading/thread_pool.lua +++ /dev/null @@ -1,83 +0,0 @@ ---- Module for creating a thread pool, based on Lua Lanes. --- @module lqc.threading.thread_pool --- @alias ThreadPool -local MsgProcessor = require 'lqc.threading.msg_processor' -local map = require 'lqc.helpers.map' -local lanes = require('lanes').configure { - with_timers = false, -} - ---- Checks if x is a positive integer (excluding 0) --- @param x value to be checked --- @return true if x is a non-zero positive integer; otherwise false. -local function is_positive_integer(x) - return type(x) == 'number' and x % 1 == 0 and x > 0 -end - ---- Checks if the thread pool args are valid. --- @return nil; raises an error if invalid args are passed in. -local function check_threadpool_args(num_threads) - if not is_positive_integer(num_threads) then - error 'num_threads should be an integer > 0' - end -end - ---- Creates and starts a thread. --- @param func Function the thread should run after startup --- @return a new thread object -local function make_thread(func) - return lanes.gen('*', func)() -end - -local ThreadPool = {} -local ThreadPool_mt = { - __index = ThreadPool, -} - ---- Creates a new thread pool with a specific number of threads --- @param num_threads Amount of the threads the pool should have --- @return thread pool with a specific number of threads -function ThreadPool.new(num_threads) - check_threadpool_args(num_threads) - local linda = lanes.linda() - local thread_pool = { - threads = {}, - linda = linda, - numjobs = 0, - } - - for _ = 1, num_threads do - table.insert(thread_pool.threads, make_thread(MsgProcessor.new(linda))) - end - return setmetatable(thread_pool, ThreadPool_mt) -end - ---- Schedules a task to a thread in the thread pool --- @param task A function that should be run on the thread -function ThreadPool:schedule(task) - self.numjobs = self.numjobs + 1 - self.linda:send(nil, MsgProcessor.TASK_TAG, task) -end - ---- Stops all threads in the threadpool. Blocks until all threads are finished --- @return a table containing all results (in no specific order) -function ThreadPool:join() - map(self.threads, function() - self:schedule(MsgProcessor.STOP_VALUE) - end) - map(self.threads, function(thread) - thread:join() - end) - - local results = {} - for _ = 1, self.numjobs - #self.threads do -- don't count stop job at end - local _, result = self.linda:receive(nil, MsgProcessor.RESULT_TAG) - if result ~= MsgProcessor.VOID_RESULT then - table.insert(results, result) - end - end - return results -end - -return ThreadPool -