diff --git a/backend/Makefile.am b/backend/Makefile.am index 70aa5d0f6..c2498ae67 100644 --- a/backend/Makefile.am +++ b/backend/Makefile.am @@ -487,12 +487,12 @@ libgenesys_la_SOURCES = genesys.cc genesys.h genesys_sanei.h genesys_sanei.cc ge genesys_gl847.cc genesys_gl847.h genesys_gl124.cc genesys_gl124.h \ genesys_low.cc genesys_low.h -libgenesys_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=genesys +libgenesys_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=genesys $(XML_CFLAGS) nodist_libsane_genesys_la_SOURCES = genesys-s.cc libsane_genesys_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=genesys libsane_genesys_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) -libsane_genesys_la_LIBADD = $(COMMON_LIBS) libgenesys.la ../sanei/sanei_magic.lo ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS) +libsane_genesys_la_LIBADD = $(COMMON_LIBS) libgenesys.la ../sanei/sanei_magic.lo ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(XML_LIBS) $(RESMGR_LIBS) EXTRA_DIST += genesys.conf.in # TODO: Why are this distributed but not compiled? EXTRA_DIST += genesys_conv.cc genesys_conv_hlp.cc genesys_devices.cc diff --git a/backend/genesys_sanei.cc b/backend/genesys_sanei.cc index 5b5b40aa9..33d645e6f 100644 --- a/backend/genesys_sanei.cc +++ b/backend/genesys_sanei.cc @@ -45,6 +45,10 @@ #include "genesys_sanei.h" +#if WITH_USB_RECORD_REPLAY +#include +#endif + UsbDevice::~UsbDevice() { if (is_open()) { @@ -138,3 +142,131 @@ void UsbDevice::set_not_open() is_open_ = false; name_ = ""; } + +extern "C" { + typedef enum { + sanei_usb_testing_mode_disabled = 0, + sanei_usb_testing_mode_record, + sanei_usb_testing_mode_replay, + } + sanei_usb_testing_mode; + + extern sanei_usb_testing_mode testing_mode; +} // extern "C" + +#if WITH_USB_RECORD_REPLAY + +// from sanei_usb.c +#define FAIL_TEST(func, ...) \ + do { \ + DBG(1, "%s: FAIL: ", func); \ + DBG(1, __VA_ARGS__); \ + fail_test(); \ + } while (0) + +#define FAIL_TEST_TX(func, node, ...) \ + do { \ + sanei_xml_print_seq_if_any(node, func); \ + DBG(1, "%s: FAIL: ", func); \ + DBG(1, __VA_ARGS__); \ + fail_test(); \ + } while (0) + +extern "C" { + void fail_test(); + xmlNode* sanei_xml_get_next_tx_node(); + int sanei_xml_is_known_commands_end(xmlNode* node); + void sanei_xml_print_seq_if_any(xmlNode* node, const char* parent_fun); + void sanei_xml_set_uint_attr(xmlNode* node, const char* attr_name, + unsigned attr_value); + xmlNode* sanei_xml_append_command(xmlNode* sibling, + int indent, xmlNode* e_command); + void sanei_xml_record_seq(xmlNode* node); + void sanei_xml_break_if_needed(xmlNode* node); + int sanei_usb_check_attr(xmlNode* node, const char* attr_name, + const char* expected, const char* parent_fun); + + extern xmlNode* testing_append_commands_node; + extern unsigned testing_last_known_seq; + extern int testing_development_mode; + extern int testing_known_commands_input_failed; +} // extern "C" + +static void sanei_usb_record_debug_msg(xmlNode* node, SANE_String_Const message) +{ + int node_was_null = node == NULL; + if (node_was_null) + node = testing_append_commands_node; + + xmlNode* e_tx = xmlNewNode(NULL, (const xmlChar*)"debug"); + sanei_xml_set_uint_attr(e_tx, "seq", ++testing_last_known_seq); + xmlNewProp(e_tx, (const xmlChar*)"message", (const xmlChar*)message); + + node = sanei_xml_append_command(node, node_was_null, e_tx); + + if (node_was_null) + testing_append_commands_node = node; +} + +static void sanei_usb_record_replace_debug_msg(xmlNode* node, SANE_String_Const message) +{ + if (!testing_development_mode) + return; + + testing_last_known_seq--; + sanei_usb_record_debug_msg(node, message); + xmlUnlinkNode(node); + xmlFreeNode(node); +} + +static void sanei_usb_replay_debug_msg(SANE_String_Const message) +{ + if (testing_known_commands_input_failed) + return; + + xmlNode* node = sanei_xml_get_next_tx_node(); + if (node == NULL) + { + FAIL_TEST(__func__, "no more transactions\n"); + return; + } + + if (sanei_xml_is_known_commands_end(node)) + { + sanei_usb_record_debug_msg(NULL, message); + return; + } + + sanei_xml_record_seq(node); + sanei_xml_break_if_needed(node); + + if (xmlStrcmp(node->name, (const xmlChar*)"debug") != 0) + { + FAIL_TEST_TX(__func__, node, "unexpected transaction type %s\n", + (const char*) node->name); + sanei_usb_record_replace_debug_msg(node, message); + } + + if (!sanei_usb_check_attr(node, "message", message, __func__)) + { + sanei_usb_record_replace_debug_msg(node, message); + } +} + +void sanei_usb_testing_record_message(SANE_String_Const message) +{ + if (testing_mode == sanei_usb_testing_mode_record) + { + sanei_usb_record_debug_msg(NULL, message); + } + if (testing_mode == sanei_usb_testing_mode_replay) + { + sanei_usb_replay_debug_msg(message); + } +} +#else +void sanei_usb_testing_record_message(SANE_String_Const message) +{ + (void) message; +} +#endif diff --git a/backend/genesys_sanei.h b/backend/genesys_sanei.h index 0e4160047..7aafdb0fc 100644 --- a/backend/genesys_sanei.h +++ b/backend/genesys_sanei.h @@ -94,4 +94,6 @@ private: int device_num_ = 0; }; +void sanei_usb_testing_record_message(SANE_String_Const message); + #endif // BACKEND_GENESYS_SANEI_H diff --git a/include/sane/sanei_usb.h b/include/sane/sanei_usb.h index 8492d377e..1c1699d88 100644 --- a/include/sane/sanei_usb.h +++ b/include/sane/sanei_usb.h @@ -224,6 +224,13 @@ extern SANE_String sanei_usb_testing_get_backend(); */ extern SANE_Bool sanei_usb_is_replay_mode_enabled(); +/** Records a debug message in the captured USB data if testing mode is enabled. If testing mode + * is not enabled, this function does nothing. + * + * @param msg Message to record + */ +extern void sanei_usb_testing_record_message(SANE_String_Const message); + /** Initialize sanei_usb. * * Call this before any other sanei_usb function. diff --git a/sanei/sanei_usb.c b/sanei/sanei_usb.c index af672e5ec..2c853482c 100644 --- a/sanei/sanei_usb.c +++ b/sanei/sanei_usb.c @@ -191,10 +191,10 @@ typedef enum sanei_usb_testing_mode; // Whether testing mode has been enabled -static sanei_usb_testing_mode testing_mode = sanei_usb_testing_mode_disabled; +sanei_usb_testing_mode testing_mode = sanei_usb_testing_mode_disabled; #if WITH_USB_RECORD_REPLAY -static int testing_development_mode = 0; +int testing_development_mode = 0; int testing_known_commands_input_failed = 0; unsigned testing_last_known_seq = 0; SANE_String testing_record_backend = NULL; @@ -577,7 +577,7 @@ static int sanei_xml_get_prop_uint(xmlNode* node, const char* name) return attr_uint; } -static void sanei_xml_print_seq_if_any(xmlNode* node, const char* parent_fun) +void sanei_xml_print_seq_if_any(xmlNode* node, const char* parent_fun) { char* attr = sanei_xml_get_prop(node, "seq"); if (attr == NULL) @@ -627,7 +627,7 @@ static int sanei_xml_is_transaction_ignored(xmlNode* node) static xmlNode* sanei_xml_skip_non_tx_nodes(xmlNode* node) { const char* known_node_names[] = { - "control_tx", "bulk_tx", "interrupt_tx", "known_commands_end" + "control_tx", "bulk_tx", "interrupt_tx", "debug", "known_commands_end" }; while (node != NULL) @@ -653,7 +653,7 @@ static xmlNode* sanei_xml_skip_non_tx_nodes(xmlNode* node) return node; } -static int sanei_xml_is_known_commands_end(xmlNode* node) +int sanei_xml_is_known_commands_end(xmlNode* node) { if (!testing_development_mode || node == NULL) return 0; @@ -667,7 +667,7 @@ static xmlNode* sanei_xml_peek_next_tx_node() } // returns next transaction node that is not get_descriptor -static xmlNode* sanei_xml_get_next_tx_node() +xmlNode* sanei_xml_get_next_tx_node() { xmlNode* next = testing_xml_next_tx_node; @@ -802,8 +802,8 @@ static void sanei_xml_set_hex_attr(xmlNode* node, const char* attr_name, xmlNewProp(node, (const xmlChar*)attr_name, (const xmlChar*)buf); } -static void sanei_xml_set_uint_attr(xmlNode* node, const char* attr_name, - unsigned attr_value) +void sanei_xml_set_uint_attr(xmlNode* node, const char* attr_name, + unsigned attr_value) { const int buf_size = 128; char buf[buf_size]; @@ -811,8 +811,8 @@ static void sanei_xml_set_uint_attr(xmlNode* node, const char* attr_name, xmlNewProp(node, (const xmlChar*)attr_name, (const xmlChar*)buf); } -static xmlNode* sanei_xml_append_command(xmlNode* sibling, - int indent, xmlNode* e_command) +xmlNode* sanei_xml_append_command(xmlNode* sibling, + int indent, xmlNode* e_command) { if (indent) { @@ -831,7 +831,7 @@ static void sanei_xml_command_common_props(xmlNode* node, int endpoint_number, xmlNewProp(node, (const xmlChar*)"direction", (const xmlChar*)direction); } -static void sanei_xml_record_seq(xmlNode* node) +void sanei_xml_record_seq(xmlNode* node) { int seq = sanei_xml_get_prop_uint(node, "seq"); if (seq > 0) @@ -842,7 +842,7 @@ static void sanei_xml_break() { } -static void sanei_xml_break_if_needed(xmlNode* node) +void sanei_xml_break_if_needed(xmlNode* node) { char* attr = sanei_xml_get_prop(node, "debug_break"); if (attr != NULL) @@ -853,8 +853,8 @@ static void sanei_xml_break_if_needed(xmlNode* node) } // returns 1 on success -static int sanei_usb_check_attr(xmlNode* node, const char* attr_name, - const char* expected, const char* parent_fun) +int sanei_usb_check_attr(xmlNode* node, const char* attr_name, + const char* expected, const char* parent_fun) { char* attr = sanei_xml_get_prop(node, attr_name); if (attr == NULL)