Design discussion for a new test interface

For discussion of the code running behind the game

Moderator: Staff

User avatar
Roots
Dictator
Posts: 8665
Joined: Wed Jun 16, 2004 6:07 pm
Location: Austin TX
Contact:

Design discussion for a new test interface

Postby Roots » Wed Jan 09, 2013 12:41 pm

We've long been missing a useful interface for testing, and we're at the point now where we really can't afford to put this off any longer as we need an easier way to develop and test our maps. Therefore, I have begun work on a simple yet effective test environment with this primary goal in mind. I haven't had much time to really think through a full design that would meet all of our needs, so I expect that this code will evolve and improve over time. Anyway, here's what I got so far.


On the C++ side, I'm going to write a new game mode called TestMode. Visually, it will be a collection of GUI windows that list available tests, and test criteria. Right now I'm planning on three windows. The left side will contain a vertical list of all test categories. This includes maps, battles, shops, engine component tests, and so on. On the right side will be a list of all available tests that can be run in the selected category. And finally a long window on the bottom will list any descriptive text for the highlighted text. These menus are generated entirely via data defined in Lua, so there is very little about this class that is hard-coded.

You can enter test mode via two different avenues. The first is by running the program with the -t/--test option, which will boot the game immediately to test mode rather than boot mode. The second is via a command key combination that is available only when the game is in boot mode, or when a TestMode instance of the game is found in the game mode stack. Maybe Ctrl+T for "test". The --test command-line can optionally include an integer argument that identifies what test to run. This makes it very fast for the user to hop right into testing if they know what test they want to execute (instead of having to dig through the GUI menu to find it). As for the second method, hitting Ctrl+T at any time will immediately clear the game stack and return the user to test mode (which should always be sitting at the bottom of the game stack). This will make it easy to reload maps quickly or otherwise to jump to another test in an expedited manner. The reason why Ctrl+T is ignored when there is no TestMode on the stack is because we don't want a player to be in the middle of their game and accidentally exit it without knowing how/why it happened.


Now on to the Lua side. I'm going to make a new directory called dat/test to hold all of our testing scripts. The most important file in here is test_main.lua, which contains a directory of all test categories and available tests. Each test is assigned a unique integer number in order to make it easy to immediately begin a certain test if you know its number. A test category is simply a Lua table with a name, description, and an entry for each test. Each test entry contains a name, description, and the instructions to begin executing the test. The tests themselves can (and probably should) be defined in other test files to keep test_main.lua minimal and uncluttered. Here's an example to illustrate this:

Code: Select all

-- File: test_main.lua

maps = { name="Run Map", description="Begins a MapMode instance using the map file indicated. Global game data is also reset and initialized and game events are set according to the test parameters." }
maps[12] = { name="Desert cave before boss battle", description="Runs desert_cave.lua and spawns the player just outside the scene where the boss battle takes place.", execute="dat/test/desert_cave_tests.lua" }


There are still a lot of things to build off of this, such as what to do with global game data (party configuration, inventory, what game events should be marked as completed, and so on). I think it might be a good idea to have some options in TestMode to say "load this set of global game data, then load this map" to make it easier to share global game data configurations across tests, but that can be something that is added later. I've got all sorts of ideas swirling around in my head about this already, but for now I'm going to try to implement this simple interface, which should meet our immediate needs quite nicely, and then we'll build on to it as needed in the future.


So that's my proposal at this moment. Let me know what you think and I'd love to hear any suggestions or counter-proposals, as I haven't thought about this long enough to feel confident that this is a solid implementation idea. But I'm reasonably confident in the approach thus far.
Image
User avatar
Roots
Dictator
Posts: 8665
Joined: Wed Jun 16, 2004 6:07 pm
Location: Austin TX
Contact:

Re: Design discussion for a new test interface

Postby Roots » Fri Jan 11, 2013 12:57 am

I did some reorganization of my ideas in my most recent commit. Here's what the main test file looks like:

Code: Select all

------------------------------------------------------------------------------[[
-- Filename: test_main.lua
--
-- Description: This file serves as a directory for all available functional tests
-- that are used by the TestMode class. Tests are organized into categories, where
-- each category is given a string handle (it's table name), name (that will be
-- seen by the user in the test menu), and description of what the test category
-- contains.
--
-- Within each category are numerous tests which have a unique integer ID, that
-- serve as the test's table key. Additional information that tests are required to
-- have are a name that will be used to briefly describe the test, a description
-- that describes the test in further detail, and the instructions for executing the
-- test (TODO: define what data is needed to execute tests).
--
-- Test IDs are unsigned integers. Do not use the value of zero, as that is reserved.
-- You should declare a range of IDs that are reserved for each category of tests
------------------------------------------------------------------------------]]

