This is the third installment of my “DevOps like It’s 1999” series, where I attempt to integrate modern DevOps tools into a 20 year old online roleplaying game. If you haven’t been following the series, I previously walked through how to setup Keen.io for analytics tracking, and PagerDuty for additional transparency into in-game alerts. This week, we’ll be doing something a little more involved: unit tests.
In fact, we’ll be doing a little more than just integrating unit tests into OASIS. We’ll also be setting up Travis-CI, a continuous integration platform for automating unit tests that integrates amazingly with other third party tools like GitHub and Slack (among other things). One thing that I should mention about Travis-CI is that it is free for all open source tools. This is an initiative that shows up in a lot of DevOps tools, and one I can completely get behind.
GitHub Pull Request Status Check
Creating and managing proper and thorough test suites is crucial to the stability of any development project. While they won’t catch every problem we might run into, they will provide an added layer of security and help prevent small mistakes that can be tough to track down in a production environment. Before we can write any unit tests, however, we need to decide on a test framework to use. After quite a bit of research, I landed on CUnit, a lightweight testing framework for C.
I should mention that CUnit is far from the most advanced testing framework (it doesn’t have any function mocking support, for one), but it is the most readily available framework in most Linux environments I tested. In this case, I chose accessibility over functionality in order to get the most universally available results.
Now that we have a test framework, we need to actually write some tests and update our Makefile so we can compile a test executable. For this example, we will write a test for the check_blind() method in act_info.c:
DO_TEST(check_blind) { CU_ASSERT_TRUE(check_blind(mock_supermob)); SET_BIT(mock_supermob->affected_by, AFF_BLIND); CU_ASSERT_FALSE(check_blind(mock_supermob)); reset_mocks(); }
For a quick explanation, check_blind() checks if the BLIND effect bit is set on a character. This test makes two assertions for the mock supermob (which is basically a mock NPC). One without the BLIND effect, and then one with it (after the BLIND effect is set).
Writing this test is only half the battle, though. Next, we need to update our Makefile and build a test executable. Because of the way the codebase is structured, we need to remove the main() method in comm.c and separate it out into a simple standalone file before we can create a test executable. The reason for this is because the original main() method in comm.c does more than just initiate the game loop. A CUnit test executable requires a unique main() method, which prevents us from writing tests for the game loop in the future.
To accomplish this, the first thing we need to do is rename the main() method in comm.c to game_main(). Then, we will have to create a standalone file (called main.c for clarity) and put a basic main() method that calls the newly renamed game_main() method. The next thing we have to do is create a test main.c file that we can use to actually run our test suite.
With our updated code, we can now run our test suite. Since C is a compiled language, we need to actually compile a separate test executable. That means updating or creating a new Makefile that compiles the game source code as well as our unit tests. We’ll need to swap out the primary main.c with our new test main.c so running the newly compiled executable will our test suite without running the actual game.
Now that we have our test suite, we need to hook it up to Travis-CI. Setting up Travis-CI is as simple as creating a .travis.yml file, which tells Travis-CI how to setup and run our unit tests. The setup is actually pretty simple, we simply just tell Travis to install the library dependencies, compile our test suite, and then run it. After that, every time we push up changes to our code, Travis will run our unit tests and report the results wherever we tell it to.
Travis-CI Results
Setting up Travis-CI is only the beginning. It is crucial to stay on top of our unit tests as code gets changed and added. Stay tuned for the next article of this series, we will explore code coverage in C, and using Coveralls.io to stay on top of it.