aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorPo Lu2025-02-25 19:12:06 +0800
committerPo Lu2025-02-25 19:13:24 +0800
commit93a185a1fb874ebbcfdac257b50a3d0700a93fb5 (patch)
tree6988717a017fe0494e3f2cdb7fadfdb3468e5ef9 /test
parent53eec34da1bf2fb9381680734a99f3fb11225787 (diff)
downloademacs-93a185a1fb874ebbcfdac257b50a3d0700a93fb5.tar.gz
emacs-93a185a1fb874ebbcfdac257b50a3d0700a93fb5.zip
; Improve Android regression test execution facilities
* test/infra/android/test-controller.el (ats-associated-process): New variable. (ats-start-server): Set coding system to `no-conversion'. (ats-read-connection): If this buffer is associated with a connection, return the same. (ats-establish-connection): New arg INTERACTIVE. Interactively, open a Lisp interaction buffer with this connection as its associated process. (ats-connect): Provide this argument if called interactively. (ats-eval): New argument RAW. Request that encoded forms not be decoded if specified, and decode results. (ats-remote-eval-defuns, ats-remote-eval-print-sexp) (ats-remote-eval-for-interaction) (ats-remote-eval-print-last-sexp, ats-remote-eval-last-sexp) (ats-remote-eval-defun, ats-remote-eval-region-or-buffer) (ats-lisp-interaction-mode-map, ats-lisp-interaction-mode-menu) (ats-lisp-interaction-mode, ats-open-lisp-interaction-buffer) (ats-emacs-test-directory, ats-upload-test) (ats-list-tests-locally, ats-list-tests, ats-run-test): New functions and variables. * test/infra/android/test-driver.el (ats-eval-do-decode): New variable. (ats-process-filter, ats-establish-connection) (ats-initiate-connection): Adjust correspondingly.
Diffstat (limited to 'test')
-rw-r--r--test/infra/android/test-controller.el506
-rw-r--r--test/infra/android/test-driver.el84
2 files changed, 530 insertions, 60 deletions
diff --git a/test/infra/android/test-controller.el b/test/infra/android/test-controller.el
index e82b05d036f..711deca7d29 100644
--- a/test/infra/android/test-controller.el
+++ b/test/infra/android/test-controller.el
@@ -11,7 +11,7 @@
11 11
12;; GNU Emacs is distributed in the hope that it will be useful, 12;; GNU Emacs is distributed in the hope that it will be useful,
13;; but WITHOUT ANY WARRANTY; without even the implied warranty of 13;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;; GNU General Public License for more details. 15;; GNU General Public License for more details.
16 16
17;; You should have received a copy of the GNU General Public License 17;; You should have received a copy of the GNU General Public License
@@ -1295,6 +1295,11 @@ DEVICE is the device where COMMFILE resides."
1295(defvar ats-accepting-connection nil 1295(defvar ats-accepting-connection nil
1296 "UUID of connections being established.") 1296 "UUID of connections being established.")
1297 1297
1298(defvar-local ats-associated-process nil
1299 "ATS process associated with this buffer.
1300Such a process will be returned by `ats-read-connection' without
1301prompting the user.")
1302
1298(defun ats-address-to-hostname (address) 1303(defun ats-address-to-hostname (address)
1299 "Return the hostname component of the address ADDRESS." 1304 "Return the hostname component of the address ADDRESS."
1300 (progn 1305 (progn
@@ -1374,7 +1379,7 @@ Value is the port on which it will listen."
1374 t 1379 t
1375 ats-default-port) 1380 ats-default-port)
1376 :family 'ipv4 1381 :family 'ipv4
1377 :coding 'utf-8-emacs 1382 :coding 'no-conversion
1378 :sentinel #'ats-server-sentinel 1383 :sentinel #'ats-server-sentinel
1379 :log #'ats-server-log))) 1384 :log #'ats-server-log)))
1380 (setq ats-server process) 1385 (setq ats-server process)
@@ -1613,15 +1618,21 @@ the same port."
1613 1618
1614(defun ats-read-connection (prompt) 1619(defun ats-read-connection (prompt)
1615 "Read an ATS connection from the user, with completion. 1620 "Read an ATS connection from the user, with completion.
1616PROMPT is the prompt displayed by `completing-read'. 1621If `ats-associated-process' is set in the current buffer, return
1617Value is a process representing such a connection." 1622this process if it remains alive. PROMPT is the prompt
1618 (let ((procs)) 1623displayed by `completing-read'. Value is a process representing
1619 (dolist (proc (process-list)) 1624such a connection."
1620 (when (process-get proc 'ats-connection-details) 1625 (or (and ats-associated-process
1621 (push (buffer-name (process-buffer proc)) procs))) 1626 (eq (process-status ats-associated-process) 'open)
1622 (let ((buffer (completing-read prompt procs 1627 ats-associated-process)
1623 nil t nil 'ats-read-processes))) 1628 (let ((procs))
1624 (get-buffer-process buffer)))) 1629 (dolist (proc (process-list))
1630 (when (process-get proc 'ats-connection-details)
1631 (push (buffer-name (process-buffer proc)) procs)))
1632 (let ((buffer (completing-read prompt procs
1633 nil t nil
1634 'ats-read-processes)))
1635 (get-buffer-process buffer)))))
1625 1636
1626(defun ats-disconnect (process) 1637(defun ats-disconnect (process)
1627 "Disconnect from the ATS connection represented by PROCESS. 1638 "Disconnect from the ATS connection represented by PROCESS.
@@ -1633,7 +1644,7 @@ forwarding currently in place."
1633 (ats-in-connection-context (get-process process) details 1644 (ats-in-connection-context (get-process process) details
1634 (delete-process process))) 1645 (delete-process process)))
1635 1646
1636(defun ats-establish-connection (process details) 1647(defun ats-establish-connection (process details &optional interactive)
1637 "Finalize a connection represented by PROCESS. 1648 "Finalize a connection represented by PROCESS.
1638DETAILS should be an alist of connection information to which 1649DETAILS should be an alist of connection information to which
1639`ats-adb-host' is appended, with the following keys: 1650`ats-adb-host' is appended, with the following keys:
@@ -1662,6 +1673,9 @@ DETAILS should be an alist of connection information to which
1662 The port on the ADB host system mediating between the local 1673 The port on the ADB host system mediating between the local
1663 and the remote system. 1674 and the remote system.
1664 1675
1676If INTERACTIVE, open a Lisp interaction buffer with
1677`ats-open-lisp-interaction-buffer'.
1678
1665Value is PROCESS itself." 1679Value is PROCESS itself."
1666 (process-put process 'ats-connection-details 1680 (process-put process 'ats-connection-details
1667 (append `((host . ,ats-adb-host) 1681 (append `((host . ,ats-adb-host)
@@ -1680,10 +1694,12 @@ Value is PROCESS itself."
1680 t))) 1694 t)))
1681 (message "Connection established to %s (on %s)" 1695 (message "Connection established to %s (on %s)"
1682 (cdr (assq 'device details)) host)) 1696 (cdr (assq 'device details)) host))
1683 process) 1697 (prog1 process
1698 (when interactive
1699 (ats-open-lisp-interaction-buffer process))))
1684 1700
1685;;;###autoload 1701;;;###autoload
1686(defun ats-connect (device user &optional host) 1702(defun ats-connect (device user &optional host interactive)
1687 "Establish a connection to DEVICE on HOST executing as USER. 1703 "Establish a connection to DEVICE on HOST executing as USER.
1688HOST, if nil, defaults to `ats-adb-host'. 1704HOST, if nil, defaults to `ats-adb-host'.
1689If an instance of Emacs is already executing on DEVICE and the 1705If an instance of Emacs is already executing on DEVICE and the
@@ -1718,7 +1734,7 @@ this machine and an SSH daemon be executing on the host)."
1718 user-alist nil t)))) 1734 user-alist nil t))))
1719 (list device (or (cdr (assoc user user-alist)) 1735 (list device (or (cdr (assoc user user-alist))
1720 (error "Unknown user: %s" user)) 1736 (error "Unknown user: %s" user))
1721 host))) 1737 host t)))
1722 ;; Terminate any existing instances of Emacs executing as this user. 1738 ;; Terminate any existing instances of Emacs executing as this user.
1723 (let* ((ats-adb-host host) 1739 (let* ((ats-adb-host host)
1724 (emacs-aid (ats-get-package-aid device "org.gnu.emacs")) 1740 (emacs-aid (ats-get-package-aid device "org.gnu.emacs"))
@@ -1798,7 +1814,8 @@ this machine and an SSH daemon be executing on the host)."
1798 (remote-port . ,remote-port) 1814 (remote-port . ,remote-port)
1799 (host-port . ,host-port) 1815 (host-port . ,host-port)
1800 (user . ,user) 1816 (user . ,user)
1801 (device . ,device)))))) 1817 (device . ,device))
1818 interactive))))
1802 ;; On failure, cease forwarding to this device, but permit 1819 ;; On failure, cease forwarding to this device, but permit
1803 ;; the connection to the host to remain. 1820 ;; the connection to the host to remain.
1804 (unless process 1821 (unless process
@@ -1841,7 +1858,7 @@ this machine and an SSH daemon be executing on the host)."
1841 :buffer name 1858 :buffer name
1842 :host 'local 1859 :host 'local
1843 :service local-port 1860 :service local-port
1844 :coding 'utf-8-emacs 1861 :coding 'no-conversion
1845 :sentinel #'ats-server-sentinel)) 1862 :sentinel #'ats-server-sentinel))
1846 (process-send-string process "-ok\n") 1863 (process-send-string process "-ok\n")
1847 (ats-establish-connection process 1864 (ats-establish-connection process
@@ -1849,7 +1866,8 @@ this machine and an SSH daemon be executing on the host)."
1849 (local-port . ,local-port) 1866 (local-port . ,local-port)
1850 (host-port . ,host-port) 1867 (host-port . ,host-port)
1851 (user . ,user) 1868 (user . ,user)
1852 (device . ,device)))) 1869 (device . ,device))
1870 interactive))
1853 (error 1871 (error
1854 (when process 1872 (when process
1855 ;; Finalize the failed process as best as can be 1873 ;; Finalize the failed process as best as can be
@@ -1875,18 +1893,22 @@ this machine and an SSH daemon be executing on the host)."
1875 1893
1876;; (defvar ats-eval-tm 0) 1894;; (defvar ats-eval-tm 0)
1877 1895
1878(defun ats-eval (process form &optional as-printed) 1896(defun ats-eval (process form &optional as-printed raw)
1879 "Evaluate FORM in PROCESS, which form must be printable. 1897 "Evaluate FORM in PROCESS, which form must be printable.
1880Form should evaluate to a value that must be printable, or 1898Form should evaluate to a value that must be printable, or
1881signal an error. Value is (ok . VALUE) if no error was 1899signal an error. Value is (ok . VALUE) if no error was
1882signaled, or (error . VALUE) otherwise. 1900signaled, or (error . VALUE) otherwise. If RAW, instruct
1901PROCESS not to attempt to decode the printed representation of
1902FORM as multibyte text; this does not influence the decoding
1903whatever value it returns.
1883 1904
1884Set AS-PRINTED to insist that the value be returned as a string; 1905Set AS-PRINTED to insist that the value be returned as a string;
1885this enables non-printable values to be returned in a meaningful 1906this enables non-printable values to be returned in a meaningful
1886manner." 1907manner."
1887 (ats-in-connection-context process details 1908 (ats-in-connection-context process details
1888 (save-restriction 1909 (save-restriction
1889 (let* ((str (prin1-to-string form)) 1910 (let* ((str (encode-coding-string
1911 (prin1-to-string form) 'utf-8-emacs t))
1890 (length (length str)) 1912 (length (length str))
1891 (serial (setf (alist-get 'eval-serial details) 1913 (serial (setf (alist-get 'eval-serial details)
1892 (1+ (alist-get 'eval-serial details)))) 1914 (1+ (alist-get 'eval-serial details))))
@@ -1897,9 +1919,10 @@ manner."
1897 (point (point)) 1919 (point (point))
1898 size form) 1920 size form)
1899 (process-send-string process 1921 (process-send-string process
1900 (format "-eval %d %d %s\n" serial 1922 (format "-eval %d %d %s %s\n" serial
1901 length 1923 length
1902 (if as-printed "t" "nil"))) 1924 (if as-printed "t" "nil")
1925 (if raw "nil" "t")))
1903 (process-send-string process str) 1926 (process-send-string process str)
1904 ;; Read the resultant form. 1927 ;; Read the resultant form.
1905 (while (not form) 1928 (while (not form)
@@ -1923,9 +1946,444 @@ manner."
1923 (when (>= (- (point-max) (point-min)) size) 1946 (when (>= (- (point-max) (point-min)) size)
1924 (narrow-to-region (point-min) (+ (point-min) size)) 1947 (narrow-to-region (point-min) (+ (point-min) size))
1925 (goto-char (point-min)) 1948 (goto-char (point-min))
1926 (setq form (read (current-buffer))))))) 1949 (setq form (car (read-from-string
1950 (decode-coding-string
1951 (buffer-string)
1952 'utf-8-unix t))))))))
1927 form)))) 1953 form))))
1928 1954
1955
1956
1957;; Remote Lisp Interaction mode.
1958
1959(defvar ats-remote-eval-defuns
1960 '(progn
1961 (defalias 'ats-remote-eval-on-device
1962 #'(lambda (form)
1963 "Remotely evaluate a submitted form FORM.
1964Collect FORM's standard output and return values, and return a
1965list of the form (ok STANDARD-OUTPUT VALUE VALUE-TRUNCATED),
1966where STANDARD-OUTPUT is any output the form has printed or
1967inserted, VALUE is FORM's value, and VALUE-TRUNCATED is FORM's
1968value after truncation as in the manner of `eval-expression',
1969both as strings.
1970
1971If FORM should signal an error, value becomes (error ERROR),
1972where ERROR is a cons of the error's symbol and of its data."
1973 (condition-case error
1974 (let ((standard-output
1975 (get-buffer-create "*ats-standard-output*")))
1976 (with-current-buffer standard-output
1977 (erase-buffer)
1978 (let ((value (eval form nil)))
1979 (list 'ok (buffer-string)
1980 (prin1-to-string value)
1981 (let ((print-length eval-expression-print-length)
1982 (print-level eval-expression-print-level))
1983 (prin1-to-string value))))))
1984 (error (list 'error error))))))
1985 "Forms to be evaluated on the remote device before remote evaluation.")
1986
1987(defun ats-remote-eval-print-sexp
1988 (value value-truncated output &optional no-truncate)
1989 "Print VALUE and VALUE-TRUNCATED (a string) to OUTPUT.
1990The manner of printing is subject to NO-TRUNCATE.
1991Adapted from `elisp--eval-last-sexp-print-value' in
1992`elisp-mode.el'."
1993 (let* ((unabbreviated value) (beg (point)) end)
1994 (prog1 (princ (if no-truncate
1995 value
1996 value-truncated)
1997 output)
1998 (setq end (point))
1999 (when (and (bufferp output)
2000 (or (not (null print-length))
2001 (not (null print-level)))
2002 (not (string= unabbreviated
2003 (buffer-substring-no-properties beg end))))
2004 (last-sexp-setup-props beg end value
2005 unabbreviated
2006 (buffer-substring-no-properties beg end))))))
2007
2008(defun ats-remote-eval-for-interaction (process form &optional no-truncate)
2009 "Evaluate FORM for Lisp interaction in a remote device.
2010PROCESS represents the connection to the said device. Insert
2011text printed by FORM to standard output and its return value on
2012success, as would `eval-last-sexp', and signal an error on
2013failure.
2014If NO-TRUNCATE, print FORM's value in full without truncation."
2015 (let ((details (process-get process 'ats-connection-details))
2016 rc)
2017 ;; First, set up a utility function.
2018 (unless (cdr (assq 'remote-eval-initialized details))
2019 (setq rc (ats-eval process ats-remote-eval-defuns))
2020 (when (eq (car rc) 'error)
2021 (error "Could not initialize remote evaluation: %S"
2022 (cdr rc)))
2023 (process-put process 'ats-connection-details
2024 (cons '(remote-eval-initialized . t) details)))
2025 ;; Next, really evaluate the form, and also, recognize and convert
2026 ;; errors in preparing to evaluate the form appropriately.
2027 (let ((value (ats-eval process
2028 `(let ((eval-expression-print-length
2029 ,eval-expression-print-length)
2030 (eval-expression-print-level
2031 ,eval-expression-print-level))
2032 (ats-remote-eval-on-device ',form)))))
2033 (cond ((eq (car value) 'ok)
2034 ;; The form was read successfully, but evaluation may
2035 ;; nevertheless have terminated with an error.
2036 (let ((value (cdr value)))
2037 (cond ((eq (car value) 'ok)
2038 (insert (cadr value))
2039 (ats-remote-eval-print-sexp (caddr value)
2040 (cadddr value)
2041 (current-buffer)
2042 no-truncate))
2043 ((eq (car value) 'error)
2044 (signal (caadr value)
2045 (cdadr value))))))
2046 ((eq (car value) 'error)
2047 ;; The device could not decode the form.
2048 (error "Error decoding form on device: %S" (cdr value)))))))
2049
2050(defun ats-remote-eval-print-last-sexp (process &optional arg)
2051 "Evaluate sexp before point; print value into the current buffer.
2052Evaluation transpires in the device controlled by the remote
2053connection represented by PROCESS. ARG inhibits truncation of
2054printed values, as in `eval-print-last-sexp'."
2055 (interactive (list (ats-read-connection "Connection: ")
2056 current-prefix-arg))
2057 (insert "\n")
2058 (ats-remote-eval-for-interaction process (elisp--preceding-sexp)
2059 arg)
2060 (insert "\n"))
2061
2062(defun ats-remote-eval-last-sexp (process &optional arg)
2063 "Evaluate sexp before point.
2064Subsequently, print value and inserted text in the echo area.
2065Evaluation transpires in the device controlled by the remote
2066connection represented by PROCESS. ARG inhibits truncation of
2067printed values, as in `eval-print-last-sexp'."
2068 (interactive (list (ats-read-connection "Connection: ")
2069 current-prefix-arg))
2070 (let ((sexp (elisp--preceding-sexp)))
2071 (with-temp-buffer
2072 (ats-remote-eval-for-interaction process sexp arg)
2073 (message (buffer-string)))))
2074
2075(defun ats-remote-eval-defun (process)
2076 "Evaluate defun around or after point.
2077Evaluation transpires in the device controlled by the remote
2078connection represented by PROCESS."
2079 (interactive (list (ats-read-connection "Connection: ")))
2080 (let ((standard-output t) form)
2081 ;; Read the form from the buffer, and record where it ends.
2082 (save-excursion
2083 (end-of-defun)
2084 (beginning-of-defun)
2085 (setq form (read (current-buffer))))
2086 (with-temp-buffer
2087 (ats-remote-eval-for-interaction process form)
2088 (message (buffer-string)))))
2089
2090(defun ats-remote-eval-region-or-buffer (process)
2091 "Evaluate the forms in the active region or the whole buffer.
2092Evaluation transpires in the device controlled by the remote
2093connection represented by PROCESS."
2094 (interactive (list (ats-read-connection "Connection: ")))
2095 (let ((evalstring (if (use-region-p)
2096 (buffer-substring (region-beginning)
2097 (region-end))
2098 (buffer-string))))
2099 (ats-eval process `(with-temp-buffer
2100 (insert ,evalstring)
2101 (eval-buffer)))))
2102
2103(defvar ats-lisp-interaction-mode-map
2104 (let ((map (make-sparse-keymap)))
2105 (define-key map [remap eval-print-last-sexp]
2106 #'ats-remote-eval-print-last-sexp)
2107 (define-key map [remap eval-defun]
2108 #'ats-remote-eval-defun)
2109 (define-key map [remap elisp-eval-region-or-buffer]
2110 #'ats-remote-eval-region-or-buffer)
2111 (define-key map [remap eval-last-sexp]
2112 #'ats-remote-eval-last-sexp)
2113 map)
2114 "Keymap applied in `ats-lisp-interaction-mode' buffers.")
2115
2116(easy-menu-define ats-lisp-interaction-mode-menu
2117 ats-lisp-interaction-mode-map
2118 "Menu for Ats Lisp Interaction mode."
2119 '("Lisp-Interaction"
2120 ["Complete Lisp Symbol" completion-at-point
2121 :help "Perform completion on Lisp symbol preceding point"]
2122 ["Indent or Pretty-Print" indent-pp-sexp
2123 :help "Indent each line of the list starting just after point, or prettyprint it"]
2124 ["Evaluate and Print" ats-remote-eval-print-last-sexp
2125 :help "Evaluate sexp before point; print value into current buffer"]
2126 ["Evaluate Defun" ats-remote-eval-defun
2127 :help "Evaluate the top-level form containing point, or after point"]))
2128
2129(define-derived-mode ats-lisp-interaction-mode lisp-interaction-mode
2130 `("Remote Lisp Interaction"
2131 (:eval (unless (and ats-associated-process
2132 (processp ats-associated-process)
2133 (eq (process-status ats-associated-process)
2134 'open))
2135 ,(propertize " disconnected" 'face 'error))))
2136 "Variant of `lisp-interaction-mode' that executes forms remotely.
2137This derivative of `lisp-interaction-mode' rebinds such commands
2138as \\[eval-print-last-sexp] to variants which submit forms for
2139execution on remote Android devices connected over `adb'. It
2140also disables a number of features unsupported by remote
2141execution facilities, such as edebug.")
2142
2143(defun ats-open-lisp-interaction-buffer (process)
2144 "Open an Ats Lisp Interaction Mode buffer on PROCESS
2145Create and display a buffer in `ats-lisp-interaction-mode'; that
2146is, a mode akin to `lisp-interaction-mode' but which submits
2147forms typed to a remote Android device over the connection
2148represented by PROCESS."
2149 (interactive (list (ats-read-connection "Connection: ")))
2150 (ats-in-connection-context process details
2151 (let ((device (cdr (assq 'device details)))
2152 (user (cdr (assq 'user details))))
2153 (with-current-buffer (get-buffer-create
2154 (format "*Lisp Interaction in %s (on %s%s)*"
2155 device
2156 (or ats-adb-host "localhost")
2157 (if (not (eq user 0))
2158 (format ", as %d" user)
2159 "")))
2160 (ats-lisp-interaction-mode)
2161 (setq ats-associated-process process)
2162 (when (eq (buffer-size) 0)
2163 (insert (format "\
2164;; This buffer enables typed Lisp forms to be executed in the device `%s' on `%s'.
2165;; View the doc string of `ats-lisp-interaction-mode' for specifics.\n\n"
2166 device
2167 (or ats-adb-host "localhost")))
2168 (save-excursion
2169 (goto-char (point-min))
2170 (fill-region (point) (progn
2171 (end-of-line)
2172 (point)))
2173 (goto-char (point-max))
2174 (beginning-of-line)
2175 (fill-region (point) (point-max))))
2176 (pop-to-buffer (current-buffer))))))
2177
2178
2179;; ERT regression testing.
2180
2181(defvar ats-emacs-test-directory
2182 (and load-file-name
2183 (expand-file-name
2184 (concat (file-name-directory load-file-name)
2185 "../../")))
2186 "Directory in which to locate Emacs regression tests, or nil otherwise.")
2187
2188(defun ats-upload-test (process dir test-name)
2189 "Upload a test file and its resources to a remote device.
2190PROCESS represents the connection to the device.
2191TEST-NAME concatenated with \"-tests.el\" should identify a file
2192in DIR implementing a series of ERC regression tests. If there
2193is additionally a directory by the name TEST-NAME-resources in
2194the same directory, upload it to the remote device also.
2195Once uploaded, tests defined in the file may be loaded and
2196executed by means of `ats-exec-tests'."
2197 (interactive
2198 (let* ((connection (ats-read-connection "Connection: "))
2199 (dir ats-emacs-test-directory)
2200 (test (completing-read "Test to upload: "
2201 (ats-list-tests-locally dir)
2202 nil t nil
2203 'ats-uploaded-tests)))
2204 (list connection dir test)))
2205 (let* ((dir-name (file-name-as-directory
2206 (expand-file-name dir)))
2207 (test-file
2208 (concat dir-name test-name "-tests.el"))
2209 (resources-directory
2210 (concat dir-name test-name "-resources"))
2211 ;; Strip all directories from the test name.
2212 (default-directory (file-name-directory test-file)))
2213 (unless (file-regular-p test-file)
2214 (error "Not a regular file: %s" test-file))
2215 ;; Create a compressed tar file. Though a cpio implementation
2216 ;; exists in the sources for Android 2.2's command line tools, yet
2217 ;; it is often deleted in release builds of the OS to reduce storage
2218 ;; utilization, so it is best to resort to tar and gzip, which Emacs
2219 ;; is able to decompress without command line utilities.
2220 (let ((temp-file (make-temp-file "ats-" nil ".tar")))
2221 (unwind-protect
2222 (progn
2223 (let ((bare-test-file (file-name-nondirectory test-file))
2224 (bare-test-resources (file-name-nondirectory test-file)))
2225 (let ((rc (if (file-directory-p resources-directory)
2226 (call-process "tar" nil nil nil "cf" temp-file
2227 bare-test-file bare-test-resources)
2228 (call-process "tar" nil nil nil "cf" temp-file
2229 bare-test-file))))
2230 (unless (eq 0 rc)
2231 (error "tar exited with code: %d" rc)))
2232 ;; Compress this file.
2233 (with-temp-buffer
2234 (set-buffer-multibyte nil)
2235 (let ((rc (call-process "gzip" temp-file '(t nil) nil
2236 "-c" temp-file)))
2237 (unless (eq 0 rc)
2238 (error "gzip -c exited with code: %d" rc))
2239 ;; Write this compressed data to the destination and
2240 ;; decompress it there.
2241 (let ((rc (ats-eval
2242 process
2243 `(with-temp-buffer
2244 (set-buffer-multibyte nil)
2245 (insert ,(buffer-string))
2246 (zlib-decompress-region (point-min)
2247 (point-max))
2248 (let ((dir
2249 (concat (file-name-as-directory
2250 temporary-file-directory)
2251 "ats-tests/" ,test-name)))
2252 (if (file-directory-p dir)
2253 (let ((files (directory-files-recursively
2254 dir ""))
2255 (default-directory dir))
2256 (mapc #'delete-file files))
2257 (make-directory dir t))
2258 (let ((default-directory dir))
2259 (require 'tar-mode)
2260 (tar-mode)
2261 (tar-untar-buffer)))))))
2262 (when (eq (car rc) 'error)
2263 (error "Remote error: %S" (cdr rc)))
2264 (message "Uploaded test `%s'" test-name))))))
2265 (with-demoted-errors "Removing temporary file: %S"
2266 (delete-file temp-file))))))
2267
2268(defun ats-list-tests-locally (dir)
2269 "Return a list of tests defined in DIR.
2270DIR ought to be the `test' directory in the Emacs repository or
2271a likewise structured directory tree."
2272 (let* ((default-directory (expand-file-name dir))
2273 (start (length default-directory)))
2274 (let ((dirs (directory-files-recursively
2275 dir "^[[:alnum:]-]+-tests\\.el$"))
2276 tests)
2277 (dolist (dir dirs)
2278 (let ((len (length dir)))
2279 (push (substring dir start (- len 9)) tests)))
2280 (nreverse tests))))
2281
2282(defun ats-list-tests (process)
2283 "Enumerate those tests which have already been uploaded to PROCESS.
2284Return a list of strings identifying tests which have been
2285uploaded to the remote device represented by PROCESS, as by
2286`ats-upload-tests', and which may be executed with
2287`ats-exec-tests'."
2288 (let ((rc (ats-eval
2289 process
2290 `(let* ((dir (concat (file-name-as-directory
2291 temporary-file-directory)
2292 "ats-tests"))
2293 (len (length (file-name-as-directory dir)))
2294 (default-directory dir)
2295 (is-test-directory '(lambda (dir name)
2296 (file-regular-p
2297 (format "%s/%s-tests.el"
2298 dir name)))))
2299 (let ((dirs
2300 (directory-files-recursively
2301 dir "" t
2302 ;; Do not iterate into directories that are tests of
2303 ;; themselves, or their resources.
2304 (lambda (dir)
2305 (let* ((name (file-name-nondirectory dir)))
2306 (and (not (funcall is-test-directory name dir))
2307 (not (string-suffix-p name "-resources")))))))
2308 (tests nil))
2309 (dolist (dir dirs)
2310 (when (funcall is-test-directory
2311 dir
2312 (file-name-nondirectory dir))
2313 (push (substring dir len) tests)))
2314 (nreverse tests))))))
2315 (when (eq (car rc) 'error)
2316 (error "Remote error: %S" (cdr rc)))
2317 (cdr rc)))
2318
2319(defun ats-run-test (process test &optional selector)
2320 "Run tests defined in a single test TEST on a remote device.
2321PROCESS represents the device on which to execute these tests.
2322SELECTOR is an ERT test selector, as with `ert-select-tests'.
2323\(You may upload tests beforehand by calling `ats-upload-test'.)
2324Display the output of the tests executed in a buffer."
2325 (interactive
2326 (let* ((connection
2327 (ats-read-connection "Connection: "))
2328 (test
2329 (completing-read "Test to execute: "
2330 (ats-list-tests connection)
2331 nil t nil 'ats-tests-executed)))
2332 (list connection test)))
2333 ;; Attempt to byte-compile this test file.
2334 (let ((rc (ats-eval
2335 process
2336 `(progn
2337 (let* ((dir (concat (file-name-as-directory
2338 temporary-file-directory)
2339 "ats-tests/" ,test))
2340 (name ,(file-name-nondirectory test))
2341 (testfile (concat (file-name-as-directory dir)
2342 name "-tests.el")))
2343 (with-temp-buffer
2344 (let ((value (byte-compile-file testfile))
2345 (byte-compile-log-buffer (buffer-name)))
2346 (cond ((eq value 'no-byte-compile)
2347 testfile)
2348 (value
2349 (byte-compile-dest-file testfile))
2350 (t (list (buffer-string))))))))))
2351 (device (cdr (assq 'device (process-get
2352 process 'ats-connection-details))))
2353 file-name)
2354 (cond ((eq (car rc) 'error)
2355 (error "Error during byte-compilation of `%s-tests.el': %S"
2356 test (cdr rc)))
2357 ((listp (cdr rc))
2358 (error
2359 "Encountered errors byte-compiling `%s-tests.el':\n%s"
2360 test (cadr rc)))
2361 (t (setq file-name (cdr rc))))
2362 ;; Delete all tests, load the byte-compiled test file, and execute
2363 ;; those tests just defined subject to SELECTOR.
2364 (setq rc (ats-eval process
2365 `(progn
2366 (require 'ert)
2367 (ert-delete-all-tests)
2368 (load ,file-name)
2369 (with-temp-buffer
2370 (let ((standard-output (current-buffer))
2371 (set-message-function
2372 (lambda (message)
2373 (insert message "\n"))))
2374 (insert ,(format "=== Executing %s on %s ===\n"
2375 test device))
2376 (let ((noninteractive t))
2377 (ert-run-tests-batch ',selector))
2378 (insert "=== Test execution complete ===\n")
2379 (buffer-string))))))
2380 (cond ((eq (car rc) 'error)
2381 (error "Error executing `%s-tests.el': %S" test (cdr rc)))
2382 (t (with-current-buffer (get-buffer-create "*Test Output*")
2383 (goto-char (point-max))
2384 (insert (cdr rc))
2385 (pop-to-buffer (current-buffer)))))))
2386
1929(provide 'test-controller) 2387(provide 'test-controller)
1930 2388
1931;;; test-controller.el ends here 2389;;; test-controller.el ends here
diff --git a/test/infra/android/test-driver.el b/test/infra/android/test-driver.el
index cebe5f032d7..78774176f02 100644
--- a/test/infra/android/test-driver.el
+++ b/test/infra/android/test-driver.el
@@ -1,5 +1,5 @@
1;;; Receive and execute Lisp code submitted by a test controller. -*- lexical-binding: t; -*- 1;;; Receive and execute Lisp code submitted by a test controller. -*- lexical-binding: t; -*-
2;;; $Id: ats-driver.el,v 1.6 2025/02/19 01:56:55 jw Exp $ 2;;; $Id: ats-driver.el,v 1.7 2025/02/25 07:58:35 jw Exp $
3 3
4;; Copyright (C) 2025 Free Software Foundation, Inc. 4;; Copyright (C) 2025 Free Software Foundation, Inc.
5 5
@@ -52,6 +52,9 @@
52(defvar-local ats-eval-serial nil 52(defvar-local ats-eval-serial nil
53 "Serial number identifying this result.") 53 "Serial number identifying this result.")
54 54
55(defvar-local ats-eval-do-decode nil
56 "Whether to decode the form provided as utf-8-emacs.")
57
55(defun ats-process-filter (process string) 58(defun ats-process-filter (process string)
56 "Filter input from `ats-process'. 59 "Filter input from `ats-process'.
57Insert STRING into the connection buffer, till a full command is 60Insert STRING into the connection buffer, till a full command is
@@ -90,7 +93,7 @@ read."
90 (error "Connection rejected; wanted ID=%s, received ID=%s" 93 (error "Connection rejected; wanted ID=%s, received ID=%s"
91 (match-string 2 command) (match-string 1 command))) 94 (match-string 2 command) (match-string 1 command)))
92 ((string-match 95 ((string-match
93 "^-eval \\([[:digit:]]+\\) \\([[:digit:]]+\\) \\(t\\|nil\\)$" 96 "^-eval \\([[:digit:]]+\\) \\([[:digit:]]+\\) \\(t\\|nil\\) \\(t\\|nil\\)$"
94 command) 97 command)
95 (setq ats-eval-serial (string-to-number 98 (setq ats-eval-serial (string-to-number
96 (match-string 1 command)) 99 (match-string 1 command))
@@ -98,45 +101,54 @@ read."
98 (match-string 2 command)) 101 (match-string 2 command))
99 ats-eval-as-printed (equal 102 ats-eval-as-printed (equal
100 (match-string 3 command) 103 (match-string 3 command)
101 "t"))) 104 "t")
105 ats-eval-do-decode (equal
106 (match-string 4 command)
107 "t")))
102 (t (error (concat "Unknown command: " command)))))))) 108 (t (error (concat "Unknown command: " command))))))))
103 (when ats-in-eval 109 (when ats-in-eval
104 ;; Proceed till `ats-in-eval' characters are read. 110 ;; Proceed till `ats-in-eval' characters are read.
105 (when (>= (- (point-max) (point-min)) ats-in-eval) 111 (when (>= (- (point-max) (point-min)) ats-in-eval)
106 (let ((value 112 (unwind-protect
107 (save-restriction 113 (let ((value
108 (narrow-to-region (point-min) (1+ ats-in-eval)) 114 (save-restriction
109 (condition-case err 115 (narrow-to-region (point-min) (1+ ats-in-eval))
110 (let* ((str (buffer-string))) 116 (condition-case err
111 (with-current-buffer "*ATS*" 117 (let* ((str (buffer-string)))
112 (goto-char (point-max)) 118 (with-current-buffer "*ATS*"
113 (let ((inhibit-read-only t)) 119 (goto-char (point-max))
114 (insert "--> " (truncate-string-to-width 120 (let ((inhibit-read-only t))
115 str 72) 121 (insert "--> " (truncate-string-to-width
116 "\n"))) 122 str 256)
117 (let* ((expr (car (read-from-string str))) 123 "\n")))
118 (value (eval expr))) 124 (let* ((str (if ats-eval-do-decode
119 (cons 'ok value))) 125 (decode-coding-string
120 (error (cons 'error err)))))) 126 str 'utf-8-emacs t)
121 (let* ((print-escape-control-characters t) 127 str))
122 (print-escape-newlines t) 128 (expr (car (read-from-string str)))
123 (str (prin1-to-string value))) 129 (value (eval expr)))
124 (if ats-eval-as-printed 130 (cons 'ok value)))
125 (let* ((quoted (prin1-to-string str))) 131 (t (cons 'error err))))))
132 (let* ((print-escape-control-characters t)
133 (print-escape-newlines t)
134 (str (encode-coding-string
135 (prin1-to-string value) 'utf-8-emacs t)))
136 (if ats-eval-as-printed
137 (let* ((quoted (prin1-to-string str)))
138 (process-send-string
139 process (format "\fats-request:%d %d\n"
140 ats-eval-serial
141 (length quoted)))
142 (process-send-string process quoted))
126 (process-send-string 143 (process-send-string
127 process (format "\fats-request:%d %d\n" 144 process (format "\fats-request:%d %d\n"
128 ats-eval-serial 145 ats-eval-serial
129 (length quoted))) 146 (length str)))
130 (process-send-string process quoted)) 147 (process-send-string process str)))
131 (process-send-string 148 (process-send-string process "\n"))
132 process (format "\fats-request:%d %d\n" 149 (delete-region (point-min)
133 ats-eval-serial 150 (+ (point-min) ats-in-eval))
134 (length str))) 151 (setq ats-in-eval nil))))
135 (process-send-string process str)))
136 (process-send-string process "\n"))
137 (delete-region (point-min)
138 (+ (point-min) ats-in-eval))
139 (setq ats-in-eval nil)))
140 ;; Don't loop if the form data is yet to arrive. 152 ;; Don't loop if the form data is yet to arrive.
141 (setq firstchar (char-after (point-min)) 153 (setq firstchar (char-after (point-min))
142 in-eval nil)))))) 154 in-eval nil))))))
@@ -170,7 +182,7 @@ failure."
170 :buffer "*ats connection*" 182 :buffer "*ats connection*"
171 :host host 183 :host host
172 :service port 184 :service port
173 :coding 'utf-8-emacs 185 :coding 'no-conversion
174 :filter #'ats-process-filter)) 186 :filter #'ats-process-filter))
175 (process-send-string ats-process (concat id "\n"))) 187 (process-send-string ats-process (concat id "\n")))
176 188
@@ -191,7 +203,7 @@ the controller."
191 :host 'local 203 :host 'local
192 :service t 204 :service t
193 :family 'ipv4 205 :family 'ipv4
194 :coding 'utf-8-emacs 206 :coding 'no-conversion
195 :log #'ats-driver-log)) 207 :log #'ats-driver-log))
196 (service (process-contact process :service))) 208 (service (process-contact process :service)))
197 (with-temp-buffer 209 (with-temp-buffer