Skip to content

Commit

Permalink
Add coverage automation and UT
Browse files Browse the repository at this point in the history
Add coverage script and some UTs.

Also,

- Fix some method names.
  • Loading branch information
testillano authored and Eduardo Ramos Testillano (eramedu) committed Nov 5, 2023
1 parent 6099e39 commit ac37b8c
Show file tree
Hide file tree
Showing 12 changed files with 243 additions and 52 deletions.
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,36 @@ $ make install
$ cat install_manifest.txt | sudo xargs rm
```

## Testing

### Unit test

Check the badge above to know the current coverage level.
You can execute it after project building, for example for `Release` target:

```bash
$> build/Release/bin/unit-test # native executable
- or -
$> docker run -it --rm -v ${PWD}/build/Release/bin/unit-test:/ut --entrypoint "/ut" ghcr.io/testillano/diametercodec:latest # docker
```

To shortcut docker run execution, `./ut.sh` script at root directory can also be used.
You may provide extra arguments to Google test executable, for example:

```bash
$> ./ut.sh --gtest_list_tests # to list the available tests
$> ./ut.sh --gtest_filter=Avp_test.getId # to filter and run 1 specific test
$> ./ut.sh --gtest_filter=Avp_test.* # to filter and run 1 specific suite
etc.
```

#### Coverage

Unit test coverage could be easily calculated executing the script `./tools/coverage.sh`. This script builds and runs an image based in `./Dockerfile.coverage` which uses the `lcov` utility behind. Finally, a `firefox` instance is launched showing the coverage report where you could navigate the source tree to check the current status of the project. This stage is also executed as part of `h2agent` continuous integration (`github workflow`).

Both `ubuntu` and `alpine` base images are supported, but the official image uploaded is the one based in `ubuntu`.
If you want to work with alpine-based images, you may build everything from scratch, including all docker base images which are project dependencies.

## Integration

### CMake
Expand Down
6 changes: 3 additions & 3 deletions include/ert/diametercodec/stack/Avp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ class Avp {
const char * getEnums(void) const {
return enums_.getLiteral().c_str();
}
const char * getAlias(const std::string data) const {
const char * getAlias(const std::string &data) const {
auto it = labels_.find(data);
return ((it != labels_.end()) ? ((*it).second.c_str()) : nullptr);
}
Expand Down Expand Up @@ -188,10 +188,10 @@ class Avp {
void setFormatName(const std::string & fn) {
format_name_ = fn;
}
void setVbit(bool b = true) {
void setVBit(bool b = true) {
v_bit_ = b;
}
void setMbit(bool b = true) {
void setMBit(bool b = true) {
m_bit_ = b;
}

Expand Down
2 changes: 1 addition & 1 deletion include/ert/diametercodec/stack/Command.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class Command {
void setRequest(bool r = true) {
id_.second = r;
}
void setPbit(bool b = true) {
void setPBit(bool b = true) {
p_bit_ = b;
}
void setName(const std::string & n) {
Expand Down
8 changes: 4 additions & 4 deletions include/ert/diametercodec/stack/Dictionary.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,10 @@ class Dictionary {
const Format * getFormat(const std::string & formatName) const;
const Vendor * getVendor(core::S32 vendorId) const;
const Vendor * getVendor(const std::string & vendorName) const;
const Avp * getAvp(const core::AvpId & avpId) const;
const Avp * getAvp(const std::string & avpName) const;
const Command * getCommand(const core::CommandId & commandId) const;
const Command * getCommand(const std::string & commandName) const;
Avp * getAvp(const core::AvpId & avpId) const;
Avp * getAvp(const std::string & avpName) const;
Command * getCommand(const core::CommandId & commandId) const;
Command * getCommand(const std::string & commandName) const;

// set
void addFormat(const Format &, bool reserved = false);
Expand Down
19 changes: 3 additions & 16 deletions src/core/functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,25 +49,12 @@ namespace diametercodec
namespace core
{

std::string functions::avpIdAsPairString(const AvpId & avpId) {
std::string result;
result = "(";
result += std::to_string(avpId.first);
result += ",";
result += std::to_string(avpId.second);
result += ")";
return (result);
std::string functions::avpIdAsPairString(const AvpId &avpId) {
return "(" + std::to_string(avpId.first) + "," + std::to_string(avpId.second) + ")";
}


std::string functions::commandIdAsPairString(const CommandId & commandId) {
std::string result;
result = "(";
result += std::to_string(commandId.first);
result += ",";
result += (commandId.second ? "request" : "answer");
result += ")";
return (result);
return "(" + std::to_string(commandId.first) + "," + (commandId.second ? "request" : "answer") + ")";
}

}
Expand Down
28 changes: 14 additions & 14 deletions src/stack/Dictionary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ void Dictionary::initialize() {
genericAvp.setVendorId(0/*Vendor::Code::Ietf*/);
genericAvp.setName("AVP");
genericAvp.setFormatName(Any.getName());
genericAvp.setVbit(false);
genericAvp.setMbit(false);
genericAvp.setVBit(false);
genericAvp.setMBit(false);
addAvp(genericAvp);
}

Expand Down Expand Up @@ -187,34 +187,34 @@ const Vendor * Dictionary::getVendor(const std::string & vendorName) const {
return (nullptr);
}

const Avp * Dictionary::getAvp(const core::AvpId & avpId) const {
Avp * Dictionary::getAvp(const core::AvpId & avpId) const {
auto it = avps_.find(avpId);

if(it != avps_.end()) return &(it->second);
if(it != avps_.end()) return (Avp*)&(it->second);

return (nullptr);
}

const Avp * Dictionary::getAvp(const std::string & avpName) const {
Avp * Dictionary::getAvp(const std::string & avpName) const {
auto it = avp_names_.find(avpName);

if(it != avp_names_.end()) return (it->second);
if(it != avp_names_.end()) return (Avp*)(it->second);

return (nullptr);
}

const Command * Dictionary::getCommand(const core::CommandId & commandId) const {
Command * Dictionary::getCommand(const core::CommandId & commandId) const {
auto it = commands_.find(commandId);

if(it != commands_.end()) return &(it->second);
if(it != commands_.end()) return (Command*)&(it->second);

return (nullptr);
}

const Command * Dictionary::getCommand(const std::string & commandName) const {
Command * Dictionary::getCommand(const std::string & commandName) const {
auto it = command_names_.find(commandName);

if(it != command_names_.end()) return (it->second);
if(it != command_names_.end()) return (Command*)(it->second);

return (nullptr);
}
Expand Down Expand Up @@ -317,8 +317,8 @@ void Dictionary::extractAvps(const nlohmann::json &doc) {
aux.setCode(*code_it);
aux.setVendorId(vendorCode);
aux.setName(*name_it);
aux.setVbit((vbit_it!=it.end()) ? bool(*vbit_it) : false);
aux.setMbit((mbit_it!=it.end()) ? bool(*mbit_it) : false);
aux.setVBit((vbit_it!=it.end()) ? bool(*vbit_it) : false);
aux.setMBit((mbit_it!=it.end()) ? bool(*mbit_it) : false);

// Check vendor specific bit:
if(vendorCode && !aux.vBit()) {
Expand Down Expand Up @@ -400,7 +400,7 @@ void Dictionary::extractAvps(const nlohmann::json &doc) {

const Avp * avp = getAvp(name);
if(avp == nullptr) {
std::string s_ex = ert::tracing::Logger::asString("Avp '%s', referenced at avp rule definition, not found at xml", name.c_str());
std::string s_ex = ert::tracing::Logger::asString("Avp '%s', referenced at avp rule definition within grouped '%s', not found at xml", name.c_str(), std::string(*name_it).c_str());
throw std::runtime_error(s_ex);
}

Expand Down Expand Up @@ -488,7 +488,7 @@ void Dictionary::extractCommands(const nlohmann::json &doc) {
aux.setCode(*code_it);
aux.setApplicationId((appid_it!=it.end()) ? core::U32(*appid_it) : 0);
aux.setRequest((rbit_it!=it.end()) ? bool(*rbit_it) : false);
aux.setPbit((pbit_it!=it.end()) ? bool(*pbit_it) : false);
aux.setPBit((pbit_it!=it.end()) ? bool(*pbit_it) : false);

AvpRule auxAvpRule(this); // set everything below (even empty, zeroed, etc.) to avoid reset() function
for(auto it: *avprule_it) {
Expand Down
22 changes: 22 additions & 0 deletions tools/coverage.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash
# Coverage image build helper and report generation

echo
git_root_dir="$(git rev-parse --show-toplevel 2>/dev/null)"
[ -z "$git_root_dir" ] && { echo "Go into the git repository !" ; exit 1 ; }

# Base OS:
echo "Base image (alpine/ubuntu) [ubuntu]:"
read base_os
[ -z "${base_os}" ] && base_os=ubuntu

# Build debug target:
bargs="--build-arg base_os=${base_os}"
bargs+=" --build-arg base_tag=latest"
bargs+=" --build-arg make_procs=$(grep processor /proc/cpuinfo -c)"

cd ${git_root_dir}
rm -rf coverage
docker build --rm ${bargs} -f Dockerfile.coverage -t diametercodec:latest-cov . || exit 1
docker run -it --rm -v ${PWD}/coverage:/code/coverage diametercodec:latest-cov || exit 1
firefox coverage/index.html &
1 change: 1 addition & 0 deletions ut/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ target_sources( unit-test
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/MultiRangeExpression.cpp
${CMAKE_CURRENT_SOURCE_DIR}/defines.cpp
${CMAKE_CURRENT_SOURCE_DIR}/functions.cpp
)
25 changes: 25 additions & 0 deletions ut/core/functions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include <ert/diametercodec/core/functions.hpp>

#include <gmock/gmock.h>
#include <gtest/gtest.h>


class Functions_test : public ::testing::Test
{
public:

Functions_test() {
}

};

TEST_F(Functions_test, avpIdAsPairString) {
ert::diametercodec::core::AvpId id = {200, 10};
EXPECT_EQ(ert::diametercodec::core::functions::avpIdAsPairString(id), "(200,10)");
}

TEST_F(Functions_test, commandIdAsPairString) {
ert::diametercodec::core::CommandId id = {200, true};
EXPECT_EQ(ert::diametercodec::core::functions::commandIdAsPairString(id), "(200,request)");
}

125 changes: 125 additions & 0 deletions ut/stack/Avp.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#include <ert/diametercodec/json/stacks.hpp>
#include <ert/diametercodec/stack/Dictionary.hpp>
#include <ert/diametercodec/stack/Avp.hpp>
#include <nlohmann/json.hpp>

#include <gmock/gmock.h>
#include <gtest/gtest.h>


class Avp_test : public ::testing::Test
{
public:
ert::diametercodec::stack::Dictionary dictionary_{};
ert::diametercodec::stack::Avp *avp_{};

Avp_test() {
dictionary_.load(ert::diametercodec::json::stacks::base);
avp_ = new ert::diametercodec::stack::Avp(&dictionary_);

avp_->setCode(2000);
avp_->setVendorId(200);
avp_->setName("Test-AVP");
avp_->setVendorName("TEST");
avp_->setFormatName("Enumerated");
avp_->setVBit(true);
avp_->setMBit(true);
avp_->setEnums("1,2");
avp_->addEnums("3-5");
avp_->addLabel("1", "one");
avp_->addLabel("2", "two");
avp_->addLabel("3", "three");
avp_->addLabel("4", "four");
avp_->addLabel("5", "five");
}
};

TEST_F(Avp_test, getId) {
ert::diametercodec::core::AvpId id = {2000,200};
EXPECT_EQ(avp_->getId(), id);
}

TEST_F(Avp_test, getName) {
EXPECT_EQ(avp_->getName(), "Test-AVP");
}

TEST_F(Avp_test, getFormatName) {
EXPECT_EQ(avp_->getFormatName(), "Enumerated");
}

TEST_F(Avp_test, vBit) {
EXPECT_EQ(avp_->vBit(), true);
}

TEST_F(Avp_test, mBit) {
EXPECT_EQ(avp_->mBit(), true);
}

TEST_F(Avp_test, getEnums) {
EXPECT_EQ(std::string(avp_->getEnums()), "1-5");
}

TEST_F(Avp_test, isChild) {
EXPECT_FALSE(avp_->isChild({100,0}));
}

TEST_F(Avp_test, getFormat) {
const ert::diametercodec::stack::Format *format = avp_->getFormat();
EXPECT_EQ(format->getName(), "Enumerated");
}

TEST_F(Avp_test, getAlias) {
EXPECT_EQ("two", std::string(avp_->getAlias("2")));
}

TEST_F(Avp_test, addLabel) {
ert::diametercodec::stack::Avp::label_container labels = avp_->labels();
ert::diametercodec::stack::Avp::label_container expected_labels;

expected_labels["1"] = "one";
expected_labels["2"] = "two";
expected_labels["3"] = "three";
expected_labels["4"] = "four";
expected_labels["5"] = "five";
EXPECT_EQ(expected_labels, labels);
}

TEST_F(Avp_test, addAvpRuleOnGrouped) {
ert::diametercodec::stack::Avp *groupedAvp = dictionary_.getAvp("Vendor-Specific-Application-Id");

auto avpRule = new ert::diametercodec::stack::AvpRule(&dictionary_);
ert::diametercodec::core::AvpId id(258,0); // Auth-Application-Id
avpRule->setAvpId(id);
avpRule->setPresence(ert::diametercodec::stack::AvpRule::Presence::Optional);
avpRule->setQual("1*");
EXPECT_THROW(groupedAvp->addAvpRule(*avpRule), std::runtime_error); // Cannot add two rules ...
id = {1,0}; // User-Name
avpRule->setAvpId(id);
avpRule->setPresence(ert::diametercodec::stack::AvpRule::Presence::Optional);
groupedAvp->addAvpRule(*avpRule); // no exception in this case
}

TEST_F(Avp_test, addAvpRuleOnNonGrouped) {
auto avpRule = new ert::diametercodec::stack::AvpRule(&dictionary_);
ert::diametercodec::core::AvpId id(258,0); // Auth-Application-Id
avpRule->setAvpId(id);
avpRule->setPresence(ert::diametercodec::stack::AvpRule::Presence::Optional);
avpRule->setQual("1*");
EXPECT_THROW(avp_->addAvpRule(*avpRule), std::runtime_error);
}

TEST_F(Avp_test, allowEnum) {
EXPECT_TRUE(avp_->allowEnum(5));
EXPECT_FALSE(avp_->allowEnum(6));
avp_->addEnums("6-9");
EXPECT_TRUE(avp_->allowEnum(6));
}

TEST_F(Avp_test, hasAliases) {
EXPECT_TRUE(avp_->hasAliases());
}

TEST_F(Avp_test, asJson) {
const nlohmann::json doc = R"({"code":2000,"m-bit":true,"name":"Test-AVP","single":{"enum":"1-5","format":"Enumerated","label":[{"alias":"one","data":"1"},{"alias":"two","data":"2"},{"alias":"three","data":"3"},{"alias":"four","data":"4"},{"alias":"five","data":"5"}]},"v-bit":true,"vendor-name":"TEST"})"_json;
EXPECT_EQ(avp_->asJson(), doc);
}
Loading

0 comments on commit ac37b8c

Please sign in to comment.