#!/bin/bash # Does Wonder Ink print — and does a job really reach CUPS? # # The lab's CUPS gets an "Elmer" built from Elmer's own PPD and options, # writing to a file instead of socket://10.10.0.155:9100, so every job # goes through the same filters the real printer's jobs do and none can # reach a printer. A second queue refuses jobs, to show a teacher is told. # # Each scenario runs the INSTALLED classroom-wonderink under a real # QtWebEngine (offscreen), presses the preview's buttons, and is then # judged by CUPS's own record of what it was given. # # tools/lab/wonderink-print-check [SCENARIO...] set -uo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" NAME="${LAB_NAME:-classroom-v2-lab}" COMMAND="${WONDERINK:-/usr/local/bin/classroom-wonderink}" lab() { docker exec -i "$NAME" bash -c "$1"; } docker cp "$ROOT/tools/lab/wonderink-print-rig.py" "$NAME:/tmp/wonderink-print-rig.py" >/dev/null lab 'chmod a+r /tmp/wonderink-print-rig.py' # The church's printer network is fenced off inside the lab. Checked, not # assumed: a lab job must never be able to reach the real Elmer. lab 'ip route replace blackhole 10.10.0.0/16 2>/dev/null ! timeout 3 bash -c "exec 3<>/dev/tcp/10.10.0.155/9100" 2>/dev/null' || { echo "REFUSING TO CONTINUE: the real Elmer is reachable from the lab" >&2 exit 1 } lab ' set -e export DEBIAN_FRONTEND=noninteractive command -v lpadmin >/dev/null && command -v pdftoppm >/dev/null || \ apt-get install -y -qq cups cups-client poppler-utils >/dev/null 2>&1 dpkg -s foomatic-db-compressed-ppds >/dev/null 2>&1 || \ apt-get install -y -qq foomatic-db-compressed-ppds cups-filters ghostscript >/dev/null 2>&1 grep -q "^FileDevice Yes" /etc/cups/cups-files.conf || echo "FileDevice Yes" >> /etc/cups/cups-files.conf pkill -x cupsd || true; sleep 1; /usr/sbin/cupsd; sleep 1 cupsctl --debug-logging : > /var/log/cups/error_log mkdir -p /var/lab-print && chown lp /var/lab-print driver="foomatic-db-compressed-ppds:0/ppd/foomatic-ppd/Generic-PCL_6_PCL_XL_Printer-pxlcolor.ppd" for q in $(lpstat -e 2>/dev/null); do lpadmin -x "$q"; done lpadmin -p Elmer -E -v file:///var/lab-print/elmer.pxl -m "$driver" \ -D "LAB STAND-IN for Elmer: its PPD, a file for a printer" \ -o PrintoutMode=Normal -o ColorModel=Color 2>/dev/null lpadmin -d Elmer lpadmin -p Refusing -E -v file:///dev/null -m "$driver" -D "LAB: refuses jobs" 2>/dev/null cupsreject Refusing id teacher >/dev/null 2>&1 || useradd -m teacher install -d -o teacher -m 700 /tmp/teacher-rt # An inline PDF, served the way a site shows one rather than sends it. mkdir -p /tmp/www pgrep -f "http.server 8088" >/dev/null || (cd /tmp/www && setsid python3 -m http.server 8088 >/dev/null 2>&1 &) ' || { echo "could not prepare the lab's CUPS" >&2; exit 1; } passed=0 failed=0 judge() { if eval "$2"; then printf ' \033[32mPASS\033[0m %s\n' "$1" passed=$((passed + 1)) else printf ' \033[31mFAIL\033[0m %s\n' "$1" failed=$((failed + 1)) fi } # Runs one scenario; leaves its output in $out and the jobs it caused in $jobs. run() { local before before=$(lab 'lpstat -W all -o 2>/dev/null | wc -l') out=$(lab "su teacher -c 'cd /tmp; XDG_RUNTIME_DIR=/tmp/teacher-rt QT_QPA_PLATFORM=offscreen \ QTWEBENGINE_CHROMIUM_FLAGS=\"--no-sandbox --disable-gpu\" \ timeout 120 python3 /tmp/wonderink-print-rig.py $COMMAND $1 2>&1'; echo \"exit=\$?\"") sleep 3 jobs=$(lab "lpstat -W all -o | tail -n +$((before + 1)) | awk '{print \$1}' | tr '\n' ' '") job_options() { lab "grep -E '\\[Job ${1#*-}\\] argv\\[(4|5)\\]' /var/log/cups/error_log"; } job_device() { lab "grep -E '\\[Job ${1#*-}\\]' /var/log/cups/error_log | grep -oE 'sDEVICE=[a-z]+' | sort -u"; } } count() { wc -w <<< "$jobs"; } said() { grep -q -- "$1" <<< "$out"; } exited_cleanly() { said 'exit=0' && ! said 'Traceback' && ! said 'Segmentation' && ! said 'Aborted'; } scenarios=("$@") [[ ${#scenarios[@]} -gt 0 ]] || scenarios=(web pdf pdf-options pdf-viewer-button inline options bad-range broken cancel page-closes-itself close-window page-prints-again repeat) # The inline scenario needs a PDF to serve; the worksheet scenario writes one. lab 'test -f /tmp/www/ark.pdf' || { run pdf >/dev/null; lab 'cp "/home/teacher/Downloads/Wonder Ink/Colour the Ark.pdf" /tmp/www/ark.pdf; chmod a+r /tmp/www/ark.pdf'; } for scenario in "${scenarios[@]}"; do echo "$scenario" run "$scenario" judge "Wonder Ink did not crash, and exited by itself" exited_cleanly case "$scenario" in web) judge "the preview showed the lesson, 3 Letter pages, Elmer chosen" \ "said \"pages=3 printers=\\['Elmer', 'Refusing'\\] paper=Letter\"" judge "one job reached CUPS" '[[ $(count) -eq 1 ]]' judge "Elmer's filters printed it in colour" '[[ "$(job_device $jobs)" == *pxlcolor* ]]' judge "and nothing was left behind" "said 'scratch_left=\\[\\]'" ;; pdf|pdf-viewer-button) judge "the preview showed the worksheet's own 2 pages" "said 'pages=2'" judge "one job reached CUPS" '[[ $(count) -eq 1 ]]' ;; pdf-options) judge "one job" '[[ $(count) -eq 1 ]]' judge "page 2 only, three copies" \ '[[ "$(job_options $jobs)" == *"argv[4]=\"3\""* && "$(job_options $jobs)" == *page-ranges=2* ]]' judge "in black and white, by the driver's own option" \ '[[ "$(job_options $jobs)" == *ColorModel=Grayscale* && "$(job_device $jobs)" == *pxlmono* ]]' ;; inline) judge "the inline PDF was recognised" "said \"mime='application/pdf'\"" judge "and opened as a worksheet tab" "said 'current_is_pdf=True'" judge "one job reached CUPS" '[[ $(count) -eq 1 ]]' ;; options) judge "pages 2-3, two copies, black and white" \ '[[ "$(job_options $jobs)" == *page-ranges=2-3* && "$(job_options $jobs)" == *"argv[4]=\"2\""* && "$(job_device $jobs)" == *pxlmono* ]]' ;; bad-range) judge "a page that does not exist is explained, not printed" "said 'This document has 3 pages'" judge "and once corrected, one job" '[[ $(count) -eq 1 ]]' ;; broken) judge "a refusing printer is reported in CUPS's words" "said 'Refusing did not take the job'" judge "and no job is claimed" '[[ $(count) -eq 0 ]] && ! said "NOTIFY Sent"' ;; cancel) judge "Cancel sends nothing" '[[ $(count) -eq 0 ]]' ;; page-closes-itself) judge "the preview says the page closed, and cannot print" \ "said \"status='The page was closed.' print_enabled=False\"" judge "and sends nothing" '[[ $(count) -eq 0 ]]' ;; close-window) judge "closing Wonder Ink with the preview open sends nothing" '[[ $(count) -eq 0 ]]' ;; page-prints-again) judge "a second print request does not open a second preview" "said 'still one preview=True'" judge "one job" '[[ $(count) -eq 1 ]]' ;; repeat) judge "five prints in a row, five jobs" '[[ $(count) -eq 5 ]]' ;; esac [[ "$failed" -eq 0 ]] || { echo "$out" | tail -15 | sed 's/^/ /'; } done echo echo "$passed passed, $failed failed" [[ "$failed" -eq 0 ]]