Skip to content

Commit

Permalink
RSDK-5226 Add tests for gizmo and summation (viamrobotics#159)
Browse files Browse the repository at this point in the history
  • Loading branch information
benjirewis authored Oct 9, 2023
1 parent 14fbd6e commit abd0540
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 2 deletions.
6 changes: 5 additions & 1 deletion etc/docker/tests/run_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,9 @@ cmake .. -G Ninja -DVIAMCPPSDK_USE_DYNAMIC_PROTOS=ON \
-DVIAMCPPSDK_CLANG_TIDY=ON
ninja all
ninja install
cd src/viam/sdk/tests
pushd src/viam/sdk/tests
UBSAN_OPTIONS="print_stacktrace=1" ctest --output-on-failure
popd
pushd src/viam/examples/modules/complex
UBSAN_OPTIONS="print_stacktrace=1" ctest --output-on-failure
popd
24 changes: 24 additions & 0 deletions src/viam/examples/modules/complex/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,27 @@ install(
TARGETS complex_module_client
COMPONENT examples
)

# Testing related cmake logic; the section below is likely irrelevant for users
# writing their own modules. We are simply integrating tests from
# `test_complex_module.cpp` into the Viam C++ SDK testing suite.

enable_testing()
target_sources(viamsdk_test
PRIVATE
gizmo/impl.cpp
gizmo/impl.hpp
gizmo/api.cpp
gizmo/api.hpp
summation/api.cpp
summation/api.hpp
summation/impl.cpp
summation/impl.hpp

${MODULE_PROTO_OUTPUT_FILES}
)
target_include_directories(viamsdk_test
PUBLIC
${MODULE_PROTO_GEN_DIR}
)
viamcppsdk_add_boost_test(test_complex_module.cpp)
2 changes: 2 additions & 0 deletions src/viam/examples/modules/complex/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ In the main directory, there is also a `main.cpp` file, which creates a module,

Finally, the `client.cpp` file can be used to test the module once you have connected to your robot and configured it. You will have to update the credentials and robot address in that file before building. After building, the `build/install/bin/complex_module_client` generated binary can be called to run the client.

`test_complex_module.cpp` is used by the Viam team to ensure the complex module example works as expected.

## Configuring and using the module

The `complex_module` binary generated after building is the entrypoint for this module. To connect this module with your robot, you must add this module's entrypoint to the robot's config. For example, the entrypoint file may be at `/home/viam-cpp-sdk/build/install/bin/complex_module` and you must add this file path to your configuration. See the [documentation](https://docs.viam.com/program/extend/modular-resources/#use-a-modular-resource-with-your-robot) for more details.
Expand Down
2 changes: 1 addition & 1 deletion src/viam/examples/modules/complex/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ int main() {
}
bool do_one_ret = gc->do_one("arg1");
std::cout << "gizmo1 do_one returned: " << do_one_ret;
bool do_one_client_stream_ret = gc->do_one_client_stream({"arg1", "arg2", "arg3"});
bool do_one_client_stream_ret = gc->do_one_client_stream({"arg1", "arg1", "arg1"});
std::cout << "gizmo1 do_one_client_stream returned: " << do_one_client_stream_ret;
std::string do_two_ret = gc->do_two(false);
std::cout << "gizmo1 do_two returned: " << do_two_ret;
Expand Down
5 changes: 5 additions & 0 deletions src/viam/examples/modules/complex/gizmo/api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ bool GizmoClient::do_one_client_stream(std::vector<std::string> arg1) {
auto writer(stub_->DoOneClientStream(&ctx, &response));
for (std::string arg : arg1) {
DoOneClientStreamRequest curr_req = {};
*curr_req.mutable_name() = this->name();
curr_req.set_arg1(arg);
if (!writer->Write(curr_req)) {
// Stream is broken; stop writing.
Expand All @@ -244,6 +245,9 @@ std::vector<bool> GizmoClient::do_one_server_stream(std::string arg1) {
DoOneServerStreamRequest request;
grpc::ClientContext ctx;

*request.mutable_name() = this->name();
request.set_arg1(arg1);

auto reader(stub_->DoOneServerStream(&ctx, request));
DoOneServerStreamResponse curr_resp = {};
std::vector<bool> rets = {};
Expand All @@ -264,6 +268,7 @@ std::vector<bool> GizmoClient::do_one_bidi_stream(std::vector<std::string> arg1)
auto stream(stub_->DoOneBiDiStream(&ctx));
for (std::string arg : arg1) {
DoOneBiDiStreamRequest curr_req = {};
*curr_req.mutable_name() = this->name();
curr_req.set_arg1(arg);
if (!stream->Write(curr_req)) {
// Stream is broken; stop writing.
Expand Down
1 change: 1 addition & 0 deletions src/viam/examples/modules/complex/gizmo/impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ using namespace viam::sdk;
// `validate` method that checks config validity.
class MyGizmo : public Gizmo {
public:
MyGizmo(std::string name, std::string arg1) : Gizmo(std::move(name)), arg1_(std::move(arg1)){};
MyGizmo(Dependencies deps, ResourceConfig cfg) : Gizmo(cfg.name()) {
this->reconfigure(deps, cfg);
};
Expand Down
2 changes: 2 additions & 0 deletions src/viam/examples/modules/complex/summation/impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ using namespace viam::sdk;
// implements all relevant methods along with `reconfigure`.
class MySummation : public Summation {
public:
MySummation(std::string name, bool subtract)
: Summation(std::move(name)), subtract_(subtract){};
MySummation(Dependencies deps, ResourceConfig cfg) : Summation(cfg.name()) {
this->reconfigure(deps, cfg);
};
Expand Down
216 changes: 216 additions & 0 deletions src/viam/examples/modules/complex/test_complex_module.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
#define BOOST_TEST_MODULE test module test_complex_module

#include <typeinfo>
#include <unordered_map>
#include <utility>
#include <vector>

#include <boost/test/included/unit_test.hpp>

#include <viam/api/common/v1/common.pb.h>

#include <viam/sdk/common/proto_type.hpp>
#include <viam/sdk/tests/test_utils.hpp>

#include "gizmo.grpc.pb.h"
#include "gizmo.pb.h"
#include "gizmo/api.hpp"
#include "gizmo/impl.hpp"
#include "summation.grpc.pb.h"
#include "summation.pb.h"
#include "summation/api.hpp"
#include "summation/impl.hpp"

using namespace viam::sdk;

BOOST_AUTO_TEST_SUITE(test_gizmo_impl)

// get_gizmo creates a MyGizmo for testing purposes named "testgizmo" with
// `arg1_` set to "foo".
std::shared_ptr<MyGizmo> get_gizmo() {
return std::make_shared<MyGizmo>("testgizmo", "foo");
}

BOOST_AUTO_TEST_CASE(impl_do_one) {
auto gizmo = get_gizmo();
BOOST_CHECK(gizmo->do_one("foo"));
BOOST_CHECK(!gizmo->do_one("bar"));
}

BOOST_AUTO_TEST_CASE(impl_do_one_client_stream) {
auto gizmo = get_gizmo();
BOOST_CHECK(gizmo->do_one_client_stream({"foo", "foo"}));
BOOST_CHECK(!gizmo->do_one_client_stream({"foo", "bar"}));
}

BOOST_AUTO_TEST_CASE(impl_do_one_server_stream) {
auto gizmo = get_gizmo();
std::vector<bool> ret1 = {true, false, true, false};
std::vector<bool> ret2 = {false, false, true, false};
BOOST_CHECK(gizmo->do_one_server_stream("foo") == ret1);
BOOST_CHECK(gizmo->do_one_server_stream("bar") == ret2);
}

BOOST_AUTO_TEST_CASE(impl_do_one_bidi_stream) {
auto gizmo = get_gizmo();
std::vector<bool> ret1 = {true, false};
std::vector<bool> ret2 = {false, true};
BOOST_CHECK(gizmo->do_one_bidi_stream({"foo", "bar"}) == ret1);
BOOST_CHECK(gizmo->do_one_bidi_stream({"bar", "foo"}) == ret2);
}

BOOST_AUTO_TEST_CASE(impl_do_two) {
auto gizmo = get_gizmo();
BOOST_CHECK(gizmo->do_two(true) == "arg1=true");
BOOST_CHECK(gizmo->do_two(false) == "arg1=false");
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(test_gizmo_client_server)

// This sets up the following architecture
// -- MockComponent
// /\
//
// | (function calls)
//
// \/
// -- ComponentServer (Real)
// /\
//
// | (grpc InProcessChannel)
//
// \/
// -- ComponentClient (Real)
//
// This is as close to a real setup as we can get
// without starting another process
//
// The passed in lambda function has access to the ComponentClient
template <typename Lambda>
void gizmo_server_to_mock_pipeline(Lambda&& func) {
GizmoServer gizmo_server;
gizmo_server.resource_manager()->add(std::string("testgizmo"), test_gizmo_impl::get_gizmo());

grpc::ServerBuilder builder;
builder.RegisterService(&gizmo_server);

std::unique_ptr<grpc::Server> server = builder.BuildAndStart();

grpc::ChannelArguments args;
auto grpc_channel = server->InProcessChannel(args);
GizmoClient client("testgizmo", grpc_channel);
// Run the passed test on the created stack
std::forward<Lambda>(func)(client);
// shutdown afterwards
server->Shutdown();
}

BOOST_AUTO_TEST_CASE(test_do_one) {
gizmo_server_to_mock_pipeline([](Gizmo& client) -> void {
BOOST_CHECK(client.do_one("foo"));
BOOST_CHECK(!client.do_one("bar"));
});
}

BOOST_AUTO_TEST_CASE(test_do_one_client_stream) {
gizmo_server_to_mock_pipeline([](Gizmo& client) -> void {
BOOST_CHECK(client.do_one_client_stream({"foo", "foo"}));
BOOST_CHECK(!client.do_one_client_stream({"foo", "bar"}));
});
}

BOOST_AUTO_TEST_CASE(test_do_one_server_stream) {
gizmo_server_to_mock_pipeline([](Gizmo& client) -> void {
std::vector<bool> ret1 = {true, false, true, false};
std::vector<bool> ret2 = {false, false, true, false};
BOOST_CHECK(client.do_one_server_stream("foo") == ret1);
BOOST_CHECK(client.do_one_server_stream("bar") == ret2);
});
}

BOOST_AUTO_TEST_CASE(test_do_one_bidi_stream) {
gizmo_server_to_mock_pipeline([](Gizmo& client) -> void {
std::vector<bool> ret1 = {true, false};
std::vector<bool> ret2 = {false, true};
BOOST_CHECK(client.do_one_bidi_stream({"foo", "bar"}) == ret1);
BOOST_CHECK(client.do_one_bidi_stream({"bar", "foo"}) == ret2);
});
}

BOOST_AUTO_TEST_CASE(test_do_two) {
gizmo_server_to_mock_pipeline([](Gizmo& client) -> void {
BOOST_CHECK(client.do_two(true) == "arg1=true");
BOOST_CHECK(client.do_two(false) == "arg1=false");
});
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(test_summation_impl)

// get_summation creates a MySummation for testing purposes named "testsum"
// with `subtract_` set to false.
std::shared_ptr<MySummation> get_summation() {
return std::make_shared<MySummation>("testsum", false);
}

BOOST_AUTO_TEST_CASE(impl_sum) {
auto summation = get_summation();
double sum = summation->sum({0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0});
BOOST_CHECK(sum == 45);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(test_summation_client_server)

// This sets up the following architecture
// -- MockComponent
// /\
//
// | (function calls)
//
// \/
// -- ComponentServer (Real)
// /\
//
// | (grpc InProcessChannel)
//
// \/
// -- ComponentClient (Real)
//
// This is as close to a real setup as we can get
// without starting another process
//
// The passed in lambda function has access to the ComponentClient
//
template <typename Lambda>
void sum_server_to_mock_pipeline(Lambda&& func) {
SummationServer sum_server;
sum_server.resource_manager()->add(std::string("testsum"),
test_summation_impl::get_summation());

grpc::ServerBuilder builder;
builder.RegisterService(&sum_server);

std::unique_ptr<grpc::Server> server = builder.BuildAndStart();

grpc::ChannelArguments args;
auto grpc_channel = server->InProcessChannel(args);
SummationClient client("testsum", grpc_channel);
// Run the passed test on the created stack
std::forward<Lambda>(func)(client);
// shutdown afterwards
server->Shutdown();
}

BOOST_AUTO_TEST_CASE(test_sum) {
sum_server_to_mock_pipeline([](Summation& client) -> void {
double sum = client.sum({0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0});
BOOST_CHECK(sum == 45);
});
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit abd0540

Please sign in to comment.