diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 4ebf8d507b73..0a6fc40ea588 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -751,6 +751,7 @@ in { sabnzbd = handleTest ./sabnzbd.nix {}; samba = handleTest ./samba.nix {}; samba-wsdd = handleTest ./samba-wsdd.nix {}; + sane = handleTest ./sane.nix {}; sanoid = handleTest ./sanoid.nix {}; scaphandre = handleTest ./scaphandre.nix {}; schleuder = handleTest ./schleuder.nix {}; diff --git a/nixos/tests/sane.nix b/nixos/tests/sane.nix new file mode 100644 index 000000000000..cba1b4d1dc4d --- /dev/null +++ b/nixos/tests/sane.nix @@ -0,0 +1,85 @@ +import ./make-test-python.nix ({ pkgs, ... }: +/* + SANE NixOS test + =============== + SANE is intrisically tied to hardware, so testing it is not straightforward. + However: + - a fake webcam can be created with v4l2loopback + - sane has a backend (v4l) to use a webcam as a scanner + This test creates a webcam /dev/video0, streams a still image with some text + through this webcam, uses SANE to scan from the webcam, and uses OCR to check + that the expected text was scanned. +*/ +let + text = "66263666188646651519653683416"; + fontsConf = pkgs.makeFontsConf { + fontDirectories = [ + pkgs.dejavu_fonts.minimal + ]; + }; + # an image with black on white text spelling "${text}" + # for some reason, the test fails if it's jpg instead of png + # the font is quite large to make OCR easier + image = pkgs.runCommand "image.png" + { + # only imagemagickBig can render text + nativeBuildInputs = [ pkgs.imagemagickBig ]; + FONTCONFIG_FILE = fontsConf; + } '' + magick -pointsize 100 label:${text} $out + ''; +in +{ + name = "sane"; + nodes.machine = { pkgs, config, ... }: { + boot = { + # create /dev/video0 as a fake webcam whose content is filled by ffmpeg + extraModprobeConfig = '' + options v4l2loopback devices=1 max_buffers=2 exclusive_caps=1 card_label=VirtualCam + ''; + kernelModules = [ "v4l2loopback" ]; + extraModulePackages = [ config.boot.kernelPackages.v4l2loopback ]; + }; + systemd.services.fake-webcam = { + wantedBy = [ "multi-user.target" ]; + description = "fill /dev/video0 with ${image}"; + /* HACK: /dev/video0 is a v4l2 only device, it misses one single v4l1 + ioctl, VIDIOCSPICT. But sane only supports v4l1, so it will log that this + ioctl failed, and assume that the pixel format is Y8 (gray). So we tell + ffmpeg to produce this pixel format. + */ + serviceConfig.ExecStart = [ "${pkgs.ffmpeg}/bin/ffmpeg -framerate 30 -re -stream_loop -1 -i ${image} -f v4l2 -pix_fmt gray /dev/video0" ]; + }; + hardware.sane.enable = true; + system.extraDependencies = [ image ]; + environment.systemPackages = [ + pkgs.fswebcam + pkgs.tesseract + pkgs.v4l-utils + ]; + environment.variables.SANE_DEBUG_V4L = "128"; + }; + testScript = '' + start_all() + machine.wait_for_unit("fake-webcam.service") + + # the device only appears when ffmpeg starts producing frames + machine.wait_until_succeeds("scanimage -L | grep /dev/video0") + + machine.succeed("scanimage -L >&2") + + with subtest("debugging: /dev/video0 works"): + machine.succeed("v4l2-ctl --all >&2") + machine.succeed("fswebcam --no-banner /tmp/webcam.jpg") + machine.copy_from_vm("/tmp/webcam.jpg", "webcam") + + # scan with the webcam + machine.succeed("scanimage -o /tmp/scan.png >&2") + machine.copy_from_vm("/tmp/scan.png", "scan") + + # the image should contain "${text}" + output = machine.succeed("tesseract /tmp/scan.png -") + print(output) + assert "${text}" in output, f"expected text ${text} was not found, OCR found {output!r}" + ''; +}) diff --git a/pkgs/applications/graphics/sane/backends/default.nix b/pkgs/applications/graphics/sane/backends/default.nix index 1dc46e1b5ebe..000e8c17f9c9 100644 --- a/pkgs/applications/graphics/sane/backends/default.nix +++ b/pkgs/applications/graphics/sane/backends/default.nix @@ -3,6 +3,7 @@ , avahi, libgphoto2, libieee1284, libjpeg, libpng, libtiff, libusb1, libv4l, net-snmp , curl, systemd, libxml2, poppler, gawk , sane-drivers +, nixosTests # List of { src name backend } attibute sets - see installFirmware below: , extraFirmware ? [] @@ -132,6 +133,10 @@ stdenv.mkDerivation { # https://github.com/NixOS/nixpkgs/issues/224569 enableParallelInstalling = false; + passthru.tests = { + inherit (nixosTests) sane; + }; + meta = with lib; { description = "SANE (Scanner Access Now Easy) backends"; longDescription = ''