kopia lustrzana https://gitlab.com/sane-project/backends
genesys: Add a way to record backend internals using test scanner iface
rodzic
95366b1f1b
commit
eadfdb57a4
|
@ -520,6 +520,7 @@ libgenesys_la_SOURCES = genesys/genesys.cpp genesys/genesys.h \
|
|||
genesys/tables_motor_profile.cpp \
|
||||
genesys/tables_sensor.cpp \
|
||||
genesys/test_scanner_interface.h genesys/test_scanner_interface.cpp \
|
||||
genesys/test_settings.h genesys/test_settings.cpp \
|
||||
genesys/test_usb_device.h genesys/test_usb_device.cpp \
|
||||
genesys/usb_device.h genesys/usb_device.cpp \
|
||||
genesys/low.cpp genesys/low.h \
|
||||
|
|
|
@ -65,6 +65,8 @@
|
|||
#include "usb_device.h"
|
||||
#include "utilities.h"
|
||||
#include "scanner_interface_usb.h"
|
||||
#include "test_scanner_interface.h"
|
||||
#include "test_settings.h"
|
||||
#include "../include/sane/sanei_config.h"
|
||||
#include "../include/sane/sanei_magic.h"
|
||||
|
||||
|
@ -4424,9 +4426,15 @@ config_attach_genesys(SANEI_Config __sane_unused__ *config, const char *devname)
|
|||
}
|
||||
|
||||
/* probes for scanner to attach to the backend */
|
||||
static void probe_genesys_devices (void)
|
||||
static void probe_genesys_devices()
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
if (is_testing_mode()) {
|
||||
attach_usb_device(get_testing_device_name().c_str(),
|
||||
get_testing_vendor_id(), get_testing_product_id());
|
||||
return;
|
||||
}
|
||||
|
||||
SANEI_Config config;
|
||||
|
||||
// set configuration options structure : no option for this backend
|
||||
|
@ -4630,19 +4638,23 @@ void sane_init_impl(SANE_Int * version_code, SANE_Auth_Callback authorize)
|
|||
DBG_INIT ();
|
||||
DBG_HELPER_ARGS(dbg, "authorize %s null", authorize ? "!=" : "==");
|
||||
DBG(DBG_init, "SANE Genesys backend from %s\n", PACKAGE_STRING);
|
||||
|
||||
if (!is_testing_mode()) {
|
||||
#ifdef HAVE_LIBUSB
|
||||
DBG(DBG_init, "SANE Genesys backend built with libusb-1.0\n");
|
||||
DBG(DBG_init, "SANE Genesys backend built with libusb-1.0\n");
|
||||
#endif
|
||||
#ifdef HAVE_LIBUSB_LEGACY
|
||||
DBG(DBG_init, "SANE Genesys backend built with libusb\n");
|
||||
DBG(DBG_init, "SANE Genesys backend built with libusb\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (version_code) {
|
||||
*version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0);
|
||||
}
|
||||
|
||||
/* init usb use */
|
||||
sanei_usb_init ();
|
||||
if (!is_testing_mode()) {
|
||||
sanei_usb_init();
|
||||
}
|
||||
|
||||
/* init sanei_magic */
|
||||
sanei_magic_init();
|
||||
|
@ -4686,7 +4698,9 @@ sane_exit_impl(void)
|
|||
{
|
||||
DBG_HELPER(dbg);
|
||||
|
||||
sanei_usb_exit();
|
||||
if (!is_testing_mode()) {
|
||||
sanei_usb_exit();
|
||||
}
|
||||
|
||||
run_functions_at_backend_exit();
|
||||
}
|
||||
|
@ -4701,9 +4715,11 @@ void sane_get_devices_impl(const SANE_Device *** device_list, SANE_Bool local_on
|
|||
{
|
||||
DBG_HELPER_ARGS(dbg, "local_only = %s", local_only ? "true" : "false");
|
||||
|
||||
/* hot-plug case : detection of newly connected scanners */
|
||||
sanei_usb_scan_devices ();
|
||||
probe_genesys_devices ();
|
||||
if (!is_testing_mode()) {
|
||||
// hot-plug case : detection of newly connected scanners */
|
||||
sanei_usb_scan_devices();
|
||||
}
|
||||
probe_genesys_devices();
|
||||
|
||||
s_sane_devices->clear();
|
||||
s_sane_devices_data->clear();
|
||||
|
@ -4713,8 +4729,14 @@ void sane_get_devices_impl(const SANE_Device *** device_list, SANE_Bool local_on
|
|||
s_sane_devices_ptrs->reserve(s_devices->size() + 1);
|
||||
|
||||
for (auto dev_it = s_devices->begin(); dev_it != s_devices->end();) {
|
||||
present = false;
|
||||
sanei_usb_find_devices(dev_it->vendorId, dev_it->productId, check_present);
|
||||
|
||||
if (is_testing_mode()) {
|
||||
present = true;
|
||||
} else {
|
||||
present = false;
|
||||
sanei_usb_find_devices(dev_it->vendorId, dev_it->productId, check_present);
|
||||
}
|
||||
|
||||
if (present) {
|
||||
s_sane_devices->emplace_back();
|
||||
s_sane_devices_data->emplace_back();
|
||||
|
@ -4753,8 +4775,7 @@ static void sane_open_impl(SANE_String_Const devicename, SANE_Handle * handle)
|
|||
/* devicename="" or devicename="genesys" are default values that use
|
||||
* first available device
|
||||
*/
|
||||
if (devicename[0] && strcmp ("genesys", devicename) != 0)
|
||||
{
|
||||
if (devicename[0] && strcmp ("genesys", devicename) != 0) {
|
||||
/* search for the given devicename in the device list */
|
||||
for (auto& d : *s_devices) {
|
||||
if (d.file_name == devicename) {
|
||||
|
@ -4763,14 +4784,16 @@ static void sane_open_impl(SANE_String_Const devicename, SANE_Handle * handle)
|
|||
}
|
||||
}
|
||||
|
||||
if (!dev) {
|
||||
if (dev) {
|
||||
DBG(DBG_info, "%s: found `%s' in devlist\n", __func__, dev->model->name);
|
||||
} else if (is_testing_mode()) {
|
||||
DBG(DBG_info, "%s: couldn't find `%s' in devlist, not attaching", __func__, devicename);
|
||||
} else {
|
||||
DBG(DBG_info, "%s: couldn't find `%s' in devlist, trying attach\n", __func__,
|
||||
devicename);
|
||||
dbg.status("attach_device_by_name");
|
||||
dev = attach_device_by_name(devicename, true);
|
||||
dbg.clear();
|
||||
} else {
|
||||
DBG(DBG_info, "%s: found `%s' in devlist\n", __func__, dev->model->name);
|
||||
}
|
||||
} else {
|
||||
// empty devicename or "genesys" -> use first device
|
||||
|
@ -4795,7 +4818,14 @@ static void sane_open_impl(SANE_String_Const devicename, SANE_Handle * handle)
|
|||
}
|
||||
|
||||
dbg.vstatus("open device '%s'", dev->file_name.c_str());
|
||||
dev->interface = std::unique_ptr<ScannerInterfaceUsb>{new ScannerInterfaceUsb{dev}};
|
||||
|
||||
if (is_testing_mode()) {
|
||||
auto interface = std::unique_ptr<TestScannerInterface>{new TestScannerInterface{dev}};
|
||||
interface->set_checkpoint_callback(get_testing_checkpoint_callback());
|
||||
dev->interface = std::move(interface);
|
||||
} else {
|
||||
dev->interface = std::unique_ptr<ScannerInterfaceUsb>{new ScannerInterfaceUsb{dev}};
|
||||
}
|
||||
dev->interface->get_usb_device().open(dev->file_name.c_str());
|
||||
dbg.clear();
|
||||
|
||||
|
@ -4890,7 +4920,7 @@ sane_close_impl(SANE_Handle handle)
|
|||
s->dev->cmd_set->save_power(s->dev, true);
|
||||
|
||||
// here is the place to store calibration cache
|
||||
if (s->dev->force_calibration == 0) {
|
||||
if (s->dev->force_calibration == 0 && !is_testing_mode()) {
|
||||
catch_all_exceptions(__func__, [&](){ write_calibration(s->dev->calibration_cache,
|
||||
s->dev->calib_file); });
|
||||
}
|
||||
|
|
|
@ -216,9 +216,9 @@ struct Genesys_USB_Device_Entry {
|
|||
{}
|
||||
|
||||
// USB vendor identifier
|
||||
unsigned vendor;
|
||||
std::uint16_t vendor;
|
||||
// USB product identifier
|
||||
unsigned product;
|
||||
std::uint16_t product;
|
||||
// Scanner model information
|
||||
Genesys_Model model;
|
||||
};
|
||||
|
|
|
@ -100,6 +100,8 @@ public:
|
|||
virtual void record_progress_message(const char* msg) = 0;
|
||||
|
||||
virtual void record_key_value(const std::string& key, const std::string& value) = 0;
|
||||
|
||||
virtual void test_checkpoint(const std::string& name) = 0;
|
||||
};
|
||||
|
||||
} // namespace genesys
|
||||
|
|
|
@ -500,4 +500,9 @@ void ScannerInterfaceUsb::record_key_value(const std::string& key, const std::st
|
|||
(void) value;
|
||||
}
|
||||
|
||||
void ScannerInterfaceUsb::test_checkpoint(const std::string& name)
|
||||
{
|
||||
(void) name;
|
||||
}
|
||||
|
||||
} // namespace genesys
|
||||
|
|
|
@ -84,6 +84,8 @@ public:
|
|||
|
||||
void record_key_value(const std::string& key, const std::string& value) override;
|
||||
|
||||
void test_checkpoint(const std::string& name) override;
|
||||
|
||||
private:
|
||||
Genesys_Device* dev_;
|
||||
UsbDevice usb_dev_;
|
||||
|
|
|
@ -44,10 +44,56 @@
|
|||
#define DEBUG_DECLARE_ONLY
|
||||
|
||||
#include "test_scanner_interface.h"
|
||||
#include "device.h"
|
||||
#include <cstring>
|
||||
|
||||
namespace genesys {
|
||||
|
||||
TestScannerInterface::TestScannerInterface(Genesys_Device* dev) : dev_{dev}
|
||||
{
|
||||
// initialize status registers
|
||||
if (dev_->model->asic_type == AsicType::GL124) {
|
||||
write_register(0x101, 0x00);
|
||||
} else {
|
||||
write_register(0x41, 0x00);
|
||||
}
|
||||
if (dev_->model->asic_type == AsicType::GL841 ||
|
||||
dev_->model->asic_type == AsicType::GL843 ||
|
||||
dev_->model->asic_type == AsicType::GL845 ||
|
||||
dev_->model->asic_type == AsicType::GL846 ||
|
||||
dev_->model->asic_type == AsicType::GL847)
|
||||
{
|
||||
write_register(0x40, 0x00);
|
||||
}
|
||||
|
||||
// initialize other registers that we read on init
|
||||
if (dev_->model->asic_type == AsicType::GL124) {
|
||||
write_register(0x33, 0x00);
|
||||
write_register(0xbd, 0x00);
|
||||
write_register(0xbe, 0x00);
|
||||
write_register(0x100, 0x00);
|
||||
}
|
||||
|
||||
if (dev_->model->asic_type == AsicType::GL845 ||
|
||||
dev_->model->asic_type == AsicType::GL846 ||
|
||||
dev_->model->asic_type == AsicType::GL847)
|
||||
{
|
||||
write_register(0xbd, 0x00);
|
||||
write_register(0xbe, 0x00);
|
||||
|
||||
write_register(0xd0, 0x00);
|
||||
write_register(0xd1, 0x01);
|
||||
write_register(0xd2, 0x02);
|
||||
write_register(0xd3, 0x03);
|
||||
write_register(0xd4, 0x04);
|
||||
write_register(0xd5, 0x05);
|
||||
write_register(0xd6, 0x06);
|
||||
write_register(0xd7, 0x07);
|
||||
write_register(0xd8, 0x08);
|
||||
write_register(0xd9, 0x09);
|
||||
}
|
||||
}
|
||||
|
||||
TestScannerInterface::~TestScannerInterface() = default;
|
||||
|
||||
bool TestScannerInterface::is_mock() const
|
||||
|
@ -157,4 +203,16 @@ std::map<std::string, std::string>& TestScannerInterface::recorded_key_values()
|
|||
return key_values_;
|
||||
}
|
||||
|
||||
void TestScannerInterface::test_checkpoint(const std::string& name)
|
||||
{
|
||||
if (checkpoint_callback_) {
|
||||
checkpoint_callback_(*dev_, *this, name);
|
||||
}
|
||||
}
|
||||
|
||||
void TestScannerInterface::set_checkpoint_callback(TestCheckpointCallback callback)
|
||||
{
|
||||
checkpoint_callback_ = callback;
|
||||
}
|
||||
|
||||
} // namespace genesys
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "scanner_interface.h"
|
||||
#include "register_cache.h"
|
||||
#include "test_usb_device.h"
|
||||
#include "test_settings.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
|
@ -55,10 +56,15 @@ namespace genesys {
|
|||
class TestScannerInterface : public ScannerInterface
|
||||
{
|
||||
public:
|
||||
TestScannerInterface(Genesys_Device* dev);
|
||||
|
||||
~TestScannerInterface() override;
|
||||
|
||||
bool is_mock() const override;
|
||||
|
||||
const RegisterCache<std::uint8_t>& cached_regs() const { return cached_regs_; }
|
||||
const RegisterCache<std::uint16_t>& cached_fe_regs() const { return cached_fe_regs_; }
|
||||
|
||||
std::uint8_t read_register(std::uint16_t address) override;
|
||||
void write_register(std::uint16_t address, std::uint8_t value) override;
|
||||
void write_registers(const Genesys_Register_Set& regs) override;
|
||||
|
@ -88,10 +94,18 @@ public:
|
|||
|
||||
std::map<std::string, std::string>& recorded_key_values();
|
||||
|
||||
void test_checkpoint(const std::string& name) override;
|
||||
|
||||
void set_checkpoint_callback(TestCheckpointCallback callback);
|
||||
|
||||
private:
|
||||
Genesys_Device* dev_;
|
||||
|
||||
RegisterCache<std::uint8_t> cached_regs_;
|
||||
RegisterCache<std::uint16_t> cached_fe_regs_;
|
||||
TestUsbDevice usb_dev_;
|
||||
|
||||
TestCheckpointCallback checkpoint_callback_;
|
||||
std::string last_progress_message_;
|
||||
std::map<std::string, std::string> key_values_;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
/* sane - Scanner Access Now Easy.
|
||||
|
||||
Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
|
||||
|
||||
This file is part of the SANE package.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA.
|
||||
|
||||
As a special exception, the authors of SANE give permission for
|
||||
additional uses of the libraries contained in this release of SANE.
|
||||
|
||||
The exception is that, if you link a SANE library with other files
|
||||
to produce an executable, this does not by itself cause the
|
||||
resulting executable to be covered by the GNU General Public
|
||||
License. Your use of that executable is in no way restricted on
|
||||
account of linking the SANE library code into it.
|
||||
|
||||
This exception does not, however, invalidate any other reasons why
|
||||
the executable file might be covered by the GNU General Public
|
||||
License.
|
||||
|
||||
If you submit changes to SANE to the maintainers to be included in
|
||||
a subsequent release, you agree by submitting the changes that
|
||||
those changes may be distributed with this exception intact.
|
||||
|
||||
If you write modifications of your own for SANE, it is your choice
|
||||
whether to permit this exception to apply to your modifications.
|
||||
If you do not wish that, delete this exception notice.
|
||||
*/
|
||||
|
||||
#define DEBUG_DECLARE_ONLY
|
||||
|
||||
#include "test_settings.h"
|
||||
|
||||
namespace genesys {
|
||||
|
||||
namespace {
|
||||
|
||||
bool s_testing_mode = false;
|
||||
std::uint16_t s_vendor_id = 0;
|
||||
std::uint16_t s_product_id = 0;
|
||||
TestCheckpointCallback s_checkpoint_callback;
|
||||
|
||||
} // namespace
|
||||
|
||||
bool is_testing_mode()
|
||||
{
|
||||
return s_testing_mode;
|
||||
}
|
||||
|
||||
void disable_testing_mode()
|
||||
{
|
||||
s_testing_mode = false;
|
||||
s_vendor_id = 0;
|
||||
s_product_id = 0;
|
||||
|
||||
}
|
||||
|
||||
void enable_testing_mode(std::uint16_t vendor_id, std::uint16_t product_id,
|
||||
TestCheckpointCallback checkpoint_callback)
|
||||
{
|
||||
s_testing_mode = true;
|
||||
s_vendor_id = vendor_id;
|
||||
s_product_id = product_id;
|
||||
s_checkpoint_callback = checkpoint_callback;
|
||||
}
|
||||
|
||||
std::uint16_t get_testing_vendor_id()
|
||||
{
|
||||
return s_vendor_id;
|
||||
}
|
||||
|
||||
std::uint16_t get_testing_product_id()
|
||||
{
|
||||
return s_product_id;
|
||||
}
|
||||
|
||||
std::string get_testing_device_name()
|
||||
{
|
||||
std::string name;
|
||||
unsigned max_size = 50;
|
||||
name.resize(max_size);
|
||||
name.resize(std::snprintf(&name.front(), max_size, "test device:0x%04x:0x%04x",
|
||||
s_vendor_id, s_product_id));
|
||||
return name;
|
||||
}
|
||||
|
||||
TestCheckpointCallback get_testing_checkpoint_callback()
|
||||
{
|
||||
return s_checkpoint_callback;
|
||||
}
|
||||
|
||||
} // namespace genesys
|
|
@ -0,0 +1,70 @@
|
|||
/* sane - Scanner Access Now Easy.
|
||||
|
||||
Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
|
||||
|
||||
This file is part of the SANE package.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA.
|
||||
|
||||
As a special exception, the authors of SANE give permission for
|
||||
additional uses of the libraries contained in this release of SANE.
|
||||
|
||||
The exception is that, if you link a SANE library with other files
|
||||
to produce an executable, this does not by itself cause the
|
||||
resulting executable to be covered by the GNU General Public
|
||||
License. Your use of that executable is in no way restricted on
|
||||
account of linking the SANE library code into it.
|
||||
|
||||
This exception does not, however, invalidate any other reasons why
|
||||
the executable file might be covered by the GNU General Public
|
||||
License.
|
||||
|
||||
If you submit changes to SANE to the maintainers to be included in
|
||||
a subsequent release, you agree by submitting the changes that
|
||||
those changes may be distributed with this exception intact.
|
||||
|
||||
If you write modifications of your own for SANE, it is your choice
|
||||
whether to permit this exception to apply to your modifications.
|
||||
If you do not wish that, delete this exception notice.
|
||||
*/
|
||||
|
||||
#ifndef BACKEND_GENESYS_TEST_SETTINGS_H
|
||||
#define BACKEND_GENESYS_TEST_SETTINGS_H
|
||||
|
||||
#include "scanner_interface.h"
|
||||
#include "register_cache.h"
|
||||
#include "test_usb_device.h"
|
||||
#include <functional>
|
||||
|
||||
namespace genesys {
|
||||
|
||||
using TestCheckpointCallback = std::function<void(const Genesys_Device&,
|
||||
TestScannerInterface&,
|
||||
const std::string&)>;
|
||||
|
||||
bool is_testing_mode();
|
||||
void disable_testing_mode();
|
||||
void enable_testing_mode(std::uint16_t vendor_id, std::uint16_t product_id,
|
||||
TestCheckpointCallback checkpoint_callback);
|
||||
std::uint16_t get_testing_vendor_id();
|
||||
std::uint16_t get_testing_product_id();
|
||||
std::string get_testing_device_name();
|
||||
TestCheckpointCallback get_testing_checkpoint_callback();
|
||||
|
||||
|
||||
} // namespace genesys
|
||||
|
||||
#endif // BACKEND_GENESYS_TEST_SETTINGS_H
|
|
@ -13,13 +13,13 @@ TEST_LDADD = \
|
|||
../../../backend/sane_strstatus.lo \
|
||||
$(MATH_LIB) $(USB_LIBS) $(XML_LIBS) $(PTHREAD_LIBS)
|
||||
|
||||
check_PROGRAMS = genesys_tests
|
||||
TESTS = $(check_PROGRAMS)
|
||||
check_PROGRAMS = genesys_unit_tests genesys_session_config_tests
|
||||
TESTS = genesys_unit_tests
|
||||
|
||||
AM_CPPFLAGS += -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include $(USB_CFLAGS) \
|
||||
-DBACKEND_NAME=genesys
|
||||
-DBACKEND_NAME=genesys -DTESTSUITE_BACKEND_GENESYS_SRCDIR=$(srcdir)
|
||||
|
||||
genesys_tests_SOURCES = tests.cpp tests.h \
|
||||
genesys_unit_tests_SOURCES = tests.cpp tests.h \
|
||||
minigtest.cpp minigtest.h tests_printers.h \
|
||||
tests_calibration.cpp \
|
||||
tests_image.cpp \
|
||||
|
@ -27,4 +27,8 @@ genesys_tests_SOURCES = tests.cpp tests.h \
|
|||
tests_row_buffer.cpp \
|
||||
tests_utilities.cpp
|
||||
|
||||
genesys_tests_LDADD = $(TEST_LDADD)
|
||||
genesys_unit_tests_LDADD = $(TEST_LDADD)
|
||||
|
||||
genesys_session_config_tests_SOURCES = session_config_test.cpp
|
||||
|
||||
genesys_session_config_tests_LDADD = $(TEST_LDADD)
|
||||
|
|
|
@ -0,0 +1,490 @@
|
|||
/* sane - Scanner Access Now Easy.
|
||||
|
||||
Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
|
||||
|
||||
This file is part of the SANE package.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#define DEBUG_DECLARE_ONLY
|
||||
|
||||
#include "../../../backend/genesys/device.h"
|
||||
#include "../../../backend/genesys/enums.h"
|
||||
#include "../../../backend/genesys/error.h"
|
||||
#include "../../../backend/genesys/low.h"
|
||||
#include "../../../backend/genesys/genesys.h"
|
||||
#include "../../../backend/genesys/test_settings.h"
|
||||
#include "../../../backend/genesys/test_scanner_interface.h"
|
||||
#include "../../../backend/genesys/utilities.h"
|
||||
#include "../../include/sane/saneopts.h"
|
||||
#include "sys/stat.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
#define XSTR(s) STR(s)
|
||||
#define STR(s) #s
|
||||
#define CURR_SRCDIR XSTR(TESTSUITE_BACKEND_GENESYS_SRCDIR)
|
||||
|
||||
struct TestConfig
|
||||
{
|
||||
std::uint16_t vendor_id = 0;
|
||||
std::uint16_t product_id = 0;
|
||||
std::string model_name;
|
||||
genesys::ScanMethod method = genesys::ScanMethod::FLATBED;
|
||||
genesys::ScanColorMode color_mode = genesys::ScanColorMode::COLOR_SINGLE_PASS;
|
||||
unsigned depth = 0;
|
||||
unsigned resolution = 0;
|
||||
|
||||
std::string name() const
|
||||
{
|
||||
std::stringstream out;
|
||||
out << "capture_" << model_name
|
||||
<< '_' << method
|
||||
<< '_' << color_mode
|
||||
<< "_depth" << depth
|
||||
<< "_dpi" << resolution;
|
||||
return out.str();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class SaneOptions
|
||||
{
|
||||
public:
|
||||
void fetch(SANE_Handle handle)
|
||||
{
|
||||
handle_ = handle;
|
||||
options_.resize(1);
|
||||
options_[0] = fetch_option(0);
|
||||
|
||||
if (std::strcmp(options_[0].name, SANE_NAME_NUM_OPTIONS) != 0 ||
|
||||
options_[0].type != SANE_TYPE_INT)
|
||||
{
|
||||
throw std::runtime_error("Expected option number option");
|
||||
}
|
||||
int option_count = 0;
|
||||
TIE(sane_control_option(handle, 0, SANE_ACTION_GET_VALUE, &option_count, nullptr));
|
||||
|
||||
options_.resize(option_count);
|
||||
for (int i = 0; i < option_count; ++i) {
|
||||
options_[i] = fetch_option(i);
|
||||
}
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
handle_ = nullptr;
|
||||
}
|
||||
|
||||
bool get_value_bool(const std::string& name) const
|
||||
{
|
||||
auto i = find_option(name, SANE_TYPE_BOOL);
|
||||
int value = 0;
|
||||
TIE(sane_control_option(handle_, i, SANE_ACTION_GET_VALUE, &value, nullptr));
|
||||
return value;
|
||||
}
|
||||
|
||||
void set_value_bool(const std::string& name, bool value)
|
||||
{
|
||||
auto i = find_option(name, SANE_TYPE_BOOL);
|
||||
int value_int = value;
|
||||
TIE(sane_control_option(handle_, i, SANE_ACTION_SET_VALUE, &value_int, nullptr));
|
||||
}
|
||||
|
||||
bool get_value_button(const std::string& name) const
|
||||
{
|
||||
auto i = find_option(name, SANE_TYPE_BUTTON);
|
||||
int value = 0;
|
||||
TIE(sane_control_option(handle_, i, SANE_ACTION_GET_VALUE, &value, nullptr));
|
||||
return value;
|
||||
}
|
||||
|
||||
void set_value_button(const std::string& name, bool value)
|
||||
{
|
||||
auto i = find_option(name, SANE_TYPE_BUTTON);
|
||||
int value_int = value;
|
||||
TIE(sane_control_option(handle_, i, SANE_ACTION_SET_VALUE, &value_int, nullptr));
|
||||
}
|
||||
|
||||
int get_value_int(const std::string& name) const
|
||||
{
|
||||
auto i = find_option(name, SANE_TYPE_INT);
|
||||
int value = 0;
|
||||
TIE(sane_control_option(handle_, i, SANE_ACTION_GET_VALUE, &value, nullptr));
|
||||
return value;
|
||||
}
|
||||
|
||||
void set_value_int(const std::string& name, int value)
|
||||
{
|
||||
auto i = find_option(name, SANE_TYPE_INT);
|
||||
TIE(sane_control_option(handle_, i, SANE_ACTION_SET_VALUE, &value, nullptr));
|
||||
}
|
||||
|
||||
float get_value_float(const std::string& name) const
|
||||
{
|
||||
auto i = find_option(name, SANE_TYPE_FIXED);
|
||||
int value = 0;
|
||||
TIE(sane_control_option(handle_, i, SANE_ACTION_GET_VALUE, &value, nullptr));
|
||||
return static_cast<float>(SANE_UNFIX(value));
|
||||
}
|
||||
|
||||
void set_value_float(const std::string& name, float value)
|
||||
{
|
||||
auto i = find_option(name, SANE_TYPE_FIXED);
|
||||
int value_int = SANE_FIX(value);
|
||||
TIE(sane_control_option(handle_, i, SANE_ACTION_SET_VALUE, &value_int, nullptr));
|
||||
}
|
||||
|
||||
std::string get_value_string(const std::string& name) const
|
||||
{
|
||||
auto i = find_option(name, SANE_TYPE_STRING);
|
||||
std::string value;
|
||||
value.resize(options_[i].size + 1);
|
||||
TIE(sane_control_option(handle_, i, SANE_ACTION_GET_VALUE, &value.front(), nullptr));
|
||||
value.resize(std::strlen(&value.front()));
|
||||
return value;
|
||||
}
|
||||
|
||||
void set_value_string(const std::string& name, const std::string& value)
|
||||
{
|
||||
auto i = find_option(name, SANE_TYPE_STRING);
|
||||
TIE(sane_control_option(handle_, i, SANE_ACTION_SET_VALUE,
|
||||
const_cast<char*>(&value.front()), nullptr));
|
||||
}
|
||||
|
||||
private:
|
||||
SANE_Option_Descriptor fetch_option(int index)
|
||||
{
|
||||
const auto* option = sane_get_option_descriptor(handle_, index);
|
||||
if (option == nullptr) {
|
||||
throw std::runtime_error("Got nullptr option");
|
||||
}
|
||||
return *option;
|
||||
}
|
||||
|
||||
std::size_t find_option(const std::string& name, SANE_Value_Type type) const
|
||||
{
|
||||
for (std::size_t i = 0; i < options_.size(); ++i) {
|
||||
if (options_[i].name == name) {
|
||||
if (options_[i].type != type) {
|
||||
throw std::runtime_error("Option has incorrect type");
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("Could not find option");
|
||||
}
|
||||
|
||||
SANE_Handle handle_;
|
||||
std::vector<SANE_Option_Descriptor> options_;
|
||||
};
|
||||
|
||||
|
||||
void build_checkpoint(const genesys::Genesys_Device& dev,
|
||||
genesys::TestScannerInterface& iface,
|
||||
const std::string& checkpoint_name,
|
||||
std::stringstream& out)
|
||||
{
|
||||
out << "\n\n================\n"
|
||||
<< "Checkpoint: " << checkpoint_name << "\n"
|
||||
<< "================\n\n"
|
||||
<< "dev: " << genesys::format_indent_braced_list(4, dev) << "\n\n"
|
||||
<< "iface.cached_regs: "
|
||||
<< genesys::format_indent_braced_list(4, iface.cached_regs()) << "\n\n"
|
||||
<< "iface.cached_fe_regs: "
|
||||
<< genesys::format_indent_braced_list(4, iface.cached_fe_regs()) << "\n\n"
|
||||
<< "iface.last_progress_message: " << iface.last_progress_message() << "\n\n";
|
||||
if (iface.recorded_key_values().empty()) {
|
||||
out << "iface.recorded_key_values: []\n";
|
||||
} else {
|
||||
out << "iface.recorded_key_values: {\n";
|
||||
for (const auto& kv : iface.recorded_key_values()) {
|
||||
out << " " << kv.first << " : " << kv.second << '\n';
|
||||
}
|
||||
out << "}\n";
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
void run_single_test_scan(const TestConfig& config, std::stringstream& out)
|
||||
{
|
||||
auto build_checkpoint_wrapper = [&](const genesys::Genesys_Device& dev,
|
||||
genesys::TestScannerInterface& iface,
|
||||
const std::string& checkpoint_name)
|
||||
{
|
||||
build_checkpoint(dev, iface, checkpoint_name, out);
|
||||
};
|
||||
|
||||
genesys::enable_testing_mode(config.vendor_id, config.product_id, build_checkpoint_wrapper);
|
||||
|
||||
SANE_Handle handle;
|
||||
|
||||
TIE(sane_init(nullptr, nullptr));
|
||||
TIE(sane_open(genesys::get_testing_device_name().c_str(), &handle));
|
||||
|
||||
SaneOptions options;
|
||||
options.fetch(handle);
|
||||
|
||||
options.set_value_button("force-calibration", true);
|
||||
options.set_value_string(SANE_NAME_SCAN_SOURCE,
|
||||
genesys::scan_method_to_option_string(config.method));
|
||||
options.set_value_string(SANE_NAME_SCAN_MODE,
|
||||
genesys::scan_color_mode_to_option_string(config.color_mode));
|
||||
if (config.color_mode != genesys::ScanColorMode::LINEART) {
|
||||
options.set_value_int(SANE_NAME_BIT_DEPTH, config.depth);
|
||||
}
|
||||
options.set_value_int(SANE_NAME_SCAN_RESOLUTION, config.resolution);
|
||||
options.close();
|
||||
|
||||
TIE(sane_start(handle));
|
||||
|
||||
SANE_Parameters params;
|
||||
TIE(sane_get_parameters(handle, ¶ms));
|
||||
|
||||
int buffer_size = 1024 * 1024;
|
||||
std::vector<std::uint8_t> buffer;
|
||||
buffer.resize(buffer_size);
|
||||
|
||||
std::uint64_t total_data_size = std::uint64_t(params.bytes_per_line) * params.lines;
|
||||
std::uint64_t total_got_data = 0;
|
||||
|
||||
while (total_got_data < total_data_size) {
|
||||
int ask_len = std::min<std::size_t>(buffer_size, total_data_size - total_got_data);
|
||||
|
||||
int got_data = 0;
|
||||
auto status = sane_read(handle, buffer.data(), ask_len, &got_data);
|
||||
total_got_data += got_data;
|
||||
if (status == SANE_STATUS_EOF) {
|
||||
break;
|
||||
}
|
||||
TIE(status);
|
||||
}
|
||||
|
||||
sane_cancel(handle);
|
||||
sane_close(handle);
|
||||
sane_exit();
|
||||
|
||||
genesys::disable_testing_mode();
|
||||
}
|
||||
|
||||
std::string read_file_to_string(const std::string& path)
|
||||
{
|
||||
std::ifstream in;
|
||||
in.open(path);
|
||||
if (!in.is_open()) {
|
||||
return "";
|
||||
}
|
||||
std::stringstream in_str;
|
||||
in_str << in.rdbuf();
|
||||
return in_str.str();
|
||||
}
|
||||
|
||||
void write_string_to_file(const std::string& path, const std::string& contents)
|
||||
{
|
||||
std::ofstream out;
|
||||
out.open(path);
|
||||
if (!out.is_open()) {
|
||||
throw std::runtime_error("Could not open output file: " + path);
|
||||
}
|
||||
out << contents;
|
||||
out.close();
|
||||
}
|
||||
|
||||
struct TestResult
|
||||
{
|
||||
bool success = true;
|
||||
TestConfig config;
|
||||
std::string failure_message;
|
||||
};
|
||||
|
||||
TestResult perform_single_test(const TestConfig& config, const std::string& check_directory,
|
||||
const std::string& output_directory)
|
||||
{
|
||||
TestResult test_result;
|
||||
test_result.config = config;
|
||||
|
||||
std::stringstream result_output_stream;
|
||||
std::string exception_output;
|
||||
try {
|
||||
run_single_test_scan(config, result_output_stream);
|
||||
} catch (const std::exception& exc) {
|
||||
exception_output = std::string("got exception: ") + typeid(exc).name() +
|
||||
" with message\n" + exc.what() + "\n";
|
||||
test_result.success = false;
|
||||
test_result.failure_message += exception_output;
|
||||
} catch (...) {
|
||||
exception_output = "got unknown exception\n";
|
||||
test_result.success = false;
|
||||
test_result.failure_message += exception_output;
|
||||
}
|
||||
auto result_output = result_output_stream.str();
|
||||
if (!exception_output.empty()) {
|
||||
result_output += "\n\n" + exception_output;
|
||||
}
|
||||
|
||||
auto test_filename = config.name() + ".txt";
|
||||
auto expected_session_path = check_directory + "/" + test_filename;
|
||||
auto current_session_path = output_directory + "/" + test_filename;
|
||||
|
||||
auto expected_output = read_file_to_string(expected_session_path);
|
||||
|
||||
bool has_output = !output_directory.empty();
|
||||
|
||||
if (has_output) {
|
||||
mkdir(output_directory.c_str(), 0777);
|
||||
// note that check_directory and output_directory may be the same, so make sure removal
|
||||
// happens after the expected output has already been read.
|
||||
std::remove(current_session_path.c_str());
|
||||
}
|
||||
|
||||
if (expected_output.empty()) {
|
||||
test_result.failure_message += "the expected data file does not exist\n";
|
||||
test_result.success = false;
|
||||
} else if (expected_output != result_output) {
|
||||
test_result.failure_message += "expected and current output are not equal\n";
|
||||
if (has_output) {
|
||||
test_result.failure_message += "To examine, run:\ndiff -u \"" + current_session_path +
|
||||
"\" \"" + expected_session_path + "\"\n";
|
||||
}
|
||||
test_result.success = false;
|
||||
}
|
||||
|
||||
if (has_output) {
|
||||
write_string_to_file(current_session_path, result_output);
|
||||
}
|
||||
return test_result;
|
||||
}
|
||||
|
||||
std::vector<TestConfig> get_all_test_configs()
|
||||
{
|
||||
genesys::genesys_init_usb_device_tables();
|
||||
|
||||
std::vector<TestConfig> configs;
|
||||
std::unordered_set<std::string> model_names;
|
||||
|
||||
for (const auto& usb_dev : *genesys::s_usb_devices) {
|
||||
if (usb_dev.model.flags & GENESYS_FLAG_UNTESTED) {
|
||||
continue;
|
||||
}
|
||||
if (model_names.find(usb_dev.model.name) != model_names.end()) {
|
||||
continue;
|
||||
}
|
||||
model_names.insert(usb_dev.model.name);
|
||||
|
||||
for (auto scan_mode : { genesys::ScanColorMode::LINEART,
|
||||
genesys::ScanColorMode::GRAY,
|
||||
genesys::ScanColorMode::COLOR_SINGLE_PASS }) {
|
||||
|
||||
auto depth_values = usb_dev.model.bpp_gray_values;
|
||||
if (scan_mode == genesys::ScanColorMode::COLOR_SINGLE_PASS) {
|
||||
depth_values = usb_dev.model.bpp_color_values;
|
||||
}
|
||||
for (unsigned depth : depth_values) {
|
||||
for (auto method_resolutions : usb_dev.model.resolutions) {
|
||||
for (auto method : method_resolutions.methods) {
|
||||
for (unsigned resolution : method_resolutions.get_resolutions()) {
|
||||
TestConfig config;
|
||||
config.vendor_id = usb_dev.vendor;
|
||||
config.product_id = usb_dev.product;
|
||||
config.model_name = usb_dev.model.name;
|
||||
config.method = method;
|
||||
config.depth = depth;
|
||||
config.resolution = resolution;
|
||||
config.color_mode = scan_mode;
|
||||
configs.push_back(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return configs;
|
||||
}
|
||||
|
||||
void print_help()
|
||||
{
|
||||
std::cerr << "Usage:\n"
|
||||
<< "session_config_test [--test={test_name}] {check_directory} [{output_directory}]\n"
|
||||
<< "session_config_test --help\n"
|
||||
<< "session_config_test --print_test_names\n";
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
std::string check_directory;
|
||||
std::string output_directory;
|
||||
std::string test_name_filter;
|
||||
bool print_test_names = false;
|
||||
|
||||
for (int argi = 1; argi < argc; ++argi) {
|
||||
std::string arg = argv[argi];
|
||||
if (arg.rfind("--test=", 0) == 0) {
|
||||
test_name_filter = arg.substr(7);
|
||||
} else if (arg == "-h" || arg == "--help") {
|
||||
print_help();
|
||||
return 0;
|
||||
} else if (arg == "--print_test_names") {
|
||||
print_test_names = true;
|
||||
} else if (check_directory.empty()) {
|
||||
check_directory = arg;
|
||||
} else if (output_directory.empty()) {
|
||||
output_directory = arg;
|
||||
}
|
||||
}
|
||||
|
||||
auto configs = get_all_test_configs();
|
||||
|
||||
if (print_test_names) {
|
||||
for (const auto& config : configs) {
|
||||
std::cout << config.name() << "\n";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (check_directory.empty()) {
|
||||
print_help();
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool test_success = true;
|
||||
for (unsigned i = 0; i < configs.size(); ++i) {
|
||||
const auto& config = configs[i];
|
||||
|
||||
if (!test_name_filter.empty() && config.name() != test_name_filter) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto result = perform_single_test(config, check_directory, output_directory);
|
||||
std::cerr << "(" << i << "/" << configs.size() << "): "
|
||||
<< (result.success ? "SUCCESS: " : "FAIL: ")
|
||||
<< result.config.name() << "\n";
|
||||
if (!result.success) {
|
||||
std::cerr << result.failure_message;
|
||||
}
|
||||
|
||||
test_success &= result.success;
|
||||
}
|
||||
|
||||
if (!test_success) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
Ładowanie…
Reference in New Issue