View raw

1 ;;; ste-lint-tests.el --- Tests for the STE linter -*- lexical-binding: t; -*- 2 3 ;;; Commentary: 4 5 ;; These tests lint small Org texts in temporary buffers and files. Run them 6 ;; with the runner script in this directory: 7 ;; 8 ;; ./run-tests.sh 9 10 ;;; Code: 11 12 (require 'ert) 13 (require 'ste-lint) 14 15 ;;; Helpers 16 17 (defun ste-lint-tests--findings (text) 18 "Return the findings of the Org TEXT as (CATEGORY MATCH LINE) lists." 19 (let ((file (make-temp-file "ste-lint-" nil ".org" text))) 20 (unwind-protect 21 (mapcar (lambda (entry) 22 (let ((finding (nth 2 entry))) 23 (list (ste-lint-finding-category finding) 24 (ste-lint-finding-match finding) 25 (nth 0 entry)))) 26 (ste-lint-file file)) 27 (delete-file file)))) 28 29 (defun ste-lint-tests--categories (text) 30 "Return the categories of the findings of the Org TEXT." 31 (mapcar #'car (ste-lint-tests--findings text))) 32 33 (defun ste-lint-tests--words (count) 34 "Return a sentence of COUNT words that ends with a period." 35 (concat (mapconcat (lambda (_) "word") (number-sequence 1 count) " ") ".")) 36 37 ;;; Rules 38 39 (ert-deftest ste-lint-test-contraction () 40 (should (equal (ste-lint-tests--findings "We can't stop.\n") 41 '(("contraction" "can't" 1)))) 42 (should (equal (ste-lint-tests--categories "We don’t stop.\n") 43 '("contraction")))) 44 45 (ert-deftest ste-lint-test-possessive-is-not-contraction () 46 (should (null (ste-lint-tests--findings "The user's file is open.\n")))) 47 48 (ert-deftest ste-lint-test-latin-abbreviation-ignores-case () 49 (should (equal (ste-lint-tests--findings "Use a tool, E.g. a linter.\n") 50 '(("latin-abbreviation" "E.g." 1))))) 51 52 (ert-deftest ste-lint-test-semicolon-and-idiom () 53 (should (equal (ste-lint-tests--categories "Stop it; start it.\n") 54 '("semicolon"))) 55 (should (null (ste-lint-tests--findings "Read the TL;DR first.\n")))) 56 57 (ert-deftest ste-lint-test-substitution () 58 (should (equal (ste-lint-tests--findings "Utilize the tool.\n") 59 '(("wordy-phrase" "Utilize" 1))))) 60 61 (ert-deftest ste-lint-test-phrase-across-line-break () 62 (should (equal (ste-lint-tests--findings 63 "Read the file in order\n to see the rules.\n") 64 '(("wordy-phrase" "in order to" 1))))) 65 66 (ert-deftest ste-lint-test-instruction-mood () 67 (should (equal (ste-lint-tests--categories "You should run the script.\n") 68 '("instruction-mood")))) 69 70 ;;; Prose selection 71 72 (ert-deftest ste-lint-test-inline-markup-is-masked () 73 (should (null (ste-lint-tests--findings 74 "Run =don't;= and ~a;b~ at <2026-10-05 Mon>.\n")))) 75 76 (ert-deftest ste-lint-test-link-label-is-linted-and-target-is-not () 77 (should (null (ste-lint-tests--findings 78 "Read [[https://a.example/x;y][the page]] now.\n"))) 79 (should (equal (ste-lint-tests--categories 80 "Read [[https://a.example/x][the page; now]].\n") 81 '("semicolon")))) 82 83 (ert-deftest ste-lint-test-non-prose-elements-are-skipped () 84 (should (null (ste-lint-tests--findings 85 (concat "#+TITLE: Don't; skip\n" 86 "* Plain title\n" 87 "SCHEDULED: <2026-10-05 Mon>\n" 88 ":PROPERTIES:\n:ID: a;b\n:END:\n" 89 ":LOGBOOK:\n- Note: don't; skip.\n:END:\n" 90 "#+BEGIN_SRC sh\necho \"don't;\"\n#+END_SRC\n" 91 "#+BEGIN_EXAMPLE\ndon't;\n#+END_EXAMPLE\n" 92 "#+BEGIN_QUOTE\nHe said don't.\n#+END_QUOTE\n" 93 "| don't; | cell |\n" 94 "# don't; comment\n"))))) 95 96 (ert-deftest ste-lint-test-headline-title-only () 97 (should (equal (ste-lint-tests--findings 98 "* TODO [#A] Don't wait [1/2] :he:\n") 99 '(("contraction" "Don't" 1))))) 100 101 (ert-deftest ste-lint-test-headline-has-no-length-limit () 102 (should (null (ste-lint-tests--findings 103 (concat "* " (ste-lint-tests--words 30) "\n"))))) 104 105 ;;; Sentence length 106 107 (ert-deftest ste-lint-test-sentence-length-description () 108 (should (null (ste-lint-tests--findings 109 (concat (ste-lint-tests--words 25) "\n")))) 110 (should (equal (ste-lint-tests--findings 111 (concat "Intro.\n" (ste-lint-tests--words 26) "\n")) 112 '(("sentence-length" "26 words" 2))))) 113 114 (ert-deftest ste-lint-test-sentence-length-spans-lines () 115 (let ((text (replace-regexp-in-string 116 " word word word word word word word word " 117 "\nword word word word word word word word\n" 118 (ste-lint-tests--words 30) t t))) 119 (should (equal (ste-lint-tests--categories (concat text "\n")) 120 '("sentence-length"))))) 121 122 (ert-deftest ste-lint-test-sentence-length-procedure () 123 (should (equal (ste-lint-tests--categories 124 (concat "1. " (ste-lint-tests--words 21) "\n")) 125 '("sentence-length"))) 126 (should (null (ste-lint-tests--findings 127 (concat "- " (ste-lint-tests--words 21) "\n"))))) 128 129 (ert-deftest ste-lint-test-masked-code-counts-as-one-word () 130 (should (null (ste-lint-tests--findings 131 (concat (ste-lint-tests--words 24) " =a b c d e=.\n"))))) 132 133 ;;; Positions and entry points 134 135 (ert-deftest ste-lint-test-exact-position () 136 (let ((file (make-temp-file "ste-lint-" nil ".org" 137 "First line.\nThe tool can't run.\n"))) 138 (unwind-protect 139 (let ((entry (car (ste-lint-file file)))) 140 (should (equal (list (nth 0 entry) (nth 1 entry)) '(2 10)))) 141 (delete-file file)))) 142 143 (ert-deftest ste-lint-test-flymake-backend () 144 (with-temp-buffer 145 (let ((org-mode-hook nil)) 146 (org-mode)) 147 (insert "We can't stop.\nUtilize the tool.\n") 148 (let (diagnostics) 149 (ste-lint-flymake (lambda (reported) (setq diagnostics reported))) 150 (should (equal (mapcar #'flymake-diagnostic-type diagnostics) 151 '(:error :warning))) 152 (should (equal (buffer-substring 153 (flymake-diagnostic-beg (car diagnostics)) 154 (flymake-diagnostic-end (car diagnostics))) 155 "can't"))))) 156 157 (provide 'ste-lint-tests) 158 159 ;;; ste-lint-tests.el ends here 160