Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better test coverage #340

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -339,12 +339,14 @@ if(WITH_TESTS)
../tests/util/MathTest.cpp
../tests/util/TimeTest.cpp
../tests/util/SchedulerTest.cpp
../tests/util/RandomTest.cpp
../tests/util/JsonTest.cpp
../tests/util/ThreadingTest.cpp
# Protocol/teleop tests
../tests/kinematics/DiffWristKinematicsTest.cpp)

target_link_libraries(tests
${rover_libs}
stub_world_interface
${OpenCV_LIBS})
include(CTest)
include(Catch)
Expand Down
78 changes: 78 additions & 0 deletions tests/util/CoreTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,84 @@

using namespace util;

TEST_CASE("Test almostEqual", "[util][core]") {
SECTION("Numbers are approximately equal") {
REQUIRE(almostEqual(1.000000001, 1.000000002));
REQUIRE(almostEqual(1.0, 1.0, 1e-5));
}

SECTION("Numbers are not approximately equal") {
REQUIRE_FALSE(almostEqual(1.0, 1.1));
REQUIRE_FALSE(almostEqual(1.0, 1.0 + 1e-5, 1e-6));
}
}

TEST_CASE("Test Key Set", "[util][core]") {
SECTION("Extracting keys from a given map") {
std::unordered_map<std::string, int> inputtedMap = {
{"key1", 1}, {"key2", 2}, {"key3", 3}};

auto keys = keySet(inputtedMap);

std::unordered_set<std::string> expectedSet = {"key1", "key2", "key3"};

REQUIRE(keys == expectedSet);
}

SECTION("Empty map results in empty set") {
std::unordered_map<std::string, int> emptyMap;
auto keys = keySet(emptyMap);
REQUIRE(keys.empty());
}
}

TEST_CASE("Test To String", "[util][core]") {
SECTION("Convert integers to strings") {
REQUIRE(to_string(69) == "69");
REQUIRE(to_string(-1) == "-1");
}

SECTION("Convert floats to strings") {
REQUIRE(to_string(3.14).substr(0, 4) == "3.14");
REQUIRE(to_string(1e-6) == "0.000001");
}

SECTION("Convert booleans to strings") {
REQUIRE(to_string(true) == "true");
REQUIRE(to_string(false) == "false");
}
}

TEST_CASE("Test Freeze String", "[util][core]") {
SECTION("Converting std::string to frozen::string") {
std::string stdString = "Test";
auto frozenString = freezeStr(stdString);
REQUIRE(frozenString == frozen::string("Test"));
}

SECTION("Converting empty std::string results in empty frozen::string") {
std::string stdString;
auto frozenString = freezeStr(stdString);
REQUIRE(frozenString == frozen::string(""));
}
}

TEST_CASE("Test Pair To Tuple", "[util][core]") {
SECTION("Converting pair of integers to tuple") {
std::pair<int, int> pair = {0, 1};
auto tuple = pairToTuple(pair);
REQUIRE(std::get<0>(tuple) == 0);
REQUIRE(std::get<1>(tuple) == 1);
}

SECTION("Converting mixed pair of string and integer to tuple") {
std::pair<std::string, int> pair = {"key0", 0};
auto tuple = pairToTuple(pair);
REQUIRE(std::get<0>(tuple) == "key0");
REQUIRE(std::get<1>(tuple) == 0);
}
}

