diff options
| author | Po Lu | 2025-02-25 19:12:06 +0800 |
|---|---|---|
| committer | Po Lu | 2025-02-25 19:13:24 +0800 |
| commit | 93a185a1fb874ebbcfdac257b50a3d0700a93fb5 (patch) | |
| tree | 6988717a017fe0494e3f2cdb7fadfdb3468e5ef9 /test | |
| parent | 53eec34da1bf2fb9381680734a99f3fb11225787 (diff) | |
| download | emacs-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.el | 506 | ||||
| -rw-r--r-- | test/infra/android/test-driver.el | 84 |
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. | ||
| 1300 | Such a process will be returned by `ats-read-connection' without | ||
| 1301 | prompting 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. |
| 1616 | PROMPT is the prompt displayed by `completing-read'. | 1621 | If `ats-associated-process' is set in the current buffer, return |
| 1617 | Value is a process representing such a connection." | 1622 | this process if it remains alive. PROMPT is the prompt |
| 1618 | (let ((procs)) | 1623 | displayed by `completing-read'. Value is a process representing |
| 1619 | (dolist (proc (process-list)) | 1624 | such 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. |
| 1638 | DETAILS should be an alist of connection information to which | 1649 | DETAILS 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 | ||
| 1676 | If INTERACTIVE, open a Lisp interaction buffer with | ||
| 1677 | `ats-open-lisp-interaction-buffer'. | ||
| 1678 | |||
| 1665 | Value is PROCESS itself." | 1679 | Value 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. |
| 1688 | HOST, if nil, defaults to `ats-adb-host'. | 1704 | HOST, if nil, defaults to `ats-adb-host'. |
| 1689 | If an instance of Emacs is already executing on DEVICE and the | 1705 | If 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. |
| 1880 | Form should evaluate to a value that must be printable, or | 1898 | Form should evaluate to a value that must be printable, or |
| 1881 | signal an error. Value is (ok . VALUE) if no error was | 1899 | signal an error. Value is (ok . VALUE) if no error was |
| 1882 | signaled, or (error . VALUE) otherwise. | 1900 | signaled, or (error . VALUE) otherwise. If RAW, instruct |
| 1901 | PROCESS not to attempt to decode the printed representation of | ||
| 1902 | FORM as multibyte text; this does not influence the decoding | ||
| 1903 | whatever value it returns. | ||
| 1883 | 1904 | ||
| 1884 | Set AS-PRINTED to insist that the value be returned as a string; | 1905 | Set AS-PRINTED to insist that the value be returned as a string; |
| 1885 | this enables non-printable values to be returned in a meaningful | 1906 | this enables non-printable values to be returned in a meaningful |
| 1886 | manner." | 1907 | manner." |
| 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. | ||
| 1964 | Collect FORM's standard output and return values, and return a | ||
| 1965 | list of the form (ok STANDARD-OUTPUT VALUE VALUE-TRUNCATED), | ||
| 1966 | where STANDARD-OUTPUT is any output the form has printed or | ||
| 1967 | inserted, VALUE is FORM's value, and VALUE-TRUNCATED is FORM's | ||
| 1968 | value after truncation as in the manner of `eval-expression', | ||
| 1969 | both as strings. | ||
| 1970 | |||
| 1971 | If FORM should signal an error, value becomes (error ERROR), | ||
| 1972 | where 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. | ||
| 1990 | The manner of printing is subject to NO-TRUNCATE. | ||
| 1991 | Adapted 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. | ||
| 2010 | PROCESS represents the connection to the said device. Insert | ||
| 2011 | text printed by FORM to standard output and its return value on | ||
| 2012 | success, as would `eval-last-sexp', and signal an error on | ||
| 2013 | failure. | ||
| 2014 | If 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. | ||
| 2052 | Evaluation transpires in the device controlled by the remote | ||
| 2053 | connection represented by PROCESS. ARG inhibits truncation of | ||
| 2054 | printed 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. | ||
| 2064 | Subsequently, print value and inserted text in the echo area. | ||
| 2065 | Evaluation transpires in the device controlled by the remote | ||
| 2066 | connection represented by PROCESS. ARG inhibits truncation of | ||
| 2067 | printed 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. | ||
| 2077 | Evaluation transpires in the device controlled by the remote | ||
| 2078 | connection 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. | ||
| 2092 | Evaluation transpires in the device controlled by the remote | ||
| 2093 | connection 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. | ||
| 2137 | This derivative of `lisp-interaction-mode' rebinds such commands | ||
| 2138 | as \\[eval-print-last-sexp] to variants which submit forms for | ||
| 2139 | execution on remote Android devices connected over `adb'. It | ||
| 2140 | also disables a number of features unsupported by remote | ||
| 2141 | execution facilities, such as edebug.") | ||
| 2142 | |||
| 2143 | (defun ats-open-lisp-interaction-buffer (process) | ||
| 2144 | "Open an Ats Lisp Interaction Mode buffer on PROCESS | ||
| 2145 | Create and display a buffer in `ats-lisp-interaction-mode'; that | ||
| 2146 | is, a mode akin to `lisp-interaction-mode' but which submits | ||
| 2147 | forms typed to a remote Android device over the connection | ||
| 2148 | represented 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. | ||
| 2190 | PROCESS represents the connection to the device. | ||
| 2191 | TEST-NAME concatenated with \"-tests.el\" should identify a file | ||
| 2192 | in DIR implementing a series of ERC regression tests. If there | ||
| 2193 | is additionally a directory by the name TEST-NAME-resources in | ||
| 2194 | the same directory, upload it to the remote device also. | ||
| 2195 | Once uploaded, tests defined in the file may be loaded and | ||
| 2196 | executed 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. | ||
| 2270 | DIR ought to be the `test' directory in the Emacs repository or | ||
| 2271 | a 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. | ||
| 2284 | Return a list of strings identifying tests which have been | ||
| 2285 | uploaded 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. | ||
| 2321 | PROCESS represents the device on which to execute these tests. | ||
| 2322 | SELECTOR is an ERT test selector, as with `ert-select-tests'. | ||
| 2323 | \(You may upload tests beforehand by calling `ats-upload-test'.) | ||
| 2324 | Display 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'. |
| 57 | Insert STRING into the connection buffer, till a full command is | 60 | Insert 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 |