Skip to content

Commit

Permalink
Added utility functions and updated test cases (#4)
Browse files Browse the repository at this point in the history
* memspec from json implemented

* added helper functions for memspec creation

* renamed tests

* Refactored test cases

---------

Co-authored-by: marcomoerz <[email protected]>
  • Loading branch information
IESE-T3 and marcomoerz authored Jul 19, 2024
1 parent 049a475 commit c50a8da
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 56 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ cmake-build*
.idea
.cache
.env
scripts/
scripts/
100 changes: 100 additions & 0 deletions include/DRAMUtils/memspec/MemSpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
#include <string>
#include <variant>
#include <iostream>
#include <filesystem>
#include <fstream>
#include <optional>
#include <string_view>

#include "DRAMUtils/util/json.h"
#include "DRAMUtils/util/types.h"
Expand Down Expand Up @@ -89,4 +93,100 @@ NLOHMANN_JSONIFY_ALL_THINGS(MemSpecContainer, memspec)

} // namespace DRAMUtils::MemSpec

namespace DRAMUtils {

namespace detail
{
struct keys
{
static constexpr char memSpec[] = "memspec";
};

};

/**
* @brief Parses Memspec from JSON data into a MemSpecVariant object.
*
* This function attempts to parse a MemSpecVariant object from JSON data. It first
* checks if the provided key exists in the JSON object; if found, it tries to extract
* and parse the corresponding JSON value into the MemSpecVariant. If no key is provided
* or the key is not found, it attempts to parse the entire JSON object directly.
*
* @param json The json object containing the MemSpec data
* @param key Optional key to locate the MemSpec data in the json object.
* Defaults to "memspec" if not provided.
*
* @return An optional MemSpecVariant object if the JSON data was successfully parsed or std::nullopt otherwise.
*/
std::optional<MemSpec::MemSpecVariant> parse_memspec_from_json(const json_t& json, std::string_view key = detail::keys::memSpec)
{
try
{
MemSpec::MemSpecVariant result;
if (!key.empty() && json.contains(key) && result.from_json(json.at(key)))
return result;
else if (result.from_json(json))
return result;
}
catch (std::exception&)
{
}

return std::nullopt;
}

/**
* @brief Parses Memspec from a string buffer into a MemSpecVariant object.
* This function is a wrapper around parse_memspec_from_json.
*
* @param buffer The string buffer containing the MemSpec data
* @param key Optional key to locate the MemSpec data in the json object.
* Defaults to "memspec" if not provided.
*
* @return An optional MemSpecVariant object if the JSON data was successfully parsed or std::nullopt otherwise.
*/
std::optional<MemSpec::MemSpecVariant> parse_Memspec_from_buffer(std::string_view buffer, std::string_view key = detail::keys::memSpec)
{
try
{
return parse_memspec_from_json(json_t::parse(buffer), key);
}
catch (std::exception&)
{
return std::nullopt;
}
}

/**
* @brief Parses Memspec from a file into a MemSpecVariant object.
* This function is a wrapper around parse_memspec_from_json.
*
* @param path The path to the file containing the MemSpec data
* @param key Optional key to locate the MemSpec data in the json object.
* Defaults to "memspec" if not provided.
*
* @return An optional MemSpecVariant object if the JSON data was successfully parsed or std::nullopt otherwise.
*/
std::optional<MemSpec::MemSpecVariant> parse_memspec_from_file(const std::filesystem::path &path, std::string_view key = detail::keys::memSpec)
{
try
{
if (!std::filesystem::exists(path))
return std::nullopt;

std::ifstream file(path);
if (!file.is_open())
return std::nullopt;

json_t json_obj = json_t::parse(file);
return parse_memspec_from_json(json_obj, key);
}
catch (std::exception&)
{
return std::nullopt;
}
}

} // namespace DRAMUtils

