diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2020-02-02 17:14:32 +0100 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2020-02-02 17:14:32 +0100 |
commit | 5dadc28ea784db1ba1f56c2ea8618d2db67af1c8 (patch) | |
tree | 808b2499b54563b3290f34d70d159b1024310873 /testsuite/backend | |
parent | 5bb4cf12855ec0151de15d6c5a2354ff08766957 (diff) | |
parent | 3dade5db2a37543f19f0967901d8d80a52a1e459 (diff) |
Merge branch 'feature/upstream' into develop
Diffstat (limited to 'testsuite/backend')
-rw-r--r-- | testsuite/backend/genesys/Makefile.am | 26 | ||||
-rw-r--r-- | testsuite/backend/genesys/minigtest.cpp (renamed from testsuite/backend/genesys/minigtest.cc) | 2 | ||||
-rw-r--r-- | testsuite/backend/genesys/minigtest.h | 38 | ||||
-rw-r--r-- | testsuite/backend/genesys/session_config_test.cpp | 503 | ||||
-rw-r--r-- | testsuite/backend/genesys/tests.cpp (renamed from testsuite/backend/genesys/tests.cc) | 9 | ||||
-rw-r--r-- | testsuite/backend/genesys/tests.h | 9 | ||||
-rw-r--r-- | testsuite/backend/genesys/tests_calibration.cpp (renamed from testsuite/backend/genesys/tests_calibration.cc) | 21 | ||||
-rw-r--r-- | testsuite/backend/genesys/tests_image.cpp | 576 | ||||
-rw-r--r-- | testsuite/backend/genesys/tests_image_pipeline.cpp | 519 | ||||
-rw-r--r-- | testsuite/backend/genesys/tests_motor.cpp | 365 | ||||
-rw-r--r-- | testsuite/backend/genesys/tests_printers.h | 62 | ||||
-rw-r--r-- | testsuite/backend/genesys/tests_row_buffer.cpp | 91 | ||||
-rw-r--r-- | testsuite/backend/genesys/tests_utilities.cpp | 110 |
13 files changed, 2313 insertions, 18 deletions
diff --git a/testsuite/backend/genesys/Makefile.am b/testsuite/backend/genesys/Makefile.am index 1332cf8..818a523 100644 --- a/testsuite/backend/genesys/Makefile.am +++ b/testsuite/backend/genesys/Makefile.am @@ -6,18 +6,30 @@ TEST_LDADD = \ ../../../sanei/libsanei.la \ + ../../../sanei/sanei_usb.lo \ + ../../../sanei/sanei_magic.lo \ ../../../lib/liblib.la \ ../../../backend/libgenesys.la \ ../../../backend/sane_strstatus.lo \ - $(MATH_LIB) $(USB_LIBS) $(PTHREAD_LIBS) + $(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.cc tests.h minigtest.cc minigtest.h \ - tests_calibration.cc +genesys_unit_tests_SOURCES = tests.cpp tests.h \ + minigtest.cpp minigtest.h tests_printers.h \ + tests_calibration.cpp \ + tests_image.cpp \ + tests_image_pipeline.cpp \ + tests_motor.cpp \ + 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) diff --git a/testsuite/backend/genesys/minigtest.cc b/testsuite/backend/genesys/minigtest.cpp index 5ca73c2..8afb62a 100644 --- a/testsuite/backend/genesys/minigtest.cc +++ b/testsuite/backend/genesys/minigtest.cpp @@ -22,6 +22,8 @@ #include "minigtest.h" +#define DEBUG_DECLARE_ONLY + size_t s_num_successes = 0; size_t s_num_failures = 0; diff --git a/testsuite/backend/genesys/minigtest.h b/testsuite/backend/genesys/minigtest.h index 752efe1..9a38e77 100644 --- a/testsuite/backend/genesys/minigtest.h +++ b/testsuite/backend/genesys/minigtest.h @@ -38,7 +38,7 @@ inline void print_location(std::ostream& out, const char* function, const char* template<class T, class U> void check_equal(const T& t, const U& u, const char* function, const char* path, unsigned line) { - if (t != u) { + if (!(t == u)) { s_num_failures++; std::cerr << "FAILURE at "; print_location(std::cerr, function, path, line); @@ -64,14 +64,48 @@ inline void check_true(bool x, const char* function, const char* path, unsigned std::cerr << "\n"; } +inline void check_raises_success(const char* function, const char* path, unsigned line) +{ + s_num_successes++; + std::cerr << "SUCCESS at "; + print_location(std::cerr, function, path, line); + std::cerr << "\n"; +} + +inline void check_raises_did_not_raise(const char* function, const char* path, unsigned line) +{ + s_num_failures++; + std::cerr << "FAILURE at "; + print_location(std::cerr, function, path, line); + std::cerr << " : did not raise exception\n"; + +} + +inline void check_raises_raised_unexpected(const char* function, const char* path, unsigned line) +{ + s_num_failures++; + std::cerr << "FAILURE at "; + print_location(std::cerr, function, path, line); + std::cerr << " : unexpected exception raised\n"; +} #define ASSERT_EQ(x, y) do { check_equal((x), (y), __func__, __FILE__, __LINE__); } \ while (false) #define ASSERT_TRUE(x) do { check_true(bool(x), __func__, __FILE__, __LINE__); } \ while (false) -#define ASSERT_FALSE(x) do { !check_true(bool(x), __func__, __FILE__, __LINE__); } \ +#define ASSERT_FALSE(x) do { check_true(!bool(x), __func__, __FILE__, __LINE__); } \ while (false) +#define ASSERT_RAISES(x, T) \ + do { try { \ + x; \ + check_raises_did_not_raise(__func__, __FILE__, __LINE__); \ + } catch (const T&) { \ + check_raises_success(__func__, __FILE__, __LINE__); \ + } catch (...) { \ + check_raises_raised_unexpected(__func__, __FILE__, __LINE__); \ + } } while (false) + int finish_tests(); #endif diff --git a/testsuite/backend/genesys/session_config_test.cpp b/testsuite/backend/genesys/session_config_test.cpp new file mode 100644 index 0000000..72043bb --- /dev/null +++ b/testsuite/backend/genesys/session_config_test.cpp @@ -0,0 +1,503 @@ +/* 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"; + out << "iface.slope_tables: {\n"; + for (const auto& kv : iface.recorded_slope_tables()) { + out << " " << kv.first << ": {"; + for (unsigned i = 0; i < kv.second.size(); ++i) { + if (i % 10 == 0) { + out << "\n "; + } + out << ' ' << kv.second[i]; + } + out << "\n }\n"; + } + out << "}\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"; + } + iface.recorded_key_values().clear(); + 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; +} diff --git a/testsuite/backend/genesys/tests.cc b/testsuite/backend/genesys/tests.cpp index 40b1b3e..5fe0084 100644 --- a/testsuite/backend/genesys/tests.cc +++ b/testsuite/backend/genesys/tests.cpp @@ -20,11 +20,18 @@ MA 02111-1307, USA. */ +#define DEBUG_DECLARE_ONLY + #include "tests.h" #include "minigtest.h" int main() { - test_calibration_parsing(); + genesys::test_calibration_parsing(); + genesys::test_image(); + genesys::test_image_pipeline(); + genesys::test_motor(); + genesys::test_row_buffer(); + genesys::test_utilities(); return finish_tests(); } diff --git a/testsuite/backend/genesys/tests.h b/testsuite/backend/genesys/tests.h index f4e4d2e..c48c586 100644 --- a/testsuite/backend/genesys/tests.h +++ b/testsuite/backend/genesys/tests.h @@ -23,6 +23,15 @@ #ifndef SANE_TESTSUITE_BACKEND_GENESYS_GENESYS_UNIT_TEST_H #define SANE_TESTSUITE_BACKEND_GENESYS_GENESYS_UNIT_TEST_H +namespace genesys { + void test_calibration_parsing(); +void test_image(); +void test_image_pipeline(); +void test_motor(); +void test_row_buffer(); +void test_utilities(); + +} // namespace genesys #endif diff --git a/testsuite/backend/genesys/tests_calibration.cc b/testsuite/backend/genesys/tests_calibration.cpp index 959037a..559f8a8 100644 --- a/testsuite/backend/genesys/tests_calibration.cc +++ b/testsuite/backend/genesys/tests_calibration.cpp @@ -20,28 +20,31 @@ MA 02111-1307, USA. */ +#define DEBUG_DECLARE_ONLY + #include "tests.h" #include "minigtest.h" -#include "../../../backend/genesys_low.h" +#include "../../../backend/genesys/low.h" #include <sstream> -#define DEBUG_DECLARE_ONLY +namespace genesys { Genesys_Calibration_Cache create_fake_calibration_entry() { Genesys_Calibration_Cache calib; - calib.used_setup.pixels = 10020; - calib.used_setup.lines = 150; - calib.used_setup.xres = 100.5; + calib.params.channels = 3; + calib.params.depth = 8; + calib.params.lines = 100; + calib.params.pixels = 200; GenesysFrontendLayout wolfson_layout; wolfson_layout.offset_addr = { 0x20, 0x21, 0x22 }; wolfson_layout.gain_addr = { 0x28, 0x29, 0x2a }; Genesys_Frontend fe; - fe.fe_id = DAC_WOLFSON_UMAX; + fe.id = AdcId::WOLFSON_UMAX; fe.layout = wolfson_layout; fe.regs = { { 0x00, 0x00 }, @@ -62,11 +65,11 @@ Genesys_Calibration_Cache create_fake_calibration_entry() calib.frontend = fe; Genesys_Sensor sensor; - sensor.sensor_id = CCD_UMAX; + sensor.sensor_id = SensorId::CCD_UMAX; sensor.optical_res = 1200; sensor.black_pixels = 48; sensor.dummy_pixel = 64; - sensor.CCD_start_xoffset = 0; + sensor.ccd_start_xoffset = 0; sensor.sensor_pixels = 10800; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 230; @@ -128,3 +131,5 @@ void test_calibration_parsing() { test_calibration_roundtrip(); } + +} // namespace genesys diff --git a/testsuite/backend/genesys/tests_image.cpp b/testsuite/backend/genesys/tests_image.cpp new file mode 100644 index 0000000..bc8b923 --- /dev/null +++ b/testsuite/backend/genesys/tests_image.cpp @@ -0,0 +1,576 @@ +/* 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 "tests.h" +#include "minigtest.h" +#include "tests_printers.h" + +#include "../../../backend/genesys/image.h" +#include "../../../backend/genesys/image_pipeline.h" +#include <vector> + +namespace genesys { + +void test_get_pixel_from_row() +{ + std::vector<std::uint8_t> data = { + 0x12, 0x34, 0x56, 0x67, 0x89, 0xab, + 0xcd, 0xef, 0x21, 0x43, 0x65, 0x87 + }; + ASSERT_EQ(get_pixel_from_row(data.data(), 0, PixelFormat::I1), + Pixel(0, 0, 0)); + ASSERT_EQ(get_pixel_from_row(data.data(), 3, PixelFormat::I1), + Pixel(0xffff, 0xffff, 0xffff)); + ASSERT_EQ(get_pixel_from_row(data.data(), 0, PixelFormat::RGB111), + Pixel(0, 0, 0)); + ASSERT_EQ(get_pixel_from_row(data.data(), 1, PixelFormat::RGB111), + Pixel(0xffff, 0, 0)); + ASSERT_EQ(get_pixel_from_row(data.data(), 2, PixelFormat::RGB111), + Pixel(0xffff, 0, 0)); + ASSERT_EQ(get_pixel_from_row(data.data(), 3, PixelFormat::RGB111), + Pixel(0, 0xffff, 0xffff)); + ASSERT_EQ(get_pixel_from_row(data.data(), 0, PixelFormat::I8), + Pixel(0x1212, 0x1212, 0x1212)); + ASSERT_EQ(get_pixel_from_row(data.data(), 1, PixelFormat::I8), + Pixel(0x3434, 0x3434, 0x3434)); + ASSERT_EQ(get_pixel_from_row(data.data(), 0, PixelFormat::RGB888), + Pixel(0x1212, 0x3434, 0x5656)); + ASSERT_EQ(get_pixel_from_row(data.data(), 1, PixelFormat::RGB888), + Pixel(0x6767, 0x8989, 0xabab)); + ASSERT_EQ(get_pixel_from_row(data.data(), 0, PixelFormat::BGR888), + Pixel(0x5656, 0x3434, 0x1212)); + ASSERT_EQ(get_pixel_from_row(data.data(), 1, PixelFormat::BGR888), + Pixel(0xabab, 0x8989, 0x6767)); + ASSERT_EQ(get_pixel_from_row(data.data(), 0, PixelFormat::I16), + Pixel(0x3412, 0x3412, 0x3412)); + ASSERT_EQ(get_pixel_from_row(data.data(), 1, PixelFormat::I16), + Pixel(0x6756, 0x6756, 0x6756)); + ASSERT_EQ(get_pixel_from_row(data.data(), 0, PixelFormat::RGB161616), + Pixel(0x3412, 0x6756, 0xab89)); + ASSERT_EQ(get_pixel_from_row(data.data(), 1, PixelFormat::RGB161616), + Pixel(0xefcd, 0x4321, 0x8765)); + ASSERT_EQ(get_pixel_from_row(data.data(), 0, PixelFormat::BGR161616), + Pixel(0xab89, 0x6756, 0x3412)); + ASSERT_EQ(get_pixel_from_row(data.data(), 1, PixelFormat::BGR161616), + Pixel(0x8765, 0x4321, 0xefcd)); +} + +void test_set_pixel_to_row() +{ + using Data = std::vector<std::uint8_t>; + Data data; + data.resize(12, 0); + + auto reset = [&]() { std::fill(data.begin(), data.end(), 0); }; + + Pixel pixel; + + pixel = Pixel(0x8000, 0x8000, 0x8000); + set_pixel_to_row(data.data(), 0, pixel, PixelFormat::I1); + ASSERT_EQ(data, Data({0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = Pixel(0x8000, 0x8000, 0x8000); + set_pixel_to_row(data.data(), 2, pixel, PixelFormat::I1); + ASSERT_EQ(data, Data({0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = Pixel(0x8000, 0x8000, 0x8000); + set_pixel_to_row(data.data(), 8, pixel, PixelFormat::I1); + ASSERT_EQ(data, Data({0x00, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = Pixel(0x8000, 0x0000, 0x8000); + set_pixel_to_row(data.data(), 0, pixel, PixelFormat::RGB111); + ASSERT_EQ(data, Data({0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = Pixel(0x8000, 0x0000, 0x8000); + set_pixel_to_row(data.data(), 1, pixel, PixelFormat::RGB111); + ASSERT_EQ(data, Data({0x14, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = Pixel(0x8000, 0x0000, 0x8000); + set_pixel_to_row(data.data(), 8, pixel, PixelFormat::RGB111); + ASSERT_EQ(data, Data({0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = Pixel(0x1200, 0x1200, 0x1200); + set_pixel_to_row(data.data(), 0, pixel, PixelFormat::I8); + ASSERT_EQ(data, Data({0x12, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = Pixel(0x1200, 0x1200, 0x1200); + set_pixel_to_row(data.data(), 2, pixel, PixelFormat::I8); + ASSERT_EQ(data, Data({0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = Pixel(0x1200, 0x3400, 0x5600); + set_pixel_to_row(data.data(), 0, pixel, PixelFormat::RGB888); + ASSERT_EQ(data, Data({0x12, 0x34, 0x56, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = Pixel(0x1200, 0x3400, 0x5600); + set_pixel_to_row(data.data(), 1, pixel, PixelFormat::RGB888); + ASSERT_EQ(data, Data({0x00, 0x00, 0x00, 0x12, 0x34, 0x56, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = Pixel(0x1200, 0x3400, 0x5600); + set_pixel_to_row(data.data(), 0, pixel, PixelFormat::BGR888); + ASSERT_EQ(data, Data({0x56, 0x34, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = Pixel(0x1200, 0x3400, 0x5600); + set_pixel_to_row(data.data(), 1, pixel, PixelFormat::BGR888); + ASSERT_EQ(data, Data({0x00, 0x00, 0x00, 0x56, 0x34, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = Pixel(0x1234, 0x1234, 0x1234); + set_pixel_to_row(data.data(), 0, pixel, PixelFormat::I16); + ASSERT_EQ(data, Data({0x34, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = Pixel(0x1234, 0x1234, 0x1234); + set_pixel_to_row(data.data(), 1, pixel, PixelFormat::I16); + ASSERT_EQ(data, Data({0x00, 0x00, 0x34, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = Pixel(0x1234, 0x5678, 0x9abc); + set_pixel_to_row(data.data(), 0, pixel, PixelFormat::RGB161616); + ASSERT_EQ(data, Data({0x34, 0x12, 0x78, 0x56, 0xbc, 0x9a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = Pixel(0x1234, 0x5678, 0x9abc); + set_pixel_to_row(data.data(), 1, pixel, PixelFormat::RGB161616); + ASSERT_EQ(data, Data({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x34, 0x12, 0x78, 0x56, 0xbc, 0x9a})); + reset(); + + pixel = Pixel(0x1234, 0x5678, 0x9abc); + set_pixel_to_row(data.data(), 0, pixel, PixelFormat::BGR161616); + ASSERT_EQ(data, Data({0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = Pixel(0x1234, 0x5678, 0x9abc); + set_pixel_to_row(data.data(), 1, pixel, PixelFormat::BGR161616); + ASSERT_EQ(data, Data({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12})); + reset(); +} + +void test_get_raw_pixel_from_row() +{ + std::vector<std::uint8_t> data = { + 0x12, 0x34, 0x56, 0x67, 0x89, 0xab, + 0xcd, 0xef, 0x21, 0x43, 0x65, 0x87 + }; + ASSERT_EQ(get_raw_pixel_from_row(data.data(), 0, PixelFormat::I1), + RawPixel(0x0)); + ASSERT_EQ(get_raw_pixel_from_row(data.data(), 3, PixelFormat::I1), + RawPixel(0x1)); + ASSERT_EQ(get_raw_pixel_from_row(data.data(), 0, PixelFormat::RGB111), + RawPixel(0)); + ASSERT_EQ(get_raw_pixel_from_row(data.data(), 1, PixelFormat::RGB111), + RawPixel(0x4)); + ASSERT_EQ(get_raw_pixel_from_row(data.data(), 2, PixelFormat::RGB111), + RawPixel(0x4)); + ASSERT_EQ(get_raw_pixel_from_row(data.data(), 3, PixelFormat::RGB111), + RawPixel(0x3)); + ASSERT_EQ(get_raw_pixel_from_row(data.data(), 0, PixelFormat::I8), + RawPixel(0x12)); + ASSERT_EQ(get_raw_pixel_from_row(data.data(), 1, PixelFormat::I8), + RawPixel(0x34)); + ASSERT_EQ(get_raw_pixel_from_row(data.data(), 0, PixelFormat::RGB888), + RawPixel(0x12, 0x34, 0x56)); + ASSERT_EQ(get_raw_pixel_from_row(data.data(), 1, PixelFormat::RGB888), + RawPixel(0x67, 0x89, 0xab)); + ASSERT_EQ(get_raw_pixel_from_row(data.data(), 0, PixelFormat::BGR888), + RawPixel(0x12, 0x34, 0x56)); + ASSERT_EQ(get_raw_pixel_from_row(data.data(), 1, PixelFormat::BGR888), + RawPixel(0x67, 0x89, 0xab)); + ASSERT_EQ(get_raw_pixel_from_row(data.data(), 0, PixelFormat::I16), + RawPixel(0x12, 0x34)); + ASSERT_EQ(get_raw_pixel_from_row(data.data(), 1, PixelFormat::I16), + RawPixel(0x56, 0x67)); + ASSERT_EQ(get_raw_pixel_from_row(data.data(), 0, PixelFormat::RGB161616), + RawPixel(0x12, 0x34, 0x56, 0x67, 0x89, 0xab)); + ASSERT_EQ(get_raw_pixel_from_row(data.data(), 1, PixelFormat::RGB161616), + RawPixel(0xcd, 0xef, 0x21, 0x43, 0x65, 0x87)); + ASSERT_EQ(get_raw_pixel_from_row(data.data(), 0, PixelFormat::BGR161616), + RawPixel(0x12, 0x34, 0x56, 0x67, 0x89, 0xab)); + ASSERT_EQ(get_raw_pixel_from_row(data.data(), 1, PixelFormat::BGR161616), + RawPixel(0xcd, 0xef, 0x21, 0x43, 0x65, 0x87)); +} + +void test_set_raw_pixel_to_row() +{ + using Data = std::vector<std::uint8_t>; + Data data; + data.resize(12, 0); + + auto reset = [&]() { std::fill(data.begin(), data.end(), 0); }; + + RawPixel pixel; + + pixel = RawPixel(0x01); + set_raw_pixel_to_row(data.data(), 0, pixel, PixelFormat::I1); + ASSERT_EQ(data, Data({0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = RawPixel(0x01); + set_raw_pixel_to_row(data.data(), 2, pixel, PixelFormat::I1); + ASSERT_EQ(data, Data({0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = RawPixel(0x01); + set_raw_pixel_to_row(data.data(), 8, pixel, PixelFormat::I1); + ASSERT_EQ(data, Data({0x00, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = RawPixel(0x05); + set_raw_pixel_to_row(data.data(), 0, pixel, PixelFormat::RGB111); + ASSERT_EQ(data, Data({0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = RawPixel(0x05); + set_raw_pixel_to_row(data.data(), 1, pixel, PixelFormat::RGB111); + ASSERT_EQ(data, Data({0x14, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = RawPixel(0x05); + set_raw_pixel_to_row(data.data(), 8, pixel, PixelFormat::RGB111); + ASSERT_EQ(data, Data({0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = RawPixel(0x12); + set_raw_pixel_to_row(data.data(), 0, pixel, PixelFormat::I8); + ASSERT_EQ(data, Data({0x12, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = RawPixel(0x12); + set_raw_pixel_to_row(data.data(), 2, pixel, PixelFormat::I8); + ASSERT_EQ(data, Data({0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = RawPixel(0x12, 0x34, 0x56); + set_raw_pixel_to_row(data.data(), 0, pixel, PixelFormat::RGB888); + ASSERT_EQ(data, Data({0x12, 0x34, 0x56, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = RawPixel(0x12, 0x34, 0x56); + set_raw_pixel_to_row(data.data(), 1, pixel, PixelFormat::RGB888); + ASSERT_EQ(data, Data({0x00, 0x00, 0x00, 0x12, 0x34, 0x56, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = RawPixel(0x12, 0x34, 0x56); + set_raw_pixel_to_row(data.data(), 0, pixel, PixelFormat::BGR888); + ASSERT_EQ(data, Data({0x12, 0x34, 0x56, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = RawPixel(0x12, 0x34, 0x56); + set_raw_pixel_to_row(data.data(), 1, pixel, PixelFormat::BGR888); + ASSERT_EQ(data, Data({0x00, 0x00, 0x00, 0x12, 0x34, 0x56, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = RawPixel(0x34, 0x12); + set_raw_pixel_to_row(data.data(), 0, pixel, PixelFormat::I16); + ASSERT_EQ(data, Data({0x34, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = RawPixel(0x34, 0x12); + set_raw_pixel_to_row(data.data(), 1, pixel, PixelFormat::I16); + ASSERT_EQ(data, Data({0x00, 0x00, 0x34, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = RawPixel(0x34, 0x12, 0x78, 0x56, 0xbc, 0x9a); + set_raw_pixel_to_row(data.data(), 0, pixel, PixelFormat::RGB161616); + ASSERT_EQ(data, Data({0x34, 0x12, 0x78, 0x56, 0xbc, 0x9a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = RawPixel(0x34, 0x12, 0x78, 0x56, 0xbc, 0x9a); + set_raw_pixel_to_row(data.data(), 1, pixel, PixelFormat::RGB161616); + ASSERT_EQ(data, Data({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x34, 0x12, 0x78, 0x56, 0xbc, 0x9a})); + reset(); + + pixel = RawPixel(0x34, 0x12, 0x78, 0x56, 0xbc, 0x9a); + set_raw_pixel_to_row(data.data(), 0, pixel, PixelFormat::BGR161616); + ASSERT_EQ(data, Data({0x34, 0x12, 0x78, 0x56, 0xbc, 0x9a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + pixel = RawPixel(0x34, 0x12, 0x78, 0x56, 0xbc, 0x9a); + set_raw_pixel_to_row(data.data(), 1, pixel, PixelFormat::BGR161616); + ASSERT_EQ(data, Data({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x34, 0x12, 0x78, 0x56, 0xbc, 0x9a})); + reset(); +} + +void test_get_raw_channel_from_row() +{ + std::vector<std::uint8_t> data = { + 0x12, 0x34, 0x56, 0x67, 0x89, 0xab, + 0xcd, 0xef, 0x21, 0x43, 0x65, 0x87 + }; + ASSERT_EQ(get_raw_channel_from_row(data.data(), 0, 0, PixelFormat::I1), 0); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 3, 0, PixelFormat::I1), 1); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 0, 0, PixelFormat::RGB111), 0); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 0, 1, PixelFormat::RGB111), 0); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 0, 2, PixelFormat::RGB111), 0); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 1, 0, PixelFormat::RGB111), 1); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 1, 1, PixelFormat::RGB111), 0); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 1, 2, PixelFormat::RGB111), 0); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 2, 0, PixelFormat::RGB111), 1); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 2, 1, PixelFormat::RGB111), 0); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 2, 2, PixelFormat::RGB111), 0); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 3, 0, PixelFormat::RGB111), 0); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 3, 1, PixelFormat::RGB111), 1); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 3, 2, PixelFormat::RGB111), 1); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 0, 0, PixelFormat::I8), 0x12); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 1, 0, PixelFormat::I8), 0x34); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 0, 0, PixelFormat::RGB888), 0x12); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 0, 1, PixelFormat::RGB888), 0x34); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 0, 2, PixelFormat::RGB888), 0x56); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 1, 0, PixelFormat::RGB888), 0x67); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 1, 1, PixelFormat::RGB888), 0x89); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 1, 2, PixelFormat::RGB888), 0xab); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 0, 0, PixelFormat::BGR888), 0x12); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 0, 1, PixelFormat::BGR888), 0x34); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 0, 2, PixelFormat::BGR888), 0x56); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 1, 0, PixelFormat::BGR888), 0x67); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 1, 1, PixelFormat::BGR888), 0x89); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 1, 2, PixelFormat::BGR888), 0xab); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 0, 0, PixelFormat::I16), 0x3412); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 1, 0, PixelFormat::I16), 0x6756); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 0, 0, PixelFormat::RGB161616), 0x3412); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 0, 1, PixelFormat::RGB161616), 0x6756); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 0, 2, PixelFormat::RGB161616), 0xab89); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 1, 0, PixelFormat::RGB161616), 0xefcd); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 1, 1, PixelFormat::RGB161616), 0x4321); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 1, 2, PixelFormat::RGB161616), 0x8765); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 0, 0, PixelFormat::BGR161616), 0x3412); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 0, 1, PixelFormat::BGR161616), 0x6756); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 0, 2, PixelFormat::BGR161616), 0xab89); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 1, 0, PixelFormat::BGR161616), 0xefcd); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 1, 1, PixelFormat::BGR161616), 0x4321); + ASSERT_EQ(get_raw_channel_from_row(data.data(), 1, 2, PixelFormat::BGR161616), 0x8765); +} + +void test_set_raw_channel_to_row() +{ + using Data = std::vector<std::uint8_t>; + Data data; + data.resize(12, 0); + + auto reset = [&]() { std::fill(data.begin(), data.end(), 0); }; + + set_raw_channel_to_row(data.data(), 0, 0, 1, PixelFormat::I1); + ASSERT_EQ(data, Data({0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + set_raw_channel_to_row(data.data(), 2, 0, 1, PixelFormat::I1); + ASSERT_EQ(data, Data({0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + set_raw_channel_to_row(data.data(), 8, 0, 1, PixelFormat::I1); + ASSERT_EQ(data, Data({0x00, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + set_raw_channel_to_row(data.data(), 0, 0, 1, PixelFormat::RGB111); + ASSERT_EQ(data, Data({0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + set_raw_channel_to_row(data.data(), 0, 1, 1, PixelFormat::RGB111); + ASSERT_EQ(data, Data({0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + set_raw_channel_to_row(data.data(), 0, 2, 1, PixelFormat::RGB111); + ASSERT_EQ(data, Data({0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + set_raw_channel_to_row(data.data(), 8, 0, 1, PixelFormat::RGB111); + ASSERT_EQ(data, Data({0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + set_raw_channel_to_row(data.data(), 0, 0, 0x12, PixelFormat::I8); + ASSERT_EQ(data, Data({0x12, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + set_raw_channel_to_row(data.data(), 2, 0, 0x12, PixelFormat::I8); + ASSERT_EQ(data, Data({0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + for (auto format : { PixelFormat::RGB888, PixelFormat::BGR888 }) { + set_raw_channel_to_row(data.data(), 0, 0, 0x12, format); + ASSERT_EQ(data, Data({0x12, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + set_raw_channel_to_row(data.data(), 0, 1, 0x12, format); + ASSERT_EQ(data, Data({0x00, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + set_raw_channel_to_row(data.data(), 0, 2, 0x12, format); + ASSERT_EQ(data, Data({0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + set_raw_channel_to_row(data.data(), 1, 0, 0x12, format); + ASSERT_EQ(data, Data({0x00, 0x00, 0x00, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + set_raw_channel_to_row(data.data(), 1, 1, 0x12, format); + ASSERT_EQ(data, Data({0x00, 0x00, 0x00, 0x00, 0x12, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + set_raw_channel_to_row(data.data(), 1, 2, 0x12, format); + ASSERT_EQ(data, Data({0x00, 0x00, 0x00, 0x00, 0x00, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + } + + set_raw_channel_to_row(data.data(), 0, 0, 0x1234, PixelFormat::I16); + ASSERT_EQ(data, Data({0x34, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + set_raw_channel_to_row(data.data(), 1, 0, 0x1234, PixelFormat::I16); + ASSERT_EQ(data, Data({0x00, 0x00, 0x34, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + for (auto format : { PixelFormat::RGB161616, PixelFormat::BGR161616 }) { + set_raw_channel_to_row(data.data(), 0, 0, 0x1234, format); + ASSERT_EQ(data, Data({0x34, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + set_raw_channel_to_row(data.data(), 0, 1, 0x1234, format); + ASSERT_EQ(data, Data({0x00, 0x00, 0x34, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + set_raw_channel_to_row(data.data(), 0, 2, 0x1234, format); + ASSERT_EQ(data, Data({0x00, 0x00, 0x00, 0x00, 0x34, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + reset(); + + set_raw_channel_to_row(data.data(), 1, 0, 0x1234, format); + ASSERT_EQ(data, Data({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x34, 0x12, 0x00, 0x00, 0x00, 0x00})); + reset(); + + set_raw_channel_to_row(data.data(), 1, 1, 0x1234, format); + ASSERT_EQ(data, Data({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x34, 0x12, 0x00, 0x00})); + reset(); + + set_raw_channel_to_row(data.data(), 1, 2, 0x1234, format); + ASSERT_EQ(data, Data({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x34, 0x12})); + reset(); + } +} + +void test_convert_pixel_row_format() +{ + // The actual work is done in set_channel_to_row and get_channel_from_row, so we don't need + // to test all format combinations. + using Data = std::vector<std::uint8_t>; + + Data in_data = { + 0x12, 0x34, 0x56, + 0x78, 0x98, 0xab, + 0xcd, 0xef, 0x21, + }; + Data out_data; + out_data.resize(in_data.size() * 2); + + convert_pixel_row_format(in_data.data(), PixelFormat::RGB888, + out_data.data(), PixelFormat::BGR161616, 3); + + Data expected_data = { + 0x56, 0x56, 0x34, 0x34, 0x12, 0x12, + 0xab, 0xab, 0x98, 0x98, 0x78, 0x78, + 0x21, 0x21, 0xef, 0xef, 0xcd, 0xcd, + }; + + ASSERT_EQ(out_data, expected_data); +} + +void test_image() +{ + test_get_pixel_from_row(); + test_set_pixel_to_row(); + test_get_raw_pixel_from_row(); + test_set_raw_pixel_to_row(); + test_get_raw_channel_from_row(); + test_set_raw_channel_to_row(); + test_convert_pixel_row_format(); +} + +} // namespace genesys diff --git a/testsuite/backend/genesys/tests_image_pipeline.cpp b/testsuite/backend/genesys/tests_image_pipeline.cpp new file mode 100644 index 0000000..d4853d2 --- /dev/null +++ b/testsuite/backend/genesys/tests_image_pipeline.cpp @@ -0,0 +1,519 @@ +/* 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 "tests.h" +#include "minigtest.h" +#include "tests_printers.h" + +#include "../../../backend/genesys/image_pipeline.h" + +#include <numeric> + +namespace genesys { + +void test_image_buffer_genesys_usb() +{ + std::vector<std::size_t> requests; + + auto on_read_usb = [&](std::size_t x, std::uint8_t* data) + { + (void) data; + requests.push_back(x); + }; + + FakeBufferModel model; + model.push_step(453120, 1); + model.push_step(56640, 3540); + ImageBufferGenesysUsb buffer{1086780, model, on_read_usb}; + + std::vector<std::uint8_t> dummy; + dummy.resize(1086780); + + ASSERT_TRUE(buffer.get_data(453120, dummy.data())); + ASSERT_TRUE(buffer.get_data(56640, dummy.data())); + ASSERT_TRUE(buffer.get_data(56640, dummy.data())); + ASSERT_TRUE(buffer.get_data(56640, dummy.data())); + ASSERT_TRUE(buffer.get_data(56640, dummy.data())); + ASSERT_TRUE(buffer.get_data(56640, dummy.data())); + ASSERT_TRUE(buffer.get_data(56640, dummy.data())); + ASSERT_TRUE(buffer.get_data(56640, dummy.data())); + ASSERT_TRUE(buffer.get_data(56640, dummy.data())); + ASSERT_TRUE(buffer.get_data(56640, dummy.data())); + ASSERT_TRUE(buffer.get_data(56640, dummy.data())); + ASSERT_TRUE(buffer.get_data(56640, dummy.data())); + + std::vector<std::size_t> expected = { + 453120, 56576, 56576, 56576, 56832, 56576, 56576, 56576, 56832, 56576, 56576, 56576, 11008 + }; + ASSERT_EQ(requests, expected); +} + +void test_image_buffer_genesys_usb_capped_remaining_bytes() +{ + std::vector<std::size_t> requests; + + auto on_read_usb = [&](std::size_t x, std::uint8_t* data) + { + (void) data; + requests.push_back(x); + }; + + FakeBufferModel model; + model.push_step(453120, 1); + model.push_step(56640, 3540); + ImageBufferGenesysUsb buffer{1086780, model, on_read_usb}; + + std::vector<std::uint8_t> dummy; + dummy.resize(1086780); + + ASSERT_TRUE(buffer.get_data(453120, dummy.data())); + ASSERT_TRUE(buffer.get_data(56640, dummy.data())); + ASSERT_TRUE(buffer.get_data(56640, dummy.data())); + ASSERT_TRUE(buffer.get_data(56640, dummy.data())); + ASSERT_TRUE(buffer.get_data(56640, dummy.data())); + buffer.set_remaining_size(10000); + ASSERT_FALSE(buffer.get_data(56640, dummy.data())); + + std::vector<std::size_t> expected = { + // note that the sizes are rounded-up to 256 bytes + 453120, 56576, 56576, 56576, 56832, 10240 + }; + ASSERT_EQ(requests, expected); +} + +void test_node_buffered_callable_source() +{ + using Data = std::vector<std::uint8_t>; + + Data in_data = { + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11 + }; + + std::size_t chunk_size = 3; + std::size_t curr_index = 0; + + auto data_source_cb = [&](std::size_t size, std::uint8_t* out_data) + { + ASSERT_EQ(size, chunk_size); + std::copy(in_data.begin() + curr_index, + in_data.begin() + curr_index + chunk_size, out_data); + curr_index += chunk_size; + return true; + }; + + ImagePipelineStack stack; + stack.push_first_node<ImagePipelineNodeBufferedCallableSource>(4, 3, PixelFormat::I8, + chunk_size, data_source_cb); + + Data out_data; + out_data.resize(4); + + ASSERT_EQ(curr_index, 0u); + + ASSERT_TRUE(stack.get_next_row_data(out_data.data())); + ASSERT_EQ(out_data, Data({0, 1, 2, 3})); + ASSERT_EQ(curr_index, 6u); + + ASSERT_TRUE(stack.get_next_row_data(out_data.data())); + ASSERT_EQ(out_data, Data({4, 5, 6, 7})); + ASSERT_EQ(curr_index, 9u); + + ASSERT_TRUE(stack.get_next_row_data(out_data.data())); + ASSERT_EQ(out_data, Data({8, 9, 10, 11})); + ASSERT_EQ(curr_index, 12u); +} + +void test_node_format_convert() +{ + using Data = std::vector<std::uint8_t>; + + Data in_data = { + 0x12, 0x34, 0x56, + 0x78, 0x98, 0xab, + 0xcd, 0xef, 0x21, + }; + + ImagePipelineStack stack; + stack.push_first_node<ImagePipelineNodeArraySource>(3, 1, PixelFormat::RGB888, + std::move(in_data)); + stack.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::BGR161616); + + ASSERT_EQ(stack.get_output_width(), 3u); + ASSERT_EQ(stack.get_output_height(), 1u); + ASSERT_EQ(stack.get_output_row_bytes(), 6u * 3); + ASSERT_EQ(stack.get_output_format(), PixelFormat::BGR161616); + + auto out_data = stack.get_all_data(); + + Data expected_data = { + 0x56, 0x56, 0x34, 0x34, 0x12, 0x12, + 0xab, 0xab, 0x98, 0x98, 0x78, 0x78, + 0x21, 0x21, 0xef, 0xef, 0xcd, 0xcd, + }; + + ASSERT_EQ(out_data, expected_data); +} + +void test_node_desegment_1_line() +{ + using Data = std::vector<std::uint8_t>; + + Data in_data = { + 1, 5, 9, 13, 17, + 3, 7, 11, 15, 19, + 2, 6, 10, 14, 18, + 4, 8, 12, 16, 20, + 21, 25, 29, 33, 37, + 23, 27, 31, 35, 39, + 22, 26, 30, 34, 38, + 24, 28, 32, 36, 40, + }; + + ImagePipelineStack stack; + stack.push_first_node<ImagePipelineNodeArraySource>(20, 2, PixelFormat::I8, + std::move(in_data)); + stack.push_node<ImagePipelineNodeDesegment>(20, std::vector<unsigned>{ 0, 2, 1, 3 }, 5, 1, 1); + + ASSERT_EQ(stack.get_output_width(), 20u); + ASSERT_EQ(stack.get_output_height(), 2u); + ASSERT_EQ(stack.get_output_row_bytes(), 20u); + ASSERT_EQ(stack.get_output_format(), PixelFormat::I8); + + auto out_data = stack.get_all_data(); + + Data expected_data; + expected_data.resize(40, 0); + std::iota(expected_data.begin(), expected_data.end(), 1); // will fill with 1, 2, 3, ..., 40 + + ASSERT_EQ(out_data, expected_data); +} + +void test_node_deinterleave_lines_i8() +{ + using Data = std::vector<std::uint8_t>; + + Data in_data = { + 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, + 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, + }; + + ImagePipelineStack stack; + stack.push_first_node<ImagePipelineNodeArraySource>(10, 2, PixelFormat::I8, + std::move(in_data)); + stack.push_node<ImagePipelineNodeDeinterleaveLines>(2, 1); + + ASSERT_EQ(stack.get_output_width(), 20u); + ASSERT_EQ(stack.get_output_height(), 1u); + ASSERT_EQ(stack.get_output_row_bytes(), 20u); + ASSERT_EQ(stack.get_output_format(), PixelFormat::I8); + + auto out_data = stack.get_all_data(); + + Data expected_data; + expected_data.resize(20, 0); + std::iota(expected_data.begin(), expected_data.end(), 1); // will fill with 1, 2, 3, ..., 20 + + ASSERT_EQ(out_data, expected_data); +} + +void test_node_deinterleave_lines_rgb888() +{ + using Data = std::vector<std::uint8_t>; + + Data in_data = { + 1, 2, 3, 7, 8, 9, 13, 14, 15, 19, 20, 21, + 4, 5, 6, 10, 11, 12, 16, 17, 18, 22, 23, 24, + }; + + ImagePipelineStack stack; + stack.push_first_node<ImagePipelineNodeArraySource>(4, 2, PixelFormat::RGB888, + std::move(in_data)); + stack.push_node<ImagePipelineNodeDeinterleaveLines>(2, 1); + + ASSERT_EQ(stack.get_output_width(), 8u); + ASSERT_EQ(stack.get_output_height(), 1u); + ASSERT_EQ(stack.get_output_row_bytes(), 24u); + ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB888); + + auto out_data = stack.get_all_data(); + + Data expected_data; + expected_data.resize(24, 0); + std::iota(expected_data.begin(), expected_data.end(), 1); // will fill with 1, 2, 3, ..., 20 + + ASSERT_EQ(out_data, expected_data); +} + +void test_node_swap_16bit_endian() +{ + using Data = std::vector<std::uint8_t>; + + Data in_data = { + 0x10, 0x20, 0x30, 0x11, 0x21, 0x31, + 0x12, 0x22, 0x32, 0x13, 0x23, 0x33, + 0x14, 0x24, 0x34, 0x15, 0x25, 0x35, + 0x16, 0x26, 0x36, 0x17, 0x27, 0x37, + }; + + ImagePipelineStack stack; + stack.push_first_node<ImagePipelineNodeArraySource>(4, 1, PixelFormat::RGB161616, + std::move(in_data)); + stack.push_node<ImagePipelineNodeSwap16BitEndian>(); + + ASSERT_EQ(stack.get_output_width(), 4u); + ASSERT_EQ(stack.get_output_height(), 1u); + ASSERT_EQ(stack.get_output_row_bytes(), 24u); + ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB161616); + + auto out_data = stack.get_all_data(); + + Data expected_data = { + 0x20, 0x10, 0x11, 0x30, 0x31, 0x21, + 0x22, 0x12, 0x13, 0x32, 0x33, 0x23, + 0x24, 0x14, 0x15, 0x34, 0x35, 0x25, + 0x26, 0x16, 0x17, 0x36, 0x37, 0x27, + }; + + ASSERT_EQ(out_data, expected_data); +} + +void test_node_merge_mono_lines() +{ + using Data = std::vector<std::uint8_t>; + + Data in_data = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + }; + + ImagePipelineStack stack; + stack.push_first_node<ImagePipelineNodeArraySource>(8, 3, PixelFormat::I8, + std::move(in_data)); + stack.push_node<ImagePipelineNodeMergeMonoLines>(ColorOrder::RGB); + + ASSERT_EQ(stack.get_output_width(), 8u); + ASSERT_EQ(stack.get_output_height(), 1u); + ASSERT_EQ(stack.get_output_row_bytes(), 24u); + ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB888); + + auto out_data = stack.get_all_data(); + + Data expected_data = { + 0x10, 0x20, 0x30, 0x11, 0x21, 0x31, + 0x12, 0x22, 0x32, 0x13, 0x23, 0x33, + 0x14, 0x24, 0x34, 0x15, 0x25, 0x35, + 0x16, 0x26, 0x36, 0x17, 0x27, 0x37, + }; + + ASSERT_EQ(out_data, expected_data); +} + +void test_node_split_mono_lines() +{ + using Data = std::vector<std::uint8_t>; + + Data in_data = { + 0x10, 0x20, 0x30, 0x11, 0x21, 0x31, + 0x12, 0x22, 0x32, 0x13, 0x23, 0x33, + 0x14, 0x24, 0x34, 0x15, 0x25, 0x35, + 0x16, 0x26, 0x36, 0x17, 0x27, 0x37, + }; + + ImagePipelineStack stack; + stack.push_first_node<ImagePipelineNodeArraySource>(8, 1, PixelFormat::RGB888, + std::move(in_data)); + stack.push_node<ImagePipelineNodeSplitMonoLines>(); + + ASSERT_EQ(stack.get_output_width(), 8u); + ASSERT_EQ(stack.get_output_height(), 3u); + ASSERT_EQ(stack.get_output_row_bytes(), 8u); + ASSERT_EQ(stack.get_output_format(), PixelFormat::I8); + + auto out_data = stack.get_all_data(); + + Data expected_data = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + }; + + ASSERT_EQ(out_data, expected_data); +} + +void test_node_component_shift_lines() +{ + using Data = std::vector<std::uint8_t>; + + Data in_data = { + 0x10, 0x20, 0x30, 0x11, 0x21, 0x31, 0x12, 0x22, 0x32, 0x13, 0x23, 0x33, + 0x14, 0x24, 0x34, 0x15, 0x25, 0x35, 0x16, 0x26, 0x36, 0x17, 0x27, 0x37, + 0x18, 0x28, 0x38, 0x19, 0x29, 0x39, 0x1a, 0x2a, 0x3a, 0x1b, 0x2b, 0x3b, + 0x1c, 0x2c, 0x3c, 0x1d, 0x2d, 0x3d, 0x1e, 0x2e, 0x3e, 0x1f, 0x2f, 0x3f, + }; + + ImagePipelineStack stack; + stack.push_first_node<ImagePipelineNodeArraySource>(4, 4, PixelFormat::RGB888, + std::move(in_data)); + stack.push_node<ImagePipelineNodeComponentShiftLines>(0, 1, 2); + + ASSERT_EQ(stack.get_output_width(), 4u); + ASSERT_EQ(stack.get_output_height(), 2u); + ASSERT_EQ(stack.get_output_row_bytes(), 12u); + ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB888); + + auto out_data = stack.get_all_data(); + + Data expected_data = { + 0x10, 0x24, 0x38, 0x11, 0x25, 0x39, 0x12, 0x26, 0x3a, 0x13, 0x27, 0x3b, + 0x14, 0x28, 0x3c, 0x15, 0x29, 0x3d, 0x16, 0x2a, 0x3e, 0x17, 0x2b, 0x3f, + }; + + ASSERT_EQ(out_data, expected_data); +} + +void test_node_pixel_shift_lines() +{ + using Data = std::vector<std::uint8_t>; + + Data in_data = { + 0x10, 0x20, 0x30, 0x11, 0x21, 0x31, 0x12, 0x22, 0x32, 0x13, 0x23, 0x33, + 0x14, 0x24, 0x34, 0x15, 0x25, 0x35, 0x16, 0x26, 0x36, 0x17, 0x27, 0x37, + 0x18, 0x28, 0x38, 0x19, 0x29, 0x39, 0x1a, 0x2a, 0x3a, 0x1b, 0x2b, 0x3b, + 0x1c, 0x2c, 0x3c, 0x1d, 0x2d, 0x3d, 0x1e, 0x2e, 0x3e, 0x1f, 0x2f, 0x3f, + }; + + ImagePipelineStack stack; + stack.push_first_node<ImagePipelineNodeArraySource>(4, 4, PixelFormat::RGB888, + std::move(in_data)); + stack.push_node<ImagePipelineNodePixelShiftLines>(std::vector<std::size_t>{0, 2}); + + ASSERT_EQ(stack.get_output_width(), 4u); + ASSERT_EQ(stack.get_output_height(), 2u); + ASSERT_EQ(stack.get_output_row_bytes(), 12u); + ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB888); + + auto out_data = stack.get_all_data(); + + Data expected_data = { + 0x10, 0x20, 0x30, 0x19, 0x29, 0x39, 0x12, 0x22, 0x32, 0x1b, 0x2b, 0x3b, + 0x14, 0x24, 0x34, 0x1d, 0x2d, 0x3d, 0x16, 0x26, 0x36, 0x1f, 0x2f, 0x3f, + }; + + ASSERT_EQ(out_data, expected_data); +} + +void test_node_calibrate_8bit() +{ + using Data = std::vector<std::uint8_t>; + + Data in_data = { + 0x20, 0x38, 0x38 + }; + + std::vector<std::uint16_t> bottom = { + 0x1000, 0x2000, 0x3000 + }; + + std::vector<std::uint16_t> top = { + 0x3000, 0x4000, 0x5000 + }; + + ImagePipelineStack stack; + stack.push_first_node<ImagePipelineNodeArraySource>(1, 1, PixelFormat::RGB888, + std::move(in_data)); + stack.push_node<ImagePipelineNodeCalibrate>(bottom, top); + + ASSERT_EQ(stack.get_output_width(), 1u); + ASSERT_EQ(stack.get_output_height(), 1u); + ASSERT_EQ(stack.get_output_row_bytes(), 3u); + ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB888); + + auto out_data = stack.get_all_data(); + + Data expected_data = { + // note that we don't handle rounding properly in the implementation + 0x80, 0xc1, 0x41 + }; + + ASSERT_EQ(out_data, expected_data); +} + +void test_node_calibrate_16bit() +{ + using Data = std::vector<std::uint8_t>; + + Data in_data = { + 0x00, 0x20, 0x00, 0x38, 0x00, 0x38 + }; + + std::vector<std::uint16_t> bottom = { + 0x1000, 0x2000, 0x3000 + }; + + std::vector<std::uint16_t> top = { + 0x3000, 0x4000, 0x5000 + }; + + ImagePipelineStack stack; + stack.push_first_node<ImagePipelineNodeArraySource>(1, 1, PixelFormat::RGB161616, + std::move(in_data)); + stack.push_node<ImagePipelineNodeCalibrate>(bottom, top); + + ASSERT_EQ(stack.get_output_width(), 1u); + ASSERT_EQ(stack.get_output_height(), 1u); + ASSERT_EQ(stack.get_output_row_bytes(), 6u); + ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB161616); + + auto out_data = stack.get_all_data(); + + Data expected_data = { + // note that we don't handle rounding properly in the implementation + 0x00, 0x80, 0xff, 0xbf, 0x00, 0x40 + }; + + ASSERT_EQ(out_data, expected_data); +} + +void test_image_pipeline() +{ + test_image_buffer_genesys_usb(); + test_image_buffer_genesys_usb_capped_remaining_bytes(); + test_node_buffered_callable_source(); + test_node_format_convert(); + test_node_desegment_1_line(); + test_node_deinterleave_lines_i8(); + test_node_deinterleave_lines_rgb888(); + test_node_swap_16bit_endian(); + test_node_merge_mono_lines(); + test_node_split_mono_lines(); + test_node_component_shift_lines(); + test_node_pixel_shift_lines(); + test_node_calibrate_8bit(); + test_node_calibrate_16bit(); +} + +} // namespace genesys diff --git a/testsuite/backend/genesys/tests_motor.cpp b/testsuite/backend/genesys/tests_motor.cpp new file mode 100644 index 0000000..07ca693 --- /dev/null +++ b/testsuite/backend/genesys/tests_motor.cpp @@ -0,0 +1,365 @@ +/* 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 "tests.h" +#include "minigtest.h" +#include "tests_printers.h" + +#include "../../../backend/genesys/low.h" +#include "../../../backend/genesys/enums.h" + +namespace genesys { + +void test_create_slope_table3() +{ + auto asic_type = AsicType::GL841; + auto max_table_size = get_slope_table_max_size(asic_type); + + Genesys_Motor motor; + motor.id = MotorId::CANON_LIDE_200; + motor.base_ydpi = 1200; + motor.optical_ydpi = 6400; + motor.slopes.push_back(MotorSlope::create_from_steps(10000, 1000, 20)); + motor.slopes.push_back(MotorSlope::create_from_steps(10000, 1000, 20)); + motor.slopes.push_back(MotorSlope::create_from_steps(10000, 1000, 16)); + + auto table = sanei_genesys_create_slope_table3(asic_type, motor, StepType::FULL, 10000, + motor.base_ydpi); + + ASSERT_EQ(table.pixeltime_sum, 10000u); + ASSERT_EQ(table.steps_count, 1u); + + std::vector<std::uint16_t> expected_steps = { + 10000, + }; + expected_steps.resize(max_table_size, 10000); + + ASSERT_EQ(table.table, expected_steps); + + table = sanei_genesys_create_slope_table3(asic_type, motor, StepType::FULL, 2000, + motor.base_ydpi); + + ASSERT_EQ(table.pixeltime_sum, 33830u); + ASSERT_EQ(table.steps_count, 7u); + + expected_steps = { + 10000, 10000, 4099, 3028, 2511, 2192, 2000 + }; + expected_steps.resize(max_table_size, 2000); + + ASSERT_EQ(table.table, expected_steps); + + table = sanei_genesys_create_slope_table3(asic_type, motor, StepType::HALF, 10000, + motor.base_ydpi); + + ASSERT_EQ(table.pixeltime_sum, 5000u); + ASSERT_EQ(table.steps_count, 1u); + + expected_steps = { + 5000, + }; + expected_steps.resize(max_table_size, 5000); + + + ASSERT_EQ(table.table, expected_steps); + + table = sanei_genesys_create_slope_table3(asic_type, motor, StepType::HALF, 2000, + motor.base_ydpi); + + ASSERT_EQ(table.pixeltime_sum, 16914u); + ASSERT_EQ(table.steps_count, 7u); + + expected_steps = { + 5000, 5000, 2049, 1514, 1255, 1096, 1000 + }; + expected_steps.resize(max_table_size, 1000); + + ASSERT_EQ(table.table, expected_steps); + + table = sanei_genesys_create_slope_table3(asic_type, motor, StepType::QUARTER, 10000, + motor.base_ydpi); + + ASSERT_EQ(table.pixeltime_sum, 2500u); + ASSERT_EQ(table.steps_count, 1u); + + expected_steps = { + 2500, + }; + expected_steps.resize(max_table_size, 2500); + + + ASSERT_EQ(table.table, expected_steps); + + table = sanei_genesys_create_slope_table3(asic_type, motor, StepType::QUARTER, 2000, + motor.base_ydpi); + + ASSERT_EQ(table.pixeltime_sum, 7680u); + ASSERT_EQ(table.steps_count, 6u); + + expected_steps = { + 2500, 2500, 932, 683, 565, 500 + }; + expected_steps.resize(max_table_size, 500); + + ASSERT_EQ(table.table, expected_steps); +} + +void test_create_slope_table_small_full_step() +{ + unsigned max_table_size = 1024; + + // created approximately from LIDE 110 slow table: { 62464, 7896, 2632, 0 } + MotorSlope slope; + slope.initial_speed_w = 62464; + slope.max_speed_w = 2632; + slope.acceleration = 1.2e-8; + + auto table = create_slope_table(slope, 5000, StepType::FULL, 4, 8, max_table_size); + + std::vector<std::uint16_t> expected_table = { + 62464, 62464, 6420, 5000 + }; + expected_table.resize(max_table_size, 5000); + ASSERT_EQ(table.table, expected_table); + ASSERT_EQ(table.steps_count, 8u); + ASSERT_EQ(table.pixeltime_sum, 156348u); + + + table = create_slope_table(slope, 3000, StepType::FULL, 4, 8, max_table_size); + + expected_table = { + 62464, 62464, 6420, 4552, 3720, 3223, 3000 + }; + expected_table.resize(max_table_size, 3000); + ASSERT_EQ(table.table, expected_table); + ASSERT_EQ(table.steps_count, 8u); + ASSERT_EQ(table.pixeltime_sum, 148843u); +} + +void test_create_slope_table_small_full_step_target_speed_too_high() +{ + unsigned max_table_size = 1024; + + // created approximately from LIDE 110 slow table: { 62464, 7896, 2632, 0 } + MotorSlope slope; + slope.initial_speed_w = 62464; + slope.max_speed_w = 2632; + slope.acceleration = 1.2e-8; + + auto table = create_slope_table(slope, 2000, StepType::FULL, 4, 8, max_table_size); + + std::vector<std::uint16_t> expected_table = { + 62464, 62464, 6420, 4552, 3720, 3223, 2883, 2632 + }; + expected_table.resize(max_table_size, 2632); + ASSERT_EQ(table.table, expected_table); + ASSERT_EQ(table.steps_count, 8u); + ASSERT_EQ(table.pixeltime_sum, 148358u); +} + +void test_create_slope_table_small_half_step() +{ + unsigned max_table_size = 1024; + + // created approximately from LIDE 110 slow table: { 62464, 7896, 2632, 0 } + MotorSlope slope; + slope.initial_speed_w = 62464; + slope.max_speed_w = 2632; + slope.acceleration = 1.2e-8; + + auto table = create_slope_table(slope, 5000, StepType::HALF, 4, 8, max_table_size); + + std::vector<std::uint16_t> expected_table = { + 31232, 31232, 3210, 2500 + }; + expected_table.resize(max_table_size, 2500); + ASSERT_EQ(table.table, expected_table); + ASSERT_EQ(table.steps_count, 8u); + ASSERT_EQ(table.pixeltime_sum, 78174u); + + + table = create_slope_table(slope, 3000, StepType::HALF, 4, 8, max_table_size); + + expected_table = { + 31232, 31232, 3210, 2276, 1860, 1611, 1500 + }; + expected_table.resize(max_table_size, 1500); + ASSERT_EQ(table.table, expected_table); + ASSERT_EQ(table.steps_count, 8u); + ASSERT_EQ(table.pixeltime_sum, 74421u); +} + +void test_create_slope_table_large_full_step() +{ + unsigned max_table_size = 1024; + + /* created approximately from Canon 8600F table: + 54612, 54612, 34604, 26280, 21708, 18688, 16564, 14936, 13652, 12616, + 11768, 11024, 10400, 9872, 9392, 8960, 8584, 8240, 7940, 7648, + 7404, 7160, 6948, 6732, 6544, 6376, 6208, 6056, 5912, 5776, + 5644, 5520, 5408, 5292, 5192, 5092, 5000, 4908, 4820, 4736, + 4660, 4580, 4508, 4440, 4368, 4304, 4240, 4184, 4124, 4068, + 4012, 3960, 3908, 3860, 3808, 3764, 3720, 3676, 3636, 3592, + 3552, 3516, 3476, 3440, 3400, 3368, 3332, 3300, 3268, 3236, + 3204, 3176, 3148, 3116, 3088, 3060, 3036, 3008, 2984, 2956, + 2932, 2908, 2884, 2860, 2836, 2816, 2796, 2772, 2752, 2732, + 2708, 2692, 2672, 2652, 2632, 2616, 2596, 2576, 2560, 2544, + 2528, 2508, 2492, 2476, 2460, 2444, 2432, 2416, 2400, 2384, + 2372, 2356, 2344, 2328, 2316, 2304, 2288, 2276, 2260, 2252, + 2236, 2224, 2212, 2200, 2188, 2176, 2164, 2156, 2144, 2132, + 2120, 2108, 2100, 2088, 2080, 2068, 2056, 2048, 2036, 2028, + 2020, 2008, 2000, 1988, 1980, 1972, 1964, 1952, 1944, 1936, + 1928, 1920, 1912, 1900, 1892, 1884, 1876, 1868, 1860, 1856, + 1848, 1840, 1832, 1824, 1816, 1808, 1800, 1796, 1788, 1780, + 1772, 1764, 1760, 1752, 1744, 1740, 1732, 1724, 1720, 1712, + 1708, 1700, 1692, 1688, 1680, 1676, 1668, 1664, 1656, 1652, + 1644, 1640, 1636, 1628, 1624, 1616, 1612, 1608, 1600, 1596, + 1592, 1584, 1580, 1576, 1568, 1564, 1560, 1556, 1548, 1544, + 1540, 1536, 1528, 1524, 1520, 1516, 1512, 1508, 1500, + */ + MotorSlope slope; + slope.initial_speed_w = 54612; + slope.max_speed_w = 1500; + slope.acceleration = 1.013948e-9; + + auto table = create_slope_table(slope, 3000, StepType::FULL, 4, 8, max_table_size); + + std::vector<std::uint16_t> expected_table = { + 54612, 54612, 20570, 15090, 12481, 10880, 9770, 8943, 8295, 7771, + 7335, 6964, 6645, 6366, 6120, 5900, 5702, 5523, 5359, 5210, + 5072, 4945, 4826, 4716, 4613, 4517, 4426, 4341, 4260, 4184, + 4111, 4043, 3977, 3915, 3855, 3799, 3744, 3692, 3642, 3594, + 3548, 3503, 3461, 3419, 3379, 3341, 3304, 3268, 3233, 3199, + 3166, 3135, 3104, 3074, 3045, 3017, 3000, + }; + expected_table.resize(max_table_size, 3000); + ASSERT_EQ(table.table, expected_table); + ASSERT_EQ(table.steps_count, 60u); + ASSERT_EQ(table.pixeltime_sum, 412616u); + + + table = create_slope_table(slope, 1500, StepType::FULL, 4, 8, max_table_size); + + expected_table = { + 54612, 54612, 20570, 15090, 12481, 10880, 9770, 8943, 8295, 7771, + 7335, 6964, 6645, 6366, 6120, 5900, 5702, 5523, 5359, 5210, + 5072, 4945, 4826, 4716, 4613, 4517, 4426, 4341, 4260, 4184, + 4111, 4043, 3977, 3915, 3855, 3799, 3744, 3692, 3642, 3594, + 3548, 3503, 3461, 3419, 3379, 3341, 3304, 3268, 3233, 3199, + 3166, 3135, 3104, 3074, 3045, 3017, 2989, 2963, 2937, 2911, + 2886, 2862, 2839, 2816, 2794, 2772, 2750, 2729, 2709, 2689, + 2670, 2651, 2632, 2614, 2596, 2578, 2561, 2544, 2527, 2511, + 2495, 2480, 2464, 2449, 2435, 2420, 2406, 2392, 2378, 2364, + 2351, 2338, 2325, 2313, 2300, 2288, 2276, 2264, 2252, 2241, + 2229, 2218, 2207, 2196, 2186, 2175, 2165, 2155, 2145, 2135, + 2125, 2115, 2106, 2096, 2087, 2078, 2069, 2060, 2051, 2042, + 2034, 2025, 2017, 2009, 2000, 1992, 1984, 1977, 1969, 1961, + 1953, 1946, 1938, 1931, 1924, 1917, 1910, 1903, 1896, 1889, + 1882, 1875, 1869, 1862, 1855, 1849, 1843, 1836, 1830, 1824, + 1818, 1812, 1806, 1800, 1794, 1788, 1782, 1776, 1771, 1765, + 1760, 1754, 1749, 1743, 1738, 1733, 1727, 1722, 1717, 1712, + 1707, 1702, 1697, 1692, 1687, 1682, 1677, 1673, 1668, 1663, + 1659, 1654, 1649, 1645, 1640, 1636, 1631, 1627, 1623, 1618, + 1614, 1610, 1606, 1601, 1597, 1593, 1589, 1585, 1581, 1577, + 1573, 1569, 1565, 1561, 1557, 1554, 1550, 1546, 1542, 1539, + 1535, 1531, 1528, 1524, 1520, 1517, 1513, 1510, 1506, 1503, + 1500, + }; + expected_table.resize(max_table_size, 1500); + ASSERT_EQ(table.table, expected_table); + ASSERT_EQ(table.steps_count, 224u); + ASSERT_EQ(table.pixeltime_sum, 734910u); +} + +void test_create_slope_table_large_half_step() +{ + unsigned max_table_size = 1024; + + // created approximately from Canon 8600F table, see the full step test for the data + + MotorSlope slope; + slope.initial_speed_w = 54612; + slope.max_speed_w = 1500; + slope.acceleration = 1.013948e-9; + + auto table = create_slope_table(slope, 3000, StepType::HALF, 4, 8, max_table_size); + + std::vector<std::uint16_t> expected_table = { + 27306, 27306, 10285, 7545, 6240, 5440, 4885, 4471, 4147, 3885, + 3667, 3482, 3322, 3183, 3060, 2950, 2851, 2761, 2679, 2605, + 2536, 2472, 2413, 2358, 2306, 2258, 2213, 2170, 2130, 2092, + 2055, 2021, 1988, 1957, 1927, 1899, 1872, 1846, 1821, 1797, + 1774, 1751, 1730, 1709, 1689, 1670, 1652, 1634, 1616, 1599, + 1583, 1567, 1552, 1537, 1522, 1508, 1500, + }; + expected_table.resize(max_table_size, 1500); + ASSERT_EQ(table.table, expected_table); + ASSERT_EQ(table.steps_count, 60u); + ASSERT_EQ(table.pixeltime_sum, 206294u); + + + table = create_slope_table(slope, 1500, StepType::HALF, 4, 8, max_table_size); + + expected_table = { + 27306, 27306, 10285, 7545, 6240, 5440, 4885, 4471, 4147, 3885, + 3667, 3482, 3322, 3183, 3060, 2950, 2851, 2761, 2679, 2605, + 2536, 2472, 2413, 2358, 2306, 2258, 2213, 2170, 2130, 2092, + 2055, 2021, 1988, 1957, 1927, 1899, 1872, 1846, 1821, 1797, + 1774, 1751, 1730, 1709, 1689, 1670, 1652, 1634, 1616, 1599, + 1583, 1567, 1552, 1537, 1522, 1508, 1494, 1481, 1468, 1455, + 1443, 1431, 1419, 1408, 1397, 1386, 1375, 1364, 1354, 1344, + 1335, 1325, 1316, 1307, 1298, 1289, 1280, 1272, 1263, 1255, + 1247, 1240, 1232, 1224, 1217, 1210, 1203, 1196, 1189, 1182, + 1175, 1169, 1162, 1156, 1150, 1144, 1138, 1132, 1126, 1120, + 1114, 1109, 1103, 1098, 1093, 1087, 1082, 1077, 1072, 1067, + 1062, 1057, 1053, 1048, 1043, 1039, 1034, 1030, 1025, 1021, + 1017, 1012, 1008, 1004, 1000, 996, 992, 988, 984, 980, + 976, 973, 969, 965, 962, 958, 955, 951, 948, 944, + 941, 937, 934, 931, 927, 924, 921, 918, 915, 912, + 909, 906, 903, 900, 897, 894, 891, 888, 885, 882, + 880, 877, 874, 871, 869, 866, 863, 861, 858, 856, + 853, 851, 848, 846, 843, 841, 838, 836, 834, 831, + 829, 827, 824, 822, 820, 818, 815, 813, 811, 809, + 807, 805, 803, 800, 798, 796, 794, 792, 790, 788, + 786, 784, 782, 780, 778, 777, 775, 773, 771, 769, + 767, 765, 764, 762, 760, 758, 756, 755, 753, 751, + 750, + }; + expected_table.resize(max_table_size, 750); + ASSERT_EQ(table.table, expected_table); + ASSERT_EQ(table.steps_count, 224u); + ASSERT_EQ(table.pixeltime_sum, 367399u); +} + +void test_motor() +{ + test_create_slope_table3(); + test_create_slope_table_small_full_step(); + test_create_slope_table_small_full_step_target_speed_too_high(); + test_create_slope_table_small_half_step(); + test_create_slope_table_large_full_step(); + test_create_slope_table_large_half_step(); +} + +} // namespace genesys diff --git a/testsuite/backend/genesys/tests_printers.h b/testsuite/backend/genesys/tests_printers.h new file mode 100644 index 0000000..90becea --- /dev/null +++ b/testsuite/backend/genesys/tests_printers.h @@ -0,0 +1,62 @@ +/* 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. +*/ + +#ifndef SANE_TESTSUITE_BACKEND_GENESYS_TESTS_PRINTERS_H +#define SANE_TESTSUITE_BACKEND_GENESYS_TESTS_PRINTERS_H + +#include "../../../backend/genesys/image_pixel.h" +#include "../../../backend/genesys/utilities.h" +#include <iostream> +#include <iomanip> +#include <vector> + +template<class T> +std::ostream& operator<<(std::ostream& str, const std::vector<T>& arg) +{ + str << genesys::format_vector_unsigned(4, arg) << '\n'; + return str; +} + +inline std::ostream& operator<<(std::ostream& str, const genesys::PixelFormat& arg) +{ + str << static_cast<unsigned>(arg); + return str; +} + +inline std::ostream& operator<<(std::ostream& str, const genesys::Pixel& arg) +{ + str << "{ " << arg.r << ", " << arg.g << ", " << arg.b << " }"; + return str; +} + +inline std::ostream& operator<<(std::ostream& str, const genesys::RawPixel& arg) +{ + auto flags = str.flags(); + str << std::hex; + for (auto el : arg.data) { + str << static_cast<unsigned>(el) << " "; + } + str.flags(flags); + return str; +} + +#endif diff --git a/testsuite/backend/genesys/tests_row_buffer.cpp b/testsuite/backend/genesys/tests_row_buffer.cpp new file mode 100644 index 0000000..73ca86c --- /dev/null +++ b/testsuite/backend/genesys/tests_row_buffer.cpp @@ -0,0 +1,91 @@ +/* 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 "tests.h" +#include "minigtest.h" +#include "tests_printers.h" + +#include "../../../backend/genesys/low.h" + +#include <numeric> + +namespace genesys { + +void test_row_buffer_push_pop_forward(unsigned size) +{ + RowBuffer buf{1}; + + ASSERT_TRUE(buf.empty()); + for (unsigned i = 0; i < size; i++) { + buf.push_back(); + *buf.get_back_row_ptr() = i; + for (unsigned j = 0; j < i + 1; j++) { + ASSERT_EQ(*buf.get_row_ptr(j), j); + } + } + ASSERT_FALSE(buf.empty()); + + for (unsigned i = 0; i < 10; i++) { + ASSERT_EQ(buf.height(), size); + ASSERT_EQ(static_cast<unsigned>(*buf.get_front_row_ptr()), i); + buf.pop_front(); + ASSERT_EQ(buf.height(), size - 1); + buf.push_back(); + *buf.get_back_row_ptr() = i + size; + } +} + +void test_row_buffer_push_pop_backward(unsigned size) +{ + RowBuffer buf{1}; + + ASSERT_TRUE(buf.empty()); + for (unsigned i = 0; i < size; i++) { + buf.push_front(); + *buf.get_front_row_ptr() = i; + for (unsigned j = 0; j < i + 1; j++) { + ASSERT_EQ(*buf.get_row_ptr(j), i - j); + } + } + ASSERT_FALSE(buf.empty()); + + for (unsigned i = 0; i < 10; i++) { + ASSERT_EQ(buf.height(), size); + ASSERT_EQ(static_cast<unsigned>(*buf.get_back_row_ptr()), i); + buf.pop_back(); + ASSERT_EQ(buf.height(), size - 1); + buf.push_front(); + *buf.get_front_row_ptr() = i + size; + } +} + +void test_row_buffer() +{ + for (unsigned size = 1; size < 5; ++size) { + test_row_buffer_push_pop_forward(size); + test_row_buffer_push_pop_backward(size); + } +} + +} // namespace genesys diff --git a/testsuite/backend/genesys/tests_utilities.cpp b/testsuite/backend/genesys/tests_utilities.cpp new file mode 100644 index 0000000..49b9abe --- /dev/null +++ b/testsuite/backend/genesys/tests_utilities.cpp @@ -0,0 +1,110 @@ +/* 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 "tests.h" +#include "minigtest.h" +#include "tests_printers.h" + +#include "../../../backend/genesys/utilities.h" + +namespace genesys { + +void test_utilities_compute_array_percentile_approx_empty() +{ + std::vector<std::uint16_t> data; + data.resize(1, 0); + + ASSERT_RAISES(compute_array_percentile_approx(data.data(), data.data(), 0, 0, 0.0f), + SaneException); +} + +void test_utilities_compute_array_percentile_approx_single_line() +{ + std::vector<std::uint16_t> data = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 + }; + std::vector<std::uint16_t> expected = data; + std::vector<std::uint16_t> result; + result.resize(data.size(), 0); + + compute_array_percentile_approx(result.data(), data.data(), 1, data.size(), 0.5f); + ASSERT_EQ(result, expected); +} + +void test_utilities_compute_array_percentile_approx_multiple_lines() +{ + std::vector<std::uint16_t> data = { + 5, 17, 4, 14, 3, 9, 9, 5, 10, 1, + 6, 1, 0, 18, 8, 5, 11, 11, 15, 12, + 6, 8, 7, 3, 2, 15, 5, 12, 3, 3, + 6, 12, 17, 6, 7, 7, 1, 6, 3, 18, + 10, 5, 8, 0, 14, 3, 3, 7, 10, 5, + 18, 7, 3, 11, 0, 14, 12, 19, 18, 11, + 5, 16, 2, 9, 8, 2, 7, 6, 11, 18, + 16, 5, 2, 2, 14, 18, 19, 13, 16, 1, + 5, 9, 14, 6, 17, 16, 1, 1, 16, 0, + 19, 18, 4, 12, 0, 7, 15, 3, 2, 6, + }; + std::vector<std::uint16_t> result; + result.resize(10, 0); + + std::vector<std::uint16_t> expected = { + 5, 1, 0, 0, 0, 2, 1, 1, 2, 0, + }; + compute_array_percentile_approx(result.data(), data.data(), 10, 10, 0.0f); + ASSERT_EQ(result, expected); + + expected = { + 5, 5, 2, 3, 2, 5, 3, 5, 3, 1, + }; + compute_array_percentile_approx(result.data(), data.data(), 10, 10, 0.25f); + ASSERT_EQ(result, expected); + + expected = { + 6, 9, 4, 9, 8, 9, 9, 7, 11, 6, + }; + compute_array_percentile_approx(result.data(), data.data(), 10, 10, 0.5f); + ASSERT_EQ(result, expected); + + expected = { + 16, 16, 8, 12, 14, 15, 12, 12, 16, 12, + }; + compute_array_percentile_approx(result.data(), data.data(), 10, 10, 0.75f); + ASSERT_EQ(result, expected); + + expected = { + 19, 18, 17, 18, 17, 18, 19, 19, 18, 18, + }; + compute_array_percentile_approx(result.data(), data.data(), 10, 10, 1.0f); + ASSERT_EQ(result, expected); +} + +void test_utilities() +{ + test_utilities_compute_array_percentile_approx_empty(); + test_utilities_compute_array_percentile_approx_single_line(); + test_utilities_compute_array_percentile_approx_multiple_lines(); +} + +} // namespace genesys |