diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml new file mode 100644 index 00000000..35aaa0af --- /dev/null +++ b/.github/workflows/unit_tests.yml @@ -0,0 +1,42 @@ +# This is a basic workflow to help you get started with Actions + +name: Build and run unit tests + +# Controls when the workflow will run +on: + # Triggers the workflow on push or pull request events but only for the "main" branch + push: + branches: [ "main", "testing" ] + pull_request: + branches: [ "main", "testing" ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + build: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + with: + submodules: 'recursive' + + - run: sudo apt-get update -qq + - run: sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test + - run: sudo apt-get update -qq + - run: sudo apt-get install -qq cmake build-essential python3-pip python3-virtualenv nodejs tar gzip libpthread-stubs0-dev libc6-dbg gdb + - run: g++ --version + - name: Build tests + run: | + cmake .. + cmake --build . + working-directory: tests/build + - name: Run tests + run: ./run_tests.sh + working-directory: tests/build diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..61932e24 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "third_party/Catch2"] + path = third_party/Catch2 + url = https://github.com/catchorg/Catch2.git diff --git a/docs/HowToTest.md b/docs/HowToTest.md new file mode 100644 index 00000000..e80c0a55 --- /dev/null +++ b/docs/HowToTest.md @@ -0,0 +1,99 @@ +This guide will demonstrate how to create, build, and run tests. + +***Note:*** that these instructions should work on Mac and Linux systems. Windows instructions should be similar, but please add any information about working with tests on Windows via a pull request! + +***Note:*** this guide assumes you have Catch2 downloaded. +If you haven't yet downloaded the repo, simply add a `--recursive` to the end of your `git clone`. +If you _have_ downloaded the rest of the repo, simply initialize and update the submodules: +``` +git submodule init +git submodule update +``` + +## How to compile tests + +First, navigate to the `tests` directory. +``` +cd tests +``` + +Create a build directory within `tests`, if it does not already exist. +``` +mkdir build +``` +Note that this will likely through an error/warning if the directory already exists. + +Navigate into the `build` directory. +``` +cd build +``` + +Now we need to use CMake to compile. This is a two step process. +First we tell CMake where to find the files, relative to our current directory. +``` +cmake .. +``` + +If that completes without errors, we can now build the files in our current directory. +``` +cmake --build . +``` + +That should compile the unit tests. +Note that this process will take a while the first time as it needs to compile Catch2. It should be much faster in subsequent builds. + +## Running tests + +If you are on a Mac or Linux system and wish to run _all_ the unit tests, simply run +``` +./run_tests.sh +``` +from _within the `build` directory_ (not just in `tests`). + +If you wish to run a particular test (or are on Windows), simply navigate to the test _within the build directory_. +For example, if we want to run tests for `WorldGrid` (which is in core), we would navigate like so: +``` +cd unit/core +``` + +Once we're in the correct directory, we simply run the executable. +On Unix systems that looks like: +``` +./tests-unit-core-WorldGrid +``` +On Windows it should be similar, though it may have a `.exe` extension. + +## Adding new tests + +To add tests for a new file, navigate to the corresponding folder in the `tests/unit` directory (not in `tests/build`. +For example, if we want to add a test for a new file in `core`, from the root of the repo we would navigate like so: +``` +cd tests/unit/core +``` +Once in the correct directory we need to add the actual source code of the test. +This should be done as a new `.cpp` file, which will typically have the same name as the header file in source (_e.g._, `WorldGrid.hpp` will have a new test file called `WorldGrid.cpp`). + +Once you've created that file, you can start with this skeleton code: + +``` +/** + * This file is part of the Fall 2023, CSE 491 course project. + * @brief + **/ + +// Catch2 +#define CATCH_CONFIG_MAIN +#include + +// Class project +// Place your includes here +// e.g., #include "core/Data.hpp" + +TEST_CASE("NAME", "[tags]"){ +} + +``` + +Finally, once you have some tests coded up, you need to make one more change. +Simply add the name of that file (including the `.cpp`!) to `targets.txt`. +CMake uses this file to ensure we compile the appropriate files. diff --git a/docs/README.md b/docs/README.md index 5386d5d3..7c447ff0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,3 +9,5 @@ Below are a set of guides for how to develop your own modules and joining them t - [How to Build an Interface](HowToBuildAnInterface.md) - How to build an interface that allows a human user to control an agent. - [How to Assemble a `main()` function](HowToAssembleAMain.md) - A guide to selecting worlds, agents, and interfaces and turning them into a custom executable (including how to collect data about the resulting system.) + +- [How to build, run, and create unit tests](HowToTest.md) - Walks through how to build and run existing unit tests, as well as create new unit test files. diff --git a/source/core/WorldGrid.hpp b/source/core/WorldGrid.hpp index a3405a34..bc8de740 100644 --- a/source/core/WorldGrid.hpp +++ b/source/core/WorldGrid.hpp @@ -15,6 +15,7 @@ #include "CoreObject.hpp" #include "GridPosition.hpp" +#include "Data.hpp" namespace cse491 { @@ -194,4 +195,4 @@ namespace cse491 { }; -} // End of namespace cse491 \ No newline at end of file +} // End of namespace cse491 diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 00000000..c09e3d48 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,6 @@ +# Vim swap files +*.swp +*.swo + +# CMake files and directories +CMakeFiles diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 00000000..72b0b6f5 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.10) +project(tests VERSION 0.1) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +set(CATCH_SRC_DIR ../third_party/Catch2) +set(CATCH_BUILD_DIR catch_build) +set(MAIN_SOURCE_DIR ../source) + +configure_file(run_tests.sh run_tests.sh COPYONLY) + +# Catch2 CMake integration +# From https://github.com/catchorg/Catch2/blob/devel/docs/cmake-integration.md#top +add_subdirectory(${CATCH_SRC_DIR} ${CATCH_BUILD_DIR}) + +add_subdirectory(unit) diff --git a/tests/build/.gitignore b/tests/build/.gitignore new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/tests/build/.gitignore @@ -0,0 +1 @@ +* diff --git a/tests/run_tests.sh b/tests/run_tests.sh new file mode 100755 index 00000000..a2d51a9a --- /dev/null +++ b/tests/run_tests.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +run_tests () { + local DIR_NAME=$(pwd | grep -oP "/\w+$" | grep -oP "\w+") + local FILENAMES=$(ls tests-unit-* 2> /dev/null) + for FILENAME in ${FILENAMES} + do + echo "Running test: ${FILENAME}" + ./${FILENAME} + done +} + +cd unit +TARGETS="core Agents Worlds Interfaces" +for TARGET in ${TARGETS} +do +if [ -d ${TARGET} ] + then + echo "Entering ${TARGET}" + cd ${TARGET} + run_tests + echo "Exiting ${TARGET}" + else + echo "Directory not found: ${TARGET}. Skipping." + fi +done +cd .. diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt new file mode 100644 index 00000000..cb96d01a --- /dev/null +++ b/tests/unit/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.10) + + +set(MAIN_SOURCE_DIR ../${MAIN_SOURCE_DIR}) +add_subdirectory(core) diff --git a/tests/unit/core/CMakeLists.txt b/tests/unit/core/CMakeLists.txt new file mode 100644 index 00000000..15af4f55 --- /dev/null +++ b/tests/unit/core/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.10) +#project(tests-unit VERSION 0.1) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +file(STRINGS targets.txt TARGETS) + +set(MAIN_SOURCE_DIR ../${MAIN_SOURCE_DIR}) + +foreach(TARGET IN LISTS TARGETS) + add_executable(tests-unit-core-${TARGET} ${TARGET}.cpp) + + target_include_directories(tests-unit-core-${TARGET} + PRIVATE ${MAIN_SOURCE_DIR} + ) + target_link_libraries(tests-unit-core-${TARGET} + PRIVATE Catch2::Catch2WithMain + ) +endforeach() diff --git a/tests/unit/core/Data.cpp b/tests/unit/core/Data.cpp new file mode 100644 index 00000000..48fcf188 --- /dev/null +++ b/tests/unit/core/Data.cpp @@ -0,0 +1,20 @@ +/** + * This file is part of the Fall 2023, CSE 491 course project. + * @brief Unit tests for Data.hpp in source/core + **/ + +// Catch2 +#define CATCH_CONFIG_MAIN +#include + +// Class project +#include "core/Data.hpp" +#include "core/Entity.hpp" +#include "core/AgentBase.hpp" + +TEST_CASE("CellType", "[core]"){ + cse491::CellType cell_type("name", "desc", '@'); + CHECK(cell_type.name == "name"); + CHECK(cell_type.desc == "desc"); + CHECK(cell_type.symbol == '@'); +} diff --git a/tests/unit/core/WorldGrid.cpp b/tests/unit/core/WorldGrid.cpp new file mode 100644 index 00000000..b8bc6597 --- /dev/null +++ b/tests/unit/core/WorldGrid.cpp @@ -0,0 +1,49 @@ +/** + * This file is part of the Fall 2023, CSE 491 course project. + * @brief Unit tests for Data.hpp in source/core + **/ + +// Catch2 +#define CATCH_CONFIG_MAIN +#include + +// Class project +#include "core/WorldGrid.hpp" + +TEST_CASE("WorldGrid Construction", "[core][grid]"){ + SECTION("Default construction"){ + cse491::WorldGrid grid; + CHECK(grid.GetWidth() == 0); + CHECK(grid.GetWidth() == 0); + CHECK(grid.GetNumCells() == 0); + CHECK(!grid.IsValid(0, 0)); + CHECK(!grid.IsValid(1, 1)); + CHECK(!grid.IsValid(-1, -1)); + } + SECTION("Standard construction, no type"){ + cse491::WorldGrid grid(10, 10); + CHECK(grid.GetWidth() == 10); + CHECK(grid.GetWidth() == 10); + CHECK(grid.GetNumCells() == 100); + CHECK(grid.IsValid(0, 0)); + CHECK(grid.IsValid(1, 1)); + CHECK(grid.IsValid(9, 9)); + CHECK(!grid.IsValid(0, 10)); + CHECK(!grid.IsValid(10, 0)); + CHECK(!grid.IsValid(-1, -1)); + } +} +// TODO: Test resizing to a smaller size +// TODO: Check that resizing keeps the cells that are not lost / new +TEST_CASE("WorldGrid Resize", "[core][grid]"){ + SECTION("Resize to larger"){ + cse491::WorldGrid grid; + CHECK(grid.GetWidth() == 0); + CHECK(grid.GetWidth() == 0); + CHECK(grid.GetNumCells() == 0); + grid.Resize(10, 10); + CHECK(grid.GetWidth() == 10); + CHECK(grid.GetWidth() == 10); + CHECK(grid.GetNumCells() == 100); + } +} diff --git a/tests/unit/core/targets.txt b/tests/unit/core/targets.txt new file mode 100644 index 00000000..773d3a00 --- /dev/null +++ b/tests/unit/core/targets.txt @@ -0,0 +1,2 @@ +Data +WorldGrid diff --git a/third_party/Catch2 b/third_party/Catch2 new file mode 160000 index 00000000..9c541ca7 --- /dev/null +++ b/third_party/Catch2 @@ -0,0 +1 @@ +Subproject commit 9c541ca72e7857dec71d8a41b97e42c2f1c92602