1;;; erlang-test.el -*- lexical-binding: t; coding: utf-8-unix -*- 2 3;;; Unit tests for erlang.el. 4 5;; Author: Johan Claesson 6;; Created: 2016-05-07 7;; Keywords: erlang, languages 8 9;; %CopyrightBegin% 10;; 11;; Copyright Ericsson AB 2016-2020. All Rights Reserved. 12;; 13;; Licensed under the Apache License, Version 2.0 (the "License"); 14;; you may not use this file except in compliance with the License. 15;; You may obtain a copy of the License at 16;; 17;; http://www.apache.org/licenses/LICENSE-2.0 18;; 19;; Unless required by applicable law or agreed to in writing, software 20;; distributed under the License is distributed on an "AS IS" BASIS, 21;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 22;; See the License for the specific language governing permissions and 23;; limitations under the License. 24;; 25;; %CopyrightEnd% 26 27 28;;; Commentary: 29 30;; This library require GNU Emacs 25 or later. 31;; 32;; There are three ways to run the erlang emacs unit tests. 33;; 34;; 1. Within a running emacs process. Load this file. Then to run 35;; all defined test cases: 36;; 37;; M-x ert RET t RET 38;; 39;; To run only the erlang test cases: 40;; 41;; M-x ert RET "^erlang" RET 42;; 43;; 44;; 2. In a new stand-alone emacs process. This process exits 45;; when it executed the tests. For example: 46;; 47;; emacs -Q -batch -L . -l erlang.el -l erlang-test.el \ 48;; -f ert-run-tests-batch-and-exit 49;; 50;; The -L option adds a directory to the load-path. It should be the 51;; directory containing erlang.el and erlang-test.el. 52;; 53;; 3. Run the emacs_SUITE. The testcases tests_interpreted/1 and 54;; tests_compiled/1 in this suite are using the second method. One 55;; way to run this suite is with the ct_run tool, for example like the 56;; following when standing at the OTP repo top directory: 57;; 58;; ct_run -suite lib/tools/test/emacs_SUITE 59;; 60;; Note that this creates a lot of html log files in the current 61;; directory. 62 63;;; Code: 64 65(require 'ert) 66(require 'erlang) 67 68(defvar erlang-test-code 69 '((nil . "-module(erlang_test).") 70 (nil . "-import(lists, [map/2]).") 71 (nil . "-compile(export_all).") 72 ("SYMBOL" . "-define(SYMBOL, value).") 73 ("MACRO" . "-define(MACRO(X), X + X).") 74 ("struct" . "-record(struct, {until,maps,are,everywhere}).") 75 ("function" . "function() -> #struct{}.")) 76 "Alist of erlang test code. 77Each entry have the format (TAGNAME . ERLANG_CODE). If TAGNAME 78is nil there is no definitions in the ERLANG_CODE. The 79ERLANG_CODE is a single line of erlang code. These lines will be 80concatenated to form an erlang file to test on.") 81 82 83(ert-deftest erlang-test-tags () 84 (let* ((dir (make-temp-file "erlang-test" t)) 85 (erlang-file (expand-file-name "erlang_test.erl" dir)) 86 (tags-file (expand-file-name "TAGS" dir)) 87 (old-tags-file-name (default-value 'tags-file-name)) 88 (old-tags-table-list (default-value 'tags-table-list)) 89 tags-file-name 90 tags-table-list 91 tags-table-set-list 92 tags-add-tables 93 tags-completion-table 94 erlang-buffer 95 erlang-mode-hook 96 prog-mode-hook 97 erlang-shell-mode-hook) 98 (unwind-protect 99 (progn 100 (setq-default tags-file-name nil) 101 (setq-default tags-table-list nil) 102 (erlang-test-create-erlang-file erlang-file) 103 (erlang-test-compile-tags erlang-file tags-file) 104 (setq erlang-buffer (find-file-noselect erlang-file)) 105 (if (< emacs-major-version 26) 106 (progn 107 (with-current-buffer erlang-buffer 108 (setq-local tags-file-name tags-file)) 109 ;; Setting global tags-file-name is a workaround for 110 ;; GNU Emacs bug#23164. 111 (setq tags-file-name tags-file)) 112 (visit-tags-table tags-file t)) 113 (erlang-test-complete-at-point tags-file) 114 (erlang-test-completion-table) 115 (erlang-test-xref-find-definitions erlang-file erlang-buffer)) 116 (when (buffer-live-p erlang-buffer) 117 (kill-buffer erlang-buffer)) 118 (let ((tags-buffer (find-buffer-visiting tags-file))) 119 (when (buffer-live-p tags-buffer) 120 (kill-buffer tags-buffer))) 121 (when (file-exists-p dir) 122 (delete-directory dir t)) 123 (setq-default tags-file-name old-tags-file-name) 124 (setq-default tags-table-list old-tags-table-list)))) 125 126(defun erlang-test-create-erlang-file (erlang-file) 127 (with-temp-file erlang-file 128 (cl-loop for (_ . code) in erlang-test-code 129 do (insert code "\n")))) 130 131(defun erlang-test-compile-tags (erlang-file tags-file) 132 (should (zerop (call-process "etags" nil nil nil 133 "-o" tags-file 134 erlang-file)))) 135 136(defun erlang-test-completion-table () 137 (let ((erlang-replace-etags-tags-completion-table t)) 138 (setq tags-completion-table nil) 139 (tags-completion-table)) 140 (should (equal (sort tags-completion-table #'string-lessp) 141 (sort (erlang-expected-completion-table) #'string-lessp)))) 142 143(defun erlang-expected-completion-table () 144 (append (cl-loop for (symbol . _) in erlang-test-code 145 when (stringp symbol) 146 append (list symbol (concat "erlang_test:" symbol))) 147 (list "erlang_test:" "erlang_test:module_info"))) 148 149(defun erlang-test-xref-find-definitions (erlang-file erlang-buffer) 150 (cl-loop for (tagname . code) in erlang-test-code 151 for line = 1 then (1+ line) 152 do (when tagname 153 (switch-to-buffer erlang-buffer) 154 (erlang-test-xref-jump tagname erlang-file line) 155 (when (string-equal tagname "function") 156 (erlang-test-xref-jump (concat "erlang_test:" tagname) 157 erlang-file line)))) 158 (erlang-test-xref-jump "erlang_test:" erlang-file 1)) 159 160(defun erlang-test-xref-jump (id expected-file expected-line) 161 (goto-char (point-max)) 162 (insert "\n%% " id) 163 (save-buffer) 164 (if (fboundp 'xref-find-definitions) 165 (xref-find-definitions (erlang-id-to-string 166 (erlang-get-identifier-at-point))) 167 (error "xref-find-definitions not defined (too old emacs?)")) 168 (erlang-test-verify-pos expected-file expected-line)) 169 170(defun erlang-test-verify-pos (expected-file expected-line) 171 (should (string-equal (file-truename expected-file) 172 (file-truename (buffer-file-name)))) 173 (should (eq expected-line (line-number-at-pos))) 174 (should (= (point-at-bol) (point)))) 175 176(defun erlang-test-complete-at-point (tags-file) 177 (with-temp-buffer 178 (erlang-mode) 179 (setq-local tags-file-name tags-file) 180 (insert "\nerlang_test:fun") 181 (erlang-complete-tag) 182 (should (looking-back "erlang_test:function" (point-at-bol))) 183 (insert "\nfun") 184 (erlang-complete-tag) 185 (should (looking-back "function" (point-at-bol))) 186 (insert "\nerlang_") 187 (erlang-complete-tag) 188 (should (looking-back "erlang_test:" (point-at-bol))))) 189 190 191(ert-deftest erlang-test-compile-options () 192 (erlang-test-format-opt t 193 "t") 194 (erlang-test-format-opt nil 195 "nil") 196 (erlang-test-format-opt (cons 1 2) 197 "{1, 2}") 198 (erlang-test-format-opt (list 1) 199 "[1]") 200 (erlang-test-format-opt (list 1 2) 201 "[1, 2]") 202 (erlang-test-format-opt (list 1 2 3) 203 "[1, 2, 3]") 204 (erlang-test-format-opt 'symbol 205 "symbol") 206 (erlang-test-format-opt "string" 207 "\"string\"") 208 (erlang-test-format-opt [] 209 "{}") 210 (erlang-test-format-opt [1] 211 "{1}") 212 (erlang-test-format-opt [1 2] 213 "{1, 2}") 214 (erlang-test-format-opt [1 2 (3 [4 5 6] 7)] 215 "{1, 2, [3, {4, 5, 6}, 7]}")) 216 217(defun erlang-test-format-opt (elisp &optional expected-erlang) 218 (let ((erlang (inferior-erlang-format-opt elisp))) 219 (message "%s -> %s" elisp erlang) 220 (when expected-erlang 221 (should (equal erlang expected-erlang))) 222 erlang)) 223 224 225(ert-deftest erlang-test-parse-id () 226 (cl-loop for id-string in '("fun/10" 227 "qualified-function module:fun/10" 228 "record reko" 229 "macro _SYMBOL" 230 "macro MACRO/10" 231 "module modula" 232 "macro" 233 nil) 234 for id-list in '((nil nil "fun" 10) 235 (qualified-function "module" "fun" 10) 236 (record nil "reko" nil) 237 (macro nil "_SYMBOL" nil) 238 (macro nil "MACRO" 10) 239 (module nil "modula" nil) 240 (nil nil "macro" nil) 241 nil) 242 for id-list2 = (erlang-id-to-list id-string) 243 do (should (equal id-list id-list2)) 244 for id-string2 = (erlang-id-to-string id-list) 245 do (should (equal id-string id-string2)) 246 collect id-list2)) 247 248 249(provide 'erlang-test) 250 251;;; erlang-test.el ends here 252