mirror of
https://github.com/Refactorio/RedMew.git
synced 2025-01-05 22:53:39 +02:00
commit
4b78d9eae3
@ -169,6 +169,10 @@ if _DUMP_ENV then
|
|||||||
require 'utils.dump_env'
|
require 'utils.dump_env'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if _DEBUG then
|
||||||
|
require('utils.test.main')
|
||||||
|
end
|
||||||
|
|
||||||
-- Needs to be at bottom so tokens are registered last.
|
-- Needs to be at bottom so tokens are registered last.
|
||||||
if _DEBUG then
|
if _DEBUG then
|
||||||
require 'features.gui.debug.command'
|
require 'features.gui.debug.command'
|
||||||
|
@ -56,9 +56,7 @@ local function can_select_landfill_tiles(cursor, surface, area)
|
|||||||
entity_filters[#entity_filters + 1] = 'character'
|
entity_filters[#entity_filters + 1] = 'character'
|
||||||
end
|
end
|
||||||
|
|
||||||
if surface.count_entities_filtered({area = area, name = entity_filters, invert = invert, limit = 1}) > 0 then
|
return surface.count_entities_filtered({area = area, name = entity_filters, invert = invert, limit = 1}) == 0
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function within_reach(tile_position, player_position, radius_squared)
|
local function within_reach(tile_position, player_position, radius_squared)
|
||||||
|
701
features/landfill_remover_tests.lua
Normal file
701
features/landfill_remover_tests.lua
Normal file
@ -0,0 +1,701 @@
|
|||||||
|
local Declare = require 'utils.test.declare'
|
||||||
|
local EventFactory = require 'utils.test.event_factory'
|
||||||
|
local Assert = require 'utils.test.assert'
|
||||||
|
local Helper = require 'utils.test.helper'
|
||||||
|
|
||||||
|
local main_inventory = defines.inventory.character_main
|
||||||
|
local config = global.config.landfill_remover
|
||||||
|
|
||||||
|
local tile_items = {
|
||||||
|
'stone-brick',
|
||||||
|
'concrete',
|
||||||
|
'hazard-concrete',
|
||||||
|
'refined-concrete',
|
||||||
|
'refined-hazard-concrete'
|
||||||
|
}
|
||||||
|
|
||||||
|
Declare.module(
|
||||||
|
{'features', 'landfill remover'},
|
||||||
|
function()
|
||||||
|
local teardown
|
||||||
|
|
||||||
|
Declare.module_startup(
|
||||||
|
function(context)
|
||||||
|
teardown = Helper.startup_test_surface(context)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
Declare.module_teardown(
|
||||||
|
function()
|
||||||
|
teardown()
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
local function setup_player_with_default_deconstruction_planner(player)
|
||||||
|
local inventory = player.get_inventory(main_inventory)
|
||||||
|
inventory.clear()
|
||||||
|
inventory.insert('deconstruction-planner')
|
||||||
|
local stack = inventory.find_item_stack('deconstruction-planner')
|
||||||
|
|
||||||
|
local cursor = player.cursor_stack
|
||||||
|
cursor.set_stack(stack)
|
||||||
|
|
||||||
|
return cursor
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setup_player_with_valid_deconstruction_planner(player)
|
||||||
|
local inventory = player.get_inventory(main_inventory)
|
||||||
|
inventory.clear()
|
||||||
|
inventory.insert('deconstruction-planner')
|
||||||
|
local stack = inventory.find_item_stack('deconstruction-planner')
|
||||||
|
stack.set_tile_filter(1, 'landfill')
|
||||||
|
stack.tile_selection_mode = defines.deconstruction_item.tile_selection_mode.only
|
||||||
|
|
||||||
|
local cursor = player.cursor_stack
|
||||||
|
cursor.set_stack(stack)
|
||||||
|
|
||||||
|
return cursor
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setup_player_with_tile_filter_whitelist_deconstruction_planner(player)
|
||||||
|
local inventory = player.get_inventory(main_inventory)
|
||||||
|
inventory.clear()
|
||||||
|
inventory.insert('deconstruction-planner')
|
||||||
|
local stack = inventory.find_item_stack('deconstruction-planner')
|
||||||
|
stack.set_tile_filter(1, 'landfill')
|
||||||
|
stack.tile_filter_mode = defines.deconstruction_item.tile_filter_mode.whitelist
|
||||||
|
|
||||||
|
local cursor = player.cursor_stack
|
||||||
|
cursor.set_stack(stack)
|
||||||
|
|
||||||
|
return cursor
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setup_player_with_tile_filter_blacklist_deconstruction_planner(player)
|
||||||
|
local inventory = player.get_inventory(main_inventory)
|
||||||
|
inventory.clear()
|
||||||
|
inventory.insert('deconstruction-planner')
|
||||||
|
local stack = inventory.find_item_stack('deconstruction-planner')
|
||||||
|
stack.set_tile_filter(1, 'landfill')
|
||||||
|
stack.tile_filter_mode = defines.deconstruction_item.tile_filter_mode.blacklist
|
||||||
|
|
||||||
|
local cursor = player.cursor_stack
|
||||||
|
cursor.set_stack(stack)
|
||||||
|
|
||||||
|
return cursor
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setup_player_with_normal_selection_mode_deconstruction_planner(player)
|
||||||
|
local inventory = player.get_inventory(main_inventory)
|
||||||
|
inventory.clear()
|
||||||
|
inventory.insert('deconstruction-planner')
|
||||||
|
local stack = inventory.find_item_stack('deconstruction-planner')
|
||||||
|
stack.set_tile_filter(1, 'landfill')
|
||||||
|
stack.tile_selection_mode = defines.deconstruction_item.tile_selection_mode.normal
|
||||||
|
|
||||||
|
local cursor = player.cursor_stack
|
||||||
|
cursor.set_stack(stack)
|
||||||
|
|
||||||
|
return cursor
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setup_player_with_never_selection_mode_deconstruction_planner(player)
|
||||||
|
local inventory = player.get_inventory(main_inventory)
|
||||||
|
inventory.clear()
|
||||||
|
inventory.insert('deconstruction-planner')
|
||||||
|
local stack = inventory.find_item_stack('deconstruction-planner')
|
||||||
|
stack.set_tile_filter(1, 'landfill')
|
||||||
|
stack.tile_selection_mode = defines.deconstruction_item.tile_selection_mode.never
|
||||||
|
|
||||||
|
local cursor = player.cursor_stack
|
||||||
|
cursor.set_stack(stack)
|
||||||
|
|
||||||
|
return cursor
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setup_player_with_always_selection_mode_deconstruction_planner(player)
|
||||||
|
local inventory = player.get_inventory(main_inventory)
|
||||||
|
inventory.clear()
|
||||||
|
inventory.insert('deconstruction-planner')
|
||||||
|
local stack = inventory.find_item_stack('deconstruction-planner')
|
||||||
|
stack.set_tile_filter(1, 'landfill')
|
||||||
|
stack.tile_selection_mode = defines.deconstruction_item.tile_selection_mode.always
|
||||||
|
|
||||||
|
local cursor = player.cursor_stack
|
||||||
|
cursor.set_stack(stack)
|
||||||
|
|
||||||
|
return cursor
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setup_player_with_no_landfill_deconstruction_planner(player)
|
||||||
|
local inventory = player.get_inventory(main_inventory)
|
||||||
|
inventory.clear()
|
||||||
|
inventory.insert('deconstruction-planner')
|
||||||
|
local stack = inventory.find_item_stack('deconstruction-planner')
|
||||||
|
stack.tile_selection_mode = defines.deconstruction_item.tile_selection_mode.only
|
||||||
|
|
||||||
|
local cursor = player.cursor_stack
|
||||||
|
cursor.set_stack(stack)
|
||||||
|
|
||||||
|
return cursor
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setup_player_with_trees_and_rocks_only_deconstruction_planner(player)
|
||||||
|
local inventory = player.get_inventory(main_inventory)
|
||||||
|
inventory.clear()
|
||||||
|
inventory.insert('deconstruction-planner')
|
||||||
|
local stack = inventory.find_item_stack('deconstruction-planner')
|
||||||
|
stack.set_tile_filter(1, 'landfill')
|
||||||
|
stack.trees_and_rocks_only = true
|
||||||
|
|
||||||
|
local cursor = player.cursor_stack
|
||||||
|
cursor.set_stack(stack)
|
||||||
|
|
||||||
|
return cursor
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setup_player_with_entity_filter_whitelist_deconstruction_planner(player)
|
||||||
|
local inventory = player.get_inventory(main_inventory)
|
||||||
|
inventory.clear()
|
||||||
|
inventory.insert('deconstruction-planner')
|
||||||
|
local stack = inventory.find_item_stack('deconstruction-planner')
|
||||||
|
stack.set_tile_filter(1, 'landfill')
|
||||||
|
stack.set_entity_filter(1, 'iron-chest')
|
||||||
|
stack.entity_filter_mode = defines.deconstruction_item.entity_filter_mode.whitelist
|
||||||
|
|
||||||
|
local cursor = player.cursor_stack
|
||||||
|
cursor.set_stack(stack)
|
||||||
|
|
||||||
|
return cursor
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setup_player_with_entity_filter_blacklist_deconstruction_planner(player)
|
||||||
|
local inventory = player.get_inventory(main_inventory)
|
||||||
|
inventory.clear()
|
||||||
|
inventory.insert('deconstruction-planner')
|
||||||
|
local stack = inventory.find_item_stack('deconstruction-planner')
|
||||||
|
stack.set_tile_filter(1, 'landfill')
|
||||||
|
stack.set_entity_filter(1, 'iron-chest')
|
||||||
|
stack.entity_filter_mode = defines.deconstruction_item.entity_filter_mode.blacklist
|
||||||
|
|
||||||
|
local cursor = player.cursor_stack
|
||||||
|
cursor.set_stack(stack)
|
||||||
|
|
||||||
|
return cursor
|
||||||
|
end
|
||||||
|
|
||||||
|
Declare.test(
|
||||||
|
'can remove landfill',
|
||||||
|
function(context)
|
||||||
|
-- Arrange
|
||||||
|
local player = context.player
|
||||||
|
local surface = player.surface
|
||||||
|
local cursor = setup_player_with_valid_deconstruction_planner(player)
|
||||||
|
local position = {2, 2}
|
||||||
|
local area = {{2.1, 2.1}, {2.9, 2.9}}
|
||||||
|
surface.set_tiles({{name = 'landfill', position = position}})
|
||||||
|
|
||||||
|
-- Act
|
||||||
|
EventFactory.do_player_deconstruct_area(cursor, player, area)
|
||||||
|
|
||||||
|
-- Assert
|
||||||
|
local tile = surface.get_tile(position[1], position[2])
|
||||||
|
Assert.equal(config.revert_tile, tile.name)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, item_name in pairs(tile_items) do
|
||||||
|
Declare.test(
|
||||||
|
'can remove landfill when covered by ' .. item_name,
|
||||||
|
function(context)
|
||||||
|
-- Arrange
|
||||||
|
local player = context.player
|
||||||
|
local surface = player.surface
|
||||||
|
local position = {2, 2}
|
||||||
|
local area = {{2.1, 2.1}, {2.9, 2.9}}
|
||||||
|
surface.set_tiles({{name = 'landfill', position = position}})
|
||||||
|
|
||||||
|
-- Place covering tile.
|
||||||
|
local cursor = player.cursor_stack
|
||||||
|
cursor.set_stack(item_name)
|
||||||
|
player.build_from_cursor({position = position, terrain_building_size = 1})
|
||||||
|
|
||||||
|
cursor = setup_player_with_valid_deconstruction_planner(player)
|
||||||
|
|
||||||
|
-- Act
|
||||||
|
EventFactory.do_player_deconstruct_area(cursor, player, area)
|
||||||
|
|
||||||
|
-- Assert
|
||||||
|
local tile = surface.get_tile(position[1], position[2])
|
||||||
|
Assert.equal(config.revert_tile, tile.name)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
Declare.test(
|
||||||
|
'does not remove landfill when entity present',
|
||||||
|
function(context)
|
||||||
|
-- Arrange
|
||||||
|
local player = context.player
|
||||||
|
local surface = player.surface
|
||||||
|
local position = {2, 2}
|
||||||
|
local area = {{2.1, 2.1}, {2.9, 2.9}}
|
||||||
|
surface.set_tiles({{name = 'landfill', position = position}})
|
||||||
|
|
||||||
|
-- Place entity.
|
||||||
|
local cursor = player.cursor_stack
|
||||||
|
cursor.set_stack('iron-chest')
|
||||||
|
player.build_from_cursor({position = position})
|
||||||
|
|
||||||
|
cursor = setup_player_with_valid_deconstruction_planner(player)
|
||||||
|
|
||||||
|
-- Act
|
||||||
|
EventFactory.do_player_deconstruct_area(cursor, player, area)
|
||||||
|
|
||||||
|
-- Assert
|
||||||
|
local tile = surface.get_tile(position[1], position[2])
|
||||||
|
Assert.equal('landfill', tile.name)
|
||||||
|
|
||||||
|
local entities = surface.find_entities(area)
|
||||||
|
local entity = entities[1]
|
||||||
|
Assert.is_lua_object_with_name(entity, 'iron-chest', 'iron-chest was not valid.')
|
||||||
|
entity.destroy()
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, item_name in pairs(tile_items) do
|
||||||
|
Declare.test(
|
||||||
|
'does not remove covered by ' .. item_name .. ' landfill when entity present',
|
||||||
|
function(context)
|
||||||
|
-- Arrange
|
||||||
|
local player = context.player
|
||||||
|
local surface = player.surface
|
||||||
|
local position = {2, 2}
|
||||||
|
local area = {{2.1, 2.1}, {2.9, 2.9}}
|
||||||
|
surface.set_tiles({{name = 'landfill', position = position}})
|
||||||
|
|
||||||
|
-- Place covering tile.
|
||||||
|
local cursor = player.cursor_stack
|
||||||
|
cursor.set_stack(item_name)
|
||||||
|
player.build_from_cursor({position = position, terrain_building_size = 1})
|
||||||
|
|
||||||
|
local before_tile = surface.get_tile(position[1], position[2])
|
||||||
|
|
||||||
|
-- Place entity.
|
||||||
|
cursor.set_stack('iron-chest')
|
||||||
|
player.build_from_cursor({position = position})
|
||||||
|
|
||||||
|
cursor = setup_player_with_valid_deconstruction_planner(player)
|
||||||
|
|
||||||
|
-- Act
|
||||||
|
EventFactory.do_player_deconstruct_area(cursor, player, area)
|
||||||
|
|
||||||
|
-- Assert
|
||||||
|
local tile = surface.get_tile(position[1], position[2])
|
||||||
|
Assert.equal(before_tile.name, tile.name)
|
||||||
|
|
||||||
|
local entities = surface.find_entities(area)
|
||||||
|
local entity = entities[1]
|
||||||
|
Assert.is_lua_object_with_name(entity, 'iron-chest', 'iron-chest was not valid.')
|
||||||
|
entity.destroy()
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
Declare.test(
|
||||||
|
'does not remove landfill when out of reach',
|
||||||
|
function(context)
|
||||||
|
-- Arrange
|
||||||
|
local player = context.player
|
||||||
|
local surface = player.surface
|
||||||
|
local build_distance = player.build_distance + 5
|
||||||
|
local position = {build_distance, build_distance}
|
||||||
|
local area = {
|
||||||
|
{build_distance + 0.1, build_distance + 0.1},
|
||||||
|
{build_distance + 0.9, build_distance + 0.9}
|
||||||
|
}
|
||||||
|
surface.set_tiles({{name = 'landfill', position = position}})
|
||||||
|
local cursor = setup_player_with_valid_deconstruction_planner(player)
|
||||||
|
|
||||||
|
-- Act
|
||||||
|
EventFactory.do_player_deconstruct_area(cursor, player, area)
|
||||||
|
|
||||||
|
-- Assert
|
||||||
|
local tile = surface.get_tile(position[1], position[2])
|
||||||
|
Assert.equal('landfill', tile.name)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, item_name in pairs(tile_items) do
|
||||||
|
Declare.test(
|
||||||
|
'does not remove landfill when out of reach and covered by ' .. item_name,
|
||||||
|
function(context)
|
||||||
|
-- Arrange
|
||||||
|
local player = context.player
|
||||||
|
local surface = player.surface
|
||||||
|
local build_distance = player.build_distance + 5
|
||||||
|
local position = {build_distance, build_distance}
|
||||||
|
local area = {
|
||||||
|
{build_distance + 0.1, build_distance + 0.1},
|
||||||
|
{build_distance + 0.9, build_distance + 0.9}
|
||||||
|
}
|
||||||
|
surface.set_tiles({{name = 'landfill', position = position}})
|
||||||
|
|
||||||
|
-- Place covering tile.
|
||||||
|
local cursor = player.cursor_stack
|
||||||
|
cursor.set_stack(item_name)
|
||||||
|
player.build_from_cursor({position = position, terrain_building_size = 1})
|
||||||
|
|
||||||
|
local before_tile = surface.get_tile(position[1], position[2])
|
||||||
|
|
||||||
|
cursor = setup_player_with_valid_deconstruction_planner(player)
|
||||||
|
|
||||||
|
-- Act
|
||||||
|
EventFactory.do_player_deconstruct_area(cursor, player, area)
|
||||||
|
|
||||||
|
-- Assert
|
||||||
|
local tile = surface.get_tile(position[1], position[2])
|
||||||
|
Assert.equal(before_tile.name, tile.name)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
Declare.test(
|
||||||
|
'does not remove landfill when trees and rocks only',
|
||||||
|
function(context)
|
||||||
|
-- Arrange
|
||||||
|
local player = context.player
|
||||||
|
local surface = player.surface
|
||||||
|
local position = {2, 2}
|
||||||
|
local area = {{2.1, 2.1}, {2.9, 2.9}}
|
||||||
|
surface.set_tiles({{name = 'landfill', position = position}})
|
||||||
|
local cursor = setup_player_with_trees_and_rocks_only_deconstruction_planner(player)
|
||||||
|
|
||||||
|
-- Act
|
||||||
|
EventFactory.do_player_deconstruct_area(cursor, player, area)
|
||||||
|
|
||||||
|
-- Assert
|
||||||
|
local tile = surface.get_tile(position[1], position[2])
|
||||||
|
Assert.equal('landfill', tile.name)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
Declare.test(
|
||||||
|
'does not remove landfill when default deconstruction planner',
|
||||||
|
function(context)
|
||||||
|
-- Arrange
|
||||||
|
local player = context.player
|
||||||
|
local surface = player.surface
|
||||||
|
local position = {2, 2}
|
||||||
|
local area = {{2.1, 2.1}, {2.9, 2.9}}
|
||||||
|
surface.set_tiles({{name = 'landfill', position = position}})
|
||||||
|
local cursor = setup_player_with_default_deconstruction_planner(player)
|
||||||
|
|
||||||
|
-- Act
|
||||||
|
EventFactory.do_player_deconstruct_area(cursor, player, area)
|
||||||
|
|
||||||
|
-- Assert
|
||||||
|
local tile = surface.get_tile(position[1], position[2])
|
||||||
|
Assert.equal('landfill', tile.name)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
local tile_mode_test_cases = {
|
||||||
|
{
|
||||||
|
name = 'only',
|
||||||
|
setup = setup_player_with_valid_deconstruction_planner,
|
||||||
|
should_remove = true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = 'normal',
|
||||||
|
setup = setup_player_with_normal_selection_mode_deconstruction_planner,
|
||||||
|
should_remove = true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = 'always',
|
||||||
|
setup = setup_player_with_always_selection_mode_deconstruction_planner,
|
||||||
|
should_remove = true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = 'never',
|
||||||
|
setup = setup_player_with_never_selection_mode_deconstruction_planner,
|
||||||
|
should_remove = false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = 'no landfill',
|
||||||
|
setup = setup_player_with_no_landfill_deconstruction_planner,
|
||||||
|
should_remove = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test_case in pairs(tile_mode_test_cases) do
|
||||||
|
Declare.test(
|
||||||
|
'tile mode ' ..
|
||||||
|
test_case.name .. ' should ' .. (test_case.should_remove and '' or 'not ') .. 'remove landfill',
|
||||||
|
function(context)
|
||||||
|
-- Arrange
|
||||||
|
local player = context.player
|
||||||
|
local surface = player.surface
|
||||||
|
local cursor = test_case.setup(player)
|
||||||
|
local position = {2, 2}
|
||||||
|
local area = {{2.1, 2.1}, {2.9, 2.9}}
|
||||||
|
surface.set_tiles({{name = 'landfill', position = position}})
|
||||||
|
local expected_tile = test_case.should_remove and config.revert_tile or 'landfill'
|
||||||
|
|
||||||
|
-- Act
|
||||||
|
EventFactory.do_player_deconstruct_area(cursor, player, area)
|
||||||
|
|
||||||
|
-- Assert
|
||||||
|
local tile = surface.get_tile(position[1], position[2])
|
||||||
|
Assert.equal(expected_tile, tile.name)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
local tile_filter_test_cases = {
|
||||||
|
{
|
||||||
|
name = 'whitelist',
|
||||||
|
setup = setup_player_with_tile_filter_whitelist_deconstruction_planner,
|
||||||
|
should_remove = true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = 'blacklist',
|
||||||
|
setup = setup_player_with_tile_filter_blacklist_deconstruction_planner,
|
||||||
|
should_remove = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test_case in pairs(tile_filter_test_cases) do
|
||||||
|
Declare.test(
|
||||||
|
'tile filter ' ..
|
||||||
|
test_case.name .. ' should ' .. (test_case.should_remove and '' or 'not ') .. 'remove landfill',
|
||||||
|
function(context)
|
||||||
|
-- Arrange
|
||||||
|
local player = context.player
|
||||||
|
local surface = player.surface
|
||||||
|
local cursor = test_case.setup(player)
|
||||||
|
local position = {2, 2}
|
||||||
|
local area = {{2.1, 2.1}, {2.9, 2.9}}
|
||||||
|
surface.set_tiles({{name = 'landfill', position = position}})
|
||||||
|
local expected_tile = test_case.should_remove and config.revert_tile or 'landfill'
|
||||||
|
|
||||||
|
-- Act
|
||||||
|
EventFactory.do_player_deconstruct_area(cursor, player, area)
|
||||||
|
|
||||||
|
-- Assert
|
||||||
|
local tile = surface.get_tile(position[1], position[2])
|
||||||
|
Assert.equal(expected_tile, tile.name)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
local tile_mode_with_entity_test_cases = {
|
||||||
|
{
|
||||||
|
name = 'only',
|
||||||
|
setup = setup_player_with_valid_deconstruction_planner,
|
||||||
|
should_remove = true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = 'normal',
|
||||||
|
setup = setup_player_with_normal_selection_mode_deconstruction_planner,
|
||||||
|
should_remove = false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = 'always',
|
||||||
|
setup = setup_player_with_always_selection_mode_deconstruction_planner,
|
||||||
|
should_remove = true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = 'never',
|
||||||
|
setup = setup_player_with_never_selection_mode_deconstruction_planner,
|
||||||
|
should_remove = false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = 'no landfill',
|
||||||
|
setup = setup_player_with_no_landfill_deconstruction_planner,
|
||||||
|
should_remove = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test_case in pairs(tile_mode_with_entity_test_cases) do
|
||||||
|
Declare.test(
|
||||||
|
'tile mode ' ..
|
||||||
|
test_case.name ..
|
||||||
|
' with entity should ' .. (test_case.should_remove and '' or 'not ') .. 'remove landfill',
|
||||||
|
function(context)
|
||||||
|
-- Arrange
|
||||||
|
local player = context.player
|
||||||
|
local surface = player.surface
|
||||||
|
|
||||||
|
local position1 = {2, 2}
|
||||||
|
local position2 = {3, 2}
|
||||||
|
local area = {{2.1, 2.1}, {3.9, 2.9}}
|
||||||
|
surface.set_tiles(
|
||||||
|
{{name = 'landfill', position = position1}, {name = 'landfill', position = position2}}
|
||||||
|
)
|
||||||
|
local expected_tile = test_case.should_remove and config.revert_tile or 'landfill'
|
||||||
|
|
||||||
|
-- Place entity.
|
||||||
|
local cursor = player.cursor_stack
|
||||||
|
cursor.set_stack('iron-chest')
|
||||||
|
player.build_from_cursor({position = position1})
|
||||||
|
|
||||||
|
cursor = test_case.setup(player)
|
||||||
|
|
||||||
|
-- Act
|
||||||
|
EventFactory.do_player_deconstruct_area(cursor, player, area)
|
||||||
|
|
||||||
|
-- Assert
|
||||||
|
local tile = surface.get_tile(position2[1], position2[2])
|
||||||
|
Assert.equal(expected_tile, tile.name)
|
||||||
|
|
||||||
|
local entities = surface.find_entities(area)
|
||||||
|
local entity = entities[1]
|
||||||
|
Assert.is_lua_object_with_name(entity, 'iron-chest', 'iron-chest was not valid.')
|
||||||
|
entity.destroy()
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
local tile_filter_with_entity_test_cases = {
|
||||||
|
{
|
||||||
|
name = 'whitelist',
|
||||||
|
setup = setup_player_with_tile_filter_whitelist_deconstruction_planner,
|
||||||
|
should_remove = false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = 'blacklist',
|
||||||
|
setup = setup_player_with_tile_filter_blacklist_deconstruction_planner,
|
||||||
|
should_remove = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test_case in pairs(tile_filter_with_entity_test_cases) do
|
||||||
|
Declare.test(
|
||||||
|
'tile mode ' ..
|
||||||
|
test_case.name ..
|
||||||
|
' with entity should ' .. (test_case.should_remove and '' or 'not ') .. 'remove landfill',
|
||||||
|
function(context)
|
||||||
|
-- Arrange
|
||||||
|
local player = context.player
|
||||||
|
local surface = player.surface
|
||||||
|
|
||||||
|
local position1 = {2, 2}
|
||||||
|
local position2 = {3, 2}
|
||||||
|
local area = {{2.1, 2.1}, {3.9, 2.9}}
|
||||||
|
surface.set_tiles(
|
||||||
|
{{name = 'landfill', position = position1}, {name = 'landfill', position = position2}}
|
||||||
|
)
|
||||||
|
local expected_tile = test_case.should_remove and config.revert_tile or 'landfill'
|
||||||
|
|
||||||
|
-- Place entity.
|
||||||
|
local cursor = player.cursor_stack
|
||||||
|
cursor.set_stack('iron-chest')
|
||||||
|
player.build_from_cursor({position = position1})
|
||||||
|
|
||||||
|
cursor = test_case.setup(player)
|
||||||
|
|
||||||
|
-- Act
|
||||||
|
EventFactory.do_player_deconstruct_area(cursor, player, area)
|
||||||
|
|
||||||
|
-- Assert
|
||||||
|
local tile = surface.get_tile(position2[1], position2[2])
|
||||||
|
Assert.equal(expected_tile, tile.name)
|
||||||
|
|
||||||
|
local entities = surface.find_entities(area)
|
||||||
|
local entity = entities[1]
|
||||||
|
Assert.is_lua_object_with_name(entity, 'iron-chest', 'iron-chest was not valid.')
|
||||||
|
entity.destroy()
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
local entity_filter_with_entity_test_cases = {
|
||||||
|
{
|
||||||
|
name = 'whitelist',
|
||||||
|
setup = setup_player_with_entity_filter_whitelist_deconstruction_planner,
|
||||||
|
should_remove = false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = 'blacklist',
|
||||||
|
setup = setup_player_with_entity_filter_blacklist_deconstruction_planner,
|
||||||
|
should_remove = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test_case in pairs(entity_filter_with_entity_test_cases) do
|
||||||
|
Declare.test(
|
||||||
|
'entity filter ' ..
|
||||||
|
test_case.name ..
|
||||||
|
' with entity should ' .. (test_case.should_remove and '' or 'not ') .. 'remove landfill',
|
||||||
|
function(context)
|
||||||
|
-- Arrange
|
||||||
|
local player = context.player
|
||||||
|
local surface = player.surface
|
||||||
|
|
||||||
|
local position1 = {2, 2}
|
||||||
|
local position2 = {3, 2}
|
||||||
|
local area = {{2.1, 2.1}, {3.9, 2.9}}
|
||||||
|
surface.set_tiles(
|
||||||
|
{{name = 'landfill', position = position1}, {name = 'landfill', position = position2}}
|
||||||
|
)
|
||||||
|
local expected_tile = test_case.should_remove and config.revert_tile or 'landfill'
|
||||||
|
|
||||||
|
-- Place entity.
|
||||||
|
local cursor = player.cursor_stack
|
||||||
|
cursor.set_stack('iron-chest')
|
||||||
|
player.build_from_cursor({position = position1})
|
||||||
|
|
||||||
|
cursor = test_case.setup(player)
|
||||||
|
|
||||||
|
-- Act
|
||||||
|
EventFactory.do_player_deconstruct_area(cursor, player, area)
|
||||||
|
|
||||||
|
-- Assert
|
||||||
|
local tile = surface.get_tile(position2[1], position2[2])
|
||||||
|
Assert.equal(expected_tile, tile.name)
|
||||||
|
|
||||||
|
local entities = surface.find_entities(area)
|
||||||
|
local entity = entities[1]
|
||||||
|
Assert.is_lua_object_with_name(entity, 'iron-chest', 'iron-chest was not valid.')
|
||||||
|
entity.destroy()
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
Declare.test(
|
||||||
|
'ignore character when removing landfill',
|
||||||
|
function(context)
|
||||||
|
-- Arrange
|
||||||
|
local player = context.player
|
||||||
|
local surface = player.surface
|
||||||
|
local cursor = setup_player_with_valid_deconstruction_planner(player)
|
||||||
|
local positions = {
|
||||||
|
{-1, -1},
|
||||||
|
{-1, 0},
|
||||||
|
{0, -1},
|
||||||
|
{0, 0}
|
||||||
|
}
|
||||||
|
local area = {{-1.5, -1.5}, {0.5, 0.5}}
|
||||||
|
|
||||||
|
surface.set_tiles(
|
||||||
|
{
|
||||||
|
{name = 'landfill', position = positions[1]},
|
||||||
|
{name = 'landfill', position = positions[2]},
|
||||||
|
{name = 'landfill', position = positions[3]},
|
||||||
|
{name = 'landfill', position = positions[4]}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
-- Act
|
||||||
|
EventFactory.do_player_deconstruct_area(cursor, player, area)
|
||||||
|
|
||||||
|
-- Assert
|
||||||
|
for _, pos in pairs(positions) do
|
||||||
|
local tile = surface.get_tile(pos[1], pos[2])
|
||||||
|
Assert.equal(config.revert_tile, tile.name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
)
|
@ -135,4 +135,6 @@ function Public.get_on_nth_tick_event_handlers()
|
|||||||
return on_nth_tick_event_handlers
|
return on_nth_tick_event_handlers
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Public.on_event = on_event
|
||||||
|
|
||||||
return Public
|
return Public
|
||||||
|
@ -23,6 +23,8 @@ local top_elements = {}
|
|||||||
local on_visible_handlers = {}
|
local on_visible_handlers = {}
|
||||||
local on_pre_hidden_handlers = {}
|
local on_pre_hidden_handlers = {}
|
||||||
|
|
||||||
|
Gui._top_elements = top_elements
|
||||||
|
|
||||||
function Gui.uid_name()
|
function Gui.uid_name()
|
||||||
return tostring(Token.uid())
|
return tostring(Token.uid())
|
||||||
end
|
end
|
||||||
|
59
utils/gui_tests.lua
Normal file
59
utils/gui_tests.lua
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
local Declare = require 'utils.test.declare'
|
||||||
|
local EventFactory = require 'utils.test.event_factory'
|
||||||
|
local Gui = require 'utils.gui'
|
||||||
|
local Assert = require 'utils.test.assert'
|
||||||
|
|
||||||
|
Declare.module(
|
||||||
|
{'utils', 'Gui'},
|
||||||
|
function()
|
||||||
|
Declare.module(
|
||||||
|
'can toggle top buttons',
|
||||||
|
function()
|
||||||
|
local function count_gui_elements(gui)
|
||||||
|
return #gui.top.children + #gui.left.children + #gui.center.children
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, name in pairs(Gui._top_elements) do
|
||||||
|
Declare.test(
|
||||||
|
Gui.names[name],
|
||||||
|
function(context)
|
||||||
|
local player = context.player
|
||||||
|
local element = player.gui.top[name]
|
||||||
|
|
||||||
|
if not element.enabled then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local event = EventFactory.on_gui_click(element, player.index)
|
||||||
|
local click_action = function()
|
||||||
|
EventFactory.raise(event)
|
||||||
|
end
|
||||||
|
|
||||||
|
local before_count = count_gui_elements(player.gui)
|
||||||
|
|
||||||
|
-- Open
|
||||||
|
click_action()
|
||||||
|
local after_open_count = count_gui_elements(player.gui)
|
||||||
|
Assert.is_true(
|
||||||
|
after_open_count > before_count,
|
||||||
|
'after open count should be greater than before count.'
|
||||||
|
)
|
||||||
|
|
||||||
|
-- Close
|
||||||
|
context:next(click_action):next(
|
||||||
|
function()
|
||||||
|
local after_close_count = count_gui_elements(player.gui)
|
||||||
|
Assert.equal(
|
||||||
|
before_count,
|
||||||
|
after_close_count,
|
||||||
|
'after close count should be equal to before count.'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
)
|
52
utils/test/assert.lua
Normal file
52
utils/test/assert.lua
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
local error = error
|
||||||
|
local concat = table.concat
|
||||||
|
|
||||||
|
local Public = {}
|
||||||
|
|
||||||
|
local function append_optional_message(main_message, optional_message)
|
||||||
|
if optional_message then
|
||||||
|
return concat {main_message, ' - ', optional_message}
|
||||||
|
end
|
||||||
|
return main_message
|
||||||
|
end
|
||||||
|
|
||||||
|
function Public.equal(a, b, optional_message)
|
||||||
|
if a == b then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local message = {tostring(a), ' ~= ', tostring(b)}
|
||||||
|
if optional_message then
|
||||||
|
message[#message + 1] = ' - '
|
||||||
|
message[#message + 1] = optional_message
|
||||||
|
end
|
||||||
|
|
||||||
|
message = concat(message)
|
||||||
|
error(message, 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Public.is_true(condition, optional_message)
|
||||||
|
if not condition then
|
||||||
|
error(optional_message or 'condition was not true', 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Public.valid(lua_object, optional_message)
|
||||||
|
if not lua_object then
|
||||||
|
error(append_optional_message('lua_object was nil', optional_message), 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not lua_object.valid then
|
||||||
|
error(append_optional_message('lua_object was not valid', optional_message), 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Public.is_lua_object_with_name(lua_object, name, optional_message)
|
||||||
|
Public.valid(lua_object, optional_message)
|
||||||
|
|
||||||
|
if lua_object.name ~= name then
|
||||||
|
error(append_optional_message("lua_object did not have name '" .. tostring(name) .. "'", optional_message), 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Public
|
154
utils/test/builder.lua
Normal file
154
utils/test/builder.lua
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
local ModuleStore = require 'utils.test.module_store'
|
||||||
|
local Context = require 'utils.test.context'
|
||||||
|
|
||||||
|
local Public = {}
|
||||||
|
|
||||||
|
local is_init = false
|
||||||
|
local id_count = 0
|
||||||
|
|
||||||
|
local function get_id()
|
||||||
|
id_count = id_count + 1
|
||||||
|
return id_count
|
||||||
|
end
|
||||||
|
|
||||||
|
local function init_inner(module, depth)
|
||||||
|
module.id = get_id()
|
||||||
|
module.depth = depth
|
||||||
|
|
||||||
|
local count = 0
|
||||||
|
|
||||||
|
local tests = {}
|
||||||
|
for name, func in pairs(module.test_funcs) do
|
||||||
|
count = count + 1
|
||||||
|
tests[#tests + 1] = {
|
||||||
|
id = get_id(),
|
||||||
|
name = name,
|
||||||
|
module = module,
|
||||||
|
func = func,
|
||||||
|
context = nil,
|
||||||
|
current_step = nil,
|
||||||
|
passed = nil,
|
||||||
|
error = nil
|
||||||
|
}
|
||||||
|
end
|
||||||
|
module.tests = tests
|
||||||
|
|
||||||
|
for _, child in pairs(module.children) do
|
||||||
|
count = count + init_inner(child, depth + 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
module.count = count
|
||||||
|
return count
|
||||||
|
end
|
||||||
|
|
||||||
|
function Public.init()
|
||||||
|
if is_init then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
is_init = true
|
||||||
|
init_inner(ModuleStore.root_module, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Public.get_root_modules()
|
||||||
|
Public.init()
|
||||||
|
return ModuleStore.root_module
|
||||||
|
end
|
||||||
|
|
||||||
|
local function prepare_pre_module_hooks(module, runnables, player)
|
||||||
|
local startup_func = module.startup_func
|
||||||
|
if startup_func then
|
||||||
|
runnables[#runnables + 1] = {
|
||||||
|
is_hook = true,
|
||||||
|
name = 'startup',
|
||||||
|
module = module,
|
||||||
|
func = startup_func,
|
||||||
|
context = Context.new(player),
|
||||||
|
current_step = 0,
|
||||||
|
error = nil
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function build_pre_module_hooks(module, runnables, player)
|
||||||
|
if module == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
build_pre_module_hooks(module.parent, runnables, player)
|
||||||
|
prepare_pre_module_hooks(module, runnables, player)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function prepare_post_module_hooks(module, runnables, player)
|
||||||
|
local teardown_func = module.teardown_func
|
||||||
|
if teardown_func then
|
||||||
|
runnables[#runnables + 1] = {
|
||||||
|
is_hook = true,
|
||||||
|
name = 'teardown',
|
||||||
|
module = module,
|
||||||
|
func = teardown_func,
|
||||||
|
context = Context.new(player),
|
||||||
|
current_step = 0,
|
||||||
|
error = nil
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function build_post_module_hooks(module, runnables, player)
|
||||||
|
if module == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
prepare_post_module_hooks(module, runnables, player)
|
||||||
|
build_post_module_hooks(module.parent, runnables, player)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function prepare_test(test, player)
|
||||||
|
test.context = Context.new(player)
|
||||||
|
test.current_step = 0
|
||||||
|
test.passed = nil
|
||||||
|
test.error = nil
|
||||||
|
return test
|
||||||
|
end
|
||||||
|
|
||||||
|
local function prepare_module(module, runnables, player)
|
||||||
|
module.passed = nil
|
||||||
|
prepare_pre_module_hooks(module, runnables, player)
|
||||||
|
|
||||||
|
for _, test in pairs(module.tests) do
|
||||||
|
prepare_test(test, player)
|
||||||
|
runnables[#runnables + 1] = test
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, child in pairs(module.children) do
|
||||||
|
prepare_module(child, runnables, player)
|
||||||
|
end
|
||||||
|
|
||||||
|
prepare_post_module_hooks(module, runnables, player)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Public.build_test_for_run(test, player)
|
||||||
|
Public.init()
|
||||||
|
|
||||||
|
local runnables = {}
|
||||||
|
|
||||||
|
build_pre_module_hooks(test.module, runnables, player)
|
||||||
|
runnables[#runnables + 1] = prepare_test(test, player)
|
||||||
|
build_post_module_hooks(test.module, runnables, player)
|
||||||
|
|
||||||
|
return runnables
|
||||||
|
end
|
||||||
|
|
||||||
|
function Public.build_module_for_run(module, player)
|
||||||
|
Public.init()
|
||||||
|
|
||||||
|
local runnables = {}
|
||||||
|
|
||||||
|
build_pre_module_hooks(module.parent, runnables, player)
|
||||||
|
prepare_module(module, runnables, player)
|
||||||
|
build_post_module_hooks(module.parent, runnables, player)
|
||||||
|
|
||||||
|
return runnables
|
||||||
|
end
|
||||||
|
|
||||||
|
return Public
|
21
utils/test/command.lua
Normal file
21
utils/test/command.lua
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
local Command = require 'utils.command'
|
||||||
|
local Runner = require 'utils.test.runner'
|
||||||
|
local Viewer = require 'utils.test.viewer'
|
||||||
|
|
||||||
|
Command.add(
|
||||||
|
'test-runner',
|
||||||
|
{
|
||||||
|
description = "Runs tests and opens the test runner, use flag 'open' to skip running tests first.",
|
||||||
|
arguments = {'open'},
|
||||||
|
default_values = {open = false},
|
||||||
|
allowed_by_server = false
|
||||||
|
},
|
||||||
|
function(args, player)
|
||||||
|
local open = args.open
|
||||||
|
if open == 'open' or open == 'o' then
|
||||||
|
Viewer.open(player)
|
||||||
|
else
|
||||||
|
Runner.run_module(nil, player)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
18
utils/test/context.lua
Normal file
18
utils/test/context.lua
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
local Public = {}
|
||||||
|
Public.__index = Public
|
||||||
|
|
||||||
|
function Public.new(player)
|
||||||
|
return setmetatable({player = player, _steps = {}}, Public)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Public.timeout(self, delay, func)
|
||||||
|
local steps = self._steps
|
||||||
|
steps[#steps + 1] = {func = func, delay = delay or 1}
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Public.next(self, func)
|
||||||
|
return self:timeout(1, func)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Public
|
10
utils/test/declare.lua
Normal file
10
utils/test/declare.lua
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
local ModuleStore = require 'utils.test.module_store'
|
||||||
|
|
||||||
|
local Public = {}
|
||||||
|
|
||||||
|
Public.module = ModuleStore.module
|
||||||
|
Public.test = ModuleStore.test
|
||||||
|
Public.module_startup = ModuleStore.module_startup
|
||||||
|
Public.module_teardown = ModuleStore.module_teardown
|
||||||
|
|
||||||
|
return Public
|
5
utils/test/discovery.lua
Normal file
5
utils/test/discovery.lua
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
local include = require 'utils.test.include'
|
||||||
|
|
||||||
|
for name in pairs(_G.package.loaded) do
|
||||||
|
include(name .. '_tests')
|
||||||
|
end
|
74
utils/test/event_factory.lua
Normal file
74
utils/test/event_factory.lua
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
local EventCore = require 'utils.event_core'
|
||||||
|
|
||||||
|
local Public = {}
|
||||||
|
|
||||||
|
Public.raise = EventCore.on_event
|
||||||
|
|
||||||
|
function Public.position(position)
|
||||||
|
local x = position.x or position[1]
|
||||||
|
local y = position.y or position[2]
|
||||||
|
|
||||||
|
position.x = x
|
||||||
|
position[1] = x
|
||||||
|
position.y = y
|
||||||
|
position[2] = y
|
||||||
|
|
||||||
|
return position
|
||||||
|
end
|
||||||
|
|
||||||
|
function Public.area(area)
|
||||||
|
local left_top = area.left_top or area[1]
|
||||||
|
local right_bottom = area.right_bottom or area[2]
|
||||||
|
|
||||||
|
Public.position(left_top)
|
||||||
|
Public.position(right_bottom)
|
||||||
|
|
||||||
|
area.left_top = left_top
|
||||||
|
area[1] = left_top
|
||||||
|
area.right_bottom = right_bottom
|
||||||
|
area[2] = right_bottom
|
||||||
|
|
||||||
|
return area
|
||||||
|
end
|
||||||
|
|
||||||
|
function Public.on_gui_click(element, player_index)
|
||||||
|
return {
|
||||||
|
name = defines.events.on_gui_click,
|
||||||
|
tick = game.tick,
|
||||||
|
element = element,
|
||||||
|
player_index = player_index,
|
||||||
|
button = defines.mouse_button_type.left,
|
||||||
|
alt = false,
|
||||||
|
control = false,
|
||||||
|
shift = false
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function Public.on_player_deconstructed_area(player_index, surface, area, item)
|
||||||
|
return {
|
||||||
|
name = defines.events.on_player_deconstructed_area,
|
||||||
|
tick = game.tick,
|
||||||
|
player_index = player_index,
|
||||||
|
surface = surface,
|
||||||
|
area = Public.area(area),
|
||||||
|
item = item,
|
||||||
|
alt = false
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function Public.do_player_deconstruct_area(cursor, player, area, optional_skip_fog_of_war)
|
||||||
|
cursor.deconstruct_area(
|
||||||
|
{
|
||||||
|
surface = player.surface,
|
||||||
|
force = player.force,
|
||||||
|
area = area,
|
||||||
|
by_player = player,
|
||||||
|
skip_fog_of_war = optional_skip_fog_of_war
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
local event = Public.on_player_deconstructed_area(player.index, player.surface, area, cursor.name)
|
||||||
|
Public.raise(event)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Public
|
128
utils/test/helper.lua
Normal file
128
utils/test/helper.lua
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
local Global = require 'utils.global'
|
||||||
|
|
||||||
|
local Public = {}
|
||||||
|
|
||||||
|
local surface_count = 0
|
||||||
|
|
||||||
|
Global.register(
|
||||||
|
{surface_count = surface_count},
|
||||||
|
function(tbl)
|
||||||
|
surface_count = tbl.surface_count
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
local function get_surface_name()
|
||||||
|
surface_count = surface_count + 1
|
||||||
|
return 'test_surface' .. surface_count
|
||||||
|
end
|
||||||
|
|
||||||
|
local autoplace_settings = {
|
||||||
|
tile = {
|
||||||
|
treat_missing_as_default = false,
|
||||||
|
settings = {
|
||||||
|
['grass-1'] = {frequency = 1, size = 1, richness = 1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local autoplace_controls = {
|
||||||
|
trees = {
|
||||||
|
frequency = 1,
|
||||||
|
richness = 1,
|
||||||
|
size = 0
|
||||||
|
},
|
||||||
|
['enemy-base'] = {
|
||||||
|
frequency = 1,
|
||||||
|
richness = 1,
|
||||||
|
size = 0
|
||||||
|
},
|
||||||
|
coal = {
|
||||||
|
frequency = 1,
|
||||||
|
richness = 1,
|
||||||
|
size = 0
|
||||||
|
},
|
||||||
|
['copper-ore'] = {
|
||||||
|
frequency = 1,
|
||||||
|
richness = 1,
|
||||||
|
size = 0
|
||||||
|
},
|
||||||
|
['crude-oil'] = {
|
||||||
|
frequency = 1,
|
||||||
|
richness = 1,
|
||||||
|
size = 0
|
||||||
|
},
|
||||||
|
['iron-ore'] = {
|
||||||
|
frequency = 1,
|
||||||
|
richness = 1,
|
||||||
|
size = 0
|
||||||
|
},
|
||||||
|
stone = {
|
||||||
|
frequency = 1,
|
||||||
|
richness = 1,
|
||||||
|
size = 0
|
||||||
|
},
|
||||||
|
['uranium-ore'] = {
|
||||||
|
frequency = 1,
|
||||||
|
richness = 1,
|
||||||
|
size = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local cliff_settings = {
|
||||||
|
cliff_elevation_0 = 1024,
|
||||||
|
cliff_elevation_interval = 10,
|
||||||
|
name = 'cliff'
|
||||||
|
}
|
||||||
|
|
||||||
|
function Public.startup_test_surface(context, options)
|
||||||
|
options = options or {}
|
||||||
|
local name = options.name or get_surface_name()
|
||||||
|
local area = options.area or {64, 64}
|
||||||
|
|
||||||
|
local player = context.player
|
||||||
|
local old_surface = player.surface
|
||||||
|
local old_position = player.position
|
||||||
|
local old_character = player.character
|
||||||
|
|
||||||
|
local surface =
|
||||||
|
game.create_surface(
|
||||||
|
name,
|
||||||
|
{
|
||||||
|
width = area.x or area[1],
|
||||||
|
height = area.y or area[2],
|
||||||
|
autoplace_settings = autoplace_settings,
|
||||||
|
autoplace_controls = autoplace_controls,
|
||||||
|
cliff_settings = cliff_settings
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
surface.request_to_generate_chunks({0, 0}, 32)
|
||||||
|
surface.force_generate_chunk_requests()
|
||||||
|
|
||||||
|
context:next(
|
||||||
|
function()
|
||||||
|
for k, v in pairs(surface.find_entities()) do
|
||||||
|
v.destroy()
|
||||||
|
end
|
||||||
|
|
||||||
|
surface.destroy_decoratives {area = {{-32, -32}, {32, 32}}}
|
||||||
|
|
||||||
|
player.character = nil
|
||||||
|
player.teleport({0, 0}, surface)
|
||||||
|
player.create_character()
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
return function()
|
||||||
|
player.character = nil
|
||||||
|
player.teleport(old_position, old_surface)
|
||||||
|
|
||||||
|
if old_character and old_character.valid then
|
||||||
|
player.character = old_character
|
||||||
|
end
|
||||||
|
|
||||||
|
game.delete_surface(surface)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Public
|
14
utils/test/include.lua
Normal file
14
utils/test/include.lua
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
local require = require
|
||||||
|
local pcall = pcall
|
||||||
|
local find = string.find
|
||||||
|
|
||||||
|
local function file_is_missing(message)
|
||||||
|
return find(message, 'no such file') or find(message, 'File was removed to decrease save file size')
|
||||||
|
end
|
||||||
|
|
||||||
|
return function(name)
|
||||||
|
local s, e = pcall(require, name)
|
||||||
|
if not s and not file_is_missing(e) then
|
||||||
|
error(e, 2)
|
||||||
|
end
|
||||||
|
end
|
5
utils/test/main.lua
Normal file
5
utils/test/main.lua
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
local include = require 'utils.test.include'
|
||||||
|
include 'utils.test.runner'
|
||||||
|
include 'utils.test.viewer'
|
||||||
|
include 'utils.test.command'
|
||||||
|
include 'utils.test.discovery'
|
145
utils/test/module_store.lua
Normal file
145
utils/test/module_store.lua
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
local Public = {}
|
||||||
|
|
||||||
|
local function new_module(module_name)
|
||||||
|
return {
|
||||||
|
id = nil,
|
||||||
|
name = module_name,
|
||||||
|
parent = nil,
|
||||||
|
children = {},
|
||||||
|
startup_func = nil,
|
||||||
|
startup_steps = nil,
|
||||||
|
startup_current_step = nil,
|
||||||
|
startup_error = nil,
|
||||||
|
teardown_func = nil,
|
||||||
|
teardown_steps = nil,
|
||||||
|
teardown_current_step = nil,
|
||||||
|
teardown_error = nil,
|
||||||
|
test_funcs = {},
|
||||||
|
tests = nil,
|
||||||
|
is_open = false,
|
||||||
|
depth = nil,
|
||||||
|
count = nil,
|
||||||
|
passed = nil
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local root_module = new_module(nil)
|
||||||
|
root_module.is_open = true
|
||||||
|
Public.root_module = root_module
|
||||||
|
|
||||||
|
local parent_module = nil
|
||||||
|
|
||||||
|
local function add_module(module_name, module_func, parent)
|
||||||
|
local parent_children = parent.children
|
||||||
|
local module = parent_children[module_name]
|
||||||
|
|
||||||
|
if not module then
|
||||||
|
module = new_module(module_name)
|
||||||
|
parent_children[module_name] = module
|
||||||
|
module.parent = parent_module
|
||||||
|
end
|
||||||
|
|
||||||
|
parent_module = module
|
||||||
|
module_func()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function no_op()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function add_module_range(modules_names, module_func, parent)
|
||||||
|
for i = 1, #modules_names - 1 do
|
||||||
|
local name = modules_names[i]
|
||||||
|
add_module(name, no_op, parent)
|
||||||
|
parent = parent_module
|
||||||
|
end
|
||||||
|
|
||||||
|
add_module(modules_names[#modules_names], module_func, parent)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Public.module(module_name, module_func)
|
||||||
|
local module_name_type = type(module_name)
|
||||||
|
if module_name_type ~= 'string' and module_name_type ~= 'table' then
|
||||||
|
error('module_name must be of type string or array of strings.', 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if module_name_type == 'table' and #module_name == 0 then
|
||||||
|
error('when module_name is array must be non empty.', 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(module_func) ~= 'function' then
|
||||||
|
error('module_func must be of type function.', 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local old_parent = parent_module
|
||||||
|
local parent = parent_module or root_module
|
||||||
|
|
||||||
|
if module_name_type == 'string' then
|
||||||
|
add_module(module_name, module_func, parent)
|
||||||
|
else
|
||||||
|
add_module_range(module_name, module_func, parent)
|
||||||
|
end
|
||||||
|
|
||||||
|
parent_module = old_parent
|
||||||
|
end
|
||||||
|
|
||||||
|
function Public.test(test_name, test_func)
|
||||||
|
if not parent_module then
|
||||||
|
error('test can not be declared outisde of a module.', 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(test_name) ~= 'string' then
|
||||||
|
error('test_name must be of type string.', 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(test_func) ~= 'function' then
|
||||||
|
error('test_func must be of type function.', 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local test_funcs = parent_module.test_funcs
|
||||||
|
if test_funcs[test_name] then
|
||||||
|
error(
|
||||||
|
table.concat {
|
||||||
|
"test '",
|
||||||
|
test_name,
|
||||||
|
"' already exists, can not have duplicate test names in the same module."
|
||||||
|
},
|
||||||
|
2
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test_funcs[test_name] = test_func
|
||||||
|
end
|
||||||
|
|
||||||
|
function Public.module_startup(startup_func)
|
||||||
|
if type(startup_func) ~= 'function' then
|
||||||
|
error('startup_func must be of type function.', 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if parent_module == nil then
|
||||||
|
error('root module can not have startup_func.', 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if parent_module.startup_func ~= nil then
|
||||||
|
error('startup_func can not be declared twice for the same module.', 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
parent_module.startup_func = startup_func
|
||||||
|
end
|
||||||
|
|
||||||
|
function Public.module_teardown(teardown_func)
|
||||||
|
if type(teardown_func) ~= 'function' then
|
||||||
|
error('teardown_func must be of type function.', 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if parent_module == nil then
|
||||||
|
error('root module can not have teardown_func.', 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if parent_module.teardown_func ~= nil then
|
||||||
|
error('teardown_func can not be declared twice for the same module.', 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
parent_module.teardown_func = teardown_func
|
||||||
|
end
|
||||||
|
|
||||||
|
return Public
|
244
utils/test/runner.lua
Normal file
244
utils/test/runner.lua
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
local Token = require 'utils.token'
|
||||||
|
local Task = require 'utils.task'
|
||||||
|
local ModuleStore = require 'utils.test.module_store'
|
||||||
|
local Builder = require 'utils.test.builder'
|
||||||
|
local Event = require 'utils.event'
|
||||||
|
|
||||||
|
local pcall = pcall
|
||||||
|
|
||||||
|
local Public = {}
|
||||||
|
|
||||||
|
Public.events = {
|
||||||
|
tests_run_finished = Event.generate_event_name('test_run_finished')
|
||||||
|
}
|
||||||
|
|
||||||
|
local run_runnables_token
|
||||||
|
|
||||||
|
local function print_summary(data)
|
||||||
|
local pass_count = data.count - data.fail_count
|
||||||
|
data.player.print(table.concat {pass_count, ' of ', data.count, ' tests passed.'})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function mark_module_for_passed(module)
|
||||||
|
local any_fails = false
|
||||||
|
local all_ran = true
|
||||||
|
|
||||||
|
for _, child in pairs(module.children) do
|
||||||
|
local module_any_fails, module_all_ran = mark_module_for_passed(child)
|
||||||
|
any_fails = any_fails or module_any_fails
|
||||||
|
all_ran = all_ran and module_all_ran
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, test in pairs(module.tests) do
|
||||||
|
any_fails = any_fails or (test.passed == false)
|
||||||
|
all_ran = all_ran and (test.passed ~= nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
if any_fails then
|
||||||
|
module.passed = false
|
||||||
|
elseif all_ran then
|
||||||
|
module.passed = true
|
||||||
|
else
|
||||||
|
module.passed = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return any_fails, all_ran
|
||||||
|
end
|
||||||
|
|
||||||
|
local function mark_modules_for_passed()
|
||||||
|
mark_module_for_passed(ModuleStore.root_module)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function finish_test_run(data)
|
||||||
|
print_summary(data)
|
||||||
|
mark_modules_for_passed()
|
||||||
|
script.raise_event(Public.events.tests_run_finished, {player = data.player})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function print_error(player, test_name, error_message)
|
||||||
|
player.print(table.concat {"Failed - '", test_name, "': ", tostring(error_message)}, {r = 1})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function print_success(player, test_name)
|
||||||
|
player.print(table.concat {"Passed - '", test_name, "'"}, {g = 1})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function print_hook_error(hook)
|
||||||
|
hook.context.player.print(table.concat {'Failed ', hook.name, " hook -':", tostring(hook.error)}, {r = 1})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function record_hook_error_in_module(hook)
|
||||||
|
if hook.name == 'startup' then
|
||||||
|
hook.module.startup_error = hook.error
|
||||||
|
elseif hook.name == 'teardown' then
|
||||||
|
hook.module.teardown_error = hook.error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function do_termination(data)
|
||||||
|
if not data.stop_on_first_error then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
data.player.print('Test run canceled due to stop on first error policy.')
|
||||||
|
finish_test_run(data)
|
||||||
|
data.index = -1
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function run_hook(hook)
|
||||||
|
local context = hook.context
|
||||||
|
local steps = context._steps
|
||||||
|
local current_step = hook.current_step
|
||||||
|
|
||||||
|
local func
|
||||||
|
if current_step == 0 then
|
||||||
|
func = hook.func
|
||||||
|
else
|
||||||
|
func = steps[current_step].func
|
||||||
|
end
|
||||||
|
|
||||||
|
local success, return_value = pcall(func, context)
|
||||||
|
|
||||||
|
if not success then
|
||||||
|
hook.error = return_value
|
||||||
|
print_hook_error(hook)
|
||||||
|
record_hook_error_in_module(hook)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if current_step == #steps then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function do_hook(hook, data)
|
||||||
|
local hook_success = run_hook(hook)
|
||||||
|
|
||||||
|
if hook_success == false and do_termination(data) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if hook_success == nil then
|
||||||
|
local step_index = hook.current_step + 1
|
||||||
|
local step = hook.context._steps[step_index]
|
||||||
|
hook.current_step = step_index
|
||||||
|
Task.set_timeout_in_ticks(step.delay or 1, run_runnables_token, data)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
data.index = data.index + 1
|
||||||
|
Task.set_timeout_in_ticks(1, run_runnables_token, data)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local function run_test(test)
|
||||||
|
local context = test.context
|
||||||
|
local steps = context._steps
|
||||||
|
local current_step = test.current_step
|
||||||
|
|
||||||
|
local func
|
||||||
|
if current_step == 0 then
|
||||||
|
func = test.func
|
||||||
|
else
|
||||||
|
func = steps[current_step].func
|
||||||
|
end
|
||||||
|
|
||||||
|
local success, return_value = pcall(func, context)
|
||||||
|
|
||||||
|
if not success then
|
||||||
|
print_error(context.player, test.name, return_value)
|
||||||
|
test.passed = false
|
||||||
|
test.error = return_value
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if current_step == #steps then
|
||||||
|
print_success(context.player, test.name)
|
||||||
|
test.passed = true
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function do_test(test, data)
|
||||||
|
local success = run_test(test)
|
||||||
|
|
||||||
|
if success == false then
|
||||||
|
data.count = data.count + 1
|
||||||
|
data.fail_count = data.fail_count + 1
|
||||||
|
|
||||||
|
if do_termination(data) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
data.index = data.index + 1
|
||||||
|
Task.set_timeout_in_ticks(1, run_runnables_token, data)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if success == true then
|
||||||
|
data.count = data.count + 1
|
||||||
|
data.index = data.index + 1
|
||||||
|
Task.set_timeout_in_ticks(1, run_runnables_token, data)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local step_index = test.current_step + 1
|
||||||
|
test.current_step = step_index
|
||||||
|
local step = test.context._steps[step_index]
|
||||||
|
Task.set_timeout_in_ticks(step.delay or 1, run_runnables_token, data)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function run_runnables(data)
|
||||||
|
local index = data.index
|
||||||
|
local runnable = data.runnables[index]
|
||||||
|
|
||||||
|
if runnable == nil then
|
||||||
|
finish_test_run(data)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if runnable.is_hook then
|
||||||
|
do_hook(runnable, data)
|
||||||
|
else
|
||||||
|
do_test(runnable, data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
run_runnables_token = Token.register(run_runnables)
|
||||||
|
|
||||||
|
local function validate_options(options)
|
||||||
|
options = options or {}
|
||||||
|
options.stop_on_first_error = options.stop_on_first_error or false
|
||||||
|
return options
|
||||||
|
end
|
||||||
|
|
||||||
|
local function run(runnables, player, options)
|
||||||
|
options = validate_options(options)
|
||||||
|
run_runnables(
|
||||||
|
{
|
||||||
|
runnables = runnables,
|
||||||
|
player = player,
|
||||||
|
index = 1,
|
||||||
|
count = 0,
|
||||||
|
fail_count = 0,
|
||||||
|
stop_on_first_error = options.stop_on_first_error
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Public.run_module(module, player, options)
|
||||||
|
local runnables = Builder.build_module_for_run(module or ModuleStore.root_module, player)
|
||||||
|
run(runnables, player, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Public.run_test(test, player, options)
|
||||||
|
local runnables = Builder.build_test_for_run(test, player)
|
||||||
|
run(runnables, player, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Public
|
423
utils/test/viewer.lua
Normal file
423
utils/test/viewer.lua
Normal file
@ -0,0 +1,423 @@
|
|||||||
|
local Gui = require 'utils.gui'
|
||||||
|
local Builder = require 'utils.test.builder'
|
||||||
|
local Runner = require 'utils.test.runner'
|
||||||
|
local Color = require 'resources.color_presets'
|
||||||
|
local Token = require 'utils.token'
|
||||||
|
local Task = require 'utils.task'
|
||||||
|
local Event = require 'utils.event'
|
||||||
|
local Global = require 'utils.global'
|
||||||
|
|
||||||
|
local Public = {}
|
||||||
|
|
||||||
|
local info_type_test = {}
|
||||||
|
local info_type_module = {}
|
||||||
|
|
||||||
|
local down_arrow = '▼'
|
||||||
|
local right_arrow = '►'
|
||||||
|
local color_success = {g = 1}
|
||||||
|
local color_failure = {r = 1}
|
||||||
|
local color_selected = Color.orange
|
||||||
|
local color_default = Color.white
|
||||||
|
|
||||||
|
local main_frame_name = Gui.uid_name()
|
||||||
|
local close_main_frame_name = Gui.uid_name()
|
||||||
|
local module_arrow_name = Gui.uid_name()
|
||||||
|
local module_label_name = Gui.uid_name()
|
||||||
|
local test_label_name = Gui.uid_name()
|
||||||
|
local run_all_button_name = Gui.uid_name()
|
||||||
|
local run_selected_button_name = Gui.uid_name()
|
||||||
|
local stop_on_error_checkbox_name = Gui.uid_name()
|
||||||
|
local error_test_box_name = Gui.uid_name()
|
||||||
|
|
||||||
|
local selected_test_info_by_player_index = {}
|
||||||
|
local stop_on_first_error_by_player_index = {}
|
||||||
|
|
||||||
|
Global.register(
|
||||||
|
{
|
||||||
|
selected_test_info_by_player_index = selected_test_info_by_player_index,
|
||||||
|
stop_on_first_error_by_player_index = stop_on_first_error_by_player_index
|
||||||
|
},
|
||||||
|
function(tbl)
|
||||||
|
selected_test_info_by_player_index = tbl.selected_test_info_by_player_index
|
||||||
|
stop_on_first_error_by_player_index = tbl.stop_on_first_error_by_player_index
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
local function get_module_state(module)
|
||||||
|
local passed = module.passed
|
||||||
|
if passed == false or module.startup_error or module.teardown_error then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return passed
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_test_error(test)
|
||||||
|
return test.error or ''
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_module_error(module)
|
||||||
|
local errors = {}
|
||||||
|
if module.startup_error then
|
||||||
|
errors[#errors + 1] = 'startup error: '
|
||||||
|
errors[#errors + 1] = module.startup_error
|
||||||
|
errors[#errors + 1] = '\n\n'
|
||||||
|
end
|
||||||
|
if module.teardown_error then
|
||||||
|
errors[#errors + 1] = 'teardown error: '
|
||||||
|
errors[#errors + 1] = module.teardown_error
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.concat(errors)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_text_box_error(player_index)
|
||||||
|
local test_info = selected_test_info_by_player_index[player_index]
|
||||||
|
if test_info == nil then
|
||||||
|
return ''
|
||||||
|
end
|
||||||
|
|
||||||
|
local info_type = test_info.type
|
||||||
|
|
||||||
|
if info_type == info_type_test then
|
||||||
|
return get_test_error(test_info.test)
|
||||||
|
elseif info_type == info_type_module then
|
||||||
|
return get_module_error(test_info.module)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function set_selected_style(style, selected)
|
||||||
|
if selected then
|
||||||
|
style.font_color = color_selected
|
||||||
|
else
|
||||||
|
style.font_color = color_default
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function set_passed_style(style, passed)
|
||||||
|
if passed == true then
|
||||||
|
style.font_color = color_success
|
||||||
|
elseif passed == false then
|
||||||
|
style.font_color = color_failure
|
||||||
|
else
|
||||||
|
style.font_color = color_default
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_test_selected(test, player_index)
|
||||||
|
local info = selected_test_info_by_player_index[player_index]
|
||||||
|
if not info then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local info_test = info.test
|
||||||
|
if not info_test then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return info_test.id == test.id
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_module_selected(module, player_index)
|
||||||
|
local info = selected_test_info_by_player_index[player_index]
|
||||||
|
if not info then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local info_module = info.module
|
||||||
|
if not info_module then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return info_module.id == module.id
|
||||||
|
end
|
||||||
|
|
||||||
|
local function draw_tests_test(container, test, depth)
|
||||||
|
local flow = container.add {type = 'flow'}
|
||||||
|
|
||||||
|
local label = flow.add {type = 'label', name = test_label_name, caption = test.name}
|
||||||
|
local label_style = label.style
|
||||||
|
|
||||||
|
local is_selected = is_test_selected(test, container.player_index)
|
||||||
|
set_selected_style(label_style, is_selected)
|
||||||
|
if not is_selected then
|
||||||
|
set_passed_style(label_style, test.passed)
|
||||||
|
end
|
||||||
|
|
||||||
|
label_style.left_margin = depth * 15 + 10
|
||||||
|
Gui.set_data(label, {test = test, container = container})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function draw_tests_module(container, module)
|
||||||
|
local caption = {module.name or 'All Tests', ' (', module.count, ')'}
|
||||||
|
caption = table.concat(caption)
|
||||||
|
|
||||||
|
local flow = container.add {type = 'flow'}
|
||||||
|
local arrow =
|
||||||
|
flow.add {
|
||||||
|
type = 'label',
|
||||||
|
name = module_arrow_name,
|
||||||
|
caption = module.is_open and down_arrow or right_arrow
|
||||||
|
}
|
||||||
|
arrow.style.left_margin = module.depth * 15
|
||||||
|
Gui.set_data(arrow, {module = module, container = container})
|
||||||
|
|
||||||
|
local label = flow.add {type = 'label', name = module_label_name, caption = caption}
|
||||||
|
|
||||||
|
local label_style = label.style
|
||||||
|
local is_selected = is_module_selected(module, container.player_index)
|
||||||
|
set_selected_style(label_style, is_selected)
|
||||||
|
if not is_selected then
|
||||||
|
set_passed_style(label_style, get_module_state(module))
|
||||||
|
end
|
||||||
|
|
||||||
|
Gui.set_data(label, {module = module, container = container})
|
||||||
|
|
||||||
|
if not module.is_open then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, child in pairs(module.children) do
|
||||||
|
draw_tests_module(container, child)
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, test in pairs(module.tests) do
|
||||||
|
draw_tests_test(container, test, module.depth + 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function redraw_tests(container)
|
||||||
|
Gui.clear(container)
|
||||||
|
|
||||||
|
local root_module = Builder.get_root_modules()
|
||||||
|
draw_tests_module(container, root_module)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function draw_tests(container)
|
||||||
|
local scroll_pane =
|
||||||
|
container.add {
|
||||||
|
type = 'scroll-pane',
|
||||||
|
horizontal_scroll_policy = 'auto-and-reserve-space',
|
||||||
|
vertical_scroll_policy = 'auto-and-reserve-space'
|
||||||
|
}
|
||||||
|
local scroll_pane_style = scroll_pane.style
|
||||||
|
scroll_pane_style.horizontally_stretchable = true
|
||||||
|
scroll_pane_style.height = 350
|
||||||
|
|
||||||
|
local list = scroll_pane.add {type = 'flow', direction = 'vertical'}
|
||||||
|
|
||||||
|
redraw_tests(list)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function draw_error_text_box(container)
|
||||||
|
local text = get_text_box_error(container.player_index)
|
||||||
|
|
||||||
|
local text_box = container.add {type = 'text-box', name = error_test_box_name, text = text}
|
||||||
|
local style = text_box.style
|
||||||
|
style.vertically_stretchable = true
|
||||||
|
style.horizontally_stretchable = true
|
||||||
|
style.maximal_width = 800
|
||||||
|
return text_box
|
||||||
|
end
|
||||||
|
|
||||||
|
local function create_main_frame(center)
|
||||||
|
local frame = center.add {type = 'frame', name = main_frame_name, caption = 'Test Runner', direction = 'vertical'}
|
||||||
|
local frame_style = frame.style
|
||||||
|
frame_style.width = 800
|
||||||
|
frame_style.height = 600
|
||||||
|
|
||||||
|
local top_flow = frame.add {type = 'flow', direction = 'horizontal'}
|
||||||
|
top_flow.add {type = 'button', name = run_all_button_name, caption = 'Run All'}
|
||||||
|
top_flow.add {
|
||||||
|
type = 'button',
|
||||||
|
name = run_selected_button_name,
|
||||||
|
caption = 'Run Selected'
|
||||||
|
}
|
||||||
|
top_flow.add {
|
||||||
|
type = 'checkbox',
|
||||||
|
name = stop_on_error_checkbox_name,
|
||||||
|
caption = 'Stop on first error',
|
||||||
|
state = stop_on_first_error_by_player_index[center.player_index] or false
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_tests(frame)
|
||||||
|
|
||||||
|
local error_text_box = draw_error_text_box(frame)
|
||||||
|
Gui.set_data(frame, {error_text_box = error_text_box})
|
||||||
|
|
||||||
|
local close_button = frame.add {type = 'button', name = close_main_frame_name, caption = 'Close'}
|
||||||
|
Gui.set_data(close_button, frame)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function close_main_frame(frame)
|
||||||
|
Gui.destroy(frame)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_error_text_box(player)
|
||||||
|
local frame = player.gui.center[main_frame_name]
|
||||||
|
local frame_data = Gui.get_data(frame)
|
||||||
|
return frame_data.error_text_box
|
||||||
|
end
|
||||||
|
|
||||||
|
local function make_options(player_index)
|
||||||
|
return {stop_on_first_error = stop_on_first_error_by_player_index[player_index]}
|
||||||
|
end
|
||||||
|
|
||||||
|
local run_module_token =
|
||||||
|
Token.register(
|
||||||
|
function(data)
|
||||||
|
Runner.run_module(data.module, data.player, data.options)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
local run_test_token =
|
||||||
|
Token.register(
|
||||||
|
function(data)
|
||||||
|
Runner.run_test(data.test, data.player, data.options)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
Gui.on_click(
|
||||||
|
close_main_frame_name,
|
||||||
|
function(event)
|
||||||
|
local element = event.element
|
||||||
|
local frame = Gui.get_data(element)
|
||||||
|
close_main_frame(frame)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
Gui.on_click(
|
||||||
|
module_arrow_name,
|
||||||
|
function(event)
|
||||||
|
local element = event.element
|
||||||
|
local data = Gui.get_data(element)
|
||||||
|
local module = data.module
|
||||||
|
local container = data.container
|
||||||
|
|
||||||
|
module.is_open = not module.is_open
|
||||||
|
redraw_tests(container)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
Gui.on_click(
|
||||||
|
module_label_name,
|
||||||
|
function(event)
|
||||||
|
local element = event.element
|
||||||
|
local data = Gui.get_data(element)
|
||||||
|
local module = data.module
|
||||||
|
local container = data.container
|
||||||
|
local player_index = event.player_index
|
||||||
|
|
||||||
|
local is_selected = not is_module_selected(module, player_index)
|
||||||
|
selected_test_info_by_player_index[player_index] = nil
|
||||||
|
|
||||||
|
if is_selected then
|
||||||
|
selected_test_info_by_player_index[player_index] = {type = info_type_module, module = module}
|
||||||
|
end
|
||||||
|
|
||||||
|
local error_text_box = get_error_text_box(event.player)
|
||||||
|
if is_selected then
|
||||||
|
error_text_box.text = get_module_error(module)
|
||||||
|
else
|
||||||
|
error_text_box.text = ''
|
||||||
|
end
|
||||||
|
|
||||||
|
redraw_tests(container)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
Gui.on_click(
|
||||||
|
test_label_name,
|
||||||
|
function(event)
|
||||||
|
local element = event.element
|
||||||
|
local data = Gui.get_data(element)
|
||||||
|
local test = data.test
|
||||||
|
local container = data.container
|
||||||
|
local player_index = event.player_index
|
||||||
|
|
||||||
|
local is_selected = not is_test_selected(test, player_index)
|
||||||
|
selected_test_info_by_player_index[player_index] = nil
|
||||||
|
|
||||||
|
if is_selected then
|
||||||
|
selected_test_info_by_player_index[player_index] = {type = info_type_test, test = test}
|
||||||
|
end
|
||||||
|
|
||||||
|
local error_text_box = get_error_text_box(event.player)
|
||||||
|
if is_selected then
|
||||||
|
error_text_box.text = get_test_error(test)
|
||||||
|
else
|
||||||
|
error_text_box.text = ''
|
||||||
|
end
|
||||||
|
|
||||||
|
redraw_tests(container)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
Gui.on_click(
|
||||||
|
run_all_button_name,
|
||||||
|
function(event)
|
||||||
|
local frame = event.player.gui.center[main_frame_name]
|
||||||
|
close_main_frame(frame)
|
||||||
|
|
||||||
|
local options = make_options(event.player_index)
|
||||||
|
Task.set_timeout_in_ticks(1, run_module_token, {module = nil, player = event.player, options = options})
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
Gui.on_click(
|
||||||
|
run_selected_button_name,
|
||||||
|
function(event)
|
||||||
|
local test_info = selected_test_info_by_player_index[event.player_index]
|
||||||
|
if test_info == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local options = make_options(event.player_index)
|
||||||
|
|
||||||
|
local info_type = test_info.type
|
||||||
|
if info_type == info_type_module then
|
||||||
|
Task.set_timeout_in_ticks(
|
||||||
|
1,
|
||||||
|
run_module_token,
|
||||||
|
{module = test_info.module, player = event.player, options = options}
|
||||||
|
)
|
||||||
|
elseif info_type == info_type_test then
|
||||||
|
Task.set_timeout_in_ticks(
|
||||||
|
1,
|
||||||
|
run_test_token,
|
||||||
|
{test = test_info.test, player = event.player, options = options}
|
||||||
|
)
|
||||||
|
else
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local frame = event.player.gui.center[main_frame_name]
|
||||||
|
close_main_frame(frame)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
Gui.on_checked_state_changed(
|
||||||
|
stop_on_error_checkbox_name,
|
||||||
|
function(event)
|
||||||
|
stop_on_first_error_by_player_index[event.player_index] = event.element.state or nil
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
Event.add(
|
||||||
|
Runner.events.tests_run_finished,
|
||||||
|
function(event)
|
||||||
|
Public.open(event.player)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
function Public.open(player)
|
||||||
|
local center = player.gui.center
|
||||||
|
local frame = center[main_frame_name]
|
||||||
|
if frame then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
create_main_frame(center)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Public
|
Loading…
Reference in New Issue
Block a user