Initial Lime window manager
This commit is contained in:
139
test/x11-portable-activities-smoke.lisp
Normal file
139
test/x11-portable-activities-smoke.lisp
Normal file
@@ -0,0 +1,139 @@
|
||||
;;;; Real windows, keyboard/bar navigation, geometry, and restart on OWN Xvfb.
|
||||
(require :asdf)
|
||||
(asdf:load-asd (truename (merge-pathnames "../lime.asd" *load-truename*)))
|
||||
(asdf:load-system "lime/x11")
|
||||
(lime.config:defconfiguration portable-smoke-config (:navigation-keys))
|
||||
|
||||
(defun run-portable-activity-smoke ()
|
||||
(let ((server nil) (client nil) (backend nil) (checks 0))
|
||||
(unwind-protect
|
||||
(let* ((directory (uiop:ensure-directory-pathname
|
||||
(string-trim '(#\Newline)
|
||||
(uiop:run-program '("mktemp" "-d" "/tmp/lime-portable-XXXXXX") :output :string))))
|
||||
(file (merge-pathnames "session.sexp" directory)))
|
||||
(setf server (uiop:launch-program '("Xvfb" "-displayfd" "1" "-screen" "0" "1200x700x24" "-nolisten" "tcp")
|
||||
:output :stream :error-output :interactive))
|
||||
(let* ((name (format nil ":~D" (parse-integer (read-line (uiop:process-info-output server)))))
|
||||
(heads (list (lime.x11:make-x11-head :head-id "left" :x 0 :y 0 :width 700 :height 700)
|
||||
(lime.x11:make-x11-head :head-id "right" :x 700 :y 0 :width 500 :height 700))))
|
||||
(setf client (xlib:open-default-display name))
|
||||
(labels ((check (truth label) (assert truth () "Portable Activities: ~A" label) (incf checks))
|
||||
(world () (lime.x11:x11-backend-world backend))
|
||||
(current (head) (lime.core:current-presentation (world) head))
|
||||
(start ()
|
||||
(setf backend (lime.x11:start-lime :display-name name :heads heads :session-file file
|
||||
:install-default-keybindings-p nil))
|
||||
(lime.x11:apply-lime-configuration backend portable-smoke-config))
|
||||
(pump ()
|
||||
(xlib:display-finish-output client)
|
||||
(let ((count 0))
|
||||
(lime.x11:run-event-loop backend :timeout 0.005
|
||||
:before-event
|
||||
(lambda (b)
|
||||
(assert (< (incf count) 600) () "Event queue did not settle.")
|
||||
(xlib:display-finish-output (lime.x11::x11-backend-display b))
|
||||
(unless (xlib:event-listen (lime.x11::x11-backend-display b))
|
||||
(setf (lime.x11::x11-backend-running-p b) nil))))))
|
||||
(spawn ()
|
||||
(let ((window (xlib:create-window :parent (xlib:screen-root (xlib:display-default-screen client))
|
||||
:x 0 :y 0 :width 150 :height 100)))
|
||||
(xlib:map-window window) (pump)
|
||||
(lime.x11::%backend-client-id backend window)))
|
||||
(window (id) (gethash id (lime.x11::x11-backend-client-windows backend)))
|
||||
(visible (id) (eq :viewable (xlib:window-map-state (window id))))
|
||||
(tap-activity (head activity)
|
||||
(lime.x11:backend-select-head backend head)
|
||||
(let* ((display (lime.x11::x11-backend-display backend))
|
||||
(super (first (nth-value 6 (xlib:modifier-mapping display))))
|
||||
(slot (lime.x11:backend-activity-slot backend activity))
|
||||
(code (xlib:keysym->keycodes display (+ (char-code #\0) slot))))
|
||||
(xlib/xtest:fake-key-event client super t)
|
||||
(xlib/xtest:fake-key-event client code t)
|
||||
(xlib/xtest:fake-key-event client code nil)
|
||||
(xlib/xtest:fake-key-event client super nil) (pump)))
|
||||
(click-activity (head activity)
|
||||
(let* ((bar (gethash head (lime.x11::x11-backend-head-bars backend)))
|
||||
(entries (lime.x11::%bar-activity-entries backend head))
|
||||
(index (position activity entries :key (lambda (e) (getf e :id)) :test #'equal))
|
||||
(region (nth (1+ index) (lime.x11::x11-head-bar-hit-regions bar)))
|
||||
(bar-window (lime.x11::x11-head-bar-window bar)))
|
||||
(xlib/xtest:fake-motion-event client
|
||||
(+ (xlib:drawable-x bar-window)
|
||||
(floor (+ (lime.x11::x11-head-bar-hit-region-left region)
|
||||
(lime.x11::x11-head-bar-hit-region-right region)) 2)) 10)
|
||||
(pump) (xlib/xtest:fake-button-event client 1 t)
|
||||
(xlib/xtest:fake-button-event client 1 nil) (pump))))
|
||||
(start)
|
||||
(lime.x11:backend-start-activity backend "work" "Work" :head-id "left")
|
||||
(let ((browser (spawn)) (work (current "left")))
|
||||
(lime.x11:backend-select-head backend "right")
|
||||
(lime.x11:backend-start-activity backend "chat" "Chat")
|
||||
(let* ((discord (spawn)) (hidden (spawn)) (chat (current "right")))
|
||||
(lime.x11:backend-hide-client backend hidden)
|
||||
(lime.x11:backend-save-named-view backend "Chat")
|
||||
(lime.x11:backend-toggle-client-floating backend discord)
|
||||
(let ((lease (lime.core:lease-for-client (world) discord))
|
||||
(placement (copy-tree (lime.core:placement-data (first (lime.core:presentation-placements chat))))))
|
||||
(tap-activity "left" "chat")
|
||||
(check (eq chat (current "left")) "numeric key brings original arrangement")
|
||||
(check (eq work (current "right")) "numeric key swaps displaced activity")
|
||||
(check (visible discord) "Discord stays visible")
|
||||
(check (visible browser) "browser stays visible")
|
||||
(check (not (visible hidden)) "hidden window stays hidden")
|
||||
(check (eq lease (lime.core:lease-for-client (world) discord)) "lease identity survives swap")
|
||||
(check (equal placement (lime.core:placement-data (first (lime.core:presentation-placements chat))))
|
||||
"normalized floating geometry survives monitor size changes")
|
||||
(check (< (xlib:drawable-x (window discord)) 700) "Discord is on left in X")
|
||||
(check (>= (xlib:drawable-x (window browser)) 700) "browser is on right in X")
|
||||
(check (= (xlib:window-id (window discord)) (xlib:window-id (xlib:input-focus client)))
|
||||
"keyboard focus is on incoming window")
|
||||
(click-activity "right" "chat")
|
||||
(check (eq chat (current "right")) "bar click uses same portable behavior")
|
||||
(check (eq work (current "left")) "bar click swaps back")
|
||||
(check (equal "right" (lime.x11:x11-backend-focused-head-id backend)) "clicked monitor retains command focus")
|
||||
(check (null (lime.x11::x11-backend-ui-session backend)) "no unnecessary collision menu")
|
||||
(dotimes (i 12)
|
||||
(tap-activity (if (evenp i) "left" "right") "chat")
|
||||
(check (lime.core:validate-world (world)) "repeated swaps preserve model")))
|
||||
(lime.x11:backend-select-head backend "right")
|
||||
(lime.x11:backend-start-activity backend "other" "Other")
|
||||
(let ((other (current "right")))
|
||||
(tap-activity "left" "chat")
|
||||
(check (eq other (current "right")) "inactive return leaves other monitor alone")
|
||||
(check (eq chat (current "left")) "inactive arrangement follows target monitor"))
|
||||
(lime.x11:backend-extend-activity-to-head backend "chat" "right")
|
||||
(check (eq chat (current "left")) "extension leaves original portion alone")
|
||||
(check (null (lime.core:presentation-client-ids (current "right"))) "new portion does not gather windows")
|
||||
(check (eq :extended (lime.x11:backend-activity-navigation-mode backend "chat")) "explicit extended mode")
|
||||
(tap-activity "right" "other") (tap-activity "right" "chat")
|
||||
(check (null (lime.core:presentation-client-ids (current "right"))) "extended empty portion intentionally resumes")
|
||||
(check (eq chat (current "left")) "extended returns do not pull the other portion")
|
||||
(tap-activity "right" "other")
|
||||
(lime.x11:backend-select-head backend "left")
|
||||
(lime.x11:backend-make-activity-portable backend "chat" "left")
|
||||
(check (eq :portable (lime.x11:backend-activity-navigation-mode backend "chat")) "explicit return to portability")
|
||||
(check (not (visible hidden)) "fold does not show hidden window")
|
||||
;; Leave an intentionally empty extended activity for restart.
|
||||
(lime.x11:backend-extend-activity-to-head backend "other" "right")
|
||||
(lime.x11:backend-save-session backend)
|
||||
(let ((before (lime.x11::%session-snapshot backend)))
|
||||
(check (= 3 (getf before :version)) "versioned footprint persistence")
|
||||
(check (lime.x11::%validate-session-envelope before) "saved envelope validates"))
|
||||
(lime.x11:stop-backend backend) (setf backend nil)
|
||||
(start) (pump)
|
||||
(check (not (getf (lime.x11:backend-session-status backend) :blocked-p)) "restart restore succeeds")
|
||||
(check (eq :portable (lime.x11:backend-activity-navigation-mode backend "chat")) "portable state survives restart")
|
||||
(check (eq :extended (lime.x11:backend-activity-navigation-mode backend "other")) "empty extended intent survives restart")
|
||||
(check (not (visible hidden)) "hidden window remains hidden after restart")
|
||||
(tap-activity "right" "chat")
|
||||
(check (equal "chat" (lime.core:presentation-activity-id (current "right"))) "portable key works after restart")
|
||||
(check (visible discord) "surviving application reconnected and moved")
|
||||
(check (null (lime.x11::x11-backend-last-command-error backend)) "no command errors")
|
||||
(check (lime.core:validate-world (world)) "final invariants")
|
||||
(format t "~&Portable Activities smoke: ~D checks passed.~%" checks))))))
|
||||
(when backend (ignore-errors (lime.x11:stop-backend backend)))
|
||||
(when client (ignore-errors (xlib:close-display client :abort t)))
|
||||
(when server (uiop:terminate-process server) (uiop:wait-process server)))))
|
||||
|
||||
(handler-case (run-portable-activity-smoke)
|
||||
(error (c) (format *error-output* "~&Portable Activities smoke failed: ~A~%" c) (uiop:quit 1)))
|
||||
Reference in New Issue
Block a user