#endif /* DRAMUTILS_MEMSPEC_MEMSPEC_H */
2 changes: 1 addition & 1 deletion tests/tests_memspec/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ file(GLOB_RECURSE SOURCE_FILES base/*.cpp)

add_executable(${PROJECT_NAME} ${SOURCE_FILES} ${HEADER_FILES})

target_compile_definitions(${PROJECT_NAME} PUBLIC TEST_RESOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}/resources/")
target_compile_definitions(${PROJECT_NAME} PUBLIC TEST_RESOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}/temp/")
target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR})
target_link_libraries(${PROJECT_NAME}
gtest_main
Expand Down
203 changes: 149 additions & 54 deletions tests/tests_memspec/base/test_base.cpp
Original file line number Diff line number Diff line change
@@ -1,81 +1,176 @@
#include <gtest/gtest.h>

#include <memory>
#include <stdio.h>
#include <fstream>
#include <iostream>
#include <variant>
#include <utility>
#include <sstream>

#include "DRAMUtils/util/json.h"
#include "DRAMUtils/memspec/MemSpec.h"
#include "DRAMUtils/memspec/standards/MemSpecDDR3.h"

using namespace DRAMUtils;

// TODO change Config::MemSpec namespace to MemSpec
class Memspec_Base_Test : public ::testing::Test {
protected:
// Test variables
std::string_view path = "test.json";
MemSpec::MemSpecContainer writeContainer;
MemSpec::MemSpecContainer readContainer;

bool writeContainerToJsonFile()
{
std::ofstream file(path.data());
if(!file.is_open())
return false;
nlohmann::json j = writeContainer;
file << j.dump(4);
return true;
const char* test_mem_spec = R"(
{
"memarchitecturespec": {
"burstLength": 0,
"dataRate": 0,
"nbrOfBanks": 0,
"nbrOfChannels": 0,
"nbrOfColumns": 0,
"nbrOfDevices": 0,
"nbrOfRows": 0,
"nbrOfRanks": 0,
"width": 0
},
"memoryId": "Test_DDR3",
"memoryType": "DDR3",
"memtimingspec": {
"ACTPDEN": 0,
"AL": 0,
"CCD": 0,
"CKE": 11,
"CKESR": 0,
"DQSCK": 0,
"FAW": 0,
"PRPDEN": 0,
"RAS": 0,
"RC": 0,
"RCD": 0,
"REFI": 0,
"REFPDEN": 0,
"RFC": 0,
"RL": 0,
"RP": 0,
"RRD": 0,
"RTP": 0,
"RTRS": 0,
"WL": 0,
"WR": 0,
"WTR": 0,
"XP": 0,
"XPDLL": 0,
"XS": 0,
"XSDLL": 0,
"tCK": 0.0
}
}
)";

bool readContainerFromJsonFile()
{
std::ifstream file(path.data());
if(!file.is_open())
return false;
nlohmann::json j;
file >> j;
readContainer = j;
return true;
}
class Memspec_Base_Test : public ::testing::Test
{
protected:
// Test data
MemSpec::MemSpecContainer test_container;

virtual void SetUp()
// Test util functions
std::stringstream writeContainerToStream(const MemSpec::MemSpecContainer& container)
{
std::stringstream stream;
json_t j = container;
stream << j.dump(4);
return stream;
}

virtual void TearDown()
MemSpec::MemSpecContainer readContainerFromStream(std::stringstream& stream)
{
json_t j;
stream >> j;

MemSpec::MemSpecContainer result = j;
return result;
}
};

TEST_F(Memspec_Base_Test, Test_0)
{
using namespace DRAMUtils::MemSpec;
// Write MemSpecContainer to file
MemSpec::MemSpecVariant createDummyMemSpec()
{
MemSpecDDR3 memspec;
memspec.memarchitecturespec = MemArchitectureSpecTypeDDR3{};
MemSpec::MemSpecDDR3 memspec;
memspec.memarchitecturespec = MemSpec::MemArchitectureSpecTypeDDR3{};
memspec.memoryId = "Test_DDR3";
memspec.memarchitecturespec.nbrOfRows = 10;
memspec.memtimingspec = MemTimingSpecDDR3{};
memspec.memtimingspec = MemSpec::MemTimingSpecDDR3{};
memspec.memtimingspec.CKE = 11;
writeContainer.memspec.setVariant(memspec);
ASSERT_TRUE(writeContainerToJsonFile());
MemSpec::MemSpecVariant variant;
variant.setVariant(memspec);
return variant;
}
// Readback container from file

void compareMemSpec(MemSpec::MemSpecVariant& variant)
{
// Read file
ASSERT_TRUE(readContainerFromJsonFile());
auto variant = readContainer.memspec.getVariant();
// Get variant
MemSpecDDR3 memspec = std::get<MemSpecDDR3>(variant);
// MemoryId
MemSpec::MemSpecDDR3 memspec = std::get<MemSpec::MemSpecDDR3>(variant.getVariant());
ASSERT_EQ(memspec.memoryId, "Test_DDR3");
// MemArchitectureSpecType
ASSERT_EQ(memspec.memarchitecturespec.nbrOfRows, 10);
// MemTimingSpecType
ASSERT_EQ(memspec.memtimingspec.CKE, 11);
}
}

virtual void SetUp()
{
test_container.memspec = createDummyMemSpec();
}

virtual void TearDown()
{
}
};

TEST_F(Memspec_Base_Test, IDVariant)
{
auto stream = writeContainerToStream(test_container);
auto out_container = readContainerFromStream(stream);
compareMemSpec(out_container.memspec);
};

TEST_F(Memspec_Base_Test, ParseMemSpec)
{
// Test data
json_t test_json = test_container;

// Parse from buffer
auto ok_spec = DRAMUtils::parse_Memspec_from_buffer(test_mem_spec);
ASSERT_TRUE(ok_spec);

// Parse from json
auto ok_spec_1 = DRAMUtils::parse_memspec_from_json(test_json);
auto ok_spec_2 = DRAMUtils::parse_memspec_from_json(test_json, "memspec");

ASSERT_TRUE(ok_spec_1);
compareMemSpec(*ok_spec_1);
ASSERT_TRUE(ok_spec_2);
compareMemSpec(*ok_spec_2);

// Test with different key
json_t test_diff_key_json;
test_diff_key_json["other_key"] = test_json["memspec"];

auto ok_spec_3 = DRAMUtils::parse_memspec_from_json(test_diff_key_json, "other_key");
compareMemSpec(*ok_spec_3);

// Parse memspec without container
auto spec_json = test_json["memspec"];

auto ok_spec_4 = DRAMUtils::parse_memspec_from_json(spec_json);
compareMemSpec(*ok_spec_4);
};

TEST_F(Memspec_Base_Test, ParseMemSpec_Invalid)
{
// Test data
json_t test_json = test_container;

// Parse from empty buffer
ASSERT_FALSE(DRAMUtils::parse_Memspec_from_buffer(""));

// Parse with wrong key
auto fail_spec_1 = DRAMUtils::parse_memspec_from_json(test_json, "MemSpeck");
ASSERT_FALSE(fail_spec_1);

// Parse with empty key
auto fail_spec_2 = DRAMUtils::parse_memspec_from_json(test_json, "");
ASSERT_FALSE(fail_spec_2);

// Test with invalid json
json_t invalid = {"test", 0};
ASSERT_FALSE(DRAMUtils::parse_memspec_from_json(invalid));

// Test with invalid memspec
json_t invalid_spec = test_container;
invalid_spec["memspec"]["memarchitecturespec"].erase("nbrOfRows");
ASSERT_FALSE(DRAMUtils::parse_memspec_from_json(invalid_spec));
}

0 comments on commit c50a8da

Please sign in to comment.