local ns = {}
setmetatable(ns, {__index = _G})
test_main = ns;
setfenv(1, ns);

-- A table which holds the names of each test category table in this file
categories = {}

--------------------------------------------------------------------------------
-- Mode code tests: Reserve test IDs 1 - 10,000
--------------------------------------------------------------------------------

----- maps: Reserve test IDs 1 - 1,000
table.insert(categories, "maps");
maps = {
    name = "Test Maps";
    description = "These tests load a declared map file and set any custom criteria prior to placing the user on the map. This may include declaring the character party, inventory, what global events have been triggered, setting a custom spawn position, and so on.";
    min_id = 1;
    max_id = 1000;
    file = "dat/test/maps.lua";
}

----- battles: Reserve test IDs 1,001 - 2,000
table.insert(categories, "battles");
battles = {
    name = "Test Battles";
    description = "These tests declare the character and enemy parties, inventory, skills, and any other relevant battle information such as locations or battle scripts to use. They are very useful for game balancing and testing possible battle settings and configurations";
    min_id = 1001;
    max_id = 2000;
    file = "dat/test/battles.lua";
}

--------------------------------------------------------------------------------
-- Common code tests: Reserve test IDs 10,001 - 20,000
--------------------------------------------------------------------------------



--------------------------------------------------------------------------------
-- Engine code tests: Reserve test IDs 20,001 - 30,000
--------------------------------------------------------------------------------



--------------------------------------------------------------------------------
-- Utility code tests: Reserve test IDs 30,001 - 40,000
--------------------------------------------------------------------------------



The main test file no longer lists the id/name/description of each available test like I had originally intended it to. I decided against this because I thought it would be easier to keep things organized if all data that defined a test was kept in the same location. So I moved that data over to the test category file. Now the main test file only lists the categories of tests and the properties of each category. It now includes a min/max ID range (used for error checking of TestMode) and a filename where all the tests for the category can be found.

And now here's what a test category file looks like:

Code: Select all

------------------------------------------------------------------------------[[
-- Filename: maps.lua
--
-- Description: This file contains all tests that create an instance of MapMode
-- with various configurations. The tests here are primarily for testing map files
-- and scripting.
------------------------------------------------------------------------------]]

local ns = {}
setmetatable(ns, {__index = _G})
maps = ns;
setfenv(1, ns);

-- Test IDs 1 - 1,000 are reserved for maps
tests = {}

tests[1] = {
   name = "First Cave Dungeon Map";
   description = "Places the user in the first dungeon, effectively skipping over the introduction of the game";
   ExecuteTest = function()
      -- Make sure that any global data is cleared away
      GlobalManager:ClearAllData();

      -- Create the initial party, drunes, and inventory
      GlobalManager:AddCharacter(LUKAR);
      GlobalManager:AddCharacter(DESTER);
      GlobalManager:AddCharacter(MARK);
      GlobalManager:AddCharacter(CLAUDIUS);
      GlobalManager:AddNewEventGroup("global_events"); -- this group stores the primary list of events completed in the game
      GlobalManager:SetDrunes(100);
      GlobalManager:AddToInventory(1, 4);

      -- Set the location, load the opening map and add it to the game stack, and remove boot mode from the game stack
      local location_name = "dat/maps/river_access_cave.lua"
      GlobalManager:SetLocation(location_name);
      local opening_map = hoa_map.MapMode(location_name);

      ModeManager:Push(opening_map);
   end
}


As you can see, it's very simple. Each table contained within the "tests" table holds all the data for the test. The test ID is simply the index of the tests table where the test is found. A name and description string are required, and then you have the function that actually performs the test. In this case, it initializes the global data and begins the map.


As I mentioned before, I'd like to see some sort of sharing of global data initialization routines so that we don't have multiple tests performing the same party configurations over and over. I think what I'd like to do is to add a new file, dat/test/global_configurations.lua that all of the test files can utilize. This new file would contain functions that initialize different configurations of global data (duh). So the above test function could be reduced to something as simple as this:

Code: Select all

   ExecuteTest = function()
      UseGlobalConfiguration("new_game");
      StartAtLocation("dat/maps/river_access_cave.lua");
   end



Anyway, that's all I have for now. Again, let me know what you think and if you have any ideas for improvement. I do like how this is turning out so far and I wish I would have written this code much sooner.
Image

Return to “Programming”

Who is online

Users browsing this forum: No registered users and 2 guests