Skip to content

Commit

Permalink
add unit tests for reacton matching; update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
myk002 committed Aug 31, 2021
1 parent b7e2d5a commit 64a66e0
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 46 deletions.
46 changes: 20 additions & 26 deletions prioritize.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,32 @@ This will force them to get assigned and completed as soon as possible.
This script can also continue to monitor new jobs and automatically boost the
priority of jobs of the specified types.
This is useful for ensuring important (but low-priority -- according to DF)
tasks don't get indefinitely ignored in busy forts. The list of monitored job
types is cleared whenever you load a new map, so you can add a section like the
one below to your ``onMapLoad.init`` file to ensure important job types are
always completed promptly in your forts::
This is useful for ensuring important (but low-priority -- according to DF) jobs
don't get indefinitely ignored in busy forts. The list of monitored job types is
cleared whenever you unload a map, so you can add a section like the one below
to your ``onMapLoad.init`` file to ensure important and time-sensitive job types
are always completed promptly in your forts::
prioritize -a --haul-labor=Food StoreItemInStockpile
prioritize -a --haul-labor=Food,Body StoreItemInStockpile
prioritize -a --reaction-name=TAN_A_HIDE CustomReaction
prioritize -a PullLever CleanSelf RecoverWounded DumpItem
prioritize -a DestroyBuilding RemoveConstruction
prioritize -a StoreItemInVehicle StoreItemInBag StoreItemInBarrel
prioritize -a SlaughterAnimal PrepareRawFish ExtractFromRawFish
prioritize -a PrepareRawFish ExtractFromRawFish CleanSelf
It is important to automatically prioritize only the *most* important job types.
If you add too many job types, or if there are simply too many jobs of those
types in your fort, the other tasks in your fort can get ignored. This causes
the same problem the ``prioritize`` script is designed to solve. The example
commands above have been extensively playtested and are a good default set. If
you need a bunch of jobs of a specific type prioritized *right now*, consider
the same problem the ``prioritize`` script is designed to solve. See the
`onMapLoad-dreamfort-init` file in the ``hack/examples/init`` folder for a more
complete, playtested set of job types to automatically prioritize.
If you need a bunch of jobs of a specific type prioritized *right now*, consider
running ``prioritize`` without the ``-a`` parameter, which only affects
currently available (but unassigned) jobs. For example::
prioritize ConstructBuilding
Also see the ``do-job-now`` `tweak` for adding a hotkey to the jobs screen that
can toggle the priority of specific individual jobs and the `do-job-now`
script, which sets the priority of jobs related to the current selected
script, which boosts the priority of current jobs related to the selected
job/unit/item/building/order.
Usage::
Expand Down Expand Up @@ -73,13 +72,13 @@ Options:
for discovering the types of the jobs that you can prioritize right now. If
any job types are specified, only returns the count for those types.
:``-l``, ``--haul-labor`` <labor>[,<labor>...]:
For StoreItemInStockpile jobs, restrict prioritization to the specified
hauling labor(s). Valid types are: "Stone", "Wood", "Body", "Food",
"Refuse", "Item", "Furniture", and "Animals". If not specified, defaults to
matching all StoreItemInStockpile jobs.
For StoreItemInStockpile jobs, match only the specified hauling labor(s).
Valid <labor> strings are: "Stone", "Wood", "Body", "Food", "Refuse",
"Item", "Furniture", and "Animals". If not specified, defaults to matching
all StoreItemInStockpile jobs.
:``-n``, ``--reaction-name`` <name>[,<name>...]:
For CustomReaction jobs, restrict prioritization to the specified reaction
name(s). See the registry output (``-r``) for the full list. If not,
For CustomReaction jobs, match only the specified reaction name(s). See the
registry output (``-r``) for the full list of reaction names. If not
specified, defaults to matching all CustomReaction jobs.
:``-q``, ``--quiet``:
Suppress informational output (error messages are still printed).
Expand Down Expand Up @@ -185,14 +184,10 @@ local function update_handlers()
end

local function get_annotation_str(annotation)
if not annotation then
return nil
end
return (' (%s)'):format(annotation)
end

