From 90d371f35eeab46d91e435334eb0cf1daca0443a Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Tue, 1 Jun 2021 18:06:07 +0200 Subject: [PATCH 01/58] wip: new function to get the server config with the learn-ocaml-client --- learn-ocaml.el | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/learn-ocaml.el b/learn-ocaml.el index 04268e5..ef4b5c5 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -59,6 +59,8 @@ (defvar learn-ocaml-temp-dir nil) +(defvar learn-ocaml-use-pswd nil) + (defvar learn-ocaml-log-buffer nil) (defun learn-ocaml-log-buffer () @@ -170,6 +172,13 @@ Function added in the `kill-emacs-query-functions' hook." (ignore-errors (delete-directory (learn-ocaml-temp-dir))))) t) +(defun learn-ocaml-server-config (json) + "Set the global variable learn-ocaml-use-pswd according +to the boolean contained in the json returned by the client" + (setq learn-ocaml-use-pswd + (json-get (json-read-from-string "{\"use_passwd\": true}") + "use_passwd"))) + ;; ;; package.el shortcut ;; @@ -294,6 +303,11 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (shell-command-to-string (concat (shell-quote-argument learn-ocaml-command-name) " --version"))) +(defun learn-ocaml-client-config () + "Run \"learn-ocaml-client server-config\"." + (shell-command-to-string + (concat (shell-quote-argument learn-ocaml-command-name) " server-config"))) + (cl-defun learn-ocaml-init-cmd (&key token server nickname secret callback) "Run \"learn-ocaml-client init\" with options." (learn-ocaml-print-time-stamp) From 9cfd10c2122e152655a5e9919c69b8bbdab20704 Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Wed, 2 Jun 2021 12:15:20 +0200 Subject: [PATCH 02/58] wip: start of the implementation of the connection choice function --- learn-ocaml.el | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index ef4b5c5..c3337f7 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -95,6 +95,11 @@ Call `get-buffer-create' if need be, to ensure it is a live buffer." ;; Utility functions ;; +(defun learn-ocaml-client-version-le-0.13 (version) + "Check if the learn-ocaml-client version is lower or equal +to 0.13 to know if the user can use a password and not only a token." + (string= (seq-subseq version 2 4) "13")) + (defun learn-ocaml--rstrip (str) "Remove the trailing newline in STR." (replace-regexp-in-string "\n\\'" "" str)) @@ -176,8 +181,9 @@ Function added in the `kill-emacs-query-functions' hook." "Set the global variable learn-ocaml-use-pswd according to the boolean contained in the json returned by the client" (setq learn-ocaml-use-pswd - (json-get (json-read-from-string "{\"use_passwd\": true}") - "use_passwd"))) + (cdr (assoc 'use_passwd + (json-read-from-string + "{\"use_passwd\": true, \"version\": \"1.12\"}"))))) ;; ;; package.el shortcut @@ -788,6 +794,29 @@ Note: this function will be used by `learn-ocaml-on-load-aux'." nil callback)))))) + +(defun learn-ocaml-connection () + "Connect the user with a (login,passwd) or a token" + (if ( learn-ocaml-client-version-le-0.13 (learn-ocaml-client-version)) + (cl-case (x-popup-dialog + t `("Do you prefer to connect with a token or a login and a password?\n" + ("(login,password)" . 1) + ("token" . 2))) + (1 (let* ((login_password (learn-ocaml-get-login-password)) + (login (car login_password)) + (password (cdr login_password))))) + + (2 (let ((token (read-string "Enter token: "))) + (learn-ocaml-use-metadata-cmd + token + nil + (lambda (_) + (message-box "Token saved.")))))))) + +(defun learn-ocaml-get-login-password () + "Ask interactively the login and the password to the user" + (cons (read-string "Enter login: ") (read-passwd "Enter password: "))) + (defun learn-ocaml-on-load-aux (token server callback) "At load time: ensure a TOKEN and SERVER are set, then run CALLBACK. If SERVER is \"\", interactively ask a server url. From d51430427c9b65add2675d8d5657c09f24e3eef5 Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Thu, 3 Jun 2021 09:47:41 +0200 Subject: [PATCH 03/58] wip: implementation function connection with login password --- learn-ocaml.el | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index c3337f7..f533721 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -96,9 +96,12 @@ Call `get-buffer-create' if need be, to ensure it is a live buffer." ;; (defun learn-ocaml-client-version-le-0.13 (version) - "Check if the learn-ocaml-client version is lower or equal + "Check if the learn-ocaml-client version is lower or equal to 0.13 to know if the user can use a password and not only a token." - (string= (seq-subseq version 2 4) "13")) + (string= (seq-subseq "0.12" 2 4) "12")) + + +(learn-ocaml-client-version-le-0.13 (learn-ocaml-client-version)) (defun learn-ocaml--rstrip (str) "Remove the trailing newline in STR." @@ -293,10 +296,12 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (let ((log (buffer-string))) (error "Process errored. Full log:\n%s" log)))))))) -(cl-defun learn-ocaml-command-constructor (&key command token server local id html dont-submit param1 param2) +(cl-defun learn-ocaml-command-constructor (&key command token login password server local id html dont-submit param1 param2) "Construct a shell command with `learn-ocaml-command-name' and options." (let* ((server-option (when server (concat "--server=" server))) (token-option (when token (concat "--token=" token))) + (login-opton (when login (login))) + (password-option (when password (password))) (local-option (when local "--local")) (id-option (when id (concat "--id=" id))) (html-option (when html "--html")) @@ -794,6 +799,42 @@ Note: this function will be used by `learn-ocaml-on-load-aux'." nil callback)))))) +(cl-defun learn-ocaml-init-user-cmd (&key email password nickname secret callback) + "Run learn-ocaml init-user with options." + (learn-ocaml-print-time-stamp) + (learn-ocaml-make-process-wrapper + :name "init" + :command (learn-ocaml-command-constructor + :login email + :password password + :param1 nickname + :param2 secret + :command "init-user") + :stderr (learn-ocaml-log-buffer) + :sentinel (apply-partially + #'learn-ocaml-error-handler + nil + callback))) + ) + +(cl-defun learn-ocaml-init-cmd (&key token server nickname secret callback) + "Run \"learn-ocaml-client init\" with options." + (learn-ocaml-print-time-stamp) + (learn-ocaml-make-process-wrapper + :name "init" + :command (learn-ocaml-command-constructor + :token token + :server server + :param1 nickname + :param2 secret + :command "init") + :stderr (learn-ocaml-log-buffer) + :sentinel (apply-partially + #'learn-ocaml-error-handler + nil + callback))) + + (defun learn-ocaml-connection () "Connect the user with a (login,passwd) or a token" From b715aaa10be5e91816e1ef153f5b495a99df1b4a Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Thu, 3 Jun 2021 16:51:28 +0200 Subject: [PATCH 04/58] wip: call the learn-ocaml-client in order to login a user --- learn-ocaml.el | 78 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index f533721..5c38473 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -26,6 +26,7 @@ (require 'cl-lib) (require 'browse-url) (require 'json) +(require 'subr-x) (require 'package) ; for #'learn-ocaml-upgrade-packages @@ -95,14 +96,6 @@ Call `get-buffer-create' if need be, to ensure it is a live buffer." ;; Utility functions ;; -(defun learn-ocaml-client-version-le-0.13 (version) - "Check if the learn-ocaml-client version is lower or equal -to 0.13 to know if the user can use a password and not only a token." - (string= (seq-subseq "0.12" 2 4) "12")) - - -(learn-ocaml-client-version-le-0.13 (learn-ocaml-client-version)) - (defun learn-ocaml--rstrip (str) "Remove the trailing newline in STR." (replace-regexp-in-string "\n\\'" "" str)) @@ -272,6 +265,8 @@ add \"opam var bin\" (or another directory) in `exec-path'." (apply #'learn-ocaml-make-process-wrapper args) ; this could be a loop nil)))) + + (defun learn-ocaml-error-handler (buffer callback proc string) "Get text from BUFFER and pass it to the CALLBACK. To be used as a `make-process' sentinel, using args PROC and STRING." @@ -296,12 +291,10 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (let ((log (buffer-string))) (error "Process errored. Full log:\n%s" log)))))))) -(cl-defun learn-ocaml-command-constructor (&key command token login password server local id html dont-submit param1 param2) +(cl-defun learn-ocaml-command-constructor (&key command token server local id html dont-submit param1 param2) "Construct a shell command with `learn-ocaml-command-name' and options." (let* ((server-option (when server (concat "--server=" server))) (token-option (when token (concat "--token=" token))) - (login-opton (when login (login))) - (password-option (when password (password))) (local-option (when local "--local")) (id-option (when id (concat "--id=" id))) (html-option (when html "--html")) @@ -311,15 +304,29 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (defun learn-ocaml-client-version () "Run \"learn-ocaml-client --version\"." + (string-trim (shell-command-to-string + (concat (shell-quote-argument learn-ocaml-command-name) " --version")))) + +(defun learn-ocaml-client-sign-in-cmd (login password) + "Run learn-ocaml-client init-user with login and password as argument" + (shell-command-to-string + (concat (shell-quote-argument learn-ocaml-command-name) + " init-user --server=\"http://localhost:8080\" " login " " password))) + +(defun learn-ocaml-client-sign-up-cmd (login password nickname secret) + "Run learn-ocaml-client init-user with login password nickname +and secret as argument" (shell-command-to-string - (concat (shell-quote-argument learn-ocaml-command-name) " --version"))) + (concat (shell-quote-argument learn-ocaml-command-name) + " init-user --server=\"http://localhost:8080\" " + login password nickname secret))) (defun learn-ocaml-client-config () "Run \"learn-ocaml-client server-config\"." (shell-command-to-string (concat (shell-quote-argument learn-ocaml-command-name) " server-config"))) -(cl-defun learn-ocaml-init-cmd (&key token server nickname secret callback) +cl-defun learn-ocaml-init-cmd (&key token server nickname secret callback) "Run \"learn-ocaml-client init\" with options." (learn-ocaml-print-time-stamp) (learn-ocaml-make-process-wrapper @@ -803,7 +810,7 @@ Note: this function will be used by `learn-ocaml-on-load-aux'." "Run learn-ocaml init-user with options." (learn-ocaml-print-time-stamp) (learn-ocaml-make-process-wrapper - :name "init" + :name "init-user" :command (learn-ocaml-command-constructor :login email :password password @@ -815,7 +822,6 @@ Note: this function will be used by `learn-ocaml-on-load-aux'." #'learn-ocaml-error-handler nil callback))) - ) (cl-defun learn-ocaml-init-cmd (&key token server nickname secret callback) "Run \"learn-ocaml-client init\" with options." @@ -838,25 +844,45 @@ Note: this function will be used by `learn-ocaml-on-load-aux'." (defun learn-ocaml-connection () "Connect the user with a (login,passwd) or a token" - (if ( learn-ocaml-client-version-le-0.13 (learn-ocaml-client-version)) + (if (version-list-<= + (version-to-list (learn-ocaml-client-version)) (version-to-list "0.13")) (cl-case (x-popup-dialog - t `("Do you prefer to connect with a token or a login and a password?\n" - ("(login,password)" . 1) - ("token" . 2))) - (1 (let* ((login_password (learn-ocaml-get-login-password)) + t `("Welcome to Learn OCaml mode for Emacs.\nWhat do you to do?\n" + ("Sign in" . 1) + ("Sign up" . 2) + ("Connect with an old token" . 3))) + (1 (let* ((login_password (learn-ocaml-sign-in)) (login (car login_password)) - (password (cdr login_password))))) - - (2 (let ((token (read-string "Enter token: "))) + (password (cdr login_password))) + (learn-ocaml-client-sign-in-cmd login password))) + (2 (let* ((infos (learn-ocaml-sign-up)) + (login (nth 0 infos)) + (password (nth 1 infos)) + (nickname (nth 2 infos)) + (secret (nth 3 infos))) + (learn-ocaml-client-sign-up-cmd login password nickname secret))) + (3 (let ((token (read-string "Enter token: "))) (learn-ocaml-use-metadata-cmd token nil (lambda (_) (message-box "Token saved.")))))))) -(defun learn-ocaml-get-login-password () - "Ask interactively the login and the password to the user" - (cons (read-string "Enter login: ") (read-passwd "Enter password: "))) +(defun learn-ocaml-sign-in () + "Ask interactively the login and the password to the user to sign in" + (list (read-string "Enter login: ") (read-passwd "Enter password: "))) + +(defun learn-ocaml-sign-up () + "Ask interactively the login, password(with confirmation), nickname, secret" + (let* ((login (read-string "Enter login: ")) + (pswd (read-passwd "Enter password: ")) + (pswd-conf (read-passwd "Enter password confirmation: ")) + (nickname (read-string "Enter nickname: ")) + (secret (read-string "Enter secret: "))) + (while (not (string= pswd pswd-conf)) + (setq pswd (read-passwd "Password are not the same. Enter password: ")) + (setq pswd-conf (read-passwd "Enter password confirmation: "))) + (list login pswd nickname secret))) (defun learn-ocaml-on-load-aux (token server callback) "At load time: ensure a TOKEN and SERVER are set, then run CALLBACK. From 6c6ff2b74ecc5ec17c5b505f6a312bc74b6798fd Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Thu, 3 Jun 2021 17:05:16 +0200 Subject: [PATCH 05/58] fix: missing parenthesis on a function --- learn-ocaml.el | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index 5c38473..ddc43f3 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -326,7 +326,7 @@ and secret as argument" (shell-command-to-string (concat (shell-quote-argument learn-ocaml-command-name) " server-config"))) -cl-defun learn-ocaml-init-cmd (&key token server nickname secret callback) +(cl-defun learn-ocaml-init-cmd (&key token server nickname secret callback) "Run \"learn-ocaml-client init\" with options." (learn-ocaml-print-time-stamp) (learn-ocaml-make-process-wrapper @@ -806,23 +806,6 @@ Note: this function will be used by `learn-ocaml-on-load-aux'." nil callback)))))) -(cl-defun learn-ocaml-init-user-cmd (&key email password nickname secret callback) - "Run learn-ocaml init-user with options." - (learn-ocaml-print-time-stamp) - (learn-ocaml-make-process-wrapper - :name "init-user" - :command (learn-ocaml-command-constructor - :login email - :password password - :param1 nickname - :param2 secret - :command "init-user") - :stderr (learn-ocaml-log-buffer) - :sentinel (apply-partially - #'learn-ocaml-error-handler - nil - callback))) - (cl-defun learn-ocaml-init-cmd (&key token server nickname secret callback) "Run \"learn-ocaml-client init\" with options." (learn-ocaml-print-time-stamp) From 9439d5df0d62231f9993319d02b4c0f6b3702335 Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Thu, 3 Jun 2021 17:50:27 +0200 Subject: [PATCH 06/58] fix: update learn-ocaml-client-sign-up-cmd to add spaces between args --- learn-ocaml.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index ddc43f3..f35450d 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -319,7 +319,7 @@ and secret as argument" (shell-command-to-string (concat (shell-quote-argument learn-ocaml-command-name) " init-user --server=\"http://localhost:8080\" " - login password nickname secret))) + login " " password " " nickname " " secret))) (defun learn-ocaml-client-config () "Run \"learn-ocaml-client server-config\"." @@ -586,7 +586,7 @@ Argument SECRET may be needed by the server." (learn-ocaml-show-metadata)))))))) (defun learn-ocaml-download-server-file (id &optional directory) - "Download the last saved code for exercise ID in DIRECTORY." +con "Download the last saved code for exercise ID in DIRECTORY." (interactive `(,(let ((input (read-string (concat "Enter the id of the exercise (default " learn-ocaml-exercise-id From 3940f1e1e73a0a9ecc976416c15d4257e654e37d Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Mon, 7 Jun 2021 15:12:55 +0200 Subject: [PATCH 07/58] feat: handle password compatibility almost done. Missing bind to the learn-ocaml-client server-config --- learn-ocaml.el | 130 +++++++++++++++++++++++++++---------------------- 1 file changed, 73 insertions(+), 57 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index f35450d..7a0db3a 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -60,7 +60,7 @@ (defvar learn-ocaml-temp-dir nil) -(defvar learn-ocaml-use-pswd nil) +(defvar learn-ocaml-use-pswd t) (defvar learn-ocaml-log-buffer nil) @@ -307,19 +307,26 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (string-trim (shell-command-to-string (concat (shell-quote-argument learn-ocaml-command-name) " --version")))) -(defun learn-ocaml-client-sign-in-cmd (login password) +(defun learn-ocaml-client-sign-in-cmd (server login password) "Run learn-ocaml-client init-user with login and password as argument" (shell-command-to-string (concat (shell-quote-argument learn-ocaml-command-name) - " init-user --server=\"http://localhost:8080\" " login " " password))) + " init-user --server=" server " " login " " password))) -(defun learn-ocaml-client-sign-up-cmd (login password nickname secret) +(defun escape-secret (secret) "Run learn-ocaml-client init-user with login password nickname and secret as argument" - (shell-command-to-string - (concat (shell-quote-argument learn-ocaml-command-name) - " init-user --server=\"http://localhost:8080\" " - login " " password " " nickname " " secret))) + (if-let (secret-option (string= secret "")) + "\"\"" + secret)) + +(defun learn-ocaml-client-sign-up-cmd (server login password nickname secret) + "Run learn-ocaml-client init-user with login password nickname +and secret as argument" + (shell-command-to-string + (concat (shell-quote-argument learn-ocaml-command-name) + " init-user --server=" server " " + login " " password " " nickname " " secret))) (defun learn-ocaml-client-config () "Run \"learn-ocaml-client server-config\"." @@ -825,31 +832,38 @@ Note: this function will be used by `learn-ocaml-on-load-aux'." -(defun learn-ocaml-connection () - "Connect the user with a (login,passwd) or a token" - (if (version-list-<= - (version-to-list (learn-ocaml-client-version)) (version-to-list "0.13")) - (cl-case (x-popup-dialog - t `("Welcome to Learn OCaml mode for Emacs.\nWhat do you to do?\n" - ("Sign in" . 1) - ("Sign up" . 2) - ("Connect with an old token" . 3))) - (1 (let* ((login_password (learn-ocaml-sign-in)) - (login (car login_password)) - (password (cdr login_password))) - (learn-ocaml-client-sign-in-cmd login password))) - (2 (let* ((infos (learn-ocaml-sign-up)) - (login (nth 0 infos)) - (password (nth 1 infos)) - (nickname (nth 2 infos)) - (secret (nth 3 infos))) - (learn-ocaml-client-sign-up-cmd login password nickname secret))) - (3 (let ((token (read-string "Enter token: "))) - (learn-ocaml-use-metadata-cmd - token - nil - (lambda (_) - (message-box "Token saved.")))))))) +(defun learn-ocaml-connection (server callback) + "Connect the user with a (login,passwd) or a token and continue with the callback" + (let* ((new-server-value (when (or (not server) + (string-equal server "")) + (message-box "No server found. Please enter the server url.") + (read-string "Enter server URL: " "https://")))) + (cl-case (x-popup-dialog + t `("Welcome to Learn OCaml mode for Emacs.\nWhat do you to do?\n" + ("Sign in" . 1) + ("Sign up" . 2) + ("Connect with an old token" . 3))) + (1 (let* ((login_password (learn-ocaml-sign-in)) + (login (nth 0 login_password)) + (password (nth 1 login_password)) + (message + (learn-ocaml-client-sign-in-cmd server login password))))) + (2 (let* ((infos (learn-ocaml-sign-up)) + (login (nth 0 infos)) + (password (nth 1 infos)) + (nickname (nth 2 infos)) + (secret (nth 3 infos)) + (message (learn-ocaml-client-sign-up-cmd + server login password nickname (escape-secret secret)))) + (message-box message) + (learn-ocaml-connection server callback))) + (3 (let ((token (read-string "Enter token: "))) + (learn-ocaml-use-metadata-cmd + token + nil + (lambda (_) + (message-box "Token saved.")))))) + (funcall callback))) (defun learn-ocaml-sign-in () "Ask interactively the login and the password to the user to sign in" @@ -875,36 +889,34 @@ If TOKEN is \"\", interactively ask a token." (string-equal server "")) (message-box "No server found. Please enter the server url.") (read-string "Enter server URL: " "https://"))) - (rich-callback (lambda (_) - (funcall callback) - (learn-ocaml-show-metadata)))) + (rich-callback (lambda (_) + (funcall callback) + (learn-ocaml-show-metadata)))) (cl-destructuring-bind (token-phrase use-found-token use-another-token) - (if (or (not token) + (if (or (not token) (string-equal token "")) '("No token found" "Use found token" ("Use existing token" . 1)) `(,(concat "Token found: " token) ("Use found token" . 0) ("Use another token" . 1))) (cl-case (x-popup-dialog - t `(,(concat token-phrase "\n What do you want to do?\n") - ,use-found-token - ,use-another-token - ("Create new token" . 2))) - (0 (funcall rich-callback nil)) - - (1 (let ((token (read-string "Enter token: "))) - (learn-ocaml-init - :new-server-value new-server-value - :new-token-value token - :callback rich-callback))) - - (2 (let ((nickname (read-string "What nickname do you want to use for the token? ")) - (secret (read-string "What secret does the server require? "))) - (learn-ocaml-init - :new-server-value new-server-value - :nickname nickname - :secret secret - :callback rich-callback))))))) + t `(,(concat token-phrase "\n What do you want to do?\n") + ,use-found-token + ,use-another-token + ("Create new token" . 2))) + (0 (funcall rich-callback nil)) + (1 (let ((token (read-string "Enter token: "))) + (learn-ocaml-init + :new-server-value new-server-value + :new-token-value token + :callback rich-callback))) + (2 (let ((nickname (read-string "What nickname do you want to use for the token? ")) + (secret (read-string "What secret does the server require? "))) + (learn-ocaml-init + :new-server-value new-server-value + :nickname nickname + :secret secret + :callback rich-callback))))))) (defun learn-ocaml-on-load (callback) "Call `learn-ocaml-on-load-aux' and CALLBACK when loading mode." @@ -912,7 +924,11 @@ If TOKEN is \"\", interactively ask a token." (lambda (server) (learn-ocaml-give-token-cmd (lambda (token) - (learn-ocaml-on-load-aux token server callback)))))) + (if (version-list-<= + (version-to-list (learn-ocaml-client-version)) (version-to-list "0.13")) + (if learn-ocaml-use-pswd + (learn-ocaml-connection server callback)) + (learn-ocaml-on-load-aux token server callback))))))) ;; ;; menu definition From c4506343c0efc105cdd500301b16e9d6604d7ec1 Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Tue, 8 Jun 2021 10:30:04 +0200 Subject: [PATCH 08/58] fix: ask the server then check the server version and finally check if use_password is true --- learn-ocaml.el | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index 7a0db3a..aa625de 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -60,7 +60,7 @@ (defvar learn-ocaml-temp-dir nil) -(defvar learn-ocaml-use-pswd t) +(defvar learn-ocaml-use-pswd nil) (defvar learn-ocaml-log-buffer nil) @@ -178,8 +178,7 @@ Function added in the `kill-emacs-query-functions' hook." to the boolean contained in the json returned by the client" (setq learn-ocaml-use-pswd (cdr (assoc 'use_passwd - (json-read-from-string - "{\"use_passwd\": true, \"version\": \"1.12\"}"))))) + (json-read-from-string json))))) ;; ;; package.el shortcut @@ -834,10 +833,6 @@ Note: this function will be used by `learn-ocaml-on-load-aux'." (defun learn-ocaml-connection (server callback) "Connect the user with a (login,passwd) or a token and continue with the callback" - (let* ((new-server-value (when (or (not server) - (string-equal server "")) - (message-box "No server found. Please enter the server url.") - (read-string "Enter server URL: " "https://")))) (cl-case (x-popup-dialog t `("Welcome to Learn OCaml mode for Emacs.\nWhat do you to do?\n" ("Sign in" . 1) @@ -863,7 +858,7 @@ Note: this function will be used by `learn-ocaml-on-load-aux'." nil (lambda (_) (message-box "Token saved.")))))) - (funcall callback))) + (funcall callback)) (defun learn-ocaml-sign-in () "Ask interactively the login and the password to the user to sign in" @@ -885,13 +880,6 @@ Note: this function will be used by `learn-ocaml-on-load-aux'." "At load time: ensure a TOKEN and SERVER are set, then run CALLBACK. If SERVER is \"\", interactively ask a server url. If TOKEN is \"\", interactively ask a token." - (let* ((new-server-value (when (or (not server) - (string-equal server "")) - (message-box "No server found. Please enter the server url.") - (read-string "Enter server URL: " "https://"))) - (rich-callback (lambda (_) - (funcall callback) - (learn-ocaml-show-metadata)))) (cl-destructuring-bind (token-phrase use-found-token use-another-token) (if (or (not token) (string-equal token "")) @@ -916,7 +904,7 @@ If TOKEN is \"\", interactively ask a token." :new-server-value new-server-value :nickname nickname :secret secret - :callback rich-callback))))))) + :callback rich-callback)))))) (defun learn-ocaml-on-load (callback) "Call `learn-ocaml-on-load-aux' and CALLBACK when loading mode." @@ -924,11 +912,17 @@ If TOKEN is \"\", interactively ask a token." (lambda (server) (learn-ocaml-give-token-cmd (lambda (token) - (if (version-list-<= - (version-to-list (learn-ocaml-client-version)) (version-to-list "0.13")) - (if learn-ocaml-use-pswd - (learn-ocaml-connection server callback)) - (learn-ocaml-on-load-aux token server callback))))))) + (let* ((new-server-value (if (or (not server) + (string-equal server "")) + (progn (message-box "No server found. Please enter the server url.") + (read-string "Enter server URL: " "https://")) + server))) + (if (version-list-<= + (version-to-list (learn-ocaml-client-version)) (version-to-list "0.13")) + (progn (learn-ocaml-server-config "{\"use_passwd\": true, \"version\": \"1.12\"}") + (if learn-ocaml-use-pswd + (learn-ocaml-connection new-server-value callback))) + (learn-ocaml-on-load-aux token new-server-value callback)))))))) ;; ;; menu definition From fb5f03cde43116181f3861504082df78f8558d24 Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Tue, 8 Jun 2021 10:43:44 +0200 Subject: [PATCH 09/58] refactor: Move some code in right parts of the file. --- learn-ocaml.el | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index aa625de..52d65be 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -121,6 +121,13 @@ Call `get-buffer-create' if need be, to ensure it is a live buffer." (current-time-string) " ---------------------\n"))) +(defun escape-secret (secret) + "Run learn-ocaml-client init-user with login password nickname +and secret as argument" + (if-let (secret-option (string= secret "")) + "\"\"" + secret)) + (defun learn-ocaml-file-writter-filter (file _proc string) "Write in FILE the given STRING. To be used as a `make-process' filter." @@ -312,13 +319,6 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (concat (shell-quote-argument learn-ocaml-command-name) " init-user --server=" server " " login " " password))) -(defun escape-secret (secret) - "Run learn-ocaml-client init-user with login password nickname -and secret as argument" - (if-let (secret-option (string= secret "")) - "\"\"" - secret)) - (defun learn-ocaml-client-sign-up-cmd (server login password nickname secret) "Run learn-ocaml-client init-user with login password nickname and secret as argument" @@ -327,7 +327,7 @@ and secret as argument" " init-user --server=" server " " login " " password " " nickname " " secret))) -(defun learn-ocaml-client-config () +(defun learn-ocaml-client-config-cmd () "Run \"learn-ocaml-client server-config\"." (shell-command-to-string (concat (shell-quote-argument learn-ocaml-command-name) " server-config"))) From ed0cab4aa63845ac70fa7daf6b47bfecf2e063d1 Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Tue, 8 Jun 2021 16:49:46 +0200 Subject: [PATCH 10/58] fix: fix some details. missing 2 commands init-server and server-config from the client to finish the authentication with login and password --- learn-ocaml.el | 60 +++++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index 52d65be..51fa5f0 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -122,8 +122,7 @@ Call `get-buffer-create' if need be, to ensure it is a live buffer." " ---------------------\n"))) (defun escape-secret (secret) - "Run learn-ocaml-client init-user with login password nickname -and secret as argument" + "Add escape to the secret when it's empty" (if-let (secret-option (string= secret "")) "\"\"" secret)) @@ -183,9 +182,10 @@ Function added in the `kill-emacs-query-functions' hook." (defun learn-ocaml-server-config (json) "Set the global variable learn-ocaml-use-pswd according to the boolean contained in the json returned by the client" - (setq learn-ocaml-use-pswd - (cdr (assoc 'use_passwd - (json-read-from-string json))))) + (if (eql (cdr (assoc 'use_passwd (json-read-from-string json))) + :json-false) + (setq learn-ocaml-use-pswd nil) + (setq learn-ocaml-use-pswd t))) ;; ;; package.el shortcut @@ -874,37 +874,42 @@ Note: this function will be used by `learn-ocaml-on-load-aux'." (while (not (string= pswd pswd-conf)) (setq pswd (read-passwd "Password are not the same. Enter password: ")) (setq pswd-conf (read-passwd "Enter password confirmation: "))) - (list login pswd nickname secret))) + (list login pswd nickname secret))) -(defun learn-ocaml-on-load-aux (token server callback) +(defun learn-ocaml-on-load-aux (token new-server-value callback) "At load time: ensure a TOKEN and SERVER are set, then run CALLBACK. -If SERVER is \"\", interactively ask a server url. If TOKEN is \"\", interactively ask a token." + (let* ((rich-callback (lambda (_) + (funcall callback) + (learn-ocaml-show-metadata)))) (cl-destructuring-bind (token-phrase use-found-token use-another-token) - (if (or (not token) + (if (or (not token) (string-equal token "")) '("No token found" "Use found token" ("Use existing token" . 1)) `(,(concat "Token found: " token) ("Use found token" . 0) ("Use another token" . 1))) (cl-case (x-popup-dialog - t `(,(concat token-phrase "\n What do you want to do?\n") - ,use-found-token - ,use-another-token - ("Create new token" . 2))) - (0 (funcall rich-callback nil)) - (1 (let ((token (read-string "Enter token: "))) - (learn-ocaml-init - :new-server-value new-server-value - :new-token-value token - :callback rich-callback))) - (2 (let ((nickname (read-string "What nickname do you want to use for the token? ")) - (secret (read-string "What secret does the server require? "))) - (learn-ocaml-init - :new-server-value new-server-value - :nickname nickname - :secret secret - :callback rich-callback)))))) + t `(,(concat token-phrase "\n What do you want to do?\n") + ,use-found-token + ,use-another-token + ("Create new token" . 2))) + (0 (funcall rich-callback nil)) + + (1 (let ((token (read-string "Enter token: "))) + (learn-ocaml-init + :new-server-value new-server-value + :new-token-value token + :callback rich-callback))) + + (2 (let ((nickname (read-string "What nickname do you want to use for the token? ")) + (secret (read-string "What secret does the server require? "))) + (learn-ocaml-init + :new-server-value new-server-value + :nickname nickname + :secret secret + :callback rich-callback))))))) + (defun learn-ocaml-on-load (callback) "Call `learn-ocaml-on-load-aux' and CALLBACK when loading mode." @@ -921,7 +926,8 @@ If TOKEN is \"\", interactively ask a token." (version-to-list (learn-ocaml-client-version)) (version-to-list "0.13")) (progn (learn-ocaml-server-config "{\"use_passwd\": true, \"version\": \"1.12\"}") (if learn-ocaml-use-pswd - (learn-ocaml-connection new-server-value callback))) + (learn-ocaml-connection new-server-value callback) + (learn-ocaml-on-load-aux token new-server-value callback))) (learn-ocaml-on-load-aux token new-server-value callback)))))))) ;; From e359aec4480f840d92507d234dcdac4521983048 Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Fri, 11 Jun 2021 10:48:05 +0200 Subject: [PATCH 11/58] fix: update the client-config-cmd to match data send by the the client for the learn-ocaml-client server-config --- learn-ocaml.el | 130 +++++++++++++++--------------- tests/learn-ocaml-tests.el | 159 ++++++++++++++++++------------------- 2 files changed, 143 insertions(+), 146 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index 51fa5f0..dfa7f24 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -182,7 +182,7 @@ Function added in the `kill-emacs-query-functions' hook." (defun learn-ocaml-server-config (json) "Set the global variable learn-ocaml-use-pswd according to the boolean contained in the json returned by the client" - (if (eql (cdr (assoc 'use_passwd (json-read-from-string json))) + (if (eql (aref (json-read-from-string json) 1) :json-false) (setq learn-ocaml-use-pswd nil) (setq learn-ocaml-use-pswd t))) @@ -282,20 +282,20 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (buffer-string)))) (when buffer (kill-buffer buffer)) (if (or (string-equal string "finished\n") - (string-match "give-token" (process-name proc)) - (string-match "give-server" (process-name proc))) + (string-match "give-token" (process-name proc)) + (string-match "give-server" (process-name proc))) (funcall callback result) (if (learn-ocaml-yes-or-no learn-ocaml-warning-message) - (progn (switch-to-buffer-other-window "*learn-ocaml-log*") + (progn (switch-to-buffer-other-window "*learn-ocaml-log*") (goto-char (point-max)) ;; Do this to cope with the addition of up-to 3 lines ;; (... Process upload-demo stderr finished) (recenter-top-bottom -3)) - (when learn-ocaml-fail-noisely - (with-current-buffer (learn-ocaml-log-buffer) - ;; Remark: the log will contain earlier, unrelated info... - (let ((log (buffer-string))) - (error "Process errored. Full log:\n%s" log)))))))) + (when learn-ocaml-fail-noisely + (with-current-buffer (learn-ocaml-log-buffer) + ;; Remark: the log will contain earlier, unrelated info... + (let ((log (buffer-string))) + (error "Process errored. Full log:\n%s" log)))))))) (cl-defun learn-ocaml-command-constructor (&key command token server local id html dont-submit param1 param2) "Construct a shell command with `learn-ocaml-command-name' and options." @@ -340,9 +340,9 @@ and secret as argument" :command (learn-ocaml-command-constructor :token token :server server - :param1 nickname - :param2 secret - :command "init") + :param1 nickname + :param2 secret + :command "init") :stderr (learn-ocaml-log-buffer) :sentinel (apply-partially #'learn-ocaml-error-handler @@ -493,15 +493,15 @@ Argument CALLBACK will receive the token." (learn-ocaml-make-process-wrapper :name "exercise-list" :command (learn-ocaml-command-constructor - :command "exercise-list") + :command "exercise-list") :stderr (learn-ocaml-log-buffer) :buffer buffer :sentinel (apply-partially - #'learn-ocaml-error-handler - buffer - (lambda (s) - (funcall-interactively - callback (json-read-from-string s))))))) + #'learn-ocaml-error-handler + buffer + (lambda (s) + (funcall-interactively + callback (json-read-from-string s))))))) (defun learn-ocaml-compute-questions-url (server id token) "Get subject url for SERVER, exercise ID and user TOKEN." @@ -521,7 +521,7 @@ Argument CALLBACK will receive the token." ;; very important if you don't do it you risk to open eww (setq browse-url-browser-function 'browse-url-default-browser) (browse-url (learn-ocaml-compute-questions-url server id token))))))) - + (defun learn-ocaml-show-metadata () "Display the token and server url in mini-buffer and `message-box'." (interactive) @@ -629,8 +629,8 @@ con "Download the last saved code for exercise ID in DIRECTORY." :id learn-ocaml-exercise-id :file buffer-file-name :callback (lambda (html) - (setq browse-url-browser-function 'browse-url-default-browser) - (browse-url-of-file html)))) + (setq browse-url-browser-function 'browse-url-default-browser) + (browse-url-of-file html)))) ;; ;; exercise list display @@ -667,42 +667,42 @@ con "Download the last saved code for exercise ID in DIRECTORY." (defun learn-ocaml-print-exercise-info (indent tuple) "Render an exercise item with leading INDENT from the data in TUPLE." (let* ((id (elt tuple 0)) - (exo (elt tuple 1)) - (title (assoc-default 'title exo) ) - (short_description (assoc-default 'short_description exo)) - (stars (assoc-default 'stars exo))) + (exo (elt tuple 1)) + (title (assoc-default 'title exo) ) + (short_description (assoc-default 'short_description exo)) + (stars (assoc-default 'stars exo))) (widget-insert "\n") (widget-insert indent) (widget-create 'learn-ocaml-exercise-title - :tag title) + :tag title) (widget-insert "\n") (widget-insert (concat indent " ")) (widget-insert (if short_description short_description "No description available")) (widget-insert "\n") (widget-insert (concat indent " ")) (widget-insert (concat "Difficulty: " (number-to-string stars) "/4" - " id: " id)) + " Progression: % " " id: " id)) (widget-insert "\n") (widget-insert (concat indent " ")) (widget-create 'learn-ocaml-button - :notify (lambda (&rest _ignore) - (learn-ocaml-show-questions id)) - "Browse subject") + :notify (lambda (&rest _ignore) + (learn-ocaml-show-questions id)) + "Browse subject") (widget-insert " ") (widget-create 'learn-ocaml-button - :notify (lambda (&rest _ignore) - (learn-ocaml-download-template id)) - "Get template") + :notify (lambda (&rest _ignore) + (learn-ocaml-download-template id)) + "Get template") (widget-insert " ") (widget-create 'learn-ocaml-button - :notify (lambda (&rest _ignore) - (find-file (concat id ".ml"))) - "Open .ml") + :notify (lambda (&rest _ignore) + (find-file (concat id ".ml"))) + "Open .ml") (widget-insert " ") (widget-create 'learn-ocaml-button - :notify (lambda (&rest _ignore) - (learn-ocaml-download-server-file id)) - "Get last saved version") + :notify (lambda (&rest _ignore) + (learn-ocaml-download-server-file id)) + "Get last saved version") (widget-insert "\n"))) (defun learn-ocaml-print-groups (indent json) @@ -716,8 +716,8 @@ con "Download the last saved code for exercise ID in DIRECTORY." (widget-create 'learn-ocaml-group-title :tag (concat indent - (cdr (car (cdr group))) - "\n")) + (cdr (car (cdr group))) + "\n")) (learn-ocaml-print-groups (concat indent " ") (car (cdr (cdr group))))) queue)) @@ -820,9 +820,9 @@ Note: this function will be used by `learn-ocaml-on-load-aux'." :command (learn-ocaml-command-constructor :token token :server server - :param1 nickname - :param2 secret - :command "init") + :param1 nickname + :param2 secret + :command "init") :stderr (learn-ocaml-log-buffer) :sentinel (apply-partially #'learn-ocaml-error-handler @@ -880,35 +880,33 @@ Note: this function will be used by `learn-ocaml-on-load-aux'." "At load time: ensure a TOKEN and SERVER are set, then run CALLBACK. If TOKEN is \"\", interactively ask a token." (let* ((rich-callback (lambda (_) - (funcall callback) - (learn-ocaml-show-metadata)))) + (funcall callback) + (learn-ocaml-show-metadata)))) (cl-destructuring-bind (token-phrase use-found-token use-another-token) - (if (or (not token) + (if (or (not token) (string-equal token "")) '("No token found" "Use found token" ("Use existing token" . 1)) `(,(concat "Token found: " token) ("Use found token" . 0) ("Use another token" . 1))) (cl-case (x-popup-dialog - t `(,(concat token-phrase "\n What do you want to do?\n") - ,use-found-token - ,use-another-token - ("Create new token" . 2))) - (0 (funcall rich-callback nil)) - - (1 (let ((token (read-string "Enter token: "))) - (learn-ocaml-init - :new-server-value new-server-value - :new-token-value token - :callback rich-callback))) - - (2 (let ((nickname (read-string "What nickname do you want to use for the token? ")) - (secret (read-string "What secret does the server require? "))) - (learn-ocaml-init - :new-server-value new-server-value - :nickname nickname - :secret secret - :callback rich-callback))))))) + t `(,(concat token-phrase "\n What do you want to do?\n") + ,use-found-token + ,use-another-token + ("Create new token" . 2))) + (0 (funcall rich-callback nil)) + (1 (let ((token (read-string "Enter token: "))) + (learn-ocaml-init + :new-server-value new-server-value + :new-token-value token + :callback rich-callback))) + (2 (let ((nickname (read-string "What nickname do you want to use for the token? ")) + (secret (read-string "What secret does the server require? "))) + (learn-ocaml-init + :new-server-value new-server-value + :nickname nickname + :secret secret + :callback rich-callback))))))) (defun learn-ocaml-on-load (callback) diff --git a/tests/learn-ocaml-tests.el b/tests/learn-ocaml-tests.el index f31ffd3..e688022 100644 --- a/tests/learn-ocaml-tests.el +++ b/tests/learn-ocaml-tests.el @@ -67,97 +67,97 @@ (ert-deftest-async 1_learn-ocaml-server-management-test (done) (let ((tests (lambda (callback) - (learn-ocaml-use-metadata-cmd - nil - learn-ocaml-test-url - (lambda (_) - (learn-ocaml-give-server-cmd - (lambda (given-server) - (should (string-equal - learn-ocaml-test-url - given-server)) - (funcall callback)))))))) + (learn-ocaml-use-metadata-cmd + nil + learn-ocaml-test-url + (lambda (_) + (learn-ocaml-give-server-cmd + (lambda (given-server) + (should (string-equal + learn-ocaml-test-url + given-server)) + (funcall callback)))))))) (funcall tests done))) (ert-deftest-async 2_learn-ocaml-token-management-test (done) (let ((tests (lambda (callback) - (learn-ocaml-create-token-cmd - "test" - "test" - (lambda (token) - (learn-ocaml-use-metadata-cmd - token - nil - (lambda (_) - (learn-ocaml-give-token-cmd - (lambda (given_token) - (should - (string-equal - given_token - token )) - (funcall callback)))))))))) + (learn-ocaml-create-token-cmd + "test" + "test" + (lambda (token) + (learn-ocaml-use-metadata-cmd + token + nil + (lambda (_) + (learn-ocaml-give-token-cmd + (lambda (given_token) + (should + (string-equal + given_token + token )) + (funcall callback)))))))))) (funcall tests done))) (ert-deftest-async 3_learn-ocaml-grade-test (done) (learn-ocaml-test-remove-temp-file "demo") (let ((test (lambda(callback) - (learn-ocaml-grade-file-cmd - :id "demo" - :file learn-ocaml-test-tograde-file - :callback (lambda (_) - (should (= (shell-command - (concat - "cat " - (learn-ocaml-temp-html-file "demo") - " | grep \"Exercise complete\"") - ) - 0)) + (learn-ocaml-grade-file-cmd + :id "demo" + :file learn-ocaml-test-tograde-file + :callback (lambda (_) + (should (= (shell-command + (concat + "cat " + (learn-ocaml-temp-html-file "demo") + " | grep \"Exercise complete\"") + ) + 0)) (learn-ocaml-test-remove-temp-file "demo") - (funcall callback)))))) + (funcall callback)))))) (funcall test done))) (ert-deftest-async 4_learn-ocaml-download-server-file-test (done) (learn-ocaml-test-remove-demo-file) (let ((test (lambda(callback) - (learn-ocaml-download-server-file-cmd + (learn-ocaml-download-server-file-cmd :id "demo" :directory learn-ocaml-fixture-directory - :callback (lambda (s) - (should (= 0 (shell-command - (concat "cat " + :callback (lambda (s) + (should (= 0 (shell-command + (concat "cat " learn-ocaml-test-demo-file)))) - (learn-ocaml-test-remove-demo-file t) - (funcall callback)))))) + (learn-ocaml-test-remove-demo-file t) + (funcall callback)))))) (funcall test done))) (ert-deftest-async 5_learn-ocaml-download-template-test (done) (learn-ocaml-test-remove-demo-file) (let ((test (lambda (callback) - (learn-ocaml-download-template-cmd - :id "demo" + (learn-ocaml-download-template-cmd + :id "demo" :directory learn-ocaml-fixture-directory - :callback (lambda (s) - (should - (= 0 (shell-command - (concat "diff " - learn-ocaml-test-demo-file - " " + :callback (lambda (s) + (should + (= 0 (shell-command + (concat "diff " + learn-ocaml-test-demo-file + " " learn-ocaml-test-template-file)))) - (learn-ocaml-test-remove-demo-file t) - (funcall callback)))))) + (learn-ocaml-test-remove-demo-file t) + (funcall callback)))))) (funcall test done))) - + (ert-deftest-async 6_learn-ocaml-give-exercise-list-test (done) (let ((test (lambda (callback) - (with-temp-buffer - (insert-file-contents learn-ocaml-test-json-file) - (let ((expected (json-read-from-string (buffer-string)))) - (learn-ocaml-give-exercise-list-cmd - (lambda (json) - (should (equal json expected)) - (funcall callback)))))))) + (with-temp-buffer + (insert-file-contents learn-ocaml-test-json-file) + (let ((expected (json-read-from-string (buffer-string)))) + (learn-ocaml-give-exercise-list-cmd + (lambda (json) + (should (equal json expected)) + (funcall callback)))))))) (funcall test done))) @@ -186,11 +186,10 @@ :new-server-value nil :new-token-value token :callback (lambda (_) - (learn-ocaml-give-token-cmd - (lambda (token2) - (should (equal token token2)) - (funcall done)))))))) - + (learn-ocaml-give-token-cmd + (lambda (token2) + (should (equal token token2)) + (funcall done)))))))) (ert-deftest-async 9_learn-ocaml-init-create-token (done) (learn-ocaml-give-token-cmd @@ -200,11 +199,11 @@ :nickname "test" :secret "test" :callback (lambda (_) - (learn-ocaml-give-token-cmd - (lambda (token2) - (should-not (equal previous-token token2)) - (funcall done)))))))) - + (learn-ocaml-give-token-cmd + (lambda (token2) + (should-not (equal previous-token token2)) + (funcall done)))))))) + ;; tests without the config file (ert-deftest-async a10_learn-ocaml-on-load-test-another-token-no-config (done) @@ -215,23 +214,23 @@ :new-server-value learn-ocaml-test-url :new-token-value token :callback (lambda (_) - (learn-ocaml-give-token-cmd - (lambda (token2) - (should (equal token token2)) + (learn-ocaml-give-token-cmd + (lambda (token2) + (should (equal token token2)) ; (learn-ocaml-test-remove-client-file) - (funcall done)))))))) - -(ert-deftest-async a111_learn-ocaml-on-load-test-create-token-no-config (done) + (funcall done)))))))) + +(ert-deftest-async a11_learn-ocaml-on-load-test-create-token-no-config (done) (learn-ocaml-test-remove-client-file) (learn-ocaml-init :new-server-value learn-ocaml-test-url :nickname "test" :secret "test" :callback (lambda (_) - (learn-ocaml-give-token-cmd - (lambda (token2) + (learn-ocaml-give-token-cmd + (lambda (token2) ; (learn-ocaml-test-remove-client-file) - (funcall done)))))) + (funcall done)))))) ;; misc tests From 523d7677c8f84662d53a0efa47bb964867d5cb68 Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Fri, 11 Jun 2021 15:37:50 +0200 Subject: [PATCH 12/58] feat: handle password compatibility --- learn-ocaml.el | 12 ++++++++++-- tests/learn-ocaml-tests.el | 12 +++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index dfa7f24..a621cd4 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -332,6 +332,13 @@ and secret as argument" (shell-command-to-string (concat (shell-quote-argument learn-ocaml-command-name) " server-config"))) +(defun learn-ocaml-client-init-server-cmd (server) + "Run \"learn-ocaml-client server-config\"." + (shell-command-to-string + (concat + (shell-quote-argument learn-ocaml-command-name) " init-server -s " server))) + + (cl-defun learn-ocaml-init-cmd (&key token server nickname secret callback) "Run \"learn-ocaml-client init\" with options." (learn-ocaml-print-time-stamp) @@ -920,13 +927,14 @@ If TOKEN is \"\", interactively ask a token." (progn (message-box "No server found. Please enter the server url.") (read-string "Enter server URL: " "https://")) server))) + (progn (learn-ocaml-client-init-server-cmd new-server-value) (if (version-list-<= (version-to-list (learn-ocaml-client-version)) (version-to-list "0.13")) - (progn (learn-ocaml-server-config "{\"use_passwd\": true, \"version\": \"1.12\"}") + (progn (learn-ocaml-server-config (learn-ocaml-client-config-cmd)) (if learn-ocaml-use-pswd (learn-ocaml-connection new-server-value callback) (learn-ocaml-on-load-aux token new-server-value callback))) - (learn-ocaml-on-load-aux token new-server-value callback)))))))) + (learn-ocaml-on-load-aux token new-server-value callback))))))))) ;; ;; menu definition diff --git a/tests/learn-ocaml-tests.el b/tests/learn-ocaml-tests.el index e688022..5bf2c27 100644 --- a/tests/learn-ocaml-tests.el +++ b/tests/learn-ocaml-tests.el @@ -232,11 +232,21 @@ ; (learn-ocaml-test-remove-client-file) (funcall done)))))) +(ert-deftest-async a12_learn-ocaml-test-sign-up (done) + (learn-ocaml-init + :new-server-value learn-ocaml-test-url + :nickname "test" + :secret "test" + :callback (lambda (_) + (learn-ocaml-sign-up-cmd + (lambda (token2) + (funcall done)))))) + ;; misc tests (setq example-file shell-file-name) ; just to get a filename example -(ert-deftest a12_learn-ocaml-file-path () +(ert-deftest a13_learn-ocaml-file-path () (let* ((path example-file) (dir (file-name-directory path)) (file (file-name-nondirectory path))) From 1bd972245b354e0689c4062ee128f529f4b72dd5 Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Fri, 11 Jun 2021 16:27:53 +0200 Subject: [PATCH 13/58] test: Add a test for sign up cmd --- tests/learn-ocaml-tests.el | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/learn-ocaml-tests.el b/tests/learn-ocaml-tests.el index 5bf2c27..786c1b6 100644 --- a/tests/learn-ocaml-tests.el +++ b/tests/learn-ocaml-tests.el @@ -238,9 +238,12 @@ :nickname "test" :secret "test" :callback (lambda (_) - (learn-ocaml-sign-up-cmd - (lambda (token2) - (funcall done)))))) + (should (equal (learn-ocaml-sign-up-cmd + "test@example.com" + "Ocaml123*" + "Test" + "") "A confirmation e-mail has been sent to your address.\nPlease go to your mailbox to finish creating your account,\n then you will be able to sign in.\n")) + funcall done))) ;; misc tests From 39dee5c941a287d35759d5e3e98c158ce9aa158b Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Mon, 14 Jun 2021 14:29:25 +0200 Subject: [PATCH 14/58] fix: revert some changements which was on the wrong branch --- learn-ocaml.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index a621cd4..5d3d836 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -688,7 +688,7 @@ con "Download the last saved code for exercise ID in DIRECTORY." (widget-insert "\n") (widget-insert (concat indent " ")) (widget-insert (concat "Difficulty: " (number-to-string stars) "/4" - " Progression: % " " id: " id)) + " id: " id)) (widget-insert "\n") (widget-insert (concat indent " ")) (widget-create 'learn-ocaml-button From d36e35197a8458c6b9ef885744817478707f655f Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Tue, 15 Jun 2021 11:06:06 +0200 Subject: [PATCH 15/58] test: try a new test for sign up cmd --- tests/learn-ocaml-tests.el | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/learn-ocaml-tests.el b/tests/learn-ocaml-tests.el index 786c1b6..971dc55 100644 --- a/tests/learn-ocaml-tests.el +++ b/tests/learn-ocaml-tests.el @@ -233,17 +233,15 @@ (funcall done)))))) (ert-deftest-async a12_learn-ocaml-test-sign-up (done) - (learn-ocaml-init - :new-server-value learn-ocaml-test-url - :nickname "test" - :secret "test" - :callback (lambda (_) - (should (equal (learn-ocaml-sign-up-cmd + (learn-ocaml-init-server-server-cmd learn-ocaml-test-url) + (let* ((result (learn-ocaml-sign-up-cmd "test@example.com" "Ocaml123*" "Test" - "") "A confirmation e-mail has been sent to your address.\nPlease go to your mailbox to finish creating your account,\n then you will be able to sign in.\n")) - funcall done))) + ""))) + (should (string-equal ("A confirmation e-mail has been sent to your address.\nPlease go to your mailbox to finish creating your account,\n then you will be able to sign in.\n") + result))) + (funcall done)) ;; misc tests From d5d3f6e2094a2608281ce37d91dfe1bf1b0576e1 Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Tue, 15 Jun 2021 11:35:47 +0200 Subject: [PATCH 16/58] test: add tests for sign in and sign up --- tests/learn-ocaml-tests.el | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/tests/learn-ocaml-tests.el b/tests/learn-ocaml-tests.el index 971dc55..b049ca7 100644 --- a/tests/learn-ocaml-tests.el +++ b/tests/learn-ocaml-tests.el @@ -233,15 +233,24 @@ (funcall done)))))) (ert-deftest-async a12_learn-ocaml-test-sign-up (done) - (learn-ocaml-init-server-server-cmd learn-ocaml-test-url) - (let* ((result (learn-ocaml-sign-up-cmd - "test@example.com" - "Ocaml123*" - "Test" - ""))) - (should (string-equal ("A confirmation e-mail has been sent to your address.\nPlease go to your mailbox to finish creating your account,\n then you will be able to sign in.\n") + (learn-ocaml-client-init-server-cmd learn-ocaml-test-url) + (let* ((result (learn-ocaml-client-sign-up-cmd + learn-ocaml-test-url "ErtTest@example.com" "Ocaml123*" "Test" + (escape-secret "")))) + (should (string-equal "A confirmation e-mail has been sent to your address.\nPlease go to your mailbox to finish creating your account,\n then you will be able to sign in.\n" result))) - (funcall done)) + (funcall done)) + +(ert-deftest-async a13_learn-ocaml-test-sign-in (done) + (learn-ocaml-client-init-server-cmd learn-ocaml-test-url) + (let* ((result (learn-ocaml-client-sign-in-cmd + learn-ocaml-test-url "louis.ayroles@gmail.com" "Ocaml123*"))) + (should (string-equal + "Configuration written to /home/louis/.config/learnocaml/client.json.\n" + result))) + (funcall done)) + + ;; misc tests From 7220697029f9d129fae2d1d0d2d45f72c78a06ad Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Tue, 15 Jun 2021 11:41:27 +0200 Subject: [PATCH 17/58] test: add tests for sign in and sign up --- tests/learn-ocaml-tests.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/learn-ocaml-tests.el b/tests/learn-ocaml-tests.el index b049ca7..6e60f91 100644 --- a/tests/learn-ocaml-tests.el +++ b/tests/learn-ocaml-tests.el @@ -244,7 +244,7 @@ (ert-deftest-async a13_learn-ocaml-test-sign-in (done) (learn-ocaml-client-init-server-cmd learn-ocaml-test-url) (let* ((result (learn-ocaml-client-sign-in-cmd - learn-ocaml-test-url "louis.ayroles@gmail.com" "Ocaml123*"))) + learn-ocaml-test-url "ErtTest@example.com" "Ocaml123*"))) (should (string-equal "Configuration written to /home/louis/.config/learnocaml/client.json.\n" result))) From abcb55f925f144bcd05ad81ebe42991d8ec6b22f Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Mon, 21 Jun 2021 09:47:04 +0200 Subject: [PATCH 18/58] test: remove sign in test --- tests/learn-ocaml-tests.el | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tests/learn-ocaml-tests.el b/tests/learn-ocaml-tests.el index 6e60f91..493efea 100644 --- a/tests/learn-ocaml-tests.el +++ b/tests/learn-ocaml-tests.el @@ -241,17 +241,6 @@ result))) (funcall done)) -(ert-deftest-async a13_learn-ocaml-test-sign-in (done) - (learn-ocaml-client-init-server-cmd learn-ocaml-test-url) - (let* ((result (learn-ocaml-client-sign-in-cmd - learn-ocaml-test-url "ErtTest@example.com" "Ocaml123*"))) - (should (string-equal - "Configuration written to /home/louis/.config/learnocaml/client.json.\n" - result))) - (funcall done)) - - - ;; misc tests (setq example-file shell-file-name) ; just to get a filename example From e0fcef136afe9971a69cc3cd457645ade13b61b6 Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Mon, 21 Jun 2021 10:38:26 +0200 Subject: [PATCH 19/58] fix: replace the old server image with the right server image in order to pass test --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index f470dec..f425355 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,10 +8,9 @@ services: env: global: - EMACS_IMAGE="pfitaxel/emacs-learn-ocaml-client" - - LEARNOCAML_IMAGE="ocamlsf/learn-ocaml" + - LEARNOCAML_IMAGE="pfitaxel/learn-ocaml" jobs: - - LEARNOCAML_VERSION="0.12" - - LEARNOCAML_VERSION="master" + - LEARNOCAML_VERSION="oauth-moodle-dev" install: - docker pull "$LEARNOCAML_IMAGE:$LEARNOCAML_VERSION" From 127addbfcd30adf05bae7f195d81a09be5eea03d Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Thu, 24 Jun 2021 11:43:02 +0200 Subject: [PATCH 20/58] fix: update server-config function to match json object and not json array --- learn-ocaml.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index 5d3d836..ec5fbcc 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -182,7 +182,7 @@ Function added in the `kill-emacs-query-functions' hook." (defun learn-ocaml-server-config (json) "Set the global variable learn-ocaml-use-pswd according to the boolean contained in the json returned by the client" - (if (eql (aref (json-read-from-string json) 1) + (if (eql (cdr (assoc 'use_passwd (json-read-from-string json))) :json-false) (setq learn-ocaml-use-pswd nil) (setq learn-ocaml-use-pswd t))) From 6889e865e5ae0d470344d52349901f4dcb4527fe Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Wed, 30 Jun 2021 10:48:22 +0200 Subject: [PATCH 21/58] test: Extend the Travis CI config for use_passwd compat. * Use some ERT test selector: https://www.gnu.org/software/emacs/manual/html_node/ert/Test-Selectors.html --- .travis.yml | 4 ++-- test.sh | 15 ++++++++++++++- tests/learn-ocaml-tests.el | 6 ++++++ tests/use_passwd.json | 3 +++ 4 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 tests/use_passwd.json diff --git a/.travis.yml b/.travis.yml index f425355..0327cec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,9 +8,9 @@ services: env: global: - EMACS_IMAGE="pfitaxel/emacs-learn-ocaml-client" - - LEARNOCAML_IMAGE="pfitaxel/learn-ocaml" jobs: - - LEARNOCAML_VERSION="oauth-moodle-dev" + - LEARNOCAML_IMAGE="ocamlsf/learn-ocaml" LEARNOCAML_VERSION="0.12" + - LEARNOCAML_IMAGE="pfitaxel/learn-ocaml" LEARNOCAML_VERSION="oauth-moodle-dev" USE_PASSWD="true" install: - docker pull "$LEARNOCAML_IMAGE:$LEARNOCAML_VERSION" diff --git a/test.sh b/test.sh index e09a3d4..41db298 100755 --- a/test.sh +++ b/test.sh @@ -39,6 +39,12 @@ EMACS_NAME="emacs-client" run_server () { local oldopt="$(set +o)"; set -x # Run the server in background + if [ "$USE_PASSWD" = "true" ]; then + cp -f "$PWD/tests/use_passwd.json" "$PWD/tests/repo/server_config.json" + else + rm -f "$PWD/tests/repo/server_config.json" + fi + # Add -e "LEARNOCAML_BASE_URL=$LEARNOCAML_BASE_URL"? sudo docker run -d --rm --name="$SERVER_NAME" \ -v "$PWD/tests/repo:/repository" \ "$LEARNOCAML_IMAGE:$LEARNOCAML_VERSION" @@ -118,10 +124,17 @@ echo assert "learn-ocaml-client --version" +if [ "$USE_PASSWD" = "true" ]; then + selector="" +else + selector="learn-ocaml-test-skip-use-passwd" +fi + assert " cd /build/tests learn-ocaml-client init --server=http://localhost:8080 test test -emacs --batch -l ert -l init-tests.el -l /build/learn-ocaml.el -l learn-ocaml-tests.el -f ert-run-tests-batch-and-exit +emacs --batch -l ert -l init-tests.el -l /build/learn-ocaml.el \ + -l learn-ocaml-tests.el --eval '(ert-run-tests-batch-and-exit $selector)' " stop_emacs diff --git a/tests/learn-ocaml-tests.el b/tests/learn-ocaml-tests.el index 493efea..5238e01 100644 --- a/tests/learn-ocaml-tests.el +++ b/tests/learn-ocaml-tests.el @@ -26,6 +26,12 @@ (require 'ert-async) ;(setq ert-async-timeout 2) +;; NOTE: This symbol list gather tests specific to 'use_passwd: true' +(setq learn-ocaml-test-use-passwd-list + '(a12_learn-ocaml-test-sign-up)) + +(setq learn-ocaml-test-skip-use-passwd + `(not (member ,@learn-ocaml-test-use-passwd-list))) ;; WARNING: several tests delete the ./demo.ml and client.json files: (setq learn-ocaml-test-client-file "~/.config/learnocaml/client.json") diff --git a/tests/use_passwd.json b/tests/use_passwd.json new file mode 100644 index 0000000..a02abbc --- /dev/null +++ b/tests/use_passwd.json @@ -0,0 +1,3 @@ +{ + "use_passwd": true +} From 6e90db298a54afe570ff1f088cf31b1d12c76aba Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Wed, 30 Jun 2021 12:02:00 +0200 Subject: [PATCH 22/58] fix: update tests with the new configurations --- tests/learn-ocaml-tests.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/learn-ocaml-tests.el b/tests/learn-ocaml-tests.el index 5238e01..caac6c2 100644 --- a/tests/learn-ocaml-tests.el +++ b/tests/learn-ocaml-tests.el @@ -28,7 +28,7 @@ ;; NOTE: This symbol list gather tests specific to 'use_passwd: true' (setq learn-ocaml-test-use-passwd-list - '(a12_learn-ocaml-test-sign-up)) + '(a12_learn-ocaml-test-sign-up 2_learn-ocaml-token-management-test)) (setq learn-ocaml-test-skip-use-passwd `(not (member ,@learn-ocaml-test-use-passwd-list))) From ad251b027772d3304b60c0d724331d660ddba18c Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Mon, 5 Jul 2021 02:21:08 +0200 Subject: [PATCH 23/58] test: Add .github/workflows/test.yml * Remove .travis.yml as well --- .github/workflows/test.yml | 38 ++++++++++++++++++++++++++++++++++++++ .travis.yml | 22 ---------------------- 2 files changed, 38 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/test.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..b98f1dc --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,38 @@ +name: Test + +on: + push: + branches: + - master + pull_request: + branches: + - '**' + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + include: + - learnocaml_image: "ocamlsf/learn-ocaml" + learnocaml_version: "0.12" + use_passwd: "false" + - learnocaml_image: "pfitaxel/learn-ocaml" + learnocaml_version: "oauth-moodle-dev" + use_passwd: "true" + # at most 20 concurrent jobs per free account: + max-parallel: 4 + # don't cancel all in-progress jobs if one matrix job fails: + fail-fast: false + steps: + - uses: actions/checkout@v2 + - name: Script + env: + EMACS_IMAGE: "pfitaxel/emacs-learn-ocaml-client" + LEARNOCAML_IMAGE: ${{ matrix.learnocaml_image }} + LEARNOCAML_VERSION: ${{ matrix.learnocaml_version }} + USE_PASSWD: ${{ matrix.use_passwd }} + run: | + docker pull "$LEARNOCAML_IMAGE:$LEARNOCAML_VERSION" + docker pull "$EMACS_IMAGE:$LEARNOCAML_VERSION" + ./test.sh diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 0327cec..0000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -os: linux -dist: bionic -language: shell - -services: - - docker - -env: - global: - - EMACS_IMAGE="pfitaxel/emacs-learn-ocaml-client" - jobs: - - LEARNOCAML_IMAGE="ocamlsf/learn-ocaml" LEARNOCAML_VERSION="0.12" - - LEARNOCAML_IMAGE="pfitaxel/learn-ocaml" LEARNOCAML_VERSION="oauth-moodle-dev" USE_PASSWD="true" - -install: -- docker pull "$LEARNOCAML_IMAGE:$LEARNOCAML_VERSION" -- docker pull "$EMACS_IMAGE:$LEARNOCAML_VERSION" - -script: -- echo -e "${ANSI_YELLOW}Executing tests... ${ANSI_RESET}" && echo -en 'travis_fold:start:script\\r' -- ./test.sh -- echo -en 'travis_fold:end:script\\r' From e8d15cb09c128b5468cb7eb1ff327c45b6f76d2c Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Mon, 5 Jul 2021 02:37:51 +0200 Subject: [PATCH 24/58] fix: the use_passwd test should use init-user --- test.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test.sh b/test.sh index 41db298..c1cf893 100755 --- a/test.sh +++ b/test.sh @@ -125,14 +125,17 @@ echo assert "learn-ocaml-client --version" if [ "$USE_PASSWD" = "true" ]; then + # TODO: Refactor this to run the init command from ERT's fixture + init='learn-ocaml-client init-user -s http://localhost:8080 foo@example.com OCaml123_ Foo ""' selector="" else + init='learn-ocaml-client init --server=http://localhost:8080 Foo ""' selector="learn-ocaml-test-skip-use-passwd" fi assert " cd /build/tests -learn-ocaml-client init --server=http://localhost:8080 test test +$init emacs --batch -l ert -l init-tests.el -l /build/learn-ocaml.el \ -l learn-ocaml-tests.el --eval '(ert-run-tests-batch-and-exit $selector)' " From fb3fa1389937677f0d927a83862076ac85f477af Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Mon, 5 Jul 2021 09:47:00 +0200 Subject: [PATCH 25/58] test: learn-ocaml:oauth-moodle-dev & use_passwd=false --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b98f1dc..e3d54d1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,6 +17,9 @@ jobs: - learnocaml_image: "ocamlsf/learn-ocaml" learnocaml_version: "0.12" use_passwd: "false" + - learnocaml_image: "pfitaxel/learn-ocaml" + learnocaml_version: "oauth-moodle-dev" + use_passwd: "false" - learnocaml_image: "pfitaxel/learn-ocaml" learnocaml_version: "oauth-moodle-dev" use_passwd: "true" From 4b64357c97e30cc141cda999b6bdcb442f885bbe Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Mon, 5 Jul 2021 15:18:16 +0200 Subject: [PATCH 26/58] fix: few problems raised by Erik's review --- learn-ocaml.el | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index ec5fbcc..af7d38a 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -60,7 +60,7 @@ (defvar learn-ocaml-temp-dir nil) -(defvar learn-ocaml-use-pswd nil) +(defvar learn-ocaml-use-passwd nil) (defvar learn-ocaml-log-buffer nil) @@ -180,12 +180,12 @@ Function added in the `kill-emacs-query-functions' hook." t) (defun learn-ocaml-server-config (json) - "Set the global variable learn-ocaml-use-pswd according + "Set the global variable learn-ocaml-use-passwd according to the boolean contained in the json returned by the client" (if (eql (cdr (assoc 'use_passwd (json-read-from-string json))) - :json-false) - (setq learn-ocaml-use-pswd nil) - (setq learn-ocaml-use-pswd t))) + t) + (setq learn-ocaml-use-passwd t) + (setq learn-ocaml-use-passwd nil))) ;; ;; package.el shortcut @@ -599,7 +599,7 @@ Argument SECRET may be needed by the server." (learn-ocaml-show-metadata)))))))) (defun learn-ocaml-download-server-file (id &optional directory) -con "Download the last saved code for exercise ID in DIRECTORY." + "Download the last saved code for exercise ID in DIRECTORY." (interactive `(,(let ((input (read-string (concat "Enter the id of the exercise (default " learn-ocaml-exercise-id @@ -794,7 +794,7 @@ con "Download the last saved code for exercise ID in DIRECTORY." Otherwise, call `learn-ocaml-create-token-cmd' if NEW-TOKEN-VALUE is nil. Otherwise, call `learn-ocaml-use-metadata-cmd'. Finally, run the CALLBACK. -Note: this function will be used by `learn-ocaml-on-load-aux'." +Note: this function will be used by `learn-ocaml-login-with-token'." (if new-server-value ;; without config file (learn-ocaml-init-cmd @@ -838,8 +838,8 @@ Note: this function will be used by `learn-ocaml-on-load-aux'." -(defun learn-ocaml-connection (server callback) - "Connect the user with a (login,passwd) or a token and continue with the callback" +(defun learn-ocaml-login-possibly-with-passwd (server callback) + "Connect the user when learn-ocaml-use-passwd=true with a (login,passwd) or a token and continue with the CALLBACK" (cl-case (x-popup-dialog t `("Welcome to Learn OCaml mode for Emacs.\nWhat do you to do?\n" ("Sign in" . 1) @@ -858,7 +858,7 @@ Note: this function will be used by `learn-ocaml-on-load-aux'." (message (learn-ocaml-client-sign-up-cmd server login password nickname (escape-secret secret)))) (message-box message) - (learn-ocaml-connection server callback))) + (learn-ocaml-login-possibly-with-passwd server callback))) (3 (let ((token (read-string "Enter token: "))) (learn-ocaml-use-metadata-cmd token @@ -883,7 +883,7 @@ Note: this function will be used by `learn-ocaml-on-load-aux'." (setq pswd-conf (read-passwd "Enter password confirmation: "))) (list login pswd nickname secret))) -(defun learn-ocaml-on-load-aux (token new-server-value callback) +(defun learn-ocaml-login-with-token (token new-server-value callback) "At load time: ensure a TOKEN and SERVER are set, then run CALLBACK. If TOKEN is \"\", interactively ask a token." (let* ((rich-callback (lambda (_) @@ -917,7 +917,7 @@ If TOKEN is \"\", interactively ask a token." (defun learn-ocaml-on-load (callback) - "Call `learn-ocaml-on-load-aux' and CALLBACK when loading mode." + "Call `learn-ocaml-login-with-token' and CALLBACK when loading mode." (learn-ocaml-give-server-cmd (lambda (server) (learn-ocaml-give-token-cmd @@ -931,10 +931,10 @@ If TOKEN is \"\", interactively ask a token." (if (version-list-<= (version-to-list (learn-ocaml-client-version)) (version-to-list "0.13")) (progn (learn-ocaml-server-config (learn-ocaml-client-config-cmd)) - (if learn-ocaml-use-pswd - (learn-ocaml-connection new-server-value callback) - (learn-ocaml-on-load-aux token new-server-value callback))) - (learn-ocaml-on-load-aux token new-server-value callback))))))))) + (if learn-ocaml-use-passwd + (learn-ocaml-login-possibly-with-passwd new-server-value callback) + (learn-ocaml-login-with-token token new-server-value callback))) + (learn-ocaml-login-with-token token new-server-value callback))))))))) ;; ;; menu definition From faaad5fa5bae483fbace9833110b2d3a7bcee3ee Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Mon, 5 Jul 2021 18:17:59 +0200 Subject: [PATCH 27/58] feat: logout, exercise progression and better error message when signing in with bad combo or when grading syntax error or other errors --- learn-ocaml.el | 146 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 97 insertions(+), 49 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index af7d38a..54fb2b2 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -111,6 +111,22 @@ Call `get-buffer-create' if need be, to ensure it is a live buffer." (funcall run) (quit nil))))) +(defun learn-ocaml-global-disable-mode () + "Disable learn-ocaml-mode' in ALL buffers." + (interactive "a") + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (funcall 'learn-ocaml-mode -1)))) + +(defun close-all-buffers () + (interactive) + (mapc 'kill-buffer (buffer-list))) + +(defun learn-ocaml-get-progression-by-id (id json) + (if (cdr (assoc (intern id) json)) + (concat (number-to-string (cdr (assoc (intern id) json))) "%") + "NA")) + (defun learn-ocaml-print-time-stamp () "Insert date/time in the buffer given by function `learn-ocaml-log-buffer'." (set-buffer (learn-ocaml-log-buffer)) @@ -276,26 +292,30 @@ add \"opam var bin\" (or another directory) in `exec-path'." (defun learn-ocaml-error-handler (buffer callback proc string) "Get text from BUFFER and pass it to the CALLBACK. To be used as a `make-process' sentinel, using args PROC and STRING." -(let ((result (if (not buffer) + (let ((result (if (not buffer) "" - (set-buffer buffer) - (buffer-string)))) + (set-buffer buffer) + (buffer-string)))) (when buffer (kill-buffer buffer)) (if (or (string-equal string "finished\n") - (string-match "give-token" (process-name proc)) - (string-match "give-server" (process-name proc))) + (string-match "give-token" (process-name proc)) + (string-match "give-server" (process-name proc))) (funcall callback result) - (if (learn-ocaml-yes-or-no learn-ocaml-warning-message) - (progn (switch-to-buffer-other-window "*learn-ocaml-log*") - (goto-char (point-max)) - ;; Do this to cope with the addition of up-to 3 lines - ;; (... Process upload-demo stderr finished) - (recenter-top-bottom -3)) - (when learn-ocaml-fail-noisely - (with-current-buffer (learn-ocaml-log-buffer) - ;; Remark: the log will contain earlier, unrelated info... - (let ((log (buffer-string))) - (error "Process errored. Full log:\n%s" log)))))))) + (progn (set-buffer (learn-ocaml-log-buffer)) + (goto-char (point-max)) + (let ((message + (if (search-backward "[ERROR]" nil t 1) + (buffer-substring (point) (point-max)) ""))) + (cl-case (x-popup-dialog + t `(,message + ("Ok" . 1) + ("Check full learn-ocaml-log" . 2))) + (2 (switch-to-buffer-other-window "*learn-ocaml-log*"))))) + (when learn-ocaml-fail-noisely + (with-current-buffer (learn-ocaml-log-buffer) + ;; Remark: the log will contain earlier, unrelated info... + (let ((log (buffer-string))) + (error "Process errored. Full log:\n%s" log))))))) (cl-defun learn-ocaml-command-constructor (&key command token server local id html dont-submit param1 param2) "Construct a shell command with `learn-ocaml-command-name' and options." @@ -338,6 +358,11 @@ and secret as argument" (concat (shell-quote-argument learn-ocaml-command-name) " init-server -s " server))) +(defun learn-ocaml-client-exercise-score-cmd () + "Run \"learn-ocaml-client exercise-score\"." + (json-read-from-string (shell-command-to-string + (concat (shell-quote-argument learn-ocaml-command-name) " exercise-score")))) + (cl-defun learn-ocaml-init-cmd (&key token server nickname secret callback) "Run \"learn-ocaml-client init\" with options." @@ -671,13 +696,14 @@ Argument SECRET may be needed by the server." :format "%{%t%}" :sample-face 'learn-ocaml-header-hint-face) -(defun learn-ocaml-print-exercise-info (indent tuple) +(defun learn-ocaml-print-exercise-info (indent tuple json-progression) "Render an exercise item with leading INDENT from the data in TUPLE." (let* ((id (elt tuple 0)) (exo (elt tuple 1)) (title (assoc-default 'title exo) ) (short_description (assoc-default 'short_description exo)) - (stars (assoc-default 'stars exo))) + (stars (assoc-default 'stars exo)) + (progression (learn-ocaml-get-progression-by-id id json-progression))) (widget-insert "\n") (widget-insert indent) (widget-create 'learn-ocaml-exercise-title @@ -688,7 +714,8 @@ Argument SECRET may be needed by the server." (widget-insert "\n") (widget-insert (concat indent " ")) (widget-insert (concat "Difficulty: " (number-to-string stars) "/4" - " id: " id)) + " progression: " + progression " id: " id )) (widget-insert "\n") (widget-insert (concat indent " ")) (widget-create 'learn-ocaml-button @@ -714,7 +741,8 @@ Argument SECRET may be needed by the server." (defun learn-ocaml-print-groups (indent json) "Render an exercise group with leading INDENT from the data in JSON." - (let ((head (car json)) + (let ((json-progression (learn-ocaml-client-exercise-score-cmd)) + (head (car json)) (queue (cdr json))) (if (eq 'groups head) (progn @@ -730,7 +758,7 @@ Argument SECRET may be needed by the server." queue)) (seq-do (lambda (elt) (learn-ocaml-print-exercise-info - (concat indent " ") elt)) + (concat indent " ") elt json-progression)) queue) (widget-insert "\n")))) @@ -840,32 +868,35 @@ Note: this function will be used by `learn-ocaml-login-with-token'." (defun learn-ocaml-login-possibly-with-passwd (server callback) "Connect the user when learn-ocaml-use-passwd=true with a (login,passwd) or a token and continue with the CALLBACK" - (cl-case (x-popup-dialog - t `("Welcome to Learn OCaml mode for Emacs.\nWhat do you to do?\n" - ("Sign in" . 1) - ("Sign up" . 2) - ("Connect with an old token" . 3))) - (1 (let* ((login_password (learn-ocaml-sign-in)) - (login (nth 0 login_password)) - (password (nth 1 login_password)) - (message - (learn-ocaml-client-sign-in-cmd server login password))))) - (2 (let* ((infos (learn-ocaml-sign-up)) - (login (nth 0 infos)) - (password (nth 1 infos)) - (nickname (nth 2 infos)) - (secret (nth 3 infos)) - (message (learn-ocaml-client-sign-up-cmd - server login password nickname (escape-secret secret)))) - (message-box message) - (learn-ocaml-login-possibly-with-passwd server callback))) - (3 (let ((token (read-string "Enter token: "))) - (learn-ocaml-use-metadata-cmd - token - nil - (lambda (_) - (message-box "Token saved.")))))) - (funcall callback)) + (cl-case (x-popup-dialog + t `("Welcome to Learn OCaml mode for Emacs.\nWhat do you to do?\n" + ("Sign in" . 1) + ("Sign up" . 2) + ("Connect with an old token" . 3))) + (1 (let* ((login_password (learn-ocaml-sign-in)) + (login (nth 0 login_password)) + (password (nth 1 login_password)) + (message + (learn-ocaml-client-sign-in-cmd server login password))) + (if (string= (seq-subseq message 0 7) "[ERROR]") + (progn (message-box (seq-subseq message 46 100)) + (learn-ocaml-login-possibly-with-passwd server callback))))) + (2 (let* ((infos (learn-ocaml-sign-up)) + (login (nth 0 infos)) + (password (nth 1 infos)) + (nickname (nth 2 infos)) + (secret (nth 3 infos)) + (message (learn-ocaml-client-sign-up-cmd + server login password nickname (escape-secret secret)))) + (message-box message) + (learn-ocaml-login-possibly-with-passwd server callback))) + (3 (let ((token (read-string "Enter token: "))) + (learn-ocaml-use-metadata-cmd + token + nil + (lambda (_) + (message-box "Token saved.")))))) + (funcall callback)) (defun learn-ocaml-sign-in () "Ask interactively the login and the password to the user to sign in" @@ -936,6 +967,16 @@ If TOKEN is \"\", interactively ask a token." (learn-ocaml-login-with-token token new-server-value callback))) (learn-ocaml-login-with-token token new-server-value callback))))))))) +(defun learn-ocaml-logout () + "Logout the user from the server by deleting the config file client.json" + (interactive) + (shell-command-to-string + (concat (shell-quote-argument learn-ocaml-command-name) + " logout")) + (progn (message-box "You have been successfully disconnected") + (learn-ocaml-global-disable-mode) + (close-all-buffers))) + ;; ;; menu definition ;; @@ -948,6 +989,13 @@ If TOKEN is \"\", interactively ask a token." (define-key map [menu-bar] nil) map)) +(defvar learn-ocaml-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "C-c C-m C-l") #'learn-ocaml-display-exercise-list) + (define-key map (kbd "C-c C-m l") #'learn-ocaml-display-exercise-list) + (define-key map (kbd "C-c C-m C-m") #'learn-ocaml-grade) + (define-key map [menu-bar] nil) + map)) (easy-menu-define learn-ocaml-mode-menu learn-ocaml-mode-map "LearnOCaml Mode Menu." @@ -972,12 +1020,12 @@ If TOKEN is \"\", interactively ask a token." ["Show exercise list" learn-ocaml-display-exercise-list] ["Download template" learn-ocaml-download-template] ["Download server version" learn-ocaml-download-server-file] - ["Grade" learn-ocaml-grade])) + ["Grade" learn-ocaml-grade] + ["Logout" learn-ocaml-logout])) ;; ;; id management ;; - (defun learn-ocaml-compute-exercise-id () "Store the exercise id of current buffer in `learn-ocaml-exercise-id'." (when buffer-file-name From 1aa86d7a8c713af88419645a23d3991094a4d4eb Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Mon, 5 Jul 2021 21:48:50 +0200 Subject: [PATCH 28/58] fix: start of replacing sign in and sign up command with make process instead of shell command to string in order to avoid shell code injection --- learn-ocaml.el | 50 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index 54fb2b2..91c83eb 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -328,24 +328,48 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (list (list learn-ocaml-command-name command token-option server-option id-option html-option dont-submit-option local-option param1 param2))) (cl-remove-if-not 'stringp list))) +(cl-defun learn-ocaml-init-user-command-constructor (&key server login password nickname secret) + "Construct a shell command with `learn-ocaml-command-name' and options." + (let* ((nickname-option (when nickname (concat " " nickname))) + (secret-option (when secret (concat " " secret))) + (server-option (when server (concat "--server=" server))) + (list (list learn-ocaml-command-name "init-user" server-option login password nickname-option secret-option))) + (cl-remove-if-not 'stringp list))) + (defun learn-ocaml-client-version () "Run \"learn-ocaml-client --version\"." (string-trim (shell-command-to-string (concat (shell-quote-argument learn-ocaml-command-name) " --version")))) -(defun learn-ocaml-client-sign-in-cmd (server login password) - "Run learn-ocaml-client init-user with login and password as argument" - (shell-command-to-string - (concat (shell-quote-argument learn-ocaml-command-name) - " init-user --server=" server " " login " " password))) - -(defun learn-ocaml-client-sign-up-cmd (server login password nickname secret) - "Run learn-ocaml-client init-user with login password nickname -and secret as argument" - (shell-command-to-string - (concat (shell-quote-argument learn-ocaml-command-name) - " init-user --server=" server " " - login " " password " " nickname " " secret))) +(cl-defun learn-ocaml-sign-in-cmd (&key server login password) + "Run \"learn-ocaml-client init-user\" with server login password to sign-in an user." + (learn-ocaml-print-time-stamp) + (learn-ocaml-make-process-wrapper + :name "init-user" + :command (learn-ocaml-init-user-command-constructor :server server + :login login + :password password) + :stderr (learn-ocaml-log-buffer) + :sentinel (apply-partially + #'learn-ocaml-error-handler + nil + (lambda(_))))) + +(cl-defun learn-ocaml-sign-up-cmd (&key server login password nickname secret) + "Run \"learn-ocaml-client init-user\" with server login password nickname and secret to sign-up an user." + (learn-ocaml-print-time-stamp) + (learn-ocaml-make-process-wrapper + :name "init-user" + :command (learn-ocaml-init-user-command-constructor :server server + :login login + :password password + :nickname nickname + :secret secret) + :stderr (learn-ocaml-log-buffer) + :sentinel (apply-partially + #'learn-ocaml-error-handler + nil + (lambda(_))))) (defun learn-ocaml-client-config-cmd () "Run \"learn-ocaml-client server-config\"." From 1f1ed09716274e227191b25a8f57aea1104506fa Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Tue, 6 Jul 2021 17:55:54 +0200 Subject: [PATCH 29/58] fix: replace sign-in-cmd and sign-up-cmd with make-process instead of shell-command-to-string --- learn-ocaml.el | 122 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 82 insertions(+), 40 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index 91c83eb..4114da4 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -317,6 +317,34 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (let ((log (buffer-string))) (error "Process errored. Full log:\n%s" log))))))) +(defun learn-ocaml-error-handler-sign-in (buffer callback-ok callback-err proc string) + "Get text from BUFFER and pass it to the CALLBACK. +To be used as a `make-process' sentinel, using args PROC and STRING." + (let ((result (if (not buffer) + "" + (set-buffer buffer) + (buffer-string)))) + (when buffer (kill-buffer buffer)) + (if (or (string-equal string "finished\n")) + (funcall callback-ok) + (progn (set-buffer (learn-ocaml-log-buffer)) + (insert result) + (funcall callback-err result))))) + +(defun learn-ocaml-error-handler-sign-up (buffer callback-ok callback-err proc string) + "Get text from BUFFER and pass it to the CALLBACK. +To be used as a `make-process' sentinel, using args PROC and STRING." + (let ((result (if (not buffer) + "" + (set-buffer buffer) + (buffer-string)))) + (when buffer (kill-buffer buffer)) + (if (or (string-equal string "finished\n")) + (funcall callback-ok result) + (progn (set-buffer (learn-ocaml-log-buffer)) + (insert result) + (funcall callback-err result))))) + (cl-defun learn-ocaml-command-constructor (&key command token server local id html dont-submit param1 param2) "Construct a shell command with `learn-ocaml-command-name' and options." (let* ((server-option (when server (concat "--server=" server))) @@ -330,8 +358,8 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (cl-defun learn-ocaml-init-user-command-constructor (&key server login password nickname secret) "Construct a shell command with `learn-ocaml-command-name' and options." - (let* ((nickname-option (when nickname (concat " " nickname))) - (secret-option (when secret (concat " " secret))) + (let* ((nickname-option (when nickname nickname)) + (secret-option (when secret secret)) (server-option (when server (concat "--server=" server))) (list (list learn-ocaml-command-name "init-user" server-option login password nickname-option secret-option))) (cl-remove-if-not 'stringp list))) @@ -341,35 +369,39 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (string-trim (shell-command-to-string (concat (shell-quote-argument learn-ocaml-command-name) " --version")))) -(cl-defun learn-ocaml-sign-in-cmd (&key server login password) +(cl-defun learn-ocaml-client-sign-in-cmd (&key server login password callback-ok callback-err) "Run \"learn-ocaml-client init-user\" with server login password to sign-in an user." (learn-ocaml-print-time-stamp) - (learn-ocaml-make-process-wrapper - :name "init-user" - :command (learn-ocaml-init-user-command-constructor :server server - :login login - :password password) - :stderr (learn-ocaml-log-buffer) - :sentinel (apply-partially - #'learn-ocaml-error-handler - nil - (lambda(_))))) + (let ((buffer (generate-new-buffer "sign-in-std-out"))) + (learn-ocaml-make-process-wrapper + :name "init-user" + :command (learn-ocaml-init-user-command-constructor :server server + :login login + :password password) + :buffer buffer + :sentinel (apply-partially + #'learn-ocaml-error-handler-sign-in + buffer + callback-ok + callback-err)))) -(cl-defun learn-ocaml-sign-up-cmd (&key server login password nickname secret) +(cl-defun learn-ocaml-client-sign-up-cmd (&key server login password nickname secret callback-ok callback-err) "Run \"learn-ocaml-client init-user\" with server login password nickname and secret to sign-up an user." (learn-ocaml-print-time-stamp) - (learn-ocaml-make-process-wrapper - :name "init-user" - :command (learn-ocaml-init-user-command-constructor :server server - :login login - :password password - :nickname nickname - :secret secret) - :stderr (learn-ocaml-log-buffer) - :sentinel (apply-partially - #'learn-ocaml-error-handler - nil - (lambda(_))))) + (let ((buffer (generate-new-buffer "sign-up-std-out"))) + (learn-ocaml-make-process-wrapper + :name "init-user" + :command (learn-ocaml-init-user-command-constructor :server server + :login login + :password password + :nickname nickname + :secret secret) + :buffer buffer + :sentinel (apply-partially + #'learn-ocaml-error-handler-sign-up + buffer + callback-ok + callback-err)))) (defun learn-ocaml-client-config-cmd () "Run \"learn-ocaml-client server-config\"." @@ -891,7 +923,7 @@ Note: this function will be used by `learn-ocaml-login-with-token'." (defun learn-ocaml-login-possibly-with-passwd (server callback) - "Connect the user when learn-ocaml-use-passwd=true with a (login,passwd) or a token and continue with the CALLBACK" + "Connect the user when learn-ocaml-use-passwd=true with a (login,passwd) or a token and continue with the CALLBACK." (cl-case (x-popup-dialog t `("Welcome to Learn OCaml mode for Emacs.\nWhat do you to do?\n" ("Sign in" . 1) @@ -899,28 +931,38 @@ Note: this function will be used by `learn-ocaml-login-with-token'." ("Connect with an old token" . 3))) (1 (let* ((login_password (learn-ocaml-sign-in)) (login (nth 0 login_password)) - (password (nth 1 login_password)) - (message - (learn-ocaml-client-sign-in-cmd server login password))) - (if (string= (seq-subseq message 0 7) "[ERROR]") - (progn (message-box (seq-subseq message 46 100)) - (learn-ocaml-login-possibly-with-passwd server callback))))) + (password (nth 1 login_password))) + (learn-ocaml-client-sign-in-cmd + :server server + :login login + :password password + :callback-ok callback + :callback-err (lambda(s) + (message-box s) + (learn-ocaml-login-possibly-with-passwd server callback))))) (2 (let* ((infos (learn-ocaml-sign-up)) (login (nth 0 infos)) (password (nth 1 infos)) (nickname (nth 2 infos)) - (secret (nth 3 infos)) - (message (learn-ocaml-client-sign-up-cmd - server login password nickname (escape-secret secret)))) - (message-box message) - (learn-ocaml-login-possibly-with-passwd server callback))) + (secret (nth 3 infos))) + (learn-ocaml-client-sign-up-cmd + :server server + :login login + :password password + :nickname nickname + :secret secret + :callback-ok (lambda(s) + (message-box s) + (learn-ocaml-login-possibly-with-passwd server callback)) + :callback-err (lambda(s) + (message-box s) + (learn-ocaml-login-possibly-with-passwd server callback))))) (3 (let ((token (read-string "Enter token: "))) (learn-ocaml-use-metadata-cmd token nil (lambda (_) - (message-box "Token saved.")))))) - (funcall callback)) + (message-box "Token saved."))))))) (defun learn-ocaml-sign-in () "Ask interactively the login and the password to the user to sign in" From efd9426c7e34d203993662267dcb9c53b58aaa26 Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Wed, 7 Jul 2021 10:17:31 +0200 Subject: [PATCH 30/58] fix: replace also init-server-cmd with make-process instead of shell-command-to-string --- learn-ocaml.el | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index 4114da4..3d76cc7 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -408,11 +408,19 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (shell-command-to-string (concat (shell-quote-argument learn-ocaml-command-name) " server-config"))) -(defun learn-ocaml-client-init-server-cmd (server) - "Run \"learn-ocaml-client server-config\"." - (shell-command-to-string - (concat - (shell-quote-argument learn-ocaml-command-name) " init-server -s " server))) +(cl-defun learn-ocaml-init-server-cmd (&key server callback) + "Run \"learn-ocaml-client init\" with options." + (learn-ocaml-print-time-stamp) + (learn-ocaml-make-process-wrapper + :name "init-server" + :command (learn-ocaml-command-constructor + :server server + :command "init-server") + :stderr (learn-ocaml-log-buffer) + :sentinel (apply-partially + #'learn-ocaml-error-handler + nil + callback))) (defun learn-ocaml-client-exercise-score-cmd () "Run \"learn-ocaml-client exercise-score\"." @@ -1024,14 +1032,16 @@ If TOKEN is \"\", interactively ask a token." (progn (message-box "No server found. Please enter the server url.") (read-string "Enter server URL: " "https://")) server))) - (progn (learn-ocaml-client-init-server-cmd new-server-value) + (progn (learn-ocaml-init-server-cmd :server new-server-value + :callback + (lambda(_) (if (version-list-<= (version-to-list (learn-ocaml-client-version)) (version-to-list "0.13")) (progn (learn-ocaml-server-config (learn-ocaml-client-config-cmd)) (if learn-ocaml-use-passwd (learn-ocaml-login-possibly-with-passwd new-server-value callback) (learn-ocaml-login-with-token token new-server-value callback))) - (learn-ocaml-login-with-token token new-server-value callback))))))))) + (learn-ocaml-login-with-token token new-server-value callback))))))))))) (defun learn-ocaml-logout () "Logout the user from the server by deleting the config file client.json" From ba6f0d58e8fbce5f1c389d26c5d3bbc29e84fc17 Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Wed, 7 Jul 2021 10:22:01 +0200 Subject: [PATCH 31/58] feat: Split logout into logout which remove the token and deinit which remove the entire file cookie --- learn-ocaml.el | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index 3d76cc7..54889fa 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -1044,7 +1044,7 @@ If TOKEN is \"\", interactively ask a token." (learn-ocaml-login-with-token token new-server-value callback))))))))))) (defun learn-ocaml-logout () - "Logout the user from the server by deleting the config file client.json" + "Logout the user from the server by removing the token from the file client.json" (interactive) (shell-command-to-string (concat (shell-quote-argument learn-ocaml-command-name) @@ -1053,6 +1053,17 @@ If TOKEN is \"\", interactively ask a token." (learn-ocaml-global-disable-mode) (close-all-buffers))) + +(defun learn-ocaml-deinit () + "Logout the user and forget the server by removing the file client.json" + (interactive) + (shell-command-to-string + (concat (shell-quote-argument learn-ocaml-command-name) + " deinit")) + (progn (message-box "You have been successfully disconnected") + (learn-ocaml-global-disable-mode) + (close-all-buffers))) + ;; ;; menu definition ;; @@ -1097,7 +1108,9 @@ If TOKEN is \"\", interactively ask a token." ["Download template" learn-ocaml-download-template] ["Download server version" learn-ocaml-download-server-file] ["Grade" learn-ocaml-grade] - ["Logout" learn-ocaml-logout])) + "---" + ["Logout" learn-ocaml-logout] + ["Logout & Forget server" learn-ocaml-deinit])) ;; ;; id management ;; From 821deeabe4f71c1ed11e9c144aaf6f37f402e187 Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Wed, 7 Jul 2021 10:40:49 +0200 Subject: [PATCH 32/58] fix: missing a word in first choice connection message --- learn-ocaml.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index 54889fa..890b283 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -933,7 +933,7 @@ Note: this function will be used by `learn-ocaml-login-with-token'." (defun learn-ocaml-login-possibly-with-passwd (server callback) "Connect the user when learn-ocaml-use-passwd=true with a (login,passwd) or a token and continue with the CALLBACK." (cl-case (x-popup-dialog - t `("Welcome to Learn OCaml mode for Emacs.\nWhat do you to do?\n" + t `("Welcome to Learn OCaml mode for Emacs.\nWhat do you want to do?\n" ("Sign in" . 1) ("Sign up" . 2) ("Connect with an old token" . 3))) From 5dc2747493792ae085754b2c0453cfe7ade686e0 Mon Sep 17 00:00:00 2001 From: LouisAyroles Date: Mon, 12 Jul 2021 17:51:14 +0200 Subject: [PATCH 33/58] fix: update learn-ocaml-server-config to match with the string returnn by server-config instead of the boolean --- learn-ocaml.el | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index 890b283..b1912e4 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -198,8 +198,8 @@ Function added in the `kill-emacs-query-functions' hook." (defun learn-ocaml-server-config (json) "Set the global variable learn-ocaml-use-passwd according to the boolean contained in the json returned by the client" - (if (eql (cdr (assoc 'use_passwd (json-read-from-string json))) - t) + (if (string= (cdr (assoc 'use_passwd (json-read-from-string json))) + "true") (setq learn-ocaml-use-passwd t) (setq learn-ocaml-use-passwd nil))) @@ -543,6 +543,7 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (lambda (s) (funcall-interactively callback + (learn-ocaml--rstrip s))))))) (defun learn-ocaml-use-metadata-cmd (token server callback) From 6c0fd53d757b3fa1757e5986ccb2c1282dc1df82 Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Thu, 22 Jul 2021 14:27:15 +0200 Subject: [PATCH 34/58] refactor: s|NA|N/A|; s|login|e-mail|; s|old|legacy| --- learn-ocaml.el | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index b1912e4..ac84282 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -125,7 +125,7 @@ Call `get-buffer-create' if need be, to ensure it is a live buffer." (defun learn-ocaml-get-progression-by-id (id json) (if (cdr (assoc (intern id) json)) (concat (number-to-string (cdr (assoc (intern id) json))) "%") - "NA")) + "N/A")) (defun learn-ocaml-print-time-stamp () "Insert date/time in the buffer given by function `learn-ocaml-log-buffer'." @@ -370,7 +370,7 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (concat (shell-quote-argument learn-ocaml-command-name) " --version")))) (cl-defun learn-ocaml-client-sign-in-cmd (&key server login password callback-ok callback-err) - "Run \"learn-ocaml-client init-user\" with server login password to sign-in an user." + "Run \"learn-ocaml-client init-user\" with SERVER LOGIN PASSWORD to login an user." (learn-ocaml-print-time-stamp) (let ((buffer (generate-new-buffer "sign-in-std-out"))) (learn-ocaml-make-process-wrapper @@ -386,7 +386,7 @@ To be used as a `make-process' sentinel, using args PROC and STRING." callback-err)))) (cl-defun learn-ocaml-client-sign-up-cmd (&key server login password nickname secret callback-ok callback-err) - "Run \"learn-ocaml-client init-user\" with server login password nickname and secret to sign-up an user." + "Run \"learn-ocaml-client init-user\" with SERVER LOGIN PASSWORD NICKNAME and SECRET to sign-up an user." (learn-ocaml-print-time-stamp) (let ((buffer (generate-new-buffer "sign-up-std-out"))) (learn-ocaml-make-process-wrapper @@ -932,12 +932,12 @@ Note: this function will be used by `learn-ocaml-login-with-token'." (defun learn-ocaml-login-possibly-with-passwd (server callback) - "Connect the user when learn-ocaml-use-passwd=true with a (login,passwd) or a token and continue with the CALLBACK." + "Connect the user when learn-ocaml-use-passwd=true with an (email,passwd) or a token and continue with the CALLBACK." (cl-case (x-popup-dialog t `("Welcome to Learn OCaml mode for Emacs.\nWhat do you want to do?\n" - ("Sign in" . 1) - ("Sign up" . 2) - ("Connect with an old token" . 3))) + ("Login" . 1) + ("Sign-up" . 2) + ("Login with a legacy token" . 3))) (1 (let* ((login_password (learn-ocaml-sign-in)) (login (nth 0 login_password)) (password (nth 1 login_password))) @@ -974,12 +974,12 @@ Note: this function will be used by `learn-ocaml-login-with-token'." (message-box "Token saved."))))))) (defun learn-ocaml-sign-in () - "Ask interactively the login and the password to the user to sign in" - (list (read-string "Enter login: ") (read-passwd "Enter password: "))) + "Ask interactively the e-mail and the password for the user to login" + (list (read-string "Enter e-mail: ") (read-passwd "Enter password: "))) (defun learn-ocaml-sign-up () - "Ask interactively the login, password(with confirmation), nickname, secret" - (let* ((login (read-string "Enter login: ")) + "Ask interactively the e-mail,password(with confirmation),nickname,secret" + (let* ((login (read-string "Enter e-mail: ")) (pswd (read-passwd "Enter password: ")) (pswd-conf (read-passwd "Enter password confirmation: ")) (nickname (read-string "Enter nickname: ")) From 67d770434caa087d2481c3046f6a3e66f9f05356 Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Thu, 22 Jul 2021 15:14:18 +0200 Subject: [PATCH 35/58] refactor: Simplify the sentinels * Merge learn-ocaml-error-handler-sign-in and learn-ocaml-error-handler-sign-up to learn-ocaml-error-handler-nosplit-catch * Document the workflow for authenticating (sign-up/login) --- learn-ocaml.el | 54 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index ac84282..4a19184 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -287,7 +287,28 @@ add \"opam var bin\" (or another directory) in `exec-path'." (apply #'learn-ocaml-make-process-wrapper args) ; this could be a loop nil)))) - +;; +;; Higher-order functions, sentinels of the make-process wrapper +;; +;; NOTE: if we want to get stdout+stderr output in one go, we need to +;; omit the :stderr argument of make-process. Otherwise, to get the +;; streams apart (e.g., for grade), the :stderr arg must be specified. + +;; FIXME: Move this docstring? +;; Summary of the workflow for sign-up/sign-in +;; - Choice (Sign-up/Login ?) +;; - Sign-up: +;; - Ask login, password, nickname, secret +;; - learn-ocaml-client init-user +;; - message-box stdout+stderr (e-mail sent) +;; - GoTo Choice +;; - Login: +;; - Ask login, password (TODO Check that OK if C-g) +;; - learn-ocaml-client init-user +;; - if exit code 0: callback “open exo list?” +;; - if exit code >0: +;; - message-box stdout+stderr (which will contain ERROR) +;; - GoTo Login (defun learn-ocaml-error-handler (buffer callback proc string) "Get text from BUFFER and pass it to the CALLBACK. @@ -317,22 +338,8 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (let ((log (buffer-string))) (error "Process errored. Full log:\n%s" log))))))) -(defun learn-ocaml-error-handler-sign-in (buffer callback-ok callback-err proc string) - "Get text from BUFFER and pass it to the CALLBACK. -To be used as a `make-process' sentinel, using args PROC and STRING." - (let ((result (if (not buffer) - "" - (set-buffer buffer) - (buffer-string)))) - (when buffer (kill-buffer buffer)) - (if (or (string-equal string "finished\n")) - (funcall callback-ok) - (progn (set-buffer (learn-ocaml-log-buffer)) - (insert result) - (funcall callback-err result))))) - -(defun learn-ocaml-error-handler-sign-up (buffer callback-ok callback-err proc string) - "Get text from BUFFER and pass it to the CALLBACK. +(defun learn-ocaml-error-handler-nosplit-catch (buffer callback-ok callback-err proc string) + "Get text from BUFFER, pass it to CALLBACK-OK ($?=0) or CALLBACK-ERR. To be used as a `make-process' sentinel, using args PROC and STRING." (let ((result (if (not buffer) "" @@ -345,6 +352,10 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (insert result) (funcall callback-err result))))) +;; +;; CLI constructors +;; + (cl-defun learn-ocaml-command-constructor (&key command token server local id html dont-submit param1 param2) "Construct a shell command with `learn-ocaml-command-name' and options." (let* ((server-option (when server (concat "--server=" server))) @@ -380,7 +391,7 @@ To be used as a `make-process' sentinel, using args PROC and STRING." :password password) :buffer buffer :sentinel (apply-partially - #'learn-ocaml-error-handler-sign-in + #'learn-ocaml-error-handler-nosplit-catch buffer callback-ok callback-err)))) @@ -398,7 +409,7 @@ To be used as a `make-process' sentinel, using args PROC and STRING." :secret secret) :buffer buffer :sentinel (apply-partially - #'learn-ocaml-error-handler-sign-up + #'learn-ocaml-error-handler-nosplit-catch buffer callback-ok callback-err)))) @@ -932,7 +943,7 @@ Note: this function will be used by `learn-ocaml-login-with-token'." (defun learn-ocaml-login-possibly-with-passwd (server callback) - "Connect the user when learn-ocaml-use-passwd=true with an (email,passwd) or a token and continue with the CALLBACK." + "Connect the user when learn-ocaml-use-passwd=true with an (email,passwd) or a token and continue with the no-arg CALLBACK." (cl-case (x-popup-dialog t `("Welcome to Learn OCaml mode for Emacs.\nWhat do you want to do?\n" ("Login" . 1) @@ -945,7 +956,8 @@ Note: this function will be used by `learn-ocaml-login-with-token'." :server server :login login :password password - :callback-ok callback + :callback-ok (lambda(_) + (funcall callback)) :callback-err (lambda(s) (message-box s) (learn-ocaml-login-possibly-with-passwd server callback))))) From 7229dae40fe61426331640cd4a2c077529a9930d Mon Sep 17 00:00:00 2001 From: ImJustLouis Date: Fri, 23 Jul 2021 23:05:58 +0200 Subject: [PATCH 36/58] wip: Add .ocamlinit and .merlin to working directory --- learn-ocaml.el | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index 04268e5..81b837e 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -59,6 +59,8 @@ (defvar learn-ocaml-temp-dir nil) +(defvar learn-ocaml-working-directory nil) + (defvar learn-ocaml-log-buffer nil) (defun learn-ocaml-log-buffer () @@ -223,7 +225,16 @@ Call `learn-ocaml-display-exercise-list' if OPEN-EXO-LIST is non-nil." default-directory nil ""))) (make-directory dir t) - (setq default-directory dir)) + (setq default-directory dir) + (setq learn-ocaml-working-directory dir)) + (write-region "B .learn-ocaml" nil + (learn-ocaml-file-path learn-ocaml-working-directory ".merlin")) + (when (file-exists-p (concat learn-ocaml-working-directory "/.ocamlinit")) + (copy-file + (concat learn-ocaml-working-directory "/.ocamlinit") + (concat learn-ocaml-working-directory "/.ocamlinit~") t)) + (write-region "#load .learn-ocaml/Given_demo.cmo;;\nopen Given_demo;;" nil + (learn-ocaml-file-path learn-ocaml-working-directory ".ocamlinit")) (if open-exo-list (learn-ocaml-display-exercise-list))) (cl-defun learn-ocaml-make-process-wrapper (&rest args &key command &allow-other-keys) From 33dc4cda243cbee07292c1ec566774f534e9f375 Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Mon, 26 Jul 2021 12:23:14 +0200 Subject: [PATCH 37/58] feat: Add `learn-ocaml-await-for` & Avoid `shell-command-to-string` * Add more doc-comments * Add two "FIXME(Bug)" [learn-ocaml-error-handler, $ learn-ocaml-client deinit] --- learn-ocaml.el | 132 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 109 insertions(+), 23 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index 4a19184..41c8c02 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -48,6 +48,9 @@ (defvar learn-ocaml-fail-noisely nil "Set `learn-ocaml-fail-noisely' to non-nil for `ert'-testing purposes.") +(defconst learn-ocaml-timeout 4 + "Time in s for `learn-ocaml-await-for' to wait after calling `make-process'.") + (defconst learn-ocaml-mode-version "1.0.0-git") (defconst learn-ocaml-command-name "learn-ocaml-client") @@ -287,6 +290,75 @@ add \"opam var bin\" (or another directory) in `exec-path'." (apply #'learn-ocaml-make-process-wrapper args) ; this could be a loop nil)))) +(defun learn-ocaml-await-for (fun-kk-body time &optional descr) + "Run FUN-KK-BODY, wait TIME s for a callback to be called, or failwith DESCR. +Return (t . \"stdout+stderr\") if exit code = 0; +Return (nil . \"stdout+stderr\") if exit code > 0." + (let ((wip t)) + (catch 'result + (let ((result + #'(lambda (res) + (if wip (throw 'result `(t . ,res)) + (message "learn-ocaml-await-for%s: Got result too late [%s]." + (if descr (concat "[" descr "]") "") res)))) + (failure + #'(lambda (res) + (if wip (throw 'result `(nil . ,res)) + (message "learn-ocaml-await-for%s: Got failure too late [%s]." + (if descr (concat "[" descr "]") "") res))))) + ;; Note that FUN-K-BODY is not wrapped by `with-timeout' + ;; as FUN-KK-BODY will typically be an async call to `make-process' + ;; and we just wait for the subprocess to terminate within TIME s. + (funcall fun-kk-body result failure) + (let ((end-time (+ (float time) (float-time)))) ;; start counting here + (while (< (float-time) end-time) + (accept-process-output nil 0.1))) + (setq wip nil) + (error + "learn-ocaml-await-for%s: didn't receive result after %ds." + (if descr (concat "[" descr "]") "") time))))) + +;; ;; TEST-CASE +;; (let ((buffer (generate-new-buffer "test"))) +;; (learn-ocaml-await-for +;; #'(lambda (result failure) +;; (make-process +;; :name "arg1" +;; :command '("sh" "-c" "echo output1; sleep 1s; echo error >&2; false") +;; :buffer buffer +;; :sentinel (apply-partially +;; #'learn-ocaml-error-handler-nosplit-catch +;; buffer +;; #'(lambda (s) (funcall result s)) +;; #'(lambda (s) (funcall failure s))))) +;; 2 ;; or 0 +;; "test")) + +(defun learn-ocaml-command-to-string-await-cmd (arg1) + "Run \"learn-ocaml-client ARG1\" (only one arg supported). +This is a `learn-ocaml-update-exec-path'-enhanced replacement for +(shell-command-to-string (concat learn-ocaml-command-name arg1)), +relying on `learn-ocaml-await-for'. +Return (t . \"stdout+stderr\") if exit code = 0; +Return (nil . \"stdout+stderr\") if exit code > 0; +Raise (error \"learn-ocaml-await-for...\") if `learn-ocaml-timeout' exceeded." + ;; (learn-ocaml-print-time-stamp) ;; FIXME: enable? + (unless (stringp arg1) + (error "ARG1 must be a string (in learn-ocaml-command-to-string-cmd)")) + (let ((buffer (generate-new-buffer (concat arg1 "-std-out")))) + (learn-ocaml-await-for + #'(lambda (result failure) + (learn-ocaml-make-process-wrapper + :name arg1 + :command `(,learn-ocaml-command-name ,arg1) + :buffer buffer + :sentinel (apply-partially + #'learn-ocaml-error-handler-nosplit-catch + buffer + #'(lambda (s) (funcall result s)) + #'(lambda (s) (funcall failure s))))) + learn-ocaml-timeout arg1))) + ;; ;; Higher-order functions, sentinels of the make-process wrapper ;; @@ -325,6 +397,7 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (progn (set-buffer (learn-ocaml-log-buffer)) (goto-char (point-max)) (let ((message + ;; FIXME(Bug): this returns the whole buffer text! (if (search-backward "[ERROR]" nil t 1) (buffer-substring (point) (point-max)) ""))) (cl-case (x-popup-dialog @@ -377,8 +450,7 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (defun learn-ocaml-client-version () "Run \"learn-ocaml-client --version\"." - (string-trim (shell-command-to-string - (concat (shell-quote-argument learn-ocaml-command-name) " --version")))) + (string-trim (cdr (learn-ocaml-command-to-string-await-cmd "--version")))) (cl-defun learn-ocaml-client-sign-in-cmd (&key server login password callback-ok callback-err) "Run \"learn-ocaml-client init-user\" with SERVER LOGIN PASSWORD to login an user." @@ -416,8 +488,11 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (defun learn-ocaml-client-config-cmd () "Run \"learn-ocaml-client server-config\"." - (shell-command-to-string - (concat (shell-quote-argument learn-ocaml-command-name) " server-config"))) + (let* ((cmd "server-config") + (result (learn-ocaml-command-to-string-await-cmd cmd))) + (if (car result) (cdr result) + ;; FIXME: Use learn-ocaml-log-buffer + (error "%s %s: failed with [%s]." learn-ocaml-command-name cmd (string-trim (cdr result)))))) (cl-defun learn-ocaml-init-server-cmd (&key server callback) "Run \"learn-ocaml-client init\" with options." @@ -435,9 +510,12 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (defun learn-ocaml-client-exercise-score-cmd () "Run \"learn-ocaml-client exercise-score\"." - (json-read-from-string (shell-command-to-string - (concat (shell-quote-argument learn-ocaml-command-name) " exercise-score")))) - + (let* ((cmd "exercise-score") + (result (learn-ocaml-command-to-string-await-cmd cmd))) + (if (car result) (json-read-from-string (cdr result)) + ;; FIXME: Use learn-ocaml-log-buffer + (error "%s %s: failed with [%s]." learn-ocaml-command-name cmd + (string-trim (cdr result)))))) (cl-defun learn-ocaml-init-cmd (&key token server nickname secret callback) "Run \"learn-ocaml-client init\" with options." @@ -940,8 +1018,6 @@ Note: this function will be used by `learn-ocaml-login-with-token'." nil callback))) - - (defun learn-ocaml-login-possibly-with-passwd (server callback) "Connect the user when learn-ocaml-use-passwd=true with an (email,passwd) or a token and continue with the no-arg CALLBACK." (cl-case (x-popup-dialog @@ -1033,7 +1109,6 @@ If TOKEN is \"\", interactively ask a token." :secret secret :callback rich-callback))))))) - (defun learn-ocaml-on-load (callback) "Call `learn-ocaml-login-with-token' and CALLBACK when loading mode." (learn-ocaml-give-server-cmd @@ -1059,23 +1134,34 @@ If TOKEN is \"\", interactively ask a token." (defun learn-ocaml-logout () "Logout the user from the server by removing the token from the file client.json" (interactive) - (shell-command-to-string - (concat (shell-quote-argument learn-ocaml-command-name) - " logout")) - (progn (message-box "You have been successfully disconnected") - (learn-ocaml-global-disable-mode) - (close-all-buffers))) - + (let* ((cmd "logout") + (result (learn-ocaml-command-to-string-await-cmd cmd))) + (if (car result) + (progn (message-box "You have been successfully disconnected\n\n%s" + (cdr result)) + (learn-ocaml-global-disable-mode) + (close-all-buffers)) + ;; FIXME: Use learn-ocaml-log-buffer + (error "%s %s: failed with [%s]." learn-ocaml-command-name cmd + (string-trim (cdr result)))))) (defun learn-ocaml-deinit () "Logout the user and forget the server by removing the file client.json" + ;; FIXME(Bug): + ;; $ learn-ocaml-client deinit + ;; should fail with exit code > 0 + ;; if Cannot remove ~/.config/learnocaml/client.json : no such file or directory. (interactive) - (shell-command-to-string - (concat (shell-quote-argument learn-ocaml-command-name) - " deinit")) - (progn (message-box "You have been successfully disconnected") - (learn-ocaml-global-disable-mode) - (close-all-buffers))) + (let* ((cmd "deinit") + (result (learn-ocaml-command-to-string-await-cmd cmd))) + (if (car result) + (progn (message-box "You have been successfully disconnected\n\n%s" + (cdr result)) + (learn-ocaml-global-disable-mode) + (close-all-buffers)) + ;; FIXME: Use learn-ocaml-log-buffer + (error "%s %s: failed with [%s]." learn-ocaml-command-name cmd + (string-trim (cdr result)))))) ;; ;; menu definition From f1b7e0ddb0e3c40496db26a590866158407bfaed Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Tue, 27 Jul 2021 00:46:55 +0200 Subject: [PATCH 38/58] fix: Remove (close-all-buffers) that looks unnecessary --- learn-ocaml.el | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index 41c8c02..ec95f20 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -121,10 +121,6 @@ Call `get-buffer-create' if need be, to ensure it is a live buffer." (with-current-buffer buffer (funcall 'learn-ocaml-mode -1)))) -(defun close-all-buffers () - (interactive) - (mapc 'kill-buffer (buffer-list))) - (defun learn-ocaml-get-progression-by-id (id json) (if (cdr (assoc (intern id) json)) (concat (number-to-string (cdr (assoc (intern id) json))) "%") @@ -1139,8 +1135,7 @@ If TOKEN is \"\", interactively ask a token." (if (car result) (progn (message-box "You have been successfully disconnected\n\n%s" (cdr result)) - (learn-ocaml-global-disable-mode) - (close-all-buffers)) + (learn-ocaml-global-disable-mode)) ;; FIXME: Use learn-ocaml-log-buffer (error "%s %s: failed with [%s]." learn-ocaml-command-name cmd (string-trim (cdr result)))))) @@ -1157,8 +1152,7 @@ If TOKEN is \"\", interactively ask a token." (if (car result) (progn (message-box "You have been successfully disconnected\n\n%s" (cdr result)) - (learn-ocaml-global-disable-mode) - (close-all-buffers)) + (learn-ocaml-global-disable-mode)) ;; FIXME: Use learn-ocaml-log-buffer (error "%s %s: failed with [%s]." learn-ocaml-command-name cmd (string-trim (cdr result)))))) From 92a33023094d3da4f448e80c06aae4eae4438961 Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Tue, 27 Jul 2021 01:03:52 +0200 Subject: [PATCH 39/58] docs: Add FIXME --- learn-ocaml.el | 1 + 1 file changed, 1 insertion(+) diff --git a/learn-ocaml.el b/learn-ocaml.el index ec95f20..52a9028 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -378,6 +378,7 @@ Raise (error \"learn-ocaml-await-for...\") if `learn-ocaml-timeout' exceeded." ;; - message-box stdout+stderr (which will contain ERROR) ;; - GoTo Login +;; FIXME: Add buffer-err argument, copied to (learn-ocaml-log-buffer) (defun learn-ocaml-error-handler (buffer callback proc string) "Get text from BUFFER and pass it to the CALLBACK. To be used as a `make-process' sentinel, using args PROC and STRING." From 870a379c85d20feabe2081e4bb6260261ec0173e Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Tue, 27 Jul 2021 01:15:12 +0200 Subject: [PATCH 40/58] test(test.sh): Change default value of env vars, for local testing --- test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test.sh b/test.sh index c1cf893..8ac1763 100755 --- a/test.sh +++ b/test.sh @@ -16,13 +16,13 @@ red () { green "Beforehand: LEARNOCAML_VERSION=$LEARNOCAML_VERSION" # Default learn-ocaml version -: ${LEARNOCAML_VERSION:=dev} +: ${LEARNOCAML_VERSION:=oauth-moodle-dev} # Do "export LEARNOCAML_VERSION=…" before running test.sh to override green "Henceforth: LEARNOCAML_VERSION=$LEARNOCAML_VERSION\n" green "Beforehand: LEARNOCAML_IMAGE=$LEARNOCAML_IMAGE" # Default learn-ocaml image -: ${LEARNOCAML_IMAGE:=ocamlsf/learn-ocaml} +: ${LEARNOCAML_IMAGE:=pfitaxel/learn-ocaml} # Do "export LEARNOCAML_IMAGE=…" before running test.sh to override green "Henceforth: LEARNOCAML_IMAGE=$LEARNOCAML_IMAGE\n" From 5a6474ace8b46ee98dc6c8a2cf522eb33b13b81f Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Tue, 27 Jul 2021 12:53:32 +0200 Subject: [PATCH 41/58] chore: Update .gitignore --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 081bece..5256236 100644 --- a/.gitignore +++ b/.gitignore @@ -85,3 +85,9 @@ setup.log _opam/ # End of https://www.gitignore.io/api/emacs,ocaml + +### Misc ### + +tests/repo/server_config.json +logger +confirm.txt From a66a4a557914b9ffd6efdaccb953476f1b90bdae Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Tue, 27 Jul 2021 15:46:49 +0200 Subject: [PATCH 42/58] tests: Document the existing tests --- tests/README.md | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/tests/README.md b/tests/README.md index 1f789bb..6a2a344 100644 --- a/tests/README.md +++ b/tests/README.md @@ -43,3 +43,92 @@ To test learn-ocaml.el w.r.t. another version of learn-ocaml-client: * Push a new branch in , * Wait for the image in , * Add a new job line (`LEARNOCAML_VERSION="0.xx"`) in this repo's `.travis.yml`. + +# Description of the test-suite + +## Unit tests + +* `a13_learn-ocaml-file-path` (`ert-deftest`) + * Let ex = `/bin/bash` + * Let dir = `file-name-directory(ex)` + * Let file = `file-name-nondirectory(ex)` + * Check ex == `learn-ocaml-file-path(directory-file-name(dir),file)` + * Check ex == `learn-ocaml-file-path(dir,file)` + * Check ex == `learn-ocaml-file-path("/dummy",file)` + +## Integration tests (`ert-deftest-async`) + +**BEWARE** that some tests do `rm -f ~/.config/learnocaml/client.json` + +*Note* that the tests use `http://localhost:8080` as server URL. + +* `1_learn-ocaml-server-management-test` + * Set server (`learn-ocaml-use-metadata-cmd`) + * Read server (`learn-ocaml-give-server-cmd`) +* `2_learn-ocaml-token-management-test` + * Create token from nickname, secret (`learn-ocaml-create-token-cmd`) + * **TODO** Change nickname(test→Foo) + * **TODO** Add secret to `server_config.json` + * Set token (`learn-ocaml-use-metadata-cmd`) + * Read token (`learn-ocaml-give-token-cmd`) +* `3_learn-ocaml-grade-test` + * `rm -f /tmp/learn-ocaml-mode$XXXXXX/demo-results.html` + * Grade `tests/to_grade.ml` for `demo` (`learn-ocaml-grade-file-cmd`) + * `grep "Exercise complete" /tmp/learn-ocaml-mode$XXXXXX/demo-results.html` (`should return 0`) + * `rm -f /tmp/learn-ocaml-mode$XXXXXX/demo-results.html` +* `4_learn-ocaml-download-server-file-test` + * `rm -f /tmp/learn-ocaml-mode$XXXXXX/demo.ml` + * Download `demo.ml` (`learn-ocaml-download-server-file-cmd`) + * `cat /tmp/learn-ocaml-mode$XXXXXX/demo.ml` (`should return 0`) + * **TODO** Improve/Split this test, using `diff` or so +* `5_learn-ocaml-download-template-test` + * `rm -f /tmp/learn-ocaml-mode$XXXXXX/demo.ml` + * Download template `demo.ml` (`learn-ocaml-download-template-cmd`) + * `diff /tmp/learn-ocaml-mode$XXXXXX/demo.ml ./tests/template_demo.ml` (`should return 0`) + * `rm -f /tmp/learn-ocaml-mode$XXXXXX/demo.ml` +* `6_learn-ocaml-give-exercise-list-test` + * Get exercise list (`learn-ocaml-give-exercise-list-cmd`) + * Read `./tests/exercise_list.json` + * They should be equal + * **TODO** Extend this test, using more subdirs? +* `7_learn-ocaml-compute-questions-url-test` + * Read server, token (`learn-ocaml-give-server-cmd`, `learn-ocaml-give-token-cmd`) + * Get description URL of `demo` (`learn-ocaml-compute-questions-url`) + * `curl -fsS $URL` + * Read `./tests/expected_description.html` + * They should be equal +* `8_learn-ocaml-init-another-token` + * Create token from nickname, secret (`learn-ocaml-create-token-cmd`) + * **TODO** Change nickname(test→Foo) + * **TODO** Add secret to `server_config.json` + * Test `learn-ocaml-init` {used by `learn-ocaml-login-with-token`} with new token + * Read new token (`learn-ocaml-give-token-cmd`) + * They should be equal +* `9_learn-ocaml-init-create-token` + * Read token (`learn-ocaml-give-token-cmd`) + * Test `learn-ocaml-init` {used by `learn-ocaml-login-with-token`} with no token + * which will call `learn-ocaml-create-token-cmd` + * Read new token (`learn-ocaml-give-token-cmd`) + * It should be different +* `a10_learn-ocaml-on-load-test-another-token-no-config` + * Read token (`learn-ocaml-give-token-cmd`) + * `rm -f ~/.config/learnocaml/client.json` + * Test `learn-ocaml-init` {used by `learn-ocaml-login-with-token`} with URL, initial token + * Read token (`learn-ocaml-give-token-cmd`) + * They should be equal +* `a11_learn-ocaml-on-load-test-create-token-no-config` + * Read token (`learn-ocaml-give-token-cmd`) + * `rm -f ~/.config/learnocaml/client.json` + * Test `learn-ocaml-init` {used by `learn-ocaml-login-with-token`} with URL, nickname, secret + * **TODO** Change nickname(test→Foo) + * **TODO** Add secret to `server_config.json` + * Read token (`learn-ocaml-give-token-cmd`) + * **FIXME** It should be different +* `a12_learn-ocaml-test-sign-up` + * Test `learn-ocaml-client-sign-up-cmd` {used by `learn-ocaml-login-possibly-with-passwd`} with URL, email, passwd, nickname, empty secret + * **TODO** Change nickname(Test→Foo) + * **TODO** Add secret ≠ "" + * Get stdout+stderr + * Compare with hardcoded string + * **TODO** Improve this text, using `string-match-p` i/o `string-equa` +* **TODO** Test `learn-ocaml-login-possibly-with-passwd` From 6ba13ae8264ac427846062fdb949af855e56b572 Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Tue, 27 Jul 2021 15:47:24 +0200 Subject: [PATCH 43/58] fix: Comment-out a function that seems buggy --- tests/learn-ocaml-tests.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/learn-ocaml-tests.el b/tests/learn-ocaml-tests.el index caac6c2..b8d30db 100644 --- a/tests/learn-ocaml-tests.el +++ b/tests/learn-ocaml-tests.el @@ -239,7 +239,7 @@ (funcall done)))))) (ert-deftest-async a12_learn-ocaml-test-sign-up (done) - (learn-ocaml-client-init-server-cmd learn-ocaml-test-url) + ; FIXME: (learn-ocaml-client-init-server-cmd learn-ocaml-test-url) (let* ((result (learn-ocaml-client-sign-up-cmd learn-ocaml-test-url "ErtTest@example.com" "Ocaml123*" "Test" (escape-secret "")))) From 3b0019dd057269ad2b38b58738b9bbcc7a40f31b Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Tue, 27 Jul 2021 19:23:29 +0200 Subject: [PATCH 44/58] chore: Update .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5256236..02330ac 100644 --- a/.gitignore +++ b/.gitignore @@ -89,5 +89,6 @@ _opam/ ### Misc ### tests/repo/server_config.json -logger confirm.txt +teacher.txt +*.pid From ee78dd85cd2a3cc2e29c8054e56922c944a1fe3a Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Tue, 27 Jul 2021 19:27:26 +0200 Subject: [PATCH 45/58] test: A thorough but sensible refactoring of the test-suite Summary: * Update Makefile to also provide: $ make back # Run a docker backend for interactive ERT tests $ make emacs # Run a dockerized emacs for ERT tests $ make tests # Run dockerized ERT tests $ make stop # Stop the docker backend and/or ERT frontend $ make dist-tests # Alias-of: make back emacs tests $ make dist-tests USE_PASSWD=true * Split-remove test.sh * Add four different scripts: - run_test_backend.sh - run_emacs_image.sh - run_tests.sh - stop_emacs_image.sh - stop_test_backend.sh * Update tests/README.md * Update GHA Note however that the ERT layer still needs some update. Yet, it will be able to benefit from these two files: - confirm.txt - teacher.txt (containing useful but hidden, (docker-logs)-provided information). --- .github/workflows/test.yml | 3 +- Makefile | 27 +++++- run_emacs_image.sh | 87 +++++++++++++++++++ run_test_backend.sh | 172 +++++++++++++++++++++++++++++++++++++ run_tests.sh | 92 ++++++++++++++++++++ stop_emacs_image.sh | 36 ++++++++ stop_test_backend.sh | 37 ++++++++ test.sh | 144 ------------------------------- tests/README.md | 15 ++-- 9 files changed, 458 insertions(+), 155 deletions(-) create mode 100755 run_emacs_image.sh create mode 100755 run_test_backend.sh create mode 100755 run_tests.sh create mode 100755 stop_emacs_image.sh create mode 100755 stop_test_backend.sh delete mode 100755 test.sh diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e3d54d1..4ee2dd8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -38,4 +38,5 @@ jobs: run: | docker pull "$LEARNOCAML_IMAGE:$LEARNOCAML_VERSION" docker pull "$EMACS_IMAGE:$LEARNOCAML_VERSION" - ./test.sh + make dist-tests || { ret=$?; make stop; exit $ret; } + make stop diff --git a/Makefile b/Makefile index 41d3260..d3b33a9 100644 --- a/Makefile +++ b/Makefile @@ -9,8 +9,16 @@ ELC := $(ELFILE:.el=.elc) all: elc help: - @echo '$$ make elc # byte-compile $(ELFILE)' + @echo '$$ make elc # byte-compile $(ELFILE)' @echo '$$ make bump v=1.0.0 # Replace version strings with 1.0.0' + @echo '' + @echo 'The following three commands require sudo.' + @echo '$$ make back # Run a docker backend for interactive ERT tests' + @echo '$$ make emacs # Run a dockerized emacs for ERT tests' + @echo '$$ make tests # Run dockerized ERT tests' + @echo '$$ make stop # Stop the docker backend and/or ERT frontend' + @echo '$$ make dist-tests # Alias-of: make back emacs tests' + @echo '$$ make dist-tests USE_PASSWD=true' bump: git diff -p --raw --exit-code || { echo >&2 "*** Please commit beforehand ***"; exit 1; } @@ -26,4 +34,19 @@ elc: $(ELC) clean: $(RM) $(ELC) -.PHONY: all help bump elc clean +back: + ./run_test_backend.sh + +emacs: + ./run_emacs_image.sh + +tests: + ./run_tests.sh + +dist-tests: back emacs tests + +stop: + -./stop_emacs_image.sh + ./stop_test_backend.sh + +.PHONY: all help bump elc clean back emacs tests dist-tests stop diff --git a/run_emacs_image.sh b/run_emacs_image.sh new file mode 100755 index 0000000..b0d773b --- /dev/null +++ b/run_emacs_image.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash + +# Note: this script need to be in the parent folder, not in tests/ +# because it runs a container with $PWD as bind-mount, and relies on +# both tests/learn-ocaml-tests.el and learn-ocaml.el + +# These files contain some useful/hidden info for ERT tests. +confirm="$PWD/confirm.txt" +teacher="$PWD/teacher.txt" +# TODO Use them. + +# This file contains the Server Container ID (gen by ./run_test_backend.sh) +fcid="$PWD/learn-ocaml-server.pid" +# This file contains the Emacs Container ID (used by ./stop_emacs_image.sh) +feid="$PWD/learn-ocaml-emacs.pid" + +# Print $1 in green +green () { + echo -e "\e[32m$1\e[0m" +} + +# Print $1 in red +red () { + echo -e "\e[31m$1\e[0m" +} + +green "Beforehand: LEARNOCAML_VERSION=$LEARNOCAML_VERSION" +# Default learn-ocaml version +: ${LEARNOCAML_VERSION:=oauth-moodle-dev} +# Do "export LEARNOCAML_VERSION=…" before running the script to override +green "Henceforth: LEARNOCAML_VERSION=$LEARNOCAML_VERSION\n" + +green "Beforehand: EMACS_IMAGE=$EMACS_IMAGE" +# Default emacs image +: ${EMACS_IMAGE:=pfitaxel/emacs-learn-ocaml-client} +# Do "export EMACS_IMAGE=…" before running the script to override +green "Henceforth: EMACS_IMAGE=$EMACS_IMAGE\n" + +gen_emacs_cid () { + if [ -f "$feid" ]; then + red >&2 "Error: file '$feid' already exists: container is running?" + exit 1 + fi + echo "learn-ocaml-emacs-$$" >"$feid" + eid=$(<"$feid") +} + +read_cid () { + if [ -f "$fcid" ]; then + cid=$(<"$fcid") + else + red >&2 "Error: file '$fcid' does not exist." + exit 1 + fi +} + +stop_emacs () { + green "Stopping emacs..." + ( set -x && \ + rm -f "$feid" && \ + sudo docker stop "$EMACS_NAME" ) +} + +run_emacs () { + local oldopt="$(set +o)"; set -x + + # Run the image in background + sudo docker run -d -i --init --rm --name="$eid" \ + -v "$PWD:/build" --network="container:$cid" \ + "$EMACS_IMAGE:$LEARNOCAML_VERSION" + ret=$? + + # hacky but working + sleep 2s + + set +vx; eval "$oldopt" # has to be after "ret=$?" + + if [ "$ret" -ne 0 ]; then + red "PROBLEM, 'sudo docker run -d ... $EMACS_IMAGE:$LEARNOCAML_VERSION' failed with exit status $ret" + stop_emacs + exit $ret + fi +} + +read_cid +gen_emacs_cid +run_emacs diff --git a/run_test_backend.sh b/run_test_backend.sh new file mode 100755 index 0000000..296f623 --- /dev/null +++ b/run_test_backend.sh @@ -0,0 +1,172 @@ +#!/usr/bin/env bash + +# Note: this script need to be in the parent folder, not in tests/ +# because it runs a container with $PWD as bind-mount, and relies on +# both tests/learn-ocaml-tests.el and learn-ocaml.el + +# It reads the environment variable $USE_PASSWD (see below). + +# These files contain some useful/hidden info for ERT tests. +confirm="$PWD/confirm.txt" +teacher="$PWD/teacher.txt" + +# This file contains the Server Container ID (gen by ./run_test_backend.sh) +fcid="$PWD/learn-ocaml-server.pid" + +# Print $1 in green +green () { + echo -e "\e[32m$1\e[0m" +} + +# Print $1 in red +red () { + echo -e "\e[31m$1\e[0m" +} + +# Filter docker-logs +filter_confirm () { + stdbuf -o0 grep -e "$LEARNOCAML_BASE_URL/confirm/" || : +} + +green "Beforehand: LEARNOCAML_VERSION=$LEARNOCAML_VERSION" +# Default learn-ocaml version +: ${LEARNOCAML_VERSION:=oauth-moodle-dev} +# Do "export LEARNOCAML_VERSION=…" before running the script to override +green "Henceforth: LEARNOCAML_VERSION=$LEARNOCAML_VERSION\n" + +green "Beforehand: LEARNOCAML_IMAGE=$LEARNOCAML_IMAGE" +# Default learn-ocaml image +: ${LEARNOCAML_IMAGE:=pfitaxel/learn-ocaml} +# Do "export LEARNOCAML_IMAGE=…" before running the script to override +green "Henceforth: LEARNOCAML_IMAGE=$LEARNOCAML_IMAGE\n" + +green "Beforehand: LEARNOCAML_BASE_URL=$LEARNOCAML_BASE_URL" +# Default emacs image +: ${LEARNOCAML_BASE_URL:=http://localhost:8080} +# Do "export LEARNOCAML_BASE_URL=…" before running the script to override +green "Henceforth: LEARNOCAML_BASE_URL=$LEARNOCAML_BASE_URL\n" + +green "Beforehand: USE_PASSWD=$USE_PASSWD" +# Default mode +: ${USE_PASSWD:=false} +# Do "export USE_PASSWD=…" before running the script to override +green "Henceforth: USE_PASSWD=$USE_PASSWD\n" + +sudo docker pull pfitaxel/learn-ocaml:"$LEARNOCAML_VERSION" + +############################################################################### +### BACKUP OF OLD CODE ### +## Assuming /dev/fd/3 is free +# +#function trap_exit() { +# local fi="$1" +# exec 3>&- # close descriptor +# set -x; rm "$fi" || true +#} +# +#filter_confirm () { grep -e "$LEARNOCAML_BASE_URL/confirm/" || :; } +# +#fi="$PWD/logger" +#out="$PWD/confirm.txt" +# +#if [[ ! -p "$fi" ]]; then +# mkfifo "$fi" +#else +# echo >&2 "Info: fifo '$fi' already exists." +#fi +# +#if [[ -r "$out" ]]; then +# echo >&2 "Info: file '$out' already exists, saving..." +# cp --backup=t -fv "$out" "$out" +#fi +#>"$out" # erase file "$out" +# +#cat "$fi" | filter_confirm >>"$out" & +# +#exec 3>"$fi" # keep the input of $fi open +# +#trap 'trap_exit "$fi"' EXIT +# +## Run `cat "$fi"` or `tail --follow "$fi"` to read from the named pipe +## Run `echo ok >"$fi" &` or `echo ok >&3 &` to write in the named pipe +# +############################################################################### + +wait_for_it () { + local url="$1" + local seconds="$2" + shift 2 # "$@" => optional command to be run in case of success + local elapsed=0 + green "waiting $seconds seconds for $url\\n" + while :; do + curl -fsS "$url" >/dev/null 2>/dev/null && break + [ "$elapsed" -ge "$seconds" ] && + { red "timeout occurred after waiting $seconds seconds for $url\\n"; + return 124; } + elapsed=$((elapsed + 1)) + sleep 1s + done + green "$url available after $elapsed seconds" + if [ $# -gt 0 ]; then + ( set -x; "$@" ) + fi + return 0 +} + +gen_cid () { + if [ -f "$fcid" ]; then + red >&2 "Error: file '$fcid' already exists: container is running?" + exit 1 + fi + echo "learn-ocaml-server-$$" >"$fcid" + cid=$(<"$fcid") +} + +stop_server () { + green "Stopping server..." + ( set -x && \ + rm -f "$fcid" && \ + # sudo docker logs "$cid" + sudo docker stop "$cid" ) +} + +run_server () { + local oldopt="$(set +o)"; set -x + + if [ "$USE_PASSWD" = "true" ]; then + cp -f "$PWD/tests/use_passwd.json" "$PWD/tests/repo/server_config.json" + else + # TODO: Add a secret + rm -f "$PWD/tests/repo/server_config.json" + fi + + # Don't use the "-d" option + sudo docker run --name="$cid" --rm -p 8080:8080 \ + -e LEARNOCAML_BASE_URL="$LEARNOCAML_BASE_URL" \ + -v "$PWD/tests/repo:/repository" \ + pfitaxel/learn-ocaml:$LEARNOCAML_VERSION build serve 2>&1 | \ + filter_confirm >"$confirm" & + + set +vx; eval "$oldopt" + + # Increase this timeout if ever one sub-repo build would last > 10s + build_timeout=10 + + if ! wait_for_it "http://localhost:8080/version" "$build_timeout" sleep 1s; then + red >&2 "PROBLEM, 'sudo docker run ... $LEARNOCAML_IMAGE:$LEARNOCAML_VERSION' does not run." + stop_server + exit 1 + fi +} + +read_teacher () { + echo 'Teacher token:' + sudo docker logs "$cid" | \ + grep -e 'Initial teacher token created:' | \ + sed -e 's/^.*: //' | tr -d '[:space:]' | tee "$teacher" + echo +} + +gen_cid +run_server +read_teacher diff --git a/run_tests.sh b/run_tests.sh new file mode 100755 index 0000000..90e907d --- /dev/null +++ b/run_tests.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash + +# Note: this script need to be in the parent folder, not in tests/ +# because it runs a container with $PWD as bind-mount, and relies on +# both tests/learn-ocaml-tests.el and learn-ocaml.el + +# It reads the environment variable $USE_PASSWD (see below). + +# These files contain some useful/hidden info for ERT tests. +confirm="$PWD/confirm.txt" +teacher="$PWD/teacher.txt" + +# This file contains the Server Container ID (gen by ./run_test_backend.sh) +fcid="$PWD/learn-ocaml-server.pid" +# This file contains the Emacs Container ID (gen by ./stop_emacs_image.sh) +feid="$PWD/learn-ocaml-emacs.pid" + +# Print $1 in green +green () { + echo -e "\e[32m$1\e[0m" +} + +# Print $1 in red +red () { + echo -e "\e[31m$1\e[0m" +} + +green "Beforehand: USE_PASSWD=$USE_PASSWD" +# Default mode +: ${USE_PASSWD:=false} +# Do "export USE_PASSWD=…" before running the script to override +green "Henceforth: USE_PASSWD=$USE_PASSWD\n" + +read_cid () { + if [ -f "$fcid" ]; then + cid=$(<"$fcid") + else + red >&2 "Error: file '$fcid' does not exist." + exit 1 + fi +} + +read_eid () { + if [ -f "$feid" ]; then + eid=$(<"$feid") + else + red >&2 "Error: file '$feid' does not exist." + exit 1 + fi +} + +assert () { + if [ $# -ne 1 ]; then + red "ERROR, assert expects a single arg (the code to run)" + exit 1 + fi + sudo docker exec -i "$eid" /bin/sh -c " +set -ex +$1 +" + ret=$? + if [ "$ret" -ne 0 ]; then + red "FAILURE, this shell command returned exit status $ret: +\$ sudo docker exec -i $eid /bin/sh -c '$1'\n" + exit $ret + fi +} + +read_cid +read_eid + +assert "emacs --version" +assert "emacs --batch --eval '(pp (+ 2 2))'" +echo + +assert "learn-ocaml-client --version" + +if [ "$USE_PASSWD" = "true" ]; then + # TODO: Refactor this to run the init command from ERT's fixture + init='learn-ocaml-client init-user -s http://localhost:8080 foo@example.com OCaml123_ Foo ""' + selector="" +else + init='learn-ocaml-client init --server=http://localhost:8080 Foo ""' + selector="learn-ocaml-test-skip-use-passwd" +fi + +assert " +cd /build/tests +$init +emacs --batch -l ert -l init-tests.el -l /build/learn-ocaml.el \ + -l learn-ocaml-tests.el --eval '(ert-run-tests-batch-and-exit $selector)' +" diff --git a/stop_emacs_image.sh b/stop_emacs_image.sh new file mode 100755 index 0000000..ae7a074 --- /dev/null +++ b/stop_emacs_image.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# Note: this script need to be in the parent folder, not in tests/ +# because it runs a container with $PWD as bind-mount, and relies on +# both tests/learn-ocaml-tests.el and learn-ocaml.el + +# Print $1 in green +green () { + echo -e "\e[32m$1\e[0m" +} + +# Print $1 in red +red () { + echo -e "\e[31m$1\e[0m" +} + +read_eid () { + # File containing the Emacs Container ID + feid="$PWD/learn-ocaml-emacs.pid" + if [ -f "$feid" ]; then + eid=$(<"$feid") + else + red >&2 "Error: file '$feid' does not exist."; exit 1 + fi +} + + +stop_emacs () { + green "Stopping emacs..." + ( set -x && \ + rm -f "$feid" && \ + sudo docker stop "$eid" ) +} + +read_eid +stop_emacs diff --git a/stop_test_backend.sh b/stop_test_backend.sh new file mode 100755 index 0000000..d8818ec --- /dev/null +++ b/stop_test_backend.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +# Note: this script need to be in the parent folder, not in tests/ +# because it runs a container with $PWD as bind-mount, and relies on +# both tests/learn-ocaml-tests.el and learn-ocaml.el + +# Print $1 in green +green () { + echo -e "\e[32m$1\e[0m" +} + +# Print $1 in red +red () { + echo -e "\e[31m$1\e[0m" +} + +read_cid () { + # File containing the Server Container ID + fcid="$PWD/learn-ocaml-server.pid" + if [ -f "$fcid" ]; then + cid=$(<"$fcid") + else + red >&2 "Error: file '$fcid' does not exist." + exit 1 + fi +} + + +stop_server () { + green "Stopping server..." + ( set -x && \ + rm -f "$fcid" && \ + sudo docker stop "$cid" ) +} + +read_cid +stop_server diff --git a/test.sh b/test.sh deleted file mode 100755 index 8ac1763..0000000 --- a/test.sh +++ /dev/null @@ -1,144 +0,0 @@ -#!/bin/bash - -# Note: this script need to be in the parent folder, not in tests/ -# because it runs a container with $PWD as bind-mount, and relies on -# both tests/learn-ocaml-tests.el and learn-ocaml.el - -# Print $1 in green -green () { - echo -e "\e[32m$1\e[0m" -} - -# Print $1 in red -red () { - echo -e "\e[31m$1\e[0m" -} - -green "Beforehand: LEARNOCAML_VERSION=$LEARNOCAML_VERSION" -# Default learn-ocaml version -: ${LEARNOCAML_VERSION:=oauth-moodle-dev} -# Do "export LEARNOCAML_VERSION=…" before running test.sh to override -green "Henceforth: LEARNOCAML_VERSION=$LEARNOCAML_VERSION\n" - -green "Beforehand: LEARNOCAML_IMAGE=$LEARNOCAML_IMAGE" -# Default learn-ocaml image -: ${LEARNOCAML_IMAGE:=pfitaxel/learn-ocaml} -# Do "export LEARNOCAML_IMAGE=…" before running test.sh to override -green "Henceforth: LEARNOCAML_IMAGE=$LEARNOCAML_IMAGE\n" - -green "Beforehand: EMACS_IMAGE=$EMACS_IMAGE" -# Default emacs image -: ${EMACS_IMAGE:=pfitaxel/emacs-learn-ocaml-client} -# Do "export EMACS_IMAGE=…" before running test.sh to override -green "Henceforth: EMACS_IMAGE=$EMACS_IMAGE\n" - -SERVER_NAME="learn-ocaml-server" -EMACS_NAME="emacs-client" - -# run a server in a docker container -run_server () { - local oldopt="$(set +o)"; set -x - # Run the server in background - if [ "$USE_PASSWD" = "true" ]; then - cp -f "$PWD/tests/use_passwd.json" "$PWD/tests/repo/server_config.json" - else - rm -f "$PWD/tests/repo/server_config.json" - fi - # Add -e "LEARNOCAML_BASE_URL=$LEARNOCAML_BASE_URL"? - sudo docker run -d --rm --name="$SERVER_NAME" \ - -v "$PWD/tests/repo:/repository" \ - "$LEARNOCAML_IMAGE:$LEARNOCAML_VERSION" - ret=$? - set +vx; eval "$oldopt" # has to be after "ret=$?" - if [ "$ret" -ne 0 ]; then - red "PROBLEM, 'sudo docker run -d ... $LEARNOCAML_IMAGE:$LEARNOCAML_VERSION' failed with exit status $ret" - exit $ret - fi - - # Wait for the server to be initialized - sleep 2 - - if [ -z "$(sudo docker ps -q)" ]; then - red "PROBLEM, server is not running" - exit 1 - fi -} - -stop_server () { - green "Stopping server..." - ( set -x && \ - # sudo docker logs "$SERVER_NAME" - sudo docker stop "$SERVER_NAME" ) -} - -# run an emacs in a docker container -run_emacs () { - local oldopt="$(set +o)"; set -x - # Run the server in background - sudo docker run -d -i --init --rm --name="$EMACS_NAME" \ - -v "$PWD:/build" --network="container:$SERVER_NAME" \ - "$EMACS_IMAGE:$LEARNOCAML_VERSION" - ret=$? - set +vx; eval "$oldopt" # has to be after "ret=$?" - if [ "$ret" -ne 0 ]; then - red "PROBLEM, 'sudo docker run -d ... $EMACS_IMAGE:$LEARNOCAML_VERSION' failed with exit status $ret" - exit $ret - fi -} - -stop_emacs () { - green "Stopping emacs..." - ( set -x && \ - sudo docker stop "$EMACS_NAME" ) -} - -assert () { - if [ $# -ne 1 ]; then - red "ERROR, assert expects a single arg (the code to run)" - exit 1 - fi - sudo docker exec -i "$EMACS_NAME" /bin/sh -c " -set -ex -$1 -" - ret=$? - if [ "$ret" -ne 0 ]; then - red "FAILURE, this shell command returned exit status $ret: -\$ sudo docker exec -i $EMACS_NAME /bin/sh -c '$1'\n" - stop_emacs - stop_server - exit $ret - fi -} - -############################################################################### - -run_server -run_emacs - -assert "emacs --version" - -assert "emacs --batch --eval '(pp (+ 2 2))'" - -echo - -assert "learn-ocaml-client --version" - -if [ "$USE_PASSWD" = "true" ]; then - # TODO: Refactor this to run the init command from ERT's fixture - init='learn-ocaml-client init-user -s http://localhost:8080 foo@example.com OCaml123_ Foo ""' - selector="" -else - init='learn-ocaml-client init --server=http://localhost:8080 Foo ""' - selector="learn-ocaml-test-skip-use-passwd" -fi - -assert " -cd /build/tests -$init -emacs --batch -l ert -l init-tests.el -l /build/learn-ocaml.el \ - -l learn-ocaml-tests.el --eval '(ert-run-tests-batch-and-exit $selector)' -" - -stop_emacs -stop_server diff --git a/tests/README.md b/tests/README.md index 6a2a344..9f74988 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,13 +1,15 @@ # How to run tests -## Using the Travis CI wrapper +## Using only Docker containers +* (Useful to preserve `~/.config/learnocaml/client.json` on the host.) * Install Docker -* Run `cd .. && ./test.sh` +* Run `cd ..; make dist-tests; make stop` ## Using ert within emacs * Install Docker +* Run `cd ..; make back` * Install `learn-ocaml` using OPAM (`make && make opaminstall`) * Add `learn-ocaml-client` in the `PATH`, e.g. run in a terminal: @@ -20,12 +22,7 @@ * Run then: ```bash -cd .../learn-ocaml.el -export LOVERSION=dev -docker pull ocamlsf/learn-ocaml:$LOVERSION -docker run --name=server -d --rm -p 8080:8080 \ - -v "$PWD/tests/repo:/repository" ocamlsf/learn-ocaml:$LOVERSION build serve -learn-ocaml-client init -s http://localhost:8080 test test +# learn-ocaml-client init -s http://localhost:8080 test test emacs tests/learn-ocaml-tests.el & ``` @@ -38,6 +35,8 @@ emacs tests/learn-ocaml-tests.el & ## Note to learn-ocaml.el's CI maintainers +* (**WARNING**: Needs documentation update.) + To test learn-ocaml.el w.r.t. another version of learn-ocaml-client: * Push a new branch in , From ec8fa8ed8f1318083317406ddb6f195963a5030f Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Tue, 27 Jul 2021 23:56:35 +0200 Subject: [PATCH 46/58] fix: minor (refactoring-copy-paste) issues --- run_emacs_image.sh | 5 ++++- run_test_backend.sh | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/run_emacs_image.sh b/run_emacs_image.sh index b0d773b..6541725 100755 --- a/run_emacs_image.sh +++ b/run_emacs_image.sh @@ -36,6 +36,8 @@ green "Beforehand: EMACS_IMAGE=$EMACS_IMAGE" # Do "export EMACS_IMAGE=…" before running the script to override green "Henceforth: EMACS_IMAGE=$EMACS_IMAGE\n" +sudo docker pull "$EMACS_IMAGE:$LEARNOCAML_VERSION" + gen_emacs_cid () { if [ -f "$feid" ]; then red >&2 "Error: file '$feid' already exists: container is running?" @@ -58,7 +60,8 @@ stop_emacs () { green "Stopping emacs..." ( set -x && \ rm -f "$feid" && \ - sudo docker stop "$EMACS_NAME" ) + sudo docker logs "$eid"; \ + sudo docker stop "$eid" ) } run_emacs () { diff --git a/run_test_backend.sh b/run_test_backend.sh index 296f623..1cc3c8e 100755 --- a/run_test_backend.sh +++ b/run_test_backend.sh @@ -52,7 +52,7 @@ green "Beforehand: USE_PASSWD=$USE_PASSWD" # Do "export USE_PASSWD=…" before running the script to override green "Henceforth: USE_PASSWD=$USE_PASSWD\n" -sudo docker pull pfitaxel/learn-ocaml:"$LEARNOCAML_VERSION" +sudo docker pull "$LEARNOCAML_IMAGE:$LEARNOCAML_VERSION" ############################################################################### ### BACKUP OF OLD CODE ### @@ -126,7 +126,7 @@ stop_server () { green "Stopping server..." ( set -x && \ rm -f "$fcid" && \ - # sudo docker logs "$cid" + sudo docker logs "$cid"; \ sudo docker stop "$cid" ) } @@ -144,7 +144,7 @@ run_server () { sudo docker run --name="$cid" --rm -p 8080:8080 \ -e LEARNOCAML_BASE_URL="$LEARNOCAML_BASE_URL" \ -v "$PWD/tests/repo:/repository" \ - pfitaxel/learn-ocaml:$LEARNOCAML_VERSION build serve 2>&1 | \ + "$LEARNOCAML_IMAGE:$LEARNOCAML_VERSION" build serve 2>&1 | \ filter_confirm >"$confirm" & set +vx; eval "$oldopt" From 266a7a8341a5684f5633a0c2640a43767554ee2f Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Wed, 28 Jul 2021 00:01:46 +0200 Subject: [PATCH 47/58] fix: Make shellcheck happy --- run_emacs_image.sh | 19 +++++++------------ run_test_backend.sh | 22 +++++++++++----------- run_tests.sh | 11 ++++++----- stop_emacs_image.sh | 4 ++-- stop_test_backend.sh | 4 ++-- 5 files changed, 28 insertions(+), 32 deletions(-) diff --git a/run_emacs_image.sh b/run_emacs_image.sh index 6541725..54766b9 100755 --- a/run_emacs_image.sh +++ b/run_emacs_image.sh @@ -4,11 +4,6 @@ # because it runs a container with $PWD as bind-mount, and relies on # both tests/learn-ocaml-tests.el and learn-ocaml.el -# These files contain some useful/hidden info for ERT tests. -confirm="$PWD/confirm.txt" -teacher="$PWD/teacher.txt" -# TODO Use them. - # This file contains the Server Container ID (gen by ./run_test_backend.sh) fcid="$PWD/learn-ocaml-server.pid" # This file contains the Emacs Container ID (used by ./stop_emacs_image.sh) @@ -16,25 +11,25 @@ feid="$PWD/learn-ocaml-emacs.pid" # Print $1 in green green () { - echo -e "\e[32m$1\e[0m" + echo -e "\\e[32m$1\\e[0m" } # Print $1 in red red () { - echo -e "\e[31m$1\e[0m" + echo -e "\\e[31m$1\\e[0m" } green "Beforehand: LEARNOCAML_VERSION=$LEARNOCAML_VERSION" # Default learn-ocaml version -: ${LEARNOCAML_VERSION:=oauth-moodle-dev} +: "${LEARNOCAML_VERSION:=oauth-moodle-dev}" # Do "export LEARNOCAML_VERSION=…" before running the script to override -green "Henceforth: LEARNOCAML_VERSION=$LEARNOCAML_VERSION\n" +green "Henceforth: LEARNOCAML_VERSION=$LEARNOCAML_VERSION\\n" green "Beforehand: EMACS_IMAGE=$EMACS_IMAGE" # Default emacs image -: ${EMACS_IMAGE:=pfitaxel/emacs-learn-ocaml-client} +: "${EMACS_IMAGE:=pfitaxel/emacs-learn-ocaml-client}" # Do "export EMACS_IMAGE=…" before running the script to override -green "Henceforth: EMACS_IMAGE=$EMACS_IMAGE\n" +green "Henceforth: EMACS_IMAGE=$EMACS_IMAGE\\n" sudo docker pull "$EMACS_IMAGE:$LEARNOCAML_VERSION" @@ -65,7 +60,7 @@ stop_emacs () { } run_emacs () { - local oldopt="$(set +o)"; set -x + local oldopt; oldopt="$(set +o)"; set -x # Run the image in background sudo docker run -d -i --init --rm --name="$eid" \ diff --git a/run_test_backend.sh b/run_test_backend.sh index 1cc3c8e..b057ec2 100755 --- a/run_test_backend.sh +++ b/run_test_backend.sh @@ -15,12 +15,12 @@ fcid="$PWD/learn-ocaml-server.pid" # Print $1 in green green () { - echo -e "\e[32m$1\e[0m" + echo -e "\\e[32m$1\\e[0m" } # Print $1 in red red () { - echo -e "\e[31m$1\e[0m" + echo -e "\\e[31m$1\\e[0m" } # Filter docker-logs @@ -30,27 +30,27 @@ filter_confirm () { green "Beforehand: LEARNOCAML_VERSION=$LEARNOCAML_VERSION" # Default learn-ocaml version -: ${LEARNOCAML_VERSION:=oauth-moodle-dev} +: "${LEARNOCAML_VERSION:=oauth-moodle-dev}" # Do "export LEARNOCAML_VERSION=…" before running the script to override -green "Henceforth: LEARNOCAML_VERSION=$LEARNOCAML_VERSION\n" +green "Henceforth: LEARNOCAML_VERSION=$LEARNOCAML_VERSION\\n" green "Beforehand: LEARNOCAML_IMAGE=$LEARNOCAML_IMAGE" # Default learn-ocaml image -: ${LEARNOCAML_IMAGE:=pfitaxel/learn-ocaml} +: "${LEARNOCAML_IMAGE:=pfitaxel/learn-ocaml}" # Do "export LEARNOCAML_IMAGE=…" before running the script to override -green "Henceforth: LEARNOCAML_IMAGE=$LEARNOCAML_IMAGE\n" +green "Henceforth: LEARNOCAML_IMAGE=$LEARNOCAML_IMAGE\\n" green "Beforehand: LEARNOCAML_BASE_URL=$LEARNOCAML_BASE_URL" # Default emacs image -: ${LEARNOCAML_BASE_URL:=http://localhost:8080} +: "${LEARNOCAML_BASE_URL:=http://localhost:8080}" # Do "export LEARNOCAML_BASE_URL=…" before running the script to override -green "Henceforth: LEARNOCAML_BASE_URL=$LEARNOCAML_BASE_URL\n" +green "Henceforth: LEARNOCAML_BASE_URL=$LEARNOCAML_BASE_URL\\n" green "Beforehand: USE_PASSWD=$USE_PASSWD" # Default mode -: ${USE_PASSWD:=false} +: "${USE_PASSWD:=false}" # Do "export USE_PASSWD=…" before running the script to override -green "Henceforth: USE_PASSWD=$USE_PASSWD\n" +green "Henceforth: USE_PASSWD=$USE_PASSWD\\n" sudo docker pull "$LEARNOCAML_IMAGE:$LEARNOCAML_VERSION" @@ -131,7 +131,7 @@ stop_server () { } run_server () { - local oldopt="$(set +o)"; set -x + local oldopt; oldopt="$(set +o)"; set -x if [ "$USE_PASSWD" = "true" ]; then cp -f "$PWD/tests/use_passwd.json" "$PWD/tests/repo/server_config.json" diff --git a/run_tests.sh b/run_tests.sh index 90e907d..e710f5a 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -9,6 +9,7 @@ # These files contain some useful/hidden info for ERT tests. confirm="$PWD/confirm.txt" teacher="$PWD/teacher.txt" +# TODO Use them. # This file contains the Server Container ID (gen by ./run_test_backend.sh) fcid="$PWD/learn-ocaml-server.pid" @@ -17,19 +18,19 @@ feid="$PWD/learn-ocaml-emacs.pid" # Print $1 in green green () { - echo -e "\e[32m$1\e[0m" + echo -e "\\e[32m$1\\e[0m" } # Print $1 in red red () { - echo -e "\e[31m$1\e[0m" + echo -e "\\e[31m$1\\e[0m" } green "Beforehand: USE_PASSWD=$USE_PASSWD" # Default mode -: ${USE_PASSWD:=false} +: "${USE_PASSWD:=false}" # Do "export USE_PASSWD=…" before running the script to override -green "Henceforth: USE_PASSWD=$USE_PASSWD\n" +green "Henceforth: USE_PASSWD=$USE_PASSWD\\n" read_cid () { if [ -f "$fcid" ]; then @@ -61,7 +62,7 @@ $1 ret=$? if [ "$ret" -ne 0 ]; then red "FAILURE, this shell command returned exit status $ret: -\$ sudo docker exec -i $eid /bin/sh -c '$1'\n" +\$ sudo docker exec -i $eid /bin/sh -c '$1'\\n" exit $ret fi } diff --git a/stop_emacs_image.sh b/stop_emacs_image.sh index ae7a074..f99a36e 100755 --- a/stop_emacs_image.sh +++ b/stop_emacs_image.sh @@ -6,12 +6,12 @@ # Print $1 in green green () { - echo -e "\e[32m$1\e[0m" + echo -e "\\e[32m$1\\e[0m" } # Print $1 in red red () { - echo -e "\e[31m$1\e[0m" + echo -e "\\e[31m$1\\e[0m" } read_eid () { diff --git a/stop_test_backend.sh b/stop_test_backend.sh index d8818ec..67fd2e4 100755 --- a/stop_test_backend.sh +++ b/stop_test_backend.sh @@ -6,12 +6,12 @@ # Print $1 in green green () { - echo -e "\e[32m$1\e[0m" + echo -e "\\e[32m$1\\e[0m" } # Print $1 in red red () { - echo -e "\e[31m$1\e[0m" + echo -e "\\e[31m$1\\e[0m" } read_cid () { From 29e911195c328883dd9af4a08cea3207c7b75c0d Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Wed, 28 Jul 2021 00:18:22 +0200 Subject: [PATCH 48/58] fix: test exit code of `docker pull` * This is needed as the `*.sh` scripts don't have `set -e` currently. --- run_emacs_image.sh | 11 ++++++++++- run_test_backend.sh | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/run_emacs_image.sh b/run_emacs_image.sh index 54766b9..7ebec97 100755 --- a/run_emacs_image.sh +++ b/run_emacs_image.sh @@ -31,7 +31,15 @@ green "Beforehand: EMACS_IMAGE=$EMACS_IMAGE" # Do "export EMACS_IMAGE=…" before running the script to override green "Henceforth: EMACS_IMAGE=$EMACS_IMAGE\\n" -sudo docker pull "$EMACS_IMAGE:$LEARNOCAML_VERSION" +pull_ifneedbe () { + sudo docker pull "$EMACS_IMAGE:$LEARNOCAML_VERSION" + ret=$? + + if [ "$ret" -ne 0 ]; then + red "PROBLEM, 'sudo docker pull $EMACS_IMAGE:$LEARNOCAML_VERSION' failed with exit status $ret" + exit $ret + fi +} gen_emacs_cid () { if [ -f "$feid" ]; then @@ -80,6 +88,7 @@ run_emacs () { fi } +pull_ifneedbe read_cid gen_emacs_cid run_emacs diff --git a/run_test_backend.sh b/run_test_backend.sh index b057ec2..3fb1348 100755 --- a/run_test_backend.sh +++ b/run_test_backend.sh @@ -52,7 +52,15 @@ green "Beforehand: USE_PASSWD=$USE_PASSWD" # Do "export USE_PASSWD=…" before running the script to override green "Henceforth: USE_PASSWD=$USE_PASSWD\\n" -sudo docker pull "$LEARNOCAML_IMAGE:$LEARNOCAML_VERSION" +pull_ifneedbe () { + sudo docker pull "$LEARNOCAML_IMAGE:$LEARNOCAML_VERSION" + ret=$? + + if [ "$ret" -ne 0 ]; then + red "PROBLEM, 'sudo docker pull $LEARNOCAML_IMAGE:$LEARNOCAML_VERSION' failed with exit status $ret" + exit $ret + fi +} ############################################################################### ### BACKUP OF OLD CODE ### @@ -167,6 +175,7 @@ read_teacher () { echo } +pull_ifneedbe gen_cid run_server read_teacher From 8e1221027bd1623c10b4de26a8ad475b98a4773c Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Wed, 28 Jul 2021 00:21:15 +0200 Subject: [PATCH 49/58] docs: Improve `make help` --- Makefile | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index d3b33a9..b9a01e5 100644 --- a/Makefile +++ b/Makefile @@ -12,13 +12,18 @@ help: @echo '$$ make elc # byte-compile $(ELFILE)' @echo '$$ make bump v=1.0.0 # Replace version strings with 1.0.0' @echo '' - @echo 'The following three commands require sudo.' + @echo 'All the following commands require sudo.' + @echo '' @echo '$$ make back # Run a docker backend for interactive ERT tests' + @echo '$$ make back LEARNOCAML_IMAGE=ocamlsf/learn-ocaml LEARNOCAML_VERSION=0.12' + @echo '' @echo '$$ make emacs # Run a dockerized emacs for ERT tests' @echo '$$ make tests # Run dockerized ERT tests' - @echo '$$ make stop # Stop the docker backend and/or ERT frontend' + @echo '' @echo '$$ make dist-tests # Alias-of: make back emacs tests' @echo '$$ make dist-tests USE_PASSWD=true' + @echo '' + @echo '$$ make stop # Stop the docker backend and/or ERT frontend' bump: git diff -p --raw --exit-code || { echo >&2 "*** Please commit beforehand ***"; exit 1; } From 5beceec74eabd5828ab8be26bbbb24eb8a4cf4ae Mon Sep 17 00:00:00 2001 From: ImJustLouis Date: Wed, 28 Jul 2021 02:19:18 +0200 Subject: [PATCH 50/58] wip: update merlin flags and made a function to convert file name into a module name --- learn-ocaml.el | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index 81b837e..6f0387e 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -99,6 +99,23 @@ Call `get-buffer-create' if need be, to ensure it is a live buffer." "Remove the trailing newline in STR." (replace-regexp-in-string "\n\\'" "" str)) +(defun learn-ocaml-replace-in-string (what with in) + "Replace WHAT by WITH in string IN." + (replace-regexp-in-string (regexp-quote what) with in nil 'literal)) + +(defun learn-ocaml-exercise-to-module-name (id) + (let* ((module-name (learn-ocaml-replace-in-string "_" "__" id)) + (module-name-bis (learn-ocaml-replace-in-string "-" "_0" module-name))) + module-name-bis)) + +(defun learn-ocaml-open-ml-file (id) + "Open ID in a new buffer and load prelude and prepare according to his name." + (find-file (concat id ".ml")) + (with-current-buffer (concat id ".ml") + (setq merlin-buffer-flags + (concat "-open Given_" + (learn-ocaml-exercise-to-module-name id))))) + (defun learn-ocaml-yes-or-no (message &optional dont-trap-quit) "Display MESSAGE in a yes-or-no popup. `\\[keyboard-quit]' is seen as nil, unless DONT-TRAP-QUIT is non-nil." @@ -233,7 +250,11 @@ Call `learn-ocaml-display-exercise-list' if OPEN-EXO-LIST is non-nil." (copy-file (concat learn-ocaml-working-directory "/.ocamlinit") (concat learn-ocaml-working-directory "/.ocamlinit~") t)) - (write-region "#load .learn-ocaml/Given_demo.cmo;;\nopen Given_demo;;" nil + (write-region (concat "#load .learn-ocaml/Given_" + (learn-ocaml-exercise-to-module-name "demo") + ".cmo;;\nopen Given_" + (learn-ocaml-exercise-to-module-name "demo") + ";;") nil (learn-ocaml-file-path learn-ocaml-working-directory ".ocamlinit")) (if open-exo-list (learn-ocaml-display-exercise-list))) @@ -669,7 +690,7 @@ Argument SECRET may be needed by the server." (widget-insert " ") (widget-create 'learn-ocaml-button :notify (lambda (&rest _ignore) - (find-file (concat id ".ml"))) + (learn-ocaml-open-ml-file id)) "Open .ml") (widget-insert " ") (widget-create 'learn-ocaml-button @@ -949,7 +970,8 @@ Shortcuts for the learn-ocaml mode: (progn (learn-ocaml-update-exercise-id-view) (easy-menu-add learn-ocaml-mode-menu) - (learn-ocaml-setup t)) + (learn-ocaml-setup t) + (message (pp-to-string learn-ocaml-exercise-id))) (setq learn-ocaml-loaded nil) (remove-hook 'caml-mode-hook #'learn-ocaml-mode) (remove-hook 'tuareg-mode-hook #'learn-ocaml-mode))) From f44f1c84962593d7ef292bf953574306e5ec0744 Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Wed, 28 Jul 2021 15:44:04 +0200 Subject: [PATCH 51/58] fix: make elc --- learn-ocaml.el | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index 52a9028..e1e2d07 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -408,7 +408,7 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (let ((log (buffer-string))) (error "Process errored. Full log:\n%s" log))))))) -(defun learn-ocaml-error-handler-nosplit-catch (buffer callback-ok callback-err proc string) +(defun learn-ocaml-error-handler-nosplit-catch (buffer callback-ok callback-err _proc string) "Get text from BUFFER, pass it to CALLBACK-OK ($?=0) or CALLBACK-ERR. To be used as a `make-process' sentinel, using args PROC and STRING." (let ((result (if (not buffer) @@ -998,23 +998,6 @@ Note: this function will be used by `learn-ocaml-login-with-token'." nil callback)))))) -(cl-defun learn-ocaml-init-cmd (&key token server nickname secret callback) - "Run \"learn-ocaml-client init\" with options." - (learn-ocaml-print-time-stamp) - (learn-ocaml-make-process-wrapper - :name "init" - :command (learn-ocaml-command-constructor - :token token - :server server - :param1 nickname - :param2 secret - :command "init") - :stderr (learn-ocaml-log-buffer) - :sentinel (apply-partially - #'learn-ocaml-error-handler - nil - callback))) - (defun learn-ocaml-login-possibly-with-passwd (server callback) "Connect the user when learn-ocaml-use-passwd=true with an (email,passwd) or a token and continue with the no-arg CALLBACK." (cl-case (x-popup-dialog From 32339b989029370d4273b45f21eb36ed6125d93b Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Wed, 28 Jul 2021 15:55:35 +0200 Subject: [PATCH 52/58] fix: Uncouple (ocamlsf/learn-ocaml, pfitaxel/emacs-learn-ocaml-client) versions --- .github/workflows/test.yml | 4 +--- run_emacs_image.sh | 22 ++++++++-------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4ee2dd8..89c7d4f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,12 +31,10 @@ jobs: - uses: actions/checkout@v2 - name: Script env: - EMACS_IMAGE: "pfitaxel/emacs-learn-ocaml-client" + EMACS_IMAGE_VERSION: "pfitaxel/emacs-learn-ocaml-client:oauth-moodle-dev" LEARNOCAML_IMAGE: ${{ matrix.learnocaml_image }} LEARNOCAML_VERSION: ${{ matrix.learnocaml_version }} USE_PASSWD: ${{ matrix.use_passwd }} run: | - docker pull "$LEARNOCAML_IMAGE:$LEARNOCAML_VERSION" - docker pull "$EMACS_IMAGE:$LEARNOCAML_VERSION" make dist-tests || { ret=$?; make stop; exit $ret; } make stop diff --git a/run_emacs_image.sh b/run_emacs_image.sh index 7ebec97..57f15bc 100755 --- a/run_emacs_image.sh +++ b/run_emacs_image.sh @@ -19,24 +19,18 @@ red () { echo -e "\\e[31m$1\\e[0m" } -green "Beforehand: LEARNOCAML_VERSION=$LEARNOCAML_VERSION" -# Default learn-ocaml version -: "${LEARNOCAML_VERSION:=oauth-moodle-dev}" -# Do "export LEARNOCAML_VERSION=…" before running the script to override -green "Henceforth: LEARNOCAML_VERSION=$LEARNOCAML_VERSION\\n" - -green "Beforehand: EMACS_IMAGE=$EMACS_IMAGE" +green "Beforehand: EMACS_IMAGE_VERSION=$EMACS_IMAGE_VERSION" # Default emacs image -: "${EMACS_IMAGE:=pfitaxel/emacs-learn-ocaml-client}" -# Do "export EMACS_IMAGE=…" before running the script to override -green "Henceforth: EMACS_IMAGE=$EMACS_IMAGE\\n" +: "${EMACS_IMAGE_VERSION:=pfitaxel/emacs-learn-ocaml-client:oauth-moodle-dev}" +# Do "export EMACS_IMAGE_VERSION=…" before running the script to override +green "Henceforth: EMACS_IMAGE_VERSION=$EMACS_IMAGE_VERSION\\n" pull_ifneedbe () { - sudo docker pull "$EMACS_IMAGE:$LEARNOCAML_VERSION" + sudo docker pull "$EMACS_IMAGE_VERSION" ret=$? if [ "$ret" -ne 0 ]; then - red "PROBLEM, 'sudo docker pull $EMACS_IMAGE:$LEARNOCAML_VERSION' failed with exit status $ret" + red "PROBLEM, 'sudo docker pull $EMACS_IMAGE_VERSION' failed with exit status $ret" exit $ret fi } @@ -73,7 +67,7 @@ run_emacs () { # Run the image in background sudo docker run -d -i --init --rm --name="$eid" \ -v "$PWD:/build" --network="container:$cid" \ - "$EMACS_IMAGE:$LEARNOCAML_VERSION" + "$EMACS_IMAGE_VERSION" ret=$? # hacky but working @@ -82,7 +76,7 @@ run_emacs () { set +vx; eval "$oldopt" # has to be after "ret=$?" if [ "$ret" -ne 0 ]; then - red "PROBLEM, 'sudo docker run -d ... $EMACS_IMAGE:$LEARNOCAML_VERSION' failed with exit status $ret" + red "PROBLEM, 'sudo docker run -d ... $EMACS_IMAGE_VERSION' failed with exit status $ret" stop_emacs exit $ret fi From 9cff58e4c27cbebd2e76e49359b352aa98d17e6b Mon Sep 17 00:00:00 2001 From: ImJustLouis Date: Wed, 28 Jul 2021 20:44:04 +0200 Subject: [PATCH 53/58] wip: delete unecessary print --- learn-ocaml.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index 6f0387e..39224c3 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -970,8 +970,7 @@ Shortcuts for the learn-ocaml mode: (progn (learn-ocaml-update-exercise-id-view) (easy-menu-add learn-ocaml-mode-menu) - (learn-ocaml-setup t) - (message (pp-to-string learn-ocaml-exercise-id))) + (learn-ocaml-setup t)) (setq learn-ocaml-loaded nil) (remove-hook 'caml-mode-hook #'learn-ocaml-mode) (remove-hook 'tuareg-mode-hook #'learn-ocaml-mode))) From 6a21e27d76210ab1b863803b7104d036e4a075a8 Mon Sep 17 00:00:00 2001 From: ImJustLouis Date: Wed, 28 Jul 2021 21:30:41 +0200 Subject: [PATCH 54/58] wip: beginning of nickname handling --- learn-ocaml.el | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/learn-ocaml.el b/learn-ocaml.el index 39224c3..eda2bb1 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -641,6 +641,17 @@ Argument SECRET may be needed by the server." :underline nil :weight bold :height 1.2))) "Face group titles.") +(defface learn-ocaml-nickname-face + '((t ( + :inherit variable-pitch + :foreground "LightSalmon1" + :underline nil :weight bold :height 1.3))) + "Face nickname.") + +(define-widget 'learn-ocaml-nickname 'lazy "" + :format "%{%t%}" + :sample-face 'learn-ocaml-nickname-face) + (define-widget 'learn-ocaml-group-title 'lazy "" :format "%{%t%}" :sample-face 'learn-ocaml-group-title-face) @@ -735,6 +746,14 @@ Argument SECRET may be needed by the server." 'learn-ocaml-header-hint :tag (make-string (length learn-ocaml-exo-list-doc) 45)) ; 45='-' (widget-insert "\n\n") + (widget-create + 'learn-ocaml-nickname + :tag (concat "Hello " "Louis" "! ")) + (widget-create 'learn-ocaml-button + :notify (lambda (&rest _ignore) + (find-file default-directory)) + "Change nickname") + (widget-insert "\n\n") (widget-insert "LearnOCaml ") (widget-create 'learn-ocaml-button :notify (lambda (&rest _ignore) From b899075884e36e27e3d784215ed22680c516fe48 Mon Sep 17 00:00:00 2001 From: ImJustLouis Date: Wed, 28 Jul 2021 22:02:35 +0200 Subject: [PATCH 55/58] fix: resolve some conflicts --- learn-ocaml.el | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index 77c1e48..a10a57f 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -63,11 +63,9 @@ (defvar learn-ocaml-temp-dir nil) -<<<<<<< HEAD (defvar learn-ocaml-working-directory nil) -======= + (defvar learn-ocaml-use-passwd nil) ->>>>>>> oauth-moodle (defvar learn-ocaml-log-buffer nil) @@ -925,15 +923,9 @@ Argument SECRET may be needed by the server." "Get template") (widget-insert " ") (widget-create 'learn-ocaml-button -<<<<<<< HEAD :notify (lambda (&rest _ignore) (learn-ocaml-open-ml-file id)) "Open .ml") -======= - :notify (lambda (&rest _ignore) - (find-file (concat id ".ml"))) - "Open .ml") ->>>>>>> oauth-moodle (widget-insert " ") (widget-create 'learn-ocaml-button :notify (lambda (&rest _ignore) @@ -980,10 +972,10 @@ Argument SECRET may be needed by the server." (widget-insert "\n\n") (widget-create 'learn-ocaml-nickname - :tag (concat "Hello " "Louis" "! ")) + :tag (concat "Hello " (learn-ocaml-get-nickname) "! ")) (widget-create 'learn-ocaml-button :notify (lambda (&rest _ignore) - (find-file default-directory)) + (learn-ocaml-set-nickname)) "Change nickname") (widget-insert "\n\n") (widget-insert "LearnOCaml ") @@ -1200,6 +1192,16 @@ If TOKEN is \"\", interactively ask a token." (error "%s %s: failed with [%s]." learn-ocaml-command-name cmd (string-trim (cdr result)))))) +(defun learn-ocaml-get-nickname () + "Get the nickname from the current user" + (string-trim (cdr (learn-ocaml-command-to-string-await-cmd "print-nickname")))) + +(defun learn-ocaml-set-nickname () + "Ask a new nickname and set it." + (interactive) + (let* ((nickname (read-string "Enter your new nickname: ")) + (string-trim (cdr (learn-ocaml-command-to-string-await-cmd (concat "set-nickname " nickname))))))) + ;; ;; menu definition ;; @@ -1212,13 +1214,6 @@ If TOKEN is \"\", interactively ask a token." (define-key map [menu-bar] nil) map)) -(defvar learn-ocaml-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-c C-m C-l") #'learn-ocaml-display-exercise-list) - (define-key map (kbd "C-c C-m l") #'learn-ocaml-display-exercise-list) - (define-key map (kbd "C-c C-m C-m") #'learn-ocaml-grade) - (define-key map [menu-bar] nil) - map)) (easy-menu-define learn-ocaml-mode-menu learn-ocaml-mode-map "LearnOCaml Mode Menu." From 1deff4d27e7d79a451e3e851dadd132332dfadec Mon Sep 17 00:00:00 2001 From: ImJustLouis Date: Thu, 29 Jul 2021 04:49:01 +0200 Subject: [PATCH 56/58] wip: small modification --- learn-ocaml.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index a10a57f..6a3e48f 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -1199,8 +1199,8 @@ If TOKEN is \"\", interactively ask a token." (defun learn-ocaml-set-nickname () "Ask a new nickname and set it." (interactive) - (let* ((nickname (read-string "Enter your new nickname: ")) - (string-trim (cdr (learn-ocaml-command-to-string-await-cmd (concat "set-nickname " nickname))))))) + (let* ((nickname (read-string "Enter your new nickname: "))) + (string-trim (cdr (learn-ocaml-command-to-string-await-cmd `("set-nickname" ,nickname)))))) ;; ;; menu definition From e31f90ff156df6bf9186bea37be5225fba052eab Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Thu, 29 Jul 2021 00:51:46 +0200 Subject: [PATCH 57/58] feat: Make learn-ocaml-command-to-string-await-cmd accept list of args --- learn-ocaml.el | 67 ++++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index 6a3e48f..b965ac3 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -362,30 +362,30 @@ Return (nil . \"stdout+stderr\") if exit code > 0." ;; 2 ;; or 0 ;; "test")) -(defun learn-ocaml-command-to-string-await-cmd (arg1) - "Run \"learn-ocaml-client ARG1\" (only one arg supported). +(defun learn-ocaml-command-to-string-await-cmd (args) + "Run \"learn-ocaml-client ARGS\". This is a `learn-ocaml-update-exec-path'-enhanced replacement for -(shell-command-to-string (concat learn-ocaml-command-name arg1)), -relying on `learn-ocaml-await-for'. +(shell-command-to-string (combine-and-quote-strings + (cons \"learn-ocaml-client\" args))), relying on `learn-ocaml-await-for'. Return (t . \"stdout+stderr\") if exit code = 0; Return (nil . \"stdout+stderr\") if exit code > 0; Raise (error \"learn-ocaml-await-for...\") if `learn-ocaml-timeout' exceeded." ;; (learn-ocaml-print-time-stamp) ;; FIXME: enable? - (unless (stringp arg1) - (error "ARG1 must be a string (in learn-ocaml-command-to-string-cmd)")) - (let ((buffer (generate-new-buffer (concat arg1 "-std-out")))) + (unless (and args (listp args)) + (error "ARGS must be a nonempty list (of strings)")) + (let ((buffer (generate-new-buffer (concat (car args) "-std-out")))) (learn-ocaml-await-for #'(lambda (result failure) (learn-ocaml-make-process-wrapper - :name arg1 - :command `(,learn-ocaml-command-name ,arg1) + :name (car args) + :command (cons learn-ocaml-command-name args) :buffer buffer :sentinel (apply-partially #'learn-ocaml-error-handler-nosplit-catch buffer #'(lambda (s) (funcall result s)) #'(lambda (s) (funcall failure s))))) - learn-ocaml-timeout arg1))) + learn-ocaml-timeout (car args)))) ;; ;; Higher-order functions, sentinels of the make-process wrapper @@ -479,7 +479,8 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (defun learn-ocaml-client-version () "Run \"learn-ocaml-client --version\"." - (string-trim (cdr (learn-ocaml-command-to-string-await-cmd "--version")))) + (string-trim + (cdr (learn-ocaml-command-to-string-await-cmd (list "--version"))))) (cl-defun learn-ocaml-client-sign-in-cmd (&key server login password callback-ok callback-err) "Run \"learn-ocaml-client init-user\" with SERVER LOGIN PASSWORD to login an user." @@ -518,7 +519,7 @@ To be used as a `make-process' sentinel, using args PROC and STRING." (defun learn-ocaml-client-config-cmd () "Run \"learn-ocaml-client server-config\"." (let* ((cmd "server-config") - (result (learn-ocaml-command-to-string-await-cmd cmd))) + (result (learn-ocaml-command-to-string-await-cmd (list cmd)))) (if (car result) (cdr result) ;; FIXME: Use learn-ocaml-log-buffer (error "%s %s: failed with [%s]." learn-ocaml-command-name cmd (string-trim (cdr result)))))) @@ -537,10 +538,12 @@ To be used as a `make-process' sentinel, using args PROC and STRING." nil callback))) +(learn-ocaml-client-exercise-score-cmd) + (defun learn-ocaml-client-exercise-score-cmd () "Run \"learn-ocaml-client exercise-score\"." - (let* ((cmd "exercise-score") - (result (learn-ocaml-command-to-string-await-cmd cmd))) + (let* ((cmd "exercise-score") + (result (learn-ocaml-command-to-string-await-cmd (list cmd)))) (if (car result) (json-read-from-string (cdr result)) ;; FIXME: Use learn-ocaml-log-buffer (error "%s %s: failed with [%s]." learn-ocaml-command-name cmd @@ -1165,15 +1168,15 @@ If TOKEN is \"\", interactively ask a token." (defun learn-ocaml-logout () "Logout the user from the server by removing the token from the file client.json" (interactive) - (let* ((cmd "logout") - (result (learn-ocaml-command-to-string-await-cmd cmd))) - (if (car result) - (progn (message-box "You have been successfully disconnected\n\n%s" - (cdr result)) - (learn-ocaml-global-disable-mode)) - ;; FIXME: Use learn-ocaml-log-buffer - (error "%s %s: failed with [%s]." learn-ocaml-command-name cmd - (string-trim (cdr result)))))) + (let* ((cmd "logout") + (result (learn-ocaml-command-to-string-await-cmd (list cmd)))) + (if (car result) + (progn (message-box "You have been successfully disconnected\n\n%s" + (cdr result)) + (learn-ocaml-global-disable-mode)) + ;; FIXME: Use learn-ocaml-log-buffer + (error "%s %s: failed with [%s]." learn-ocaml-command-name cmd + (string-trim (cdr result)))))) (defun learn-ocaml-deinit () "Logout the user and forget the server by removing the file client.json" @@ -1182,15 +1185,15 @@ If TOKEN is \"\", interactively ask a token." ;; should fail with exit code > 0 ;; if Cannot remove ~/.config/learnocaml/client.json : no such file or directory. (interactive) - (let* ((cmd "deinit") - (result (learn-ocaml-command-to-string-await-cmd cmd))) - (if (car result) - (progn (message-box "You have been successfully disconnected\n\n%s" - (cdr result)) - (learn-ocaml-global-disable-mode)) - ;; FIXME: Use learn-ocaml-log-buffer - (error "%s %s: failed with [%s]." learn-ocaml-command-name cmd - (string-trim (cdr result)))))) + (let* ((cmd "deinit") + (result (learn-ocaml-command-to-string-await-cmd (list cmd)))) + (if (car result) + (progn (message-box "You have been successfully disconnected\n\n%s" + (cdr result)) + (learn-ocaml-global-disable-mode)) + ;; FIXME: Use learn-ocaml-log-buffer + (error "%s %s: failed with [%s]." learn-ocaml-command-name cmd + (string-trim (cdr result)))))) (defun learn-ocaml-get-nickname () "Get the nickname from the current user" From d32ba67b2dd82cdc931fc4d7234defb5bae0d047 Mon Sep 17 00:00:00 2001 From: ImJustLouis Date: Thu, 29 Jul 2021 05:10:46 +0200 Subject: [PATCH 58/58] feat: Nickname available on the exercise-list and can be changed --- learn-ocaml.el | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/learn-ocaml.el b/learn-ocaml.el index b965ac3..1eb9000 100644 --- a/learn-ocaml.el +++ b/learn-ocaml.el @@ -538,8 +538,6 @@ To be used as a `make-process' sentinel, using args PROC and STRING." nil callback))) -(learn-ocaml-client-exercise-score-cmd) - (defun learn-ocaml-client-exercise-score-cmd () "Run \"learn-ocaml-client exercise-score\"." (let* ((cmd "exercise-score") @@ -961,7 +959,7 @@ Argument SECRET may be needed by the server." (defun learn-ocaml-display-exercise-list-aux (json) "Render the exercise list from the server-provided JSON." - (set-buffer (learn-ocaml-exo-list-buffer)) + (switch-to-buffer (learn-ocaml-exo-list-buffer)) (kill-all-local-variables) (let ((inhibit-read-only t)) (erase-buffer)) @@ -1005,8 +1003,7 @@ Argument SECRET may be needed by the server." (with-current-buffer learn-ocaml-exo-list-buffer ; just to be safe (read-only-mode 1)) ;; should be in the end, after (read-only-mode 1): - (learn-ocaml-mode) - (switch-to-buffer-other-window learn-ocaml-exo-list-buffer)) + (learn-ocaml-mode)) ;;;###autoload (defun learn-ocaml-display-exercise-list () @@ -1197,13 +1194,15 @@ If TOKEN is \"\", interactively ask a token." (defun learn-ocaml-get-nickname () "Get the nickname from the current user" - (string-trim (cdr (learn-ocaml-command-to-string-await-cmd "print-nickname")))) + (string-trim (cdr (learn-ocaml-command-to-string-await-cmd (list "print-nickname"))))) (defun learn-ocaml-set-nickname () - "Ask a new nickname and set it." + "Ask a new nickname, set it and refresh the buffer." (interactive) (let* ((nickname (read-string "Enter your new nickname: "))) - (string-trim (cdr (learn-ocaml-command-to-string-await-cmd `("set-nickname" ,nickname)))))) + (string-trim (cdr (learn-ocaml-command-to-string-await-cmd (list "set-nickname" nickname)))) + (kill-matching-buffers "^\*learn-ocaml.*\*" nil t) + (learn-ocaml-display-exercise-list))) ;; ;; menu definition