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