local function get_unit_labor_str(unit_labor)
if not unit_labor then return nil end
local labor_str = df.unit_labor[unit_labor]
return ('%s%s'):format(labor_str:sub(6,6), labor_str:sub(7):lower())
end
Expand Down Expand Up @@ -437,8 +432,7 @@ local function remove_watch(job_matchers, opts)
function(jm) return jm.reaction_matchers end,
function(jm)
jm.reaction_matchers = {}
for _,v in ipairs(get_reactions())
do
for _,v in ipairs(get_reactions()) do
jm.reaction_matchers[v.code] = 0
end
return jm.reaction_matchers
Expand Down
121 changes: 101 additions & 20 deletions test/prioritize.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ local function get_mock_watched_job_matchers()
end
local mock_postings = {}
local function get_mock_postings() return mock_postings end
local mock_reactions = {}
local mock_reactions = {{code='TAN_A_HIDE'}}
local function get_mock_reactions() return mock_reactions end
local function test_wrapper(test_fn)
mock.patch({{eventful, 'onUnload', mock_eventful_onUnload},
Expand All @@ -26,11 +26,13 @@ local function test_wrapper(test_fn)
mock_eventful_onUnload, mock_eventful_onJobInitiated = {}, {}
mock_print = mock.func()
mock_watched_job_matchers, mock_postings = {}, {}
mock_reactions = {{code='TAN_A_HIDE'}}
end
config.wrapper = test_wrapper

local DIG, EAT, REST = df.job_type.Dig, df.job_type.Eat, df.job_type.Rest
local STORE_ITEM_IN_STOCKPILE = df.job_type.StoreItemInStockpile
local CUSTOM_REACTION = df.job_type.CustomReaction
local SUTURE = df.job_type.Suture

local HAUL_STONE, HAUL_WOOD = df.unit_labor.HAUL_STONE, df.unit_labor.HAUL_WOOD
Expand All @@ -54,17 +56,21 @@ function test.status()
end

function test.status_labor()
p.status()
expect.eq(1, mock_print.call_count)
expect.eq('Not automatically prioritizing any jobs.',
mock_print.call_args[1][1])

mock_watched_job_matchers[STORE_ITEM_IN_STOCKPILE] =
{num_prioritized=5, hauler_matchers={[HAUL_BODY]=2}}
p.status()
expect.eq(3, mock_print.call_count)
expect.eq('Automatically prioritized jobs:', mock_print.call_args[2][1])
expect.str_find('Stockpile.*Body', mock_print.call_args[3][1])
expect.eq(2, mock_print.call_count)
expect.eq('Automatically prioritized jobs:', mock_print.call_args[1][1])
expect.str_find('Stockpile.*Body', mock_print.call_args[2][1])
end

function test.status_reaction()
mock_watched_job_matchers[CUSTOM_REACTION] =
{num_prioritized=5, reaction_matchers={TAN_A_HIDE=2}}
p.status()
expect.eq(2, mock_print.call_count)
expect.eq('Automatically prioritized jobs:', mock_print.call_args[1][1])
expect.str_find('Custom.*TAN_A_HIDE', mock_print.call_args[2][1])
end

function test.boost()
Expand Down Expand Up @@ -231,6 +237,17 @@ function test.boost_and_watch_store_add_labors()
mock_watched_job_matchers)
end

function test.boost_and_watch_reactions()
p.boost_and_watch({[CUSTOM_REACTION]={num_prioritized=0,
reaction_matchers={TAN_A_HIDE=0}}}, {})
expect.eq(2, mock_print.call_count)
expect.str_find('^Prioritized 0', mock_print.call_args[1][1])
expect.str_find('^Automatically.*TAN_A_HIDE', mock_print.call_args[2][1])
expect.table_eq({[CUSTOM_REACTION]={num_prioritized=0,
reaction_matchers={TAN_A_HIDE=0}}},
mock_watched_job_matchers)
end

function test.remove_one_labor_from_all()
-- top-level num_prioritized should be persisted
mock_watched_job_matchers = {[STORE_ITEM_IN_STOCKPILE]={num_prioritized=5}}
Expand All @@ -248,7 +265,7 @@ function test.remove_one_labor_from_all()

p.remove_watch({[STORE_ITEM_IN_STOCKPILE]={num_prioritized=0,
hauler_matchers={[HAUL_FOOD]=0}}},
{})
{})
expect.eq(2, mock_print.call_count)
expect.str_find('Skipping.*Food', mock_print.call_args[2][1])
expect.table_eq({[STORE_ITEM_IN_STOCKPILE]={num_prioritized=5,
Expand All @@ -258,6 +275,20 @@ function test.remove_one_labor_from_all()
mock_watched_job_matchers)
end

function test.remove_all_reactions_from_all()
mock_watched_job_matchers = {[CUSTOM_REACTION]={num_prioritized=5}}

-- we only have one reaction in our mock registry. if we remove it by name
-- from an unrestricted CUSTOM_REACTION matcher, the entire matcher should
-- disappear
p.remove_watch({[CUSTOM_REACTION]={num_prioritized=0,
reaction_matchers={TAN_A_HIDE=0}}},
{})
expect.eq(1, mock_print.call_count)
expect.str_find('No longer.*TAN_A_HIDE', mock_print.call_args[1][1])
expect.table_eq({}, mock_watched_job_matchers)
end

function test.eventful_hook_lifecycle()
expect.nil_(mock_eventful_onUnload.prioritize)
expect.nil_(mock_eventful_onJobInitiated.prioritize)
Expand Down Expand Up @@ -321,6 +352,31 @@ function test.eventful_callbacks_labor()
expect.table_eq(expected_watched_job_matchers, mock_watched_job_matchers)
end

function test.eventful_callbacks_reaction()
mock_watched_job_matchers[CUSTOM_REACTION] =
{num_prioritized=0, reaction_matchers={TAN_A_HIDE=0}}

-- unwatched job
local job = {job_type=CUSTOM_REACTION, reaction_name='STEEL_MAKING',
flags={}}
local expected_job = utils.clone(job)
local expected_watched_job_matchers = utils.clone(mock_watched_job_matchers)
p.on_new_job(job)
expect.table_eq(expected_job, job)
expect.table_eq(expected_watched_job_matchers, mock_watched_job_matchers)

-- watched job
job = {job_type=CUSTOM_REACTION, reaction_name='TAN_A_HIDE', flags={}}
expected_job = {job_type=CUSTOM_REACTION, reaction_name='TAN_A_HIDE',
flags={do_now=true}}
expected_watched_job_matchers =
{[CUSTOM_REACTION]={num_prioritized=1,
reaction_matchers={TAN_A_HIDE=1}}}
p.on_new_job(job)
expect.table_eq(expected_job, job)
expect.table_eq(expected_watched_job_matchers, mock_watched_job_matchers)
end

function test.print_current_jobs_empty()
p.print_current_jobs({})
expect.eq(1, mock_print.call_count)
Expand All @@ -334,9 +390,11 @@ function test.print_current_jobs_full()
{job={job_type=EAT}, flags={}},
{job={job_type=REST}, flags={dead=true}},
{job={job_type=STORE_ITEM_IN_STOCKPILE,
item_subtype=HAUL_FOOD, flags={}}, flags={}}}
item_subtype=HAUL_FOOD, flags={}}, flags={}},
{job={job_type=CUSTOM_REACTION,
reaction_name='TAN_A_HIDE', flags={}}, flags={}}}
p.print_current_jobs({})
expect.eq(4, mock_print.call_count)
expect.eq(5, mock_print.call_count)
expect.eq('Current job counts by type:', mock_print.call_args[1][1])
local result = {}
for i,v in ipairs(mock_print.call_args) do
Expand All @@ -348,7 +406,8 @@ function test.print_current_jobs_full()
::continue::
end
expect.table_eq({[df.job_type[DIG]]='2', [df.job_type[EAT]]='1',
[df.job_type[STORE_ITEM_IN_STOCKPILE]]='1'},
[df.job_type[STORE_ITEM_IN_STOCKPILE]]='1',
[df.job_type[CUSTOM_REACTION]]='1'},
result)
end

