diff --git a/.gitignore b/.gitignore index a5ec4ff..39a1885 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,4 @@ cmake-build* .idea .cache .env -scripts/ +scripts/ \ No newline at end of file diff --git a/include/DRAMUtils/memspec/MemSpec.h b/include/DRAMUtils/memspec/MemSpec.h index 8aa99a5..c7dc84c 100644 --- a/include/DRAMUtils/memspec/MemSpec.h +++ b/include/DRAMUtils/memspec/MemSpec.h @@ -39,6 +39,10 @@ #include #include #include +#include +#include +#include +#include #include "DRAMUtils/util/json.h" #include "DRAMUtils/util/types.h" @@ -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 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 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 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 */ diff --git a/tests/tests_memspec/CMakeLists.txt b/tests/tests_memspec/CMakeLists.txt index 1aa8294..39b32ce 100644 --- a/tests/tests_memspec/CMakeLists.txt +++ b/tests/tests_memspec/CMakeLists.txt @@ -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 diff --git a/tests/tests_memspec/base/test_base.cpp b/tests/tests_memspec/base/test_base.cpp index 51f02bf..4a1a4fc 100644 --- a/tests/tests_memspec/base/test_base.cpp +++ b/tests/tests_memspec/base/test_base.cpp @@ -1,81 +1,176 @@ #include -#include -#include -#include -#include -#include -#include +#include +#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(variant); - // MemoryId + MemSpec::MemSpecDDR3 memspec = std::get(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)); +} \ No newline at end of file