diff --git a/cpp/command/command_dispatcher.cpp b/cpp/command/command_dispatcher.cpp index afc6b278..426b27de 100644 --- a/cpp/command/command_dispatcher.cpp +++ b/cpp/command/command_dispatcher.cpp @@ -13,13 +13,13 @@ #include "command_image_support.h" #include "command_response.h" #include "controllers/controller_factory.h" -#include "protobuf/protobuf_util.h" +#include "protobuf/target_api_util.h" #include "base/property_handler.h" #include "shared/s2p_exceptions.h" using namespace command_response; -using namespace protobuf_util; using namespace s2p_util; +using namespace target_api_util; bool CommandDispatcher::DispatchCommand(const CommandContext &context, PbResult &result) { diff --git a/cpp/command/command_executor.cpp b/cpp/command/command_executor.cpp index 9586ab8b..7282d1df 100644 --- a/cpp/command/command_executor.cpp +++ b/cpp/command/command_executor.cpp @@ -16,11 +16,11 @@ #include "controllers/controller_factory.h" #include "devices/disk.h" #include "devices/scsi_generic.h" -#include "protobuf/protobuf_util.h" +#include "protobuf/target_api_util.h" #include "shared/s2p_exceptions.h" -using namespace protobuf_util; using namespace s2p_util; +using namespace target_api_util; bool CommandExecutor::ProcessDeviceCmd(const CommandContext &context, const PbDeviceDefinition &pb_device, bool dryRun) { diff --git a/cpp/command/command_image_support.cpp b/cpp/command/command_image_support.cpp index 0fb7805f..cc0d6780 100644 --- a/cpp/command/command_image_support.cpp +++ b/cpp/command/command_image_support.cpp @@ -2,7 +2,7 @@ // // SCSI2Pi, SCSI device emulator and SCSI tools for the Raspberry Pi // -// Copyright (C) 2021-2024 Uwe Seimet +// Copyright (C) 2021-2025 Uwe Seimet // //--------------------------------------------------------------------------- @@ -12,10 +12,10 @@ #ifdef BUILD_STORAGE_DEVICE #include "devices/storage_device.h" #endif -#include "protobuf/protobuf_util.h" +#include "protobuf/target_api_util.h" using namespace s2p_util; -using namespace protobuf_util; +using namespace target_api_util; CommandImageSupport::CommandImageSupport() { diff --git a/cpp/command/command_response.cpp b/cpp/command/command_response.cpp index b2c8c6d2..2c02182a 100644 --- a/cpp/command/command_response.cpp +++ b/cpp/command/command_response.cpp @@ -12,13 +12,13 @@ #include "command_image_support.h" #include "devices/disk.h" #include "devices/scsi_generic.h" -#include "protobuf/protobuf_util.h" +#include "protobuf/target_api_util.h" #include "shared/network_util.h" #include "shared/s2p_version.h" using namespace network_util; -using namespace protobuf_util; using namespace s2p_util; +using namespace target_api_util; namespace { diff --git a/cpp/devices/host_services.cpp b/cpp/devices/host_services.cpp index ed5ad28f..7990be3d 100644 --- a/cpp/devices/host_services.cpp +++ b/cpp/devices/host_services.cpp @@ -87,7 +87,7 @@ #include "command/command_context.h" #include "command/command_dispatcher.h" #include "controllers/abstract_controller.h" -#include "protobuf/protobuf_util.h" +#include "protobuf/target_api_util.h" #include "shared/s2p_exceptions.h" #include "page_handler.h" @@ -95,7 +95,7 @@ using namespace std::chrono; using namespace google::protobuf; using namespace google::protobuf::util; using namespace memory_util; -using namespace protobuf_util; +using namespace target_api_util; HostServices::HostServices(int lun) : PrimaryDevice(SCHS, lun) { @@ -311,7 +311,7 @@ int HostServices::WriteData(cdb_t cdb, data_out_t buf, int, int l) PbResult result; CommandContext context(cmd, GetLogger()); - context.SetLocale(protobuf_util::GetParam(cmd, "locale")); + context.SetLocale(target_api_util::GetParam(cmd, "locale")); if (!dispatcher->DispatchCommand(context, result)) { LogTrace("Failed to execute " + PbOperation_Name(cmd.operation()) + " operation"); throw ScsiException(SenseKey::ABORTED_COMMAND, Asc::INTERNAL_TARGET_FAILURE); diff --git a/cpp/protobuf/protobuf_util.cpp b/cpp/protobuf/protobuf_util.cpp index 24b6f0a1..2cdb3911 100644 --- a/cpp/protobuf/protobuf_util.cpp +++ b/cpp/protobuf/protobuf_util.cpp @@ -2,171 +2,14 @@ // // SCSI2Pi, SCSI device emulator and SCSI tools for the Raspberry Pi // -// Copyright (C) 2021-2024 Uwe Seimet +// Copyright (C) 2021-2025 Uwe Seimet // //--------------------------------------------------------------------------- #include "protobuf_util.h" -#include #include #include "shared/s2p_exceptions.h" -using namespace s2p_util; - -PbDeviceType protobuf_util::ParseDeviceType(const string &value) -{ - if (PbDeviceType type; PbDeviceType_Parse(ToUpper(value), &type)) { - return type; - } - - // Handle convenience device types (shortcuts) - const auto &it = DEVICE_TYPES.find(tolower(value[0])); - return it != DEVICE_TYPES.end() ? it->second : UNDEFINED; -} - -PbCachingMode protobuf_util::ParseCachingMode(const string &value) -{ - string v = value; - ranges::replace(v, '-', '_'); - - if (PbCachingMode mode; PbCachingMode_Parse(ToUpper(v), &mode)) { - return mode; - } - - throw ParserException("Invalid caching mode '" + value + "'"); -} - -void protobuf_util::ParseParameters(PbDeviceDefinition &device, const string ¶ms) -{ - if (params.empty()) { - return; - } - - // Old style parameter (filename only), for backwards compatibility and convenience - if (params.find(KEY_VALUE_SEPARATOR) == string::npos) { - SetParam(device, "file", params); - return; - } - - for (const auto &p : Split(params, COMPONENT_SEPARATOR)) { - if (const auto ¶m = Split(p, KEY_VALUE_SEPARATOR, 2); param.size() == 2) { - SetParam(device, param[0], param[1]); - } - } -} - -string protobuf_util::SetCommandParams(PbCommand &command, const string ¶ms) -{ - if (params.empty()) { - return ""; - } - - if (params.find(KEY_VALUE_SEPARATOR) != string::npos) { - return SetFromGenericParams(command, params); - } - - switch (const auto &components = Split(params, COMPONENT_SEPARATOR, 3); components.size()) { - case 3: - SetParam(command, "operations", components[2]); - [[fallthrough]]; - - case 2: - SetParam(command, "file_pattern", components[1]); - SetParam(command, "folder_pattern", components[0]); - break; - - case 1: - SetParam(command, "file_pattern", components[0]); - break; - - default: - assert(false); - break; - } - - return ""; -} - -string protobuf_util::SetFromGenericParams(PbCommand &command, const string ¶ms) -{ - for (const string &key_value : Split(params, COMPONENT_SEPARATOR)) { - const auto ¶m = Split(key_value, KEY_VALUE_SEPARATOR, 2); - if (param.size() > 1 && !param[0].empty()) { - SetParam(command, param[0], param[1]); - } - else { - return "Parameter '" + key_value + "' has to be a key/value pair"; - } - } - - return ""; -} - -void protobuf_util::SetProductData(PbDeviceDefinition &device, const string &data) -{ - const auto &components = Split(data, COMPONENT_SEPARATOR, 3); - switch (components.size()) { - case 3: - device.set_revision(components[2]); - [[fallthrough]]; - - case 2: - device.set_product(components[1]); - [[fallthrough]]; - - case 1: - device.set_vendor(components[0]); - break; - - default: - break; - } -} - -string protobuf_util::SetIdAndLun(PbDeviceDefinition &device, const string &value) -{ - int id; - int lun; - if (const string &error = ParseIdAndLun(value, id, lun); !error.empty()) { - return error; - } - - device.set_id(id); - device.set_unit(lun != -1 ? lun : 0); - - return ""; -} - -int protobuf_util::GetLunMax(PbDeviceType type) -{ - return type == SAHD ? 2 : 32; -} - -string protobuf_util::ListDevices(const vector &devices) -{ - if (devices.empty()) { - return "No devices currently attached\n"; - } - - vector sorted_devices(devices); - ranges::sort(sorted_devices, [](const auto &a, const auto &b) {return a.id() < b.id() || a.unit() < b.unit();}); - - string s = "+--------+------+-------------------------------------------\n" - "| ID:LUN | Type | Image File/Device File/Description\n" - "+--------+------+-------------------------------------------\n"; - - for (const auto &device : sorted_devices) { - s += fmt::format("| {0}:{1:<2} | {2} | {3}{4}\n", device.id(), device.unit(), - PbDeviceType_Name(device.type()), device.file().name(), - !device.status().removed() && (device.properties().read_only() || device.status().protected_()) ? - " (READ-ONLY)" : ""); - } - - s += "+--------+------+-------------------------------------------\n"; - - return s; -} - // Serialize/Deserialize protobuf message: Length followed by the actual data. // A little endian platform is assumed. void protobuf_util::SerializeMessage(int fd, const google::protobuf::Message &message) diff --git a/cpp/protobuf/protobuf_util.h b/cpp/protobuf/protobuf_util.h index 228b49b8..949b3393 100644 --- a/cpp/protobuf/protobuf_util.h +++ b/cpp/protobuf/protobuf_util.h @@ -9,56 +9,16 @@ #pragma once #include -#include -#include -#include "generated/target_api.pb.h" +#include using namespace std; -using namespace s2p_interface; namespace protobuf_util { -static constexpr char KEY_VALUE_SEPARATOR = '='; - -string GetParam(const auto &item, const string &key) -{ - const auto &it = item.params().find(key); - return it != item.params().end() ? it->second : ""; -} - -void SetParam(auto &item, const string &key, string_view value) -{ - if (!key.empty() && !value.empty()) { - auto &map = *item.mutable_params(); - map[key] = value; - } -} - -PbDeviceType ParseDeviceType(const string&); -PbCachingMode ParseCachingMode(const string&); -void ParseParameters(PbDeviceDefinition&, const string&); -string SetCommandParams(PbCommand&, const string&); -string SetFromGenericParams(PbCommand&, const string&); -void SetProductData(PbDeviceDefinition&, const string&); -string SetIdAndLun(PbDeviceDefinition&, const string&); -int GetLunMax(PbDeviceType); - -string ListDevices(const vector&); - void SerializeMessage(int, const google::protobuf::Message&); void DeserializeMessage(int, google::protobuf::Message&); size_t ReadBytes(int, span); size_t WriteBytes(int, span); -inline static const unordered_map DEVICE_TYPES = { - { 'c', SCCD }, - { 'd', SCDP }, - { 'h', SCHD }, - { 'l', SCLP }, - { 'm', SCMO }, - { 'r', SCRM }, - { 's', SCHS }, - { 't', SCTP } -}; } diff --git a/cpp/protobuf/target_api_util.cpp b/cpp/protobuf/target_api_util.cpp new file mode 100644 index 00000000..b98bd263 --- /dev/null +++ b/cpp/protobuf/target_api_util.cpp @@ -0,0 +1,167 @@ +//--------------------------------------------------------------------------- +// +// SCSI2Pi, SCSI device emulator and SCSI tools for the Raspberry Pi +// +// Copyright (C) 2021-2025 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include "target_api_util.h" +#include "shared/s2p_exceptions.h" +#include "shared/s2p_util.h" + +using namespace s2p_util; + +PbDeviceType target_api_util::ParseDeviceType(const string &value) +{ + if (PbDeviceType type; PbDeviceType_Parse(ToUpper(value), &type)) { + return type; + } + + // Handle convenience device types (shortcuts) + const auto &it = DEVICE_TYPES.find(tolower(value[0])); + return it != DEVICE_TYPES.end() ? it->second : UNDEFINED; +} + +PbCachingMode target_api_util::ParseCachingMode(const string &value) +{ + string v = value; + ranges::replace(v, '-', '_'); + + if (PbCachingMode mode; PbCachingMode_Parse(ToUpper(v), &mode)) { + return mode; + } + + throw ParserException("Invalid caching mode '" + value + "'"); +} + +void target_api_util::ParseParameters(PbDeviceDefinition &device, const string ¶ms) +{ + if (params.empty()) { + return; + } + + // Old style parameter (filename only), for backwards compatibility and convenience + if (params.find(KEY_VALUE_SEPARATOR) == string::npos) { + SetParam(device, "file", params); + return; + } + + for (const auto &p : Split(params, COMPONENT_SEPARATOR)) { + if (const auto ¶m = Split(p, KEY_VALUE_SEPARATOR, 2); param.size() == 2) { + SetParam(device, param[0], param[1]); + } + } +} + +string target_api_util::SetCommandParams(PbCommand &command, const string ¶ms) +{ + if (params.empty()) { + return ""; + } + + if (params.find(KEY_VALUE_SEPARATOR) != string::npos) { + return SetFromGenericParams(command, params); + } + + switch (const auto &components = Split(params, COMPONENT_SEPARATOR, 3); components.size()) { + case 3: + SetParam(command, "operations", components[2]); + [[fallthrough]]; + + case 2: + SetParam(command, "file_pattern", components[1]); + SetParam(command, "folder_pattern", components[0]); + break; + + case 1: + SetParam(command, "file_pattern", components[0]); + break; + + default: + assert(false); + break; + } + + return ""; +} + +string target_api_util::SetFromGenericParams(PbCommand &command, const string ¶ms) +{ + for (const string &key_value : Split(params, COMPONENT_SEPARATOR)) { + const auto ¶m = Split(key_value, KEY_VALUE_SEPARATOR, 2); + if (param.size() > 1 && !param[0].empty()) { + SetParam(command, param[0], param[1]); + } + else { + return "Parameter '" + key_value + "' has to be a key/value pair"; + } + } + + return ""; +} + +void target_api_util::SetProductData(PbDeviceDefinition &device, const string &data) +{ + const auto &components = Split(data, COMPONENT_SEPARATOR, 3); + switch (components.size()) { + case 3: + device.set_revision(components[2]); + [[fallthrough]]; + + case 2: + device.set_product(components[1]); + [[fallthrough]]; + + case 1: + device.set_vendor(components[0]); + break; + + default: + break; + } +} + +string target_api_util::SetIdAndLun(PbDeviceDefinition &device, const string &value) +{ + int id; + int lun; + if (const string &error = ParseIdAndLun(value, id, lun); !error.empty()) { + return error; + } + + device.set_id(id); + device.set_unit(lun != -1 ? lun : 0); + + return ""; +} + +int target_api_util::GetLunMax(PbDeviceType type) +{ + return type == SAHD ? 2 : 32; +} + +string target_api_util::ListDevices(const vector &devices) +{ + if (devices.empty()) { + return "No devices currently attached\n"; + } + + vector sorted_devices(devices); + ranges::sort(sorted_devices, [](const auto &a, const auto &b) {return a.id() < b.id() || a.unit() < b.unit();}); + + string s = "+--------+------+-------------------------------------------\n" + "| ID:LUN | Type | Image File/Device File/Description\n" + "+--------+------+-------------------------------------------\n"; + + for (const auto &device : sorted_devices) { + s += fmt::format("| {0}:{1:<2} | {2} | {3}{4}\n", device.id(), device.unit(), + PbDeviceType_Name(device.type()), device.file().name(), + !device.status().removed() && (device.properties().read_only() || device.status().protected_()) ? + " (READ-ONLY)" : ""); + } + + s += "+--------+------+-------------------------------------------\n"; + + return s; +} diff --git a/cpp/protobuf/target_api_util.h b/cpp/protobuf/target_api_util.h new file mode 100644 index 00000000..89a35edb --- /dev/null +++ b/cpp/protobuf/target_api_util.h @@ -0,0 +1,59 @@ +//--------------------------------------------------------------------------- +// +// SCSI2Pi, SCSI device emulator and SCSI tools for the Raspberry Pi +// +// Copyright (C) 2021-2025 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include +#include "generated/target_api.pb.h" + +using namespace std; +using namespace s2p_interface; + +namespace target_api_util +{ + +static constexpr char KEY_VALUE_SEPARATOR = '='; + +string GetParam(const auto &item, const string &key) +{ + const auto &it = item.params().find(key); + return it != item.params().end() ? it->second : ""; +} + +void SetParam(auto &item, const string &key, string_view value) +{ + if (!key.empty() && !value.empty()) { + auto &map = *item.mutable_params(); + map[key] = value; + } +} + +PbDeviceType ParseDeviceType(const string&); +PbCachingMode ParseCachingMode(const string&); +void ParseParameters(PbDeviceDefinition&, const string&); +string SetCommandParams(PbCommand&, const string&); +string SetFromGenericParams(PbCommand&, const string&); +void SetProductData(PbDeviceDefinition&, const string&); +string SetIdAndLun(PbDeviceDefinition&, const string&); +int GetLunMax(PbDeviceType); + +string ListDevices(const vector&); + +inline static const unordered_map DEVICE_TYPES = { + { 'c', SCCD }, + { 'd', SCDP }, + { 'h', SCHD }, + { 'l', SCLP }, + { 'm', SCMO }, + { 'r', SCRM }, + { 's', SCHS }, + { 't', SCTP } +}; +} diff --git a/cpp/s2p/s2p_core.cpp b/cpp/s2p/s2p_core.cpp index 7dde8602..60cd8d58 100644 --- a/cpp/s2p/s2p_core.cpp +++ b/cpp/s2p/s2p_core.cpp @@ -24,14 +24,14 @@ #ifdef BUILD_SCHS #include "devices/host_services.h" #endif -#include "protobuf/protobuf_util.h" +#include "protobuf/target_api_util.h" #include "shared/s2p_exceptions.h" #include "shared/s2p_version.h" #include "s2p_parser.h" using namespace s2p_parser; using namespace s2p_util; -using namespace protobuf_util; +using namespace target_api_util; bool S2p::InitBus(bool in_process, bool log_signals) { diff --git a/cpp/s2pctl/s2pctl_commands.cpp b/cpp/s2pctl/s2pctl_commands.cpp index 9fdc19cf..78563161 100644 --- a/cpp/s2pctl/s2pctl_commands.cpp +++ b/cpp/s2pctl/s2pctl_commands.cpp @@ -17,6 +17,7 @@ #include #include #include "protobuf/protobuf_util.h" +#include "protobuf/target_api_util.h" #include "shared/network_util.h" #include "shared/s2p_exceptions.h" #include "s2pctl_display.h" @@ -24,10 +25,11 @@ using namespace google::protobuf; using namespace google::protobuf::util; using namespace network_util; +using namespace protobuf_util; using namespace s2p_interface; using namespace s2p_util; -using namespace protobuf_util; using namespace s2pctl_display; +using namespace target_api_util; bool S2pCtlCommands::Execute(string_view log_level, string_view default_folder, string_view reserved_ids, string_view image_params, string_view filename) diff --git a/cpp/s2pctl/s2pctl_display.cpp b/cpp/s2pctl/s2pctl_display.cpp index 4ed58edf..b104b7da 100644 --- a/cpp/s2pctl/s2pctl_display.cpp +++ b/cpp/s2pctl/s2pctl_display.cpp @@ -10,11 +10,11 @@ #include #include #include -#include "protobuf/protobuf_util.h" +#include "protobuf/target_api_util.h" #include "shared/s2p_util.h" using namespace s2p_util; -using namespace protobuf_util; +using namespace target_api_util; namespace { diff --git a/cpp/s2pctl/sp2ctl_core.cpp b/cpp/s2pctl/sp2ctl_core.cpp index 9fc0ae63..76b0261b 100644 --- a/cpp/s2pctl/sp2ctl_core.cpp +++ b/cpp/s2pctl/sp2ctl_core.cpp @@ -2,7 +2,7 @@ // // SCSI2Pi, SCSI device emulator and SCSI tools for the Raspberry Pi // -// Copyright (C) 2021-2024 Uwe Seimet +// Copyright (C) 2021-2025 Uwe Seimet // //--------------------------------------------------------------------------- @@ -12,13 +12,13 @@ #include #include #include -#include "protobuf/protobuf_util.h" +#include "protobuf/target_api_util.h" #include "shared/s2p_exceptions.h" #include "shared/s2p_version.h" #include "s2pctl_commands.h" using namespace s2p_util; -using namespace protobuf_util; +using namespace target_api_util; void S2pCtl::Banner(bool usage) const { diff --git a/cpp/test/command_dispatcher_test.cpp b/cpp/test/command_dispatcher_test.cpp index a5f0ec48..ae650af1 100644 --- a/cpp/test/command_dispatcher_test.cpp +++ b/cpp/test/command_dispatcher_test.cpp @@ -10,9 +10,9 @@ #include "command/command_context.h" #include "command/command_dispatcher.h" #include "controllers/controller_factory.h" -#include "protobuf/protobuf_util.h" +#include "protobuf/target_api_util.h" -using namespace protobuf_util; +using namespace target_api_util; TEST(CommandDispatcherTest, DispatchCommand) { diff --git a/cpp/test/command_executor_test.cpp b/cpp/test/command_executor_test.cpp index 0bd75681..e09b6bb8 100644 --- a/cpp/test/command_executor_test.cpp +++ b/cpp/test/command_executor_test.cpp @@ -11,10 +11,10 @@ #include "command/command_context.h" #include "command/command_response.h" #include "controllers/controller_factory.h" -#include "protobuf/protobuf_util.h" +#include "protobuf/target_api_util.h" #include "shared/s2p_exceptions.h" -using namespace protobuf_util; +using namespace target_api_util; TEST(CommandExecutorTest, ProcessDeviceCmd) { diff --git a/cpp/test/command_image_support_test.cpp b/cpp/test/command_image_support_test.cpp index 1682261b..e77f7c83 100644 --- a/cpp/test/command_image_support_test.cpp +++ b/cpp/test/command_image_support_test.cpp @@ -9,9 +9,9 @@ #include "mocks.h" #include "command/command_context.h" #include "command/command_image_support.h" -#include "protobuf/protobuf_util.h" +#include "protobuf/target_api_util.h" -using namespace protobuf_util; +using namespace target_api_util; TEST(CommandImageSupportTest, SetGetDepth) { diff --git a/cpp/test/command_response_test.cpp b/cpp/test/command_response_test.cpp index 185820fb..74800fb6 100644 --- a/cpp/test/command_response_test.cpp +++ b/cpp/test/command_response_test.cpp @@ -11,12 +11,12 @@ #include "command/command_image_support.h" #include "command/command_response.h" #include "controllers/controller_factory.h" -#include "protobuf/protobuf_util.h" +#include "protobuf/target_api_util.h" #include "shared/s2p_version.h" using namespace spdlog; using namespace command_response; -using namespace protobuf_util; +using namespace target_api_util; TEST(CommandResponseTest, Operation_Count) { diff --git a/cpp/test/protobuf_util_test.cpp b/cpp/test/protobuf_util_test.cpp index dcd7b3ce..48e846b1 100644 --- a/cpp/test/protobuf_util_test.cpp +++ b/cpp/test/protobuf_util_test.cpp @@ -6,213 +6,13 @@ // //--------------------------------------------------------------------------- -#include "mocks.h" +#include +#include "test_shared.h" #include "protobuf/protobuf_util.h" #include "shared/s2p_exceptions.h" using namespace protobuf_util; - -void TestSpecialDevice(const string &name) -{ - PbDeviceDefinition device; - ParseParameters(device, name); - EXPECT_EQ(name, GetParam(device, "file")); - EXPECT_EQ("", GetParam(device, "interfaces")); -} - -TEST(ProtobufUtil, ParseDeviceType) -{ - EXPECT_EQ(SCCD, ParseDeviceType("sccd")); - EXPECT_EQ(SCDP, ParseDeviceType("scdp")); - EXPECT_EQ(SCHD, ParseDeviceType("schd")); - EXPECT_EQ(SCLP, ParseDeviceType("sclp")); - EXPECT_EQ(SCMO, ParseDeviceType("scmo")); - EXPECT_EQ(SCRM, ParseDeviceType("scrm")); - EXPECT_EQ(SCHS, ParseDeviceType("schs")); - EXPECT_EQ(SCTP, ParseDeviceType("sctp")); - EXPECT_EQ(SCSG, ParseDeviceType("scsg")); - - EXPECT_EQ(SCCD, ParseDeviceType("c")); - EXPECT_EQ(SCDP, ParseDeviceType("d")); - EXPECT_EQ(SCHD, ParseDeviceType("h")); - EXPECT_EQ(SCLP, ParseDeviceType("l")); - EXPECT_EQ(SCMO, ParseDeviceType("m")); - EXPECT_EQ(SCRM, ParseDeviceType("r")); - EXPECT_EQ(SCHS, ParseDeviceType("s")); - EXPECT_EQ(SCTP, ParseDeviceType("t")); - - EXPECT_EQ(UNDEFINED, ParseDeviceType("")); - EXPECT_EQ(UNDEFINED, ParseDeviceType("xyz")); -} - -TEST(ProtobufUtil, ParseCachingMode) -{ - EXPECT_EQ(DEFAULT, ParseCachingMode("default")); - EXPECT_EQ(LINUX, ParseCachingMode("linux")); - EXPECT_EQ(WRITE_THROUGH, ParseCachingMode("write_through")); - EXPECT_EQ(WRITE_THROUGH, ParseCachingMode("write-through")); - EXPECT_EQ(LINUX_OPTIMIZED, ParseCachingMode("linux_optimized")); - EXPECT_EQ(LINUX_OPTIMIZED, ParseCachingMode("linux-optimized")); - - EXPECT_THROW(ParseCachingMode(""), ParserException); - EXPECT_THROW(ParseCachingMode("xyz"), ParserException); -} - -TEST(ProtobufUtil, GetSetParam) -{ - // The implementation is a function template, testing one possible T is sufficient - PbCommand command; - SetParam(command, "key", "value"); - EXPECT_EQ("value", GetParam(command, "key")); - EXPECT_EQ("", GetParam(command, "xyz")); - EXPECT_EQ("", GetParam(command, "")); -} - -TEST(ProtobufUtil, ParseParameters) -{ - PbDeviceDefinition device1; - ParseParameters(device1, "a=b:c=d:e"); - EXPECT_EQ("b", GetParam(device1, "a")); - EXPECT_EQ("d", GetParam(device1, "c")); - EXPECT_EQ("", GetParam(device1, "e")); - - // Old style parameter - PbDeviceDefinition device2; - ParseParameters(device2, "a"); - EXPECT_EQ("a", GetParam(device2, "file")); - - // Ensure that nothing breaks on an empty parameter list - PbDeviceDefinition device3; - ParseParameters(device3, ""); - - TestSpecialDevice("daynaport"); - TestSpecialDevice("printer"); - TestSpecialDevice("services"); -} - -TEST(ProtobufUtil, SetCommandParams) -{ - PbCommand command1; - - EXPECT_TRUE(SetCommandParams(command1, "").empty()); - - EXPECT_TRUE(SetCommandParams(command1, "file").empty()); - EXPECT_EQ("", GetParam(command1, "folder_pattern")); - EXPECT_EQ("file", GetParam(command1, "file_pattern")); - - PbCommand command2; - EXPECT_TRUE(SetCommandParams(command2, ":file").empty()); - EXPECT_EQ("", GetParam(command2, "folder_pattern")); - EXPECT_EQ("file", GetParam(command2, "file_pattern")); - - PbCommand command3; - EXPECT_TRUE(SetCommandParams(command3, "file:").empty()); - EXPECT_EQ("file", GetParam(command3, "file_pattern")); - EXPECT_EQ("", GetParam(command3, "folder_pattern")); - - PbCommand command4; - EXPECT_TRUE(SetCommandParams(command4, "folder:file").empty()); - EXPECT_EQ("folder", GetParam(command4, "folder_pattern")); - EXPECT_EQ("file", GetParam(command4, "file_pattern")); - - PbCommand command5; - EXPECT_TRUE(SetCommandParams(command5, "folder:file:").empty()); - EXPECT_EQ("folder", GetParam(command5, "folder_pattern")); - EXPECT_EQ("file", GetParam(command5, "file_pattern")); - - PbCommand command6; - EXPECT_TRUE(SetCommandParams(command6, "folder:file:operations").empty()); - EXPECT_EQ("folder", GetParam(command6, "folder_pattern")); - EXPECT_EQ("file", GetParam(command6, "file_pattern")); - EXPECT_EQ("operations", GetParam(command6, "operations")); - - PbCommand command7; - EXPECT_TRUE(SetCommandParams(command7, "folder:file:operations:unparsed").empty()); - EXPECT_EQ("folder", GetParam(command7, "folder_pattern")); - EXPECT_EQ("file", GetParam(command7, "file_pattern")); - EXPECT_EQ("operations:unparsed", GetParam(command7, "operations")); - - PbCommand command_generic; - EXPECT_TRUE(SetCommandParams(command_generic, "operations=mapping_info:folder_pattern=pattern").empty()); - EXPECT_EQ("mapping_info", GetParam(command_generic, "operations")); - EXPECT_EQ("pattern", GetParam(command_generic, "folder_pattern")); -} - -TEST(ProtobufUtil, SetFromGenericParams) -{ - PbCommand command1; - EXPECT_TRUE(SetFromGenericParams(command1, "operations=mapping_info:folder_pattern=pattern").empty()); - EXPECT_EQ("mapping_info", GetParam(command1, "operations")); - EXPECT_EQ("pattern", GetParam(command1, "folder_pattern")); - - PbCommand command2; - EXPECT_FALSE(SetFromGenericParams(command2, "=mapping_info").empty()); - - PbCommand command3; - EXPECT_FALSE(SetFromGenericParams(command3, "=").empty()); -} - -TEST(ProtobufUtil, GetLunMax) -{ - EXPECT_EQ(32, GetLunMax(SCHD)); - EXPECT_EQ(2, GetLunMax(SAHD)); -} - -TEST(ProtobufUtil, ListDevices) -{ - vector devices; - - EXPECT_FALSE(ListDevices(devices).empty()); - - PbDevice device; - device.set_type(SCHD); - devices.emplace_back(device); - device.set_type(SCDP); - devices.emplace_back(device); - device.set_type(SCHS); - devices.emplace_back(device); - device.set_type(SCLP); - devices.emplace_back(device); - const string device_list = ListDevices(devices); - EXPECT_FALSE(device_list.empty()); -} - -TEST(ProtobufUtil, SetProductData) -{ - PbDeviceDefinition device; - - SetProductData(device, ""); - EXPECT_EQ("", device.vendor()); - EXPECT_EQ("", device.product()); - EXPECT_EQ("", device.revision()); - - SetProductData(device, "vendor"); - EXPECT_EQ("vendor", device.vendor()); - EXPECT_EQ("", device.product()); - EXPECT_EQ("", device.revision()); - - SetProductData(device, "vendor:product"); - EXPECT_EQ("vendor", device.vendor()); - EXPECT_EQ("product", device.product()); - EXPECT_EQ("", device.revision()); - - SetProductData(device, "vendor:product:revision"); - EXPECT_EQ("vendor", device.vendor()); - EXPECT_EQ("product", device.product()); - EXPECT_EQ("revision", device.revision()); -} - -TEST(ProtobufUtil, SetIdAndLun) -{ - PbDeviceDefinition device; - - EXPECT_NE("", SetIdAndLun(device, "")); - EXPECT_EQ("", SetIdAndLun(device, "1")); - EXPECT_EQ(1, device.id()); - EXPECT_EQ("", SetIdAndLun(device, "2:0")); - EXPECT_EQ(2, device.id()); - EXPECT_EQ(0, device.unit()); -} +using namespace testing; TEST(ProtobufUtil, SerializeMessage) { diff --git a/cpp/test/s2pctl_commands_test.cpp b/cpp/test/s2pctl_commands_test.cpp index c54e1534..506ac546 100644 --- a/cpp/test/s2pctl_commands_test.cpp +++ b/cpp/test/s2pctl_commands_test.cpp @@ -9,13 +9,13 @@ //--------------------------------------------------------------------------- #include -#include "protobuf/protobuf_util.h" +#include "protobuf/target_api_util.h" #include "s2pctl/s2pctl_commands.h" #include "shared/s2p_exceptions.h" #include "test_shared.h" using namespace testing; -using namespace protobuf_util; +using namespace target_api_util; TEST(S2pCtlCommandsTest, Execute) { diff --git a/cpp/test/target_api_test.cpp b/cpp/test/target_api_test.cpp new file mode 100644 index 00000000..6361463c --- /dev/null +++ b/cpp/test/target_api_test.cpp @@ -0,0 +1,215 @@ +//--------------------------------------------------------------------------- +// +// SCSI2Pi, SCSI device emulator and SCSI tools for the Raspberry Pi +// +// Copyright (C) 2022-2025 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include "mocks.h" +#include "shared/s2p_exceptions.h" +#include "protobuf/target_api_util.h" + +using namespace target_api_util; + +void TestSpecialDevice(const string &name) +{ + PbDeviceDefinition device; + ParseParameters(device, name); + EXPECT_EQ(name, GetParam(device, "file")); + EXPECT_EQ("", GetParam(device, "interfaces")); +} + +TEST(ProtobufUtil, ParseDeviceType) +{ + EXPECT_EQ(SCCD, ParseDeviceType("sccd")); + EXPECT_EQ(SCDP, ParseDeviceType("scdp")); + EXPECT_EQ(SCHD, ParseDeviceType("schd")); + EXPECT_EQ(SCLP, ParseDeviceType("sclp")); + EXPECT_EQ(SCMO, ParseDeviceType("scmo")); + EXPECT_EQ(SCRM, ParseDeviceType("scrm")); + EXPECT_EQ(SCHS, ParseDeviceType("schs")); + EXPECT_EQ(SCTP, ParseDeviceType("sctp")); + EXPECT_EQ(SCSG, ParseDeviceType("scsg")); + + EXPECT_EQ(SCCD, ParseDeviceType("c")); + EXPECT_EQ(SCDP, ParseDeviceType("d")); + EXPECT_EQ(SCHD, ParseDeviceType("h")); + EXPECT_EQ(SCLP, ParseDeviceType("l")); + EXPECT_EQ(SCMO, ParseDeviceType("m")); + EXPECT_EQ(SCRM, ParseDeviceType("r")); + EXPECT_EQ(SCHS, ParseDeviceType("s")); + EXPECT_EQ(SCTP, ParseDeviceType("t")); + + EXPECT_EQ(UNDEFINED, ParseDeviceType("")); + EXPECT_EQ(UNDEFINED, ParseDeviceType("xyz")); +} + +TEST(ProtobufUtil, ParseCachingMode) +{ + EXPECT_EQ(DEFAULT, ParseCachingMode("default")); + EXPECT_EQ(LINUX, ParseCachingMode("linux")); + EXPECT_EQ(WRITE_THROUGH, ParseCachingMode("write_through")); + EXPECT_EQ(WRITE_THROUGH, ParseCachingMode("write-through")); + EXPECT_EQ(LINUX_OPTIMIZED, ParseCachingMode("linux_optimized")); + EXPECT_EQ(LINUX_OPTIMIZED, ParseCachingMode("linux-optimized")); + + EXPECT_THROW(ParseCachingMode(""), ParserException); + EXPECT_THROW(ParseCachingMode("xyz"), ParserException); +} + +TEST(ProtobufUtil, GetSetParam) +{ + // The implementation is a function template, testing one possible T is sufficient + PbCommand command; + SetParam(command, "key", "value"); + EXPECT_EQ("value", GetParam(command, "key")); + EXPECT_EQ("", GetParam(command, "xyz")); + EXPECT_EQ("", GetParam(command, "")); +} + +TEST(ProtobufUtil, ParseParameters) +{ + PbDeviceDefinition device1; + ParseParameters(device1, "a=b:c=d:e"); + EXPECT_EQ("b", GetParam(device1, "a")); + EXPECT_EQ("d", GetParam(device1, "c")); + EXPECT_EQ("", GetParam(device1, "e")); + + // Old style parameter + PbDeviceDefinition device2; + ParseParameters(device2, "a"); + EXPECT_EQ("a", GetParam(device2, "file")); + + // Ensure that nothing breaks on an empty parameter list + PbDeviceDefinition device3; + ParseParameters(device3, ""); + + TestSpecialDevice("daynaport"); + TestSpecialDevice("printer"); + TestSpecialDevice("services"); +} + +TEST(ProtobufUtil, SetCommandParams) +{ + PbCommand command1; + + EXPECT_TRUE(SetCommandParams(command1, "").empty()); + + EXPECT_TRUE(SetCommandParams(command1, "file").empty()); + EXPECT_EQ("", GetParam(command1, "folder_pattern")); + EXPECT_EQ("file", GetParam(command1, "file_pattern")); + + PbCommand command2; + EXPECT_TRUE(SetCommandParams(command2, ":file").empty()); + EXPECT_EQ("", GetParam(command2, "folder_pattern")); + EXPECT_EQ("file", GetParam(command2, "file_pattern")); + + PbCommand command3; + EXPECT_TRUE(SetCommandParams(command3, "file:").empty()); + EXPECT_EQ("file", GetParam(command3, "file_pattern")); + EXPECT_EQ("", GetParam(command3, "folder_pattern")); + + PbCommand command4; + EXPECT_TRUE(SetCommandParams(command4, "folder:file").empty()); + EXPECT_EQ("folder", GetParam(command4, "folder_pattern")); + EXPECT_EQ("file", GetParam(command4, "file_pattern")); + + PbCommand command5; + EXPECT_TRUE(SetCommandParams(command5, "folder:file:").empty()); + EXPECT_EQ("folder", GetParam(command5, "folder_pattern")); + EXPECT_EQ("file", GetParam(command5, "file_pattern")); + + PbCommand command6; + EXPECT_TRUE(SetCommandParams(command6, "folder:file:operations").empty()); + EXPECT_EQ("folder", GetParam(command6, "folder_pattern")); + EXPECT_EQ("file", GetParam(command6, "file_pattern")); + EXPECT_EQ("operations", GetParam(command6, "operations")); + + PbCommand command7; + EXPECT_TRUE(SetCommandParams(command7, "folder:file:operations:unparsed").empty()); + EXPECT_EQ("folder", GetParam(command7, "folder_pattern")); + EXPECT_EQ("file", GetParam(command7, "file_pattern")); + EXPECT_EQ("operations:unparsed", GetParam(command7, "operations")); + + PbCommand command_generic; + EXPECT_TRUE(SetCommandParams(command_generic, "operations=mapping_info:folder_pattern=pattern").empty()); + EXPECT_EQ("mapping_info", GetParam(command_generic, "operations")); + EXPECT_EQ("pattern", GetParam(command_generic, "folder_pattern")); +} + +TEST(ProtobufUtil, SetFromGenericParams) +{ + PbCommand command1; + EXPECT_TRUE(SetFromGenericParams(command1, "operations=mapping_info:folder_pattern=pattern").empty()); + EXPECT_EQ("mapping_info", GetParam(command1, "operations")); + EXPECT_EQ("pattern", GetParam(command1, "folder_pattern")); + + PbCommand command2; + EXPECT_FALSE(SetFromGenericParams(command2, "=mapping_info").empty()); + + PbCommand command3; + EXPECT_FALSE(SetFromGenericParams(command3, "=").empty()); +} + +TEST(ProtobufUtil, GetLunMax) +{ + EXPECT_EQ(32, GetLunMax(SCHD)); + EXPECT_EQ(2, GetLunMax(SAHD)); +} + +TEST(ProtobufUtil, ListDevices) +{ + vector devices; + + EXPECT_FALSE(ListDevices(devices).empty()); + + PbDevice device; + device.set_type(SCHD); + devices.emplace_back(device); + device.set_type(SCDP); + devices.emplace_back(device); + device.set_type(SCHS); + devices.emplace_back(device); + device.set_type(SCLP); + devices.emplace_back(device); + const string device_list = ListDevices(devices); + EXPECT_FALSE(device_list.empty()); +} + +TEST(ProtobufUtil, SetProductData) +{ + PbDeviceDefinition device; + + SetProductData(device, ""); + EXPECT_EQ("", device.vendor()); + EXPECT_EQ("", device.product()); + EXPECT_EQ("", device.revision()); + + SetProductData(device, "vendor"); + EXPECT_EQ("vendor", device.vendor()); + EXPECT_EQ("", device.product()); + EXPECT_EQ("", device.revision()); + + SetProductData(device, "vendor:product"); + EXPECT_EQ("vendor", device.vendor()); + EXPECT_EQ("product", device.product()); + EXPECT_EQ("", device.revision()); + + SetProductData(device, "vendor:product:revision"); + EXPECT_EQ("vendor", device.vendor()); + EXPECT_EQ("product", device.product()); + EXPECT_EQ("revision", device.revision()); +} + +TEST(ProtobufUtil, SetIdAndLun) +{ + PbDeviceDefinition device; + + EXPECT_NE("", SetIdAndLun(device, "")); + EXPECT_EQ("", SetIdAndLun(device, "1")); + EXPECT_EQ(1, device.id()); + EXPECT_EQ("", SetIdAndLun(device, "2:0")); + EXPECT_EQ(2, device.id()); + EXPECT_EQ(0, device.unit()); +}