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 | |
| parent | 5bb4cf12855ec0151de15d6c5a2354ff08766957 (diff) | |
| parent | 3dade5db2a37543f19f0967901d8d80a52a1e459 (diff) | |
Merge branch 'feature/upstream' into develop
Diffstat (limited to 'testsuite')
26 files changed, 2334 insertions, 37 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 diff --git a/testsuite/sanei/Makefile.am b/testsuite/sanei/Makefile.am index 24fc01e..8da530c 100644 --- a/testsuite/sanei/Makefile.am +++ b/testsuite/sanei/Makefile.am @@ -10,12 +10,14 @@ EXTRA_DIST = data/boolean.conf data/empty.conf data/fixed.conf data/int.conf \  	     data/wrong-fixed.conf data/wrong-range.conf \  	     data/wrong-string-list.conf -TEST_LDADD = ../../sanei/libsanei.la ../../lib/liblib.la $(MATH_LIB) $(USB_LIBS) $(PTHREAD_LIBS) +TEST_LDADD = ../../sanei/libsanei.la ../../lib/liblib.la \ +    $(MATH_LIB) $(USB_LIBS) $(XML_LIBS) $(PTHREAD_LIBS)  check_PROGRAMS = sanei_usb_test test_wire sanei_check_test sanei_config_test sanei_constrain_test  TESTS = $(check_PROGRAMS) -AM_CPPFLAGS += -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include $(USB_CFLAGS) +AM_CPPFLAGS += -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include \ +    $(USB_CFLAGS) $(XML_CFLAGS)  sanei_constrain_test_SOURCES = sanei_constrain_test.c  sanei_constrain_test_LDADD = $(TEST_LDADD) diff --git a/testsuite/tools/data/ascii.ref b/testsuite/tools/data/ascii.ref index 4d66732..20c2bf2 100644 --- a/testsuite/tools/data/ascii.ref +++ b/testsuite/tools/data/ascii.ref @@ -5709,7 +5709,7 @@ backend `genesys'      status basic      url *none*      comment `clone of the HP 2400C' -   model `ScanJet 3670C' +   model `ScanJet 3670'      interface `USB'      usb-vendor-id `0x03f0'      usb-product-id `0x1405' @@ -5722,7 +5722,7 @@ backend `genesys'      usb-product-id `0x1405'      status complete      url *none* -    comment `1200x1200 dpi max, same as HP 3670C' +    comment `1200x1200 dpi max, same as HP 3670'     model `ScanJet 4850C'      interface `USB'      usb-vendor-id `0x03f0' diff --git a/testsuite/tools/data/db.ref b/testsuite/tools/data/db.ref index d51729a..3828667 100644 --- a/testsuite/tools/data/db.ref +++ b/testsuite/tools/data/db.ref @@ -66,7 +66,7 @@  0x03f0	0x1205	root:scanner	0664  # Hewlett-Packard ScanJet 4570C | Hewlett-Packard ScanJet 5500C  0x03f0	0x1305	root:scanner	0664 -# Hewlett-Packard ScanJet 3670C | Hewlett-Packard ScanJet 3690C +# Hewlett-Packard ScanJet 3670 | Hewlett-Packard ScanJet 3690C  0x03f0	0x1405	root:scanner	0664  # Hewlett-Packard ScanJet 5590  0x03f0	0x1705	root:scanner	0664 diff --git a/testsuite/tools/data/hal-new.ref b/testsuite/tools/data/hal-new.ref index c9b7f80..fc48a71 100644 --- a/testsuite/tools/data/hal-new.ref +++ b/testsuite/tools/data/hal-new.ref @@ -175,7 +175,7 @@            <append key="info.capabilities" type="strlist">scanner</append>            <merge key="scanner.access_method" type="string">proprietary</merge>          </match> -        <!-- Hewlett-Packard ScanJet 3670C | Hewlett-Packard ScanJet 3690C --> +        <!-- Hewlett-Packard ScanJet 3670 | Hewlett-Packard ScanJet 3690C -->          <match key="usb.product_id" int="0x1405">            <append key="info.capabilities" type="strlist">scanner</append>            <merge key="scanner.access_method" type="string">proprietary</merge> diff --git a/testsuite/tools/data/hal.ref b/testsuite/tools/data/hal.ref index 18490f2..68ef92a 100644 --- a/testsuite/tools/data/hal.ref +++ b/testsuite/tools/data/hal.ref @@ -175,7 +175,7 @@            <append key="info.capabilities" type="strlist">scanner</append>            <merge key="scanner.access_method" type="string">proprietary</merge>          </match> -        <!-- Hewlett-Packard ScanJet 3670C | Hewlett-Packard ScanJet 3690C --> +        <!-- Hewlett-Packard ScanJet 3670 | Hewlett-Packard ScanJet 3690C -->          <match key="usb.product_id" int="0x1405">            <append key="info.capabilities" type="strlist">scanner</append>            <merge key="scanner.access_method" type="string">proprietary</merge> diff --git a/testsuite/tools/data/html-backends-split.ref b/testsuite/tools/data/html-backends-split.ref index 3455e20..bd8d154 100644 --- a/testsuite/tools/data/html-backends-split.ref +++ b/testsuite/tools/data/html-backends-split.ref @@ -5991,7 +5991,7 @@ Kyocera  <td>clone of the HP 2400C</td>  </tr>  <tr> -<td align=center>ScanJet 3670C</td> +<td align=center>ScanJet 3670</td>  <td align=center>USB</td>  <td align=center>0x03f0/0x1405</td>  <td align=center><font color="#007000">Complete</font></td> @@ -6002,7 +6002,7 @@ Kyocera  <td align=center>USB</td>  <td align=center>0x03f0/0x1405</td>  <td align=center><font color="#007000">Complete</font></td> -<td>1200x1200 dpi max, same as HP 3670C</td> +<td>1200x1200 dpi max, same as HP 3670</td>  </tr>  <tr>  <td align=center>ScanJet 4850C</td> diff --git a/testsuite/tools/data/html-mfgs.ref b/testsuite/tools/data/html-mfgs.ref index f82b30c..1b34bb4 100644 --- a/testsuite/tools/data/html-mfgs.ref +++ b/testsuite/tools/data/html-mfgs.ref @@ -12078,7 +12078,7 @@ hpljm1005<br>(0)  </td>  <td align=center><a href="man/sane-hp3500.5.html">sane-hp3500</a></td>  </tr> -<tr><td align=center>ScanJet 3670C</td> +<tr><td align=center>ScanJet 3670</td>  <td align=center>USB</td>  <td align=center>0x03f0/0x1405</td>  <td align=center><font color="#007000">Complete</font></td> @@ -12093,7 +12093,7 @@ hpljm1005<br>(0)  <td align=center>USB</td>  <td align=center>0x03f0/0x1405</td>  <td align=center><font color="#007000">Complete</font></td> -<td>1200x1200 dpi max, same as HP 3670C</td> +<td>1200x1200 dpi max, same as HP 3670</td>  <td align=center>  <a href="http://www.meier-geinitz.de/sane/genesys-backend/">genesys</a>  <br>(1.0-63) diff --git a/testsuite/tools/data/hwdb.ref b/testsuite/tools/data/hwdb.ref index 7d029e0..9adc73f 100644 --- a/testsuite/tools/data/hwdb.ref +++ b/testsuite/tools/data/hwdb.ref @@ -97,7 +97,7 @@ usb:v03F0p1205*  usb:v03F0p1305*   libsane_matched=yes -# Hewlett-Packard ScanJet 3670C | Hewlett-Packard ScanJet 3690C +# Hewlett-Packard ScanJet 3670 | Hewlett-Packard ScanJet 3690C  usb:v03F0p1405*   libsane_matched=yes diff --git a/testsuite/tools/data/testfile.desc b/testsuite/tools/data/testfile.desc index e59aed3..bae4099 100644 --- a/testsuite/tools/data/testfile.desc +++ b/testsuite/tools/data/testfile.desc @@ -4894,7 +4894,7 @@  :status :basic  :comment "clone of the HP 2400C" -:model "ScanJet 3670C" +:model "ScanJet 3670"  :interface "USB"  :usbid "0x03f0" "0x1405"  :status :complete @@ -4904,7 +4904,7 @@  :interface "USB"  :usbid "0x03f0" "0x1405"  :status :complete -:comment "1200x1200 dpi max, same as HP 3670C" +:comment "1200x1200 dpi max, same as HP 3670"  :model "ScanJet 4850C"  :interface "USB" diff --git a/testsuite/tools/data/udev+acl.ref b/testsuite/tools/data/udev+acl.ref index 4721034..bcedd50 100644 --- a/testsuite/tools/data/udev+acl.ref +++ b/testsuite/tools/data/udev+acl.ref @@ -81,7 +81,7 @@ ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1105", ENV{libsane_matched}="yes"  ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1205", ENV{libsane_matched}="yes"  # Hewlett-Packard ScanJet 4570C | Hewlett-Packard ScanJet 5500C  ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1305", ENV{libsane_matched}="yes" -# Hewlett-Packard ScanJet 3670C | Hewlett-Packard ScanJet 3690C +# Hewlett-Packard ScanJet 3670 | Hewlett-Packard ScanJet 3690C  ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1405", ENV{libsane_matched}="yes"  # Hewlett-Packard ScanJet 5590  ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1705", ENV{libsane_matched}="yes" diff --git a/testsuite/tools/data/udev.ref b/testsuite/tools/data/udev.ref index 9a221fc..bd448af 100644 --- a/testsuite/tools/data/udev.ref +++ b/testsuite/tools/data/udev.ref @@ -81,7 +81,7 @@ ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1105", MODE="0664", GROUP="scanner",  ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1205", MODE="0664", GROUP="scanner", ENV{libsane_matched}="yes"  # Hewlett-Packard ScanJet 4570C | Hewlett-Packard ScanJet 5500C  ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1305", MODE="0664", GROUP="scanner", ENV{libsane_matched}="yes" -# Hewlett-Packard ScanJet 3670C | Hewlett-Packard ScanJet 3690C +# Hewlett-Packard ScanJet 3670 | Hewlett-Packard ScanJet 3690C  ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1405", MODE="0664", GROUP="scanner", ENV{libsane_matched}="yes"  # Hewlett-Packard ScanJet 5590  ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1705", MODE="0664", GROUP="scanner", ENV{libsane_matched}="yes" diff --git a/testsuite/tools/data/usermap.ref b/testsuite/tools/data/usermap.ref index e02d73a..b1d5a9f 100644 --- a/testsuite/tools/data/usermap.ref +++ b/testsuite/tools/data/usermap.ref @@ -64,7 +64,7 @@ libusbscanner 0x0003 0x03f0 0x1105 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0  libusbscanner 0x0003 0x03f0 0x1205 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000  # Hewlett-Packard ScanJet 4570C | Hewlett-Packard ScanJet 5500C  libusbscanner 0x0003 0x03f0 0x1305 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 -# Hewlett-Packard ScanJet 3670C | Hewlett-Packard ScanJet 3690C +# Hewlett-Packard ScanJet 3670 | Hewlett-Packard ScanJet 3690C  libusbscanner 0x0003 0x03f0 0x1405 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000  # Hewlett-Packard ScanJet 5590  libusbscanner 0x0003 0x03f0 0x1705 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 diff --git a/testsuite/tools/data/xml.ref b/testsuite/tools/data/xml.ref index 0618093..3c5b861 100644 --- a/testsuite/tools/data/xml.ref +++ b/testsuite/tools/data/xml.ref @@ -6609,7 +6609,7 @@      <url>*none*</url>      <comment>clone of the HP 2400C</comment>     </model> -   <model name="ScanJet 3670C"> +   <model name="ScanJet 3670">      <interface>USB</interface>      <usbvendorid>0x03f0</usbvendorid>      <usbproductid>0x1405</usbproductid> @@ -6623,7 +6623,7 @@      <usbproductid>0x1405</usbproductid>      <status>complete</status>      <url>*none*</url> -    <comment>1200x1200 dpi max, same as HP 3670C</comment> +    <comment>1200x1200 dpi max, same as HP 3670</comment>     </model>     <model name="ScanJet 4850C">      <interface>USB</interface> | 
