You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

105 lines
3.8 KiB
Lua

--- 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)