TEST_CASE("Test RAIIHelper", "[util][core]") {
SECTION("Test RAIIHelper executes") {
bool called = false;
Expand Down
65 changes: 65 additions & 0 deletions tests/util/JsonTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include "../../src/utils/json.h"

#include <catch2/catch.hpp>

using namespace util;

TEST_CASE("Test Has Key", "[util][json]") {
json j = {{"name", "huskyRobos"}, {"location", "University of Washington"}};

REQUIRE(hasKey(j, "name")); // has key of 'name'
REQUIRE_FALSE(hasKey(j, "numLosses")); // doesn't have key of 'numLosses'
}

TEST_CASE("Test Validate Key (Given Individual Types)", "[util][json]") {
json j = {{"name", "huskyRobos"}, {"age", 11}, {"cool", true}};
REQUIRE(validateKey(j, "name", val_t::string)); // has key of 'name' that is a string
REQUIRE_FALSE(
validateKey(j, "age", val_t::string)); // doesn't have key of 'age' that is a string
REQUIRE(validateKey(j, "cool", val_t::boolean)); // has key of 'cool' that is a boolean
}

TEST_CASE("Test Validate Key (Given Set of Types)", "[util][json]") {
json j = {{"name", "huskyRobos"}, {"age", 11}, {"cool", true}};

std::unordered_set<val_t> keyTypesAllowed = {val_t::string, val_t::number_integer};

REQUIRE(validateKey(
j, "name", keyTypesAllowed)); // has key of 'name' that is a type of the allowed types
REQUIRE(validateKey(
j, "age", keyTypesAllowed)); // has key of 'age' that is a type of the allowed types
REQUIRE_FALSE(validateKey(
j, "cool",
keyTypesAllowed)); // doesn't have key of 'cool' that is a type of the allowed types
}

TEST_CASE("Test Validate One Of", "[util][json]") {
json j = {{"status", "active"}, {"subTeams", "software"}};

std::unordered_set<std::string> statusValueTypesAllowed = {"active", "inactive"};
std::unordered_set<std::string> subTeamValueTypesAllowed = {"software", "electrical",
"mechanical"};

REQUIRE(validateOneOf(j, "status",
statusValueTypesAllowed)); // value of 'status' is a string in set of
// statusValueTypesAllowed
REQUIRE_FALSE(validateOneOf(
j, "status", {"pending", "closed"})); // value of 'status' is a string but not in set
// of statusValueTypesAllowed
REQUIRE(validateOneOf(j, "subTeams",
subTeamValueTypesAllowed)); // value of 'subTeams' is a string in set
// of subTeamValueTypesAllowed
}

TEST_CASE("Test Validate Range", "[util][json]") {
json j = {
{"winLossRatio", 0.8375},
{"avgGPA", 3.6},
};

REQUIRE(validateRange(j, "winLossRatio", 0.0,
1.0)); // 'winLossRatio' is in range from 0.0 to 1.0
REQUIRE(validateRange(j, "avgGPA", 0.0, 4.0)); // 'avgGPA' is in range from 0.0 to 4.0
REQUIRE_FALSE(
validateRange(j, "avgGPA", 0.0, 2.0)); // 'avgGPA' isnt in range of 0.0 to 2.0
}
21 changes: 21 additions & 0 deletions tests/util/RandomTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include "../../src/utils/random.h"

#include <catch2/catch.hpp>

using namespace util;

TEST_CASE("Test Standard Normal Distribution w/ different thread_ids", "[util][random]") {
double sample0 = stdn(0);
double sample1 = stdn(1);

// samples should be different
REQUIRE(sample0 != sample1);
}

TEST_CASE("Test Get Normal Seed", "[util][random]") {
double seed1 = getNormalSeed();
double seed2 = getNormalSeed();

// seeds should be the same
REQUIRE(seed1 == seed2);
}
101 changes: 101 additions & 0 deletions tests/util/ThreadingTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#include "../../src/utils/threading.h"

#include <chrono>
#include <thread>

#include <catch2/catch.hpp>

using namespace util;

using namespace std::chrono_literals;

TEST_CASE("Test Wait", "[util][threading]") {
SECTION("Wait until latch unlocks when countdown reaches zero") {
latch l(3);
std::thread worker([&]() {
l.count_down();
l.count_down();
l.count_down();
});

l.wait();

worker.join();

REQUIRE(true); // if thread isn't blocked, will be reached
}
}

TEST_CASE("Test Wait For", "[util][threading]") {
SECTION("Wait for latch unlocks within timeout") {
latch l(1);
std::thread worker([&]() {
std::this_thread::sleep_for(50ms);
l.count_down();
});

REQUIRE(l.wait_for(100ms));

worker.join();
}

SECTION("Wait for latch times out when not unlocked") {
latch l(1);

REQUIRE_FALSE(l.wait_for(50ms));
}
}

TEST_CASE("Test Wait Until", "[util][threading]") {
SECTION("Wait until latch unlocks before timeout") {
latch l(1);
std::thread worker([&]() {
std::this_thread::sleep_for(50ms);
l.count_down();
});

auto timeout = std::chrono::steady_clock::now() + 100ms;
REQUIRE(l.wait_until(timeout));

worker.join();
}

SECTION("Wait until latch times out") {
latch l(1);
auto timeout = std::chrono::steady_clock::now() + 50ms;
REQUIRE_FALSE(l.wait_until(timeout));
}
}

TEST_CASE("Test Count Down", "[util][threading]") {
SECTION("Latch unlocks after enough countdowns") {
latch l(3);

REQUIRE_FALSE(l.wait_for(10ms));

l.count_down();
l.count_down();
REQUIRE_FALSE(l.wait_for(10ms));

l.count_down();
REQUIRE(l.wait_for(10ms));
}

SECTION("Latch remains locked if not counted down enough") {
latch l(3);

l.count_down();
l.count_down();
REQUIRE_FALSE(l.wait_for(10ms));
}

SECTION("Latch can count down by more than 1") {
latch l(3);

l.count_down(2);
REQUIRE_FALSE(l.wait_for(10ms));

l.count_down(1);
REQUIRE(l.wait_for(10ms));
}
}