Expand All @@ -375,20 +434,22 @@ end

function test.print_registry()
p.print_registry()
expect.lt(0, mock_print.call_count)
expect.lt(1, mock_print.call_count)
for i,v in ipairs(mock_print.call_args) do
local out = v[1]:trim()
if i == 1 then
expect.eq('Job types:', out)
goto continue
end
expect.ne('nil', tostring(out))
expect.str_find('^%u%l', out)
expect.ne('NONE', out)
::continue::
end
end

function test.print_registry_no_raws()
mock_reactions = {}
p.print_registry()
expect.lt(1, mock_print.call_count)
expect.eq('Load a game to see reactions',
mock_print.call_args[#mock_print.call_args][1]:trim())
end

function test.parse_commandline()
expect.table_eq({help=true}, p.parse_commandline{'help'})
expect.table_eq({help=true}, p.parse_commandline{'-h'})
Expand All @@ -414,6 +475,11 @@ function test.parse_commandline()
expect.table_eq({action=p.status, job_matchers={}},
p.parse_commandline{'-lXFoodX'})
end)
expect.printerr_match('Ignoring unknown reaction name',
function()
expect.table_eq({action=p.status, job_matchers={}},
p.parse_commandline{'-nXTAN_A_HIDEX'})
end)

expect.table_eq({action=p.status, job_matchers={}, quiet=true},
p.parse_commandline{'-q'})
Expand Down Expand Up @@ -455,6 +521,21 @@ function test.parse_commandline()
hauler_matchers={[HAUL_FOOD]=0}}}},
p.parse_commandline{'-jlfood', 'StoreItemInStockpile'})

expect.table_eq({action=p.status, job_matchers={}},
p.parse_commandline{'-nTAN_A_HIDE'})
expect.table_eq({action=p.boost,
job_matchers={[SUTURE]={num_prioritized=0}}},
p.parse_commandline{'-nTAN_A_HIDE', 'Suture'})
expect.table_eq({action=p.boost,
job_matchers={[STORE_ITEM_IN_STOCKPILE]=
{num_prioritized=0}}},
p.parse_commandline{'-nTAN_A_HIDE', 'StoreItemInStockpile'})
expect.table_eq({action=p.boost,
job_matchers={[CUSTOM_REACTION]=
{num_prioritized=0,
reaction_matchers={TAN_A_HIDE=0}}}},
p.parse_commandline{'-nTAN_A_HIDE', 'CustomReaction'})

expect.table_eq({action=p.print_registry, job_matchers={}},
p.parse_commandline{'-r'})
expect.table_eq({action=p.print_registry, job_matchers={}},
Expand Down

0 comments on commit 64a66e0

Please sign in to comment.