From eb199e3c2d71612a6c82ffc83cdd021bea7ba17a Mon Sep 17 00:00:00 2001 From: Alex Vear Date: Mon, 8 Aug 2022 16:45:52 +0100 Subject: [PATCH 1/3] Rewrite indentation testing system New implementation is: - Faster - Smaller - Configurable - Easier to add new tests As a bonus all the tests now pass on my machine (previously a couple of tests failed on my machine, but passed in CI)! --- clj/.clj-kondo/config.edn | 3 +- .../in.clj} | 2 - .../indent-test-cases/basic-sexp/out.clj | 5 ++ .../in.clj} | 2 - .../out.clj} | 2 - .../inherit-indentation/config.edn | 3 + .../in.clj} | 0 .../out.clj} | 0 .../in.clj} | 2 - .../out.clj} | 2 - .../in.clj} | 2 - .../multibyte-indentation/out.clj | 3 + .../in.clj} | 2 - .../out.clj} | 2 - .../side-effects-in-indentexpr/config.edn | 3 + .../in.clj} | 0 .../out.clj} | 0 clj/test/vim/helpers.clj | 21 +++++ clj/test/vim/indent_test.clj | 73 +++++++++--------- clj/test/vim/test.clj | 77 ++++--------------- 20 files changed, 90 insertions(+), 114 deletions(-) rename clj/resources/indent-test-cases/{test-basic-sexp-indent.txt => basic-sexp/in.clj} (93%) create mode 100644 clj/resources/indent-test-cases/basic-sexp/out.clj rename clj/resources/indent-test-cases/{test-dispatch-macro-indent.in => dispach-macro/in.clj} (88%) rename clj/resources/indent-test-cases/{test-dispatch-macro-indent.out => dispach-macro/out.clj} (89%) create mode 100644 clj/resources/indent-test-cases/inherit-indentation/config.edn rename clj/resources/indent-test-cases/{test-inherit-indent.in => inherit-indentation/in.clj} (100%) rename clj/resources/indent-test-cases/{test-inherit-indent.out => inherit-indentation/out.clj} (100%) rename clj/resources/indent-test-cases/{test-special-case-indent.in => letfn/in.clj} (96%) rename clj/resources/indent-test-cases/{test-special-case-indent.out => letfn/out.clj} (96%) rename clj/resources/indent-test-cases/{test-multibyte-indent.txt => multibyte-indentation/in.clj} (71%) create mode 100644 clj/resources/indent-test-cases/multibyte-indentation/out.clj rename clj/resources/indent-test-cases/{test-reader-conditional-indent.in => reader-conditional/in.clj} (93%) rename clj/resources/indent-test-cases/{test-reader-conditional-indent.out => reader-conditional/out.clj} (93%) create mode 100644 clj/resources/indent-test-cases/side-effects-in-indentexpr/config.edn rename clj/resources/indent-test-cases/{test-side-effects-in-indentexpr.in => side-effects-in-indentexpr/in.clj} (100%) rename clj/resources/indent-test-cases/{test-side-effects-in-indentexpr.out => side-effects-in-indentexpr/out.clj} (100%) create mode 100644 clj/test/vim/helpers.clj diff --git a/clj/.clj-kondo/config.edn b/clj/.clj-kondo/config.edn index 0c518e4..678c351 100644 --- a/clj/.clj-kondo/config.edn +++ b/clj/.clj-kondo/config.edn @@ -1,3 +1,2 @@ -{:lint-as {vim.test/with-tempfile clojure.core/fn - vim.test/defpredicates clojure.core/def +{:lint-as {vim.test/defpredicates clojure.core/def vim.test/defsyntaxtest clojure.core/def}} diff --git a/clj/resources/indent-test-cases/test-basic-sexp-indent.txt b/clj/resources/indent-test-cases/basic-sexp/in.clj similarity index 93% rename from clj/resources/indent-test-cases/test-basic-sexp-indent.txt rename to clj/resources/indent-test-cases/basic-sexp/in.clj index 108d57a..3551b97 100644 --- a/clj/resources/indent-test-cases/test-basic-sexp-indent.txt +++ b/clj/resources/indent-test-cases/basic-sexp/in.clj @@ -3,5 +3,3 @@ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.") - -;; vim:ft=clojure: diff --git a/clj/resources/indent-test-cases/basic-sexp/out.clj b/clj/resources/indent-test-cases/basic-sexp/out.clj new file mode 100644 index 0000000..3551b97 --- /dev/null +++ b/clj/resources/indent-test-cases/basic-sexp/out.clj @@ -0,0 +1,5 @@ +(ns test-basic-sexp-indent + "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, + quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat.") diff --git a/clj/resources/indent-test-cases/test-dispatch-macro-indent.in b/clj/resources/indent-test-cases/dispach-macro/in.clj similarity index 88% rename from clj/resources/indent-test-cases/test-dispatch-macro-indent.in rename to clj/resources/indent-test-cases/dispach-macro/in.clj index 78ae5ff..b59b91e 100644 --- a/clj/resources/indent-test-cases/test-dispatch-macro-indent.in +++ b/clj/resources/indent-test-cases/dispach-macro/in.clj @@ -27,5 +27,3 @@ bar)) (#_(foo bar) a) - -;; vim:ft=clojure: diff --git a/clj/resources/indent-test-cases/test-dispatch-macro-indent.out b/clj/resources/indent-test-cases/dispach-macro/out.clj similarity index 89% rename from clj/resources/indent-test-cases/test-dispatch-macro-indent.out rename to clj/resources/indent-test-cases/dispach-macro/out.clj index 39dc042..4f7356d 100644 --- a/clj/resources/indent-test-cases/test-dispatch-macro-indent.out +++ b/clj/resources/indent-test-cases/dispach-macro/out.clj @@ -27,5 +27,3 @@ (#_(foo bar) a) - -;; vim:ft=clojure: diff --git a/clj/resources/indent-test-cases/inherit-indentation/config.edn b/clj/resources/indent-test-cases/inherit-indentation/config.edn new file mode 100644 index 0000000..d972b2b --- /dev/null +++ b/clj/resources/indent-test-cases/inherit-indentation/config.edn @@ -0,0 +1,3 @@ +{:indent? false + :extra-cmds ["normal! gg" + "exec \"normal! /α\\s\\Oa\\/β\\s\\\\\\\\\\\\\\\\\\b\\c\\\\d\\\""]} diff --git a/clj/resources/indent-test-cases/test-inherit-indent.in b/clj/resources/indent-test-cases/inherit-indentation/in.clj similarity index 100% rename from clj/resources/indent-test-cases/test-inherit-indent.in rename to clj/resources/indent-test-cases/inherit-indentation/in.clj diff --git a/clj/resources/indent-test-cases/test-inherit-indent.out b/clj/resources/indent-test-cases/inherit-indentation/out.clj similarity index 100% rename from clj/resources/indent-test-cases/test-inherit-indent.out rename to clj/resources/indent-test-cases/inherit-indentation/out.clj diff --git a/clj/resources/indent-test-cases/test-special-case-indent.in b/clj/resources/indent-test-cases/letfn/in.clj similarity index 96% rename from clj/resources/indent-test-cases/test-special-case-indent.in rename to clj/resources/indent-test-cases/letfn/in.clj index badbd79..a69458d 100644 --- a/clj/resources/indent-test-cases/test-special-case-indent.in +++ b/clj/resources/indent-test-cases/letfn/in.clj @@ -35,5 +35,3 @@ (six-times [y] (* (twice y) 3))] (foo #{:foo :bar :biz} :foo)) - -;; vim:ft=clojure: diff --git a/clj/resources/indent-test-cases/test-special-case-indent.out b/clj/resources/indent-test-cases/letfn/out.clj similarity index 96% rename from clj/resources/indent-test-cases/test-special-case-indent.out rename to clj/resources/indent-test-cases/letfn/out.clj index b7b0c0e..adebd21 100644 --- a/clj/resources/indent-test-cases/test-special-case-indent.out +++ b/clj/resources/indent-test-cases/letfn/out.clj @@ -35,5 +35,3 @@ (six-times [y] (* (twice y) 3))] (foo #{:foo :bar :biz} :foo)) - -;; vim:ft=clojure: diff --git a/clj/resources/indent-test-cases/test-multibyte-indent.txt b/clj/resources/indent-test-cases/multibyte-indentation/in.clj similarity index 71% rename from clj/resources/indent-test-cases/test-multibyte-indent.txt rename to clj/resources/indent-test-cases/multibyte-indentation/in.clj index 0308106..4a17c24 100644 --- a/clj/resources/indent-test-cases/test-multibyte-indent.txt +++ b/clj/resources/indent-test-cases/multibyte-indentation/in.clj @@ -1,5 +1,3 @@ (let [Δt (if foo bar baz)]) - -;; vim:ft=clojure: diff --git a/clj/resources/indent-test-cases/multibyte-indentation/out.clj b/clj/resources/indent-test-cases/multibyte-indentation/out.clj new file mode 100644 index 0000000..4a17c24 --- /dev/null +++ b/clj/resources/indent-test-cases/multibyte-indentation/out.clj @@ -0,0 +1,3 @@ +(let [Δt (if foo + bar + baz)]) diff --git a/clj/resources/indent-test-cases/test-reader-conditional-indent.in b/clj/resources/indent-test-cases/reader-conditional/in.clj similarity index 93% rename from clj/resources/indent-test-cases/test-reader-conditional-indent.in rename to clj/resources/indent-test-cases/reader-conditional/in.clj index 59234e5..9bc1c7b 100644 --- a/clj/resources/indent-test-cases/test-reader-conditional-indent.in +++ b/clj/resources/indent-test-cases/reader-conditional/in.clj @@ -9,5 +9,3 @@ #?@(:clj [5 6 7 8] :cljs [1 2 3 4]))) - -;; vim:ft=clojure: diff --git a/clj/resources/indent-test-cases/test-reader-conditional-indent.out b/clj/resources/indent-test-cases/reader-conditional/out.clj similarity index 93% rename from clj/resources/indent-test-cases/test-reader-conditional-indent.out rename to clj/resources/indent-test-cases/reader-conditional/out.clj index 968a6cd..5c5bfc2 100644 --- a/clj/resources/indent-test-cases/test-reader-conditional-indent.out +++ b/clj/resources/indent-test-cases/reader-conditional/out.clj @@ -9,5 +9,3 @@ #?@(:clj [5 6 7 8] :cljs [1 2 3 4]))) - -;; vim:ft=clojure: diff --git a/clj/resources/indent-test-cases/side-effects-in-indentexpr/config.edn b/clj/resources/indent-test-cases/side-effects-in-indentexpr/config.edn new file mode 100644 index 0000000..8bbbf0f --- /dev/null +++ b/clj/resources/indent-test-cases/side-effects-in-indentexpr/config.edn @@ -0,0 +1,3 @@ +{:indent? false + :extra-cmds ["normal! gg" + "exec \"normal! /α\\:call GetClojureIndent()\\rxj:call GetClojureIndent()\\ry\""]} diff --git a/clj/resources/indent-test-cases/test-side-effects-in-indentexpr.in b/clj/resources/indent-test-cases/side-effects-in-indentexpr/in.clj similarity index 100% rename from clj/resources/indent-test-cases/test-side-effects-in-indentexpr.in rename to clj/resources/indent-test-cases/side-effects-in-indentexpr/in.clj diff --git a/clj/resources/indent-test-cases/test-side-effects-in-indentexpr.out b/clj/resources/indent-test-cases/side-effects-in-indentexpr/out.clj similarity index 100% rename from clj/resources/indent-test-cases/test-side-effects-in-indentexpr.out rename to clj/resources/indent-test-cases/side-effects-in-indentexpr/out.clj diff --git a/clj/test/vim/helpers.clj b/clj/test/vim/helpers.clj new file mode 100644 index 0000000..65e4b87 --- /dev/null +++ b/clj/test/vim/helpers.clj @@ -0,0 +1,21 @@ +(ns vim.helpers + (:require [clojure.edn :as edn] + [clojure.java.shell :as shell]) + (:import [java.io File FileReader PushbackReader])) + +(defn read-edn-file [^File file] + (when (.exists file) + (with-open [rdr (FileReader. file)] + (edn/read (PushbackReader. rdr))))) + +(def ^:dynamic *vim* "vim") + +(defn vim! + "Run commands on a file in Vim." + [^File file cmds & {:keys [vimrc], :or {vimrc "NONE"}}] + (let [cmds (mapcat (fn [cmd] ["-c" cmd]) cmds) + args (concat ["--clean" "-N" "-u" (str vimrc)] cmds ["-c" "quitall!" "--" (str file)]) + ret (apply shell/sh *vim* args)] + (when (pos? (:exit ret)) + (throw (ex-info "Failed to run Vim command" + (assoc ret :vim *vim*, :args args)))))) diff --git a/clj/test/vim/indent_test.clj b/clj/test/vim/indent_test.clj index 7ac9d19..bd8ed68 100644 --- a/clj/test/vim/indent_test.clj +++ b/clj/test/vim/indent_test.clj @@ -1,40 +1,43 @@ (ns vim.indent-test - (:require [clojure.test :refer [deftest]] - [vim.test :refer [test-indent]])) + (:require [clojure.test :refer [deftest testing is]] + [clojure.string :as str] + [clojure.java.io :as io] + [vim.helpers :as h]) + (:import [java.io File])) -(deftest test-basic-sexp-indent - (test-indent "works as expected with basic S-expressions" - :in "test-basic-sexp-indent.txt" - :out "test-basic-sexp-indent.txt")) +(def ^:private test-case-dir (io/file (io/resource "indent-test-cases"))) -(deftest test-multibyte-indent - (test-indent "with multibyte characters" - :in "test-multibyte-indent.txt" - :out "test-multibyte-indent.txt")) +(defn get-test-cases [^File test-case-dir] + (into [] + (comp + (filter #(.isDirectory ^File %)) + (map #(.getName ^File %))) + (.listFiles test-case-dir))) -(deftest test-inherit-indent - (test-indent "is inherited from previous element" - :in "test-inherit-indent.in" - :out "test-inherit-indent.out" - :keys "/α\\s\\Oa\\/β\\s\\\\\\\\\\\\\\\\\\b\\c\\\\d\\")) +(defn run-test-case [test-case] + (testing (str "Preparation for " test-case) + (let [input (io/file test-case-dir test-case "in.clj") + expected (io/file test-case-dir test-case "out.clj") + actual (File/createTempFile test-case ".clj") + config (let [f (io/file test-case-dir test-case "config.edn")] + (or (h/read-edn-file f) {})) + cmds (concat (:extra-cmds config) + (when (:indent? config true) ["normal! gg=G"]) + ["write"])] + (io/make-parents actual) + (io/copy input actual) + (h/vim! actual cmds :vimrc (io/file "vim/test-runtime.vim")) + {:test-case test-case + :expected (slurp expected) + :expected-file expected + :actual (slurp actual) + :actual-file actual}))) -(deftest test-side-effects-in-indentexpr - (test-indent "GetClojureIndent does not move cursor" - :in "test-side-effects-in-indentexpr.in" - :out "test-side-effects-in-indentexpr.out" - :keys "/α\\:call GetClojureIndent()\\rxj:call GetClojureIndent()\\ry")) - -(deftest test-reader-conditional-indent - (test-indent "reader conditionals are indented like maps" - :in "test-reader-conditional-indent.in" - :out "test-reader-conditional-indent.out")) - -(deftest test-dispatch-macro-indent - (test-indent "dispatch macro indentation is handled correctly" - :in "test-dispatch-macro-indent.in" - :out "test-dispatch-macro-indent.out")) - -(deftest test-special-case-indent - (test-indent "special case indentation is handled correctly" - :in "test-special-case-indent.in" - :out "test-special-case-indent.out")) +;; TODO: do this parallisation more intelligently with agents. +(deftest test-indent + "Runs all indentation tests in parallel" + (doseq [{:keys [test-case expected expected-file actual actual-file]} + (pmap run-test-case (get-test-cases test-case-dir))] + (testing test-case + (is (= expected actual) + (format "(not= \"%s\"\n \"%s\")" expected-file actual-file))))) diff --git a/clj/test/vim/test.clj b/clj/test/vim/test.clj index 0f9be01..7dd5bf3 100644 --- a/clj/test/vim/test.clj +++ b/clj/test/vim/test.clj @@ -9,34 +9,25 @@ (:import [java.io File] [java.util List])) -(defmacro with-tempfile - {:requires [File]} - [[tmp-sym] & body] - `(let [~tmp-sym (File/createTempFile "vim-clojure-static" ".tmp")] - (try - ~@body - (finally - (.delete ~tmp-sym))))) - (defn vim-exec "Spit buf into file, then execute vim-expr after Vim loads the file. The - value of vim-expr is evaluated as EDN and returned." + value of vim-expr is evaluated as EDN and returned." [file buf vim-expr & opts] - (let [{:keys [pre]} (apply hash-map opts)] - (with-tempfile [tmp] - (io/make-parents file) - (spit file buf) - (spit tmp (str "let @x = " vim-expr)) - (let [{:keys [exit err]} - (shell/sh "vim" "-N" "-u" "vim/test-runtime.vim" - "-c" (or pre "execute") - "-c" (str "source " tmp) - "-c" (str "call writefile([@x], " (pr-str (str tmp)) ")") - "-c" "quitall!" - file)] - (when-not (zero? exit) - (throw (RuntimeException. ^String err)))) - (edn/read-string (slurp tmp))))) + (let [{:keys [pre]} (apply hash-map opts) + tmp (File/createTempFile "vim-clojure-static" ".tmp")] + (io/make-parents file) + (spit file buf) + (spit tmp (str "let @x = " vim-expr)) + (let [{:keys [exit err]} + (shell/sh "vim" "-N" "-u" "vim/test-runtime.vim" + "-c" (or pre "execute") + "-c" (str "source " tmp) + "-c" (str "call writefile([@x], " (pr-str (str tmp)) ")") + "-c" "quitall!" + file)] + (when-not (zero? exit) + (throw (RuntimeException. ^String err)))) + (edn/read-string (slurp tmp)))) (defn syn-id-names "Map lines of clojure text to vim synID names at each column as keywords: @@ -115,42 +106,6 @@ [coll#] (boolean (some (partial not= ~kw) coll#))))) -(defmacro with-transform-test - "Copy contents of `in` to a tempfile, execute body with tempfile bound to - tmp-sym, then finally compare the transformed contents of the tempfile with - the contents of `out`. - - `in` and `out` are urls that will be passed to clojure.java.io/resource." - {:requires [#'test/testing #'with-tempfile]} - [string {:keys [in out]} [tmp-sym :as tmp-binding] & body] - `(test/testing ~string - (with-tempfile ~tmp-binding - (try - (spit ~tmp-sym (slurp (~io/resource (str "indent-test-cases/" ~in)))) - ~@body - (catch Throwable e# - (spit ~tmp-sym e#)) - (finally - (test/is (= (slurp ~tmp-sym) - (slurp (~io/resource (str "indent-test-cases/" ~out)))))))))) - -(defmacro test-indent - {:requires [#'with-transform-test]} - [string & opts] - (let [{:keys [in out pre keys]} (apply hash-map opts) - test-file (str "tmp/test-indent-" (string/replace string #"[^\w-]" "-") ".clj") - vim-expr (if keys - (format "TypeKeys(%s)" (string/replace (pr-str keys) "\\\\" "\\")) - "IndentFile()")] - `(with-transform-test ~string - {:in ~in :out ~out} - [tmp#] - ;; FIXME: Too much file IO - (~io/make-parents ~test-file) - (spit ~test-file "") - (~vim-exec ~test-file (slurp tmp#) ~vim-expr ~@(when pre [:pre pre])) - (spit tmp# (slurp ~test-file))))) - (defn benchmark [n file buf & exprs] (vim-exec file buf (format "Benchmark(%d, %s)" n From 6c156b849f21457edff362d97ac2a01611b86c3d Mon Sep 17 00:00:00 2001 From: Alex Vear Date: Mon, 8 Aug 2022 17:37:11 +0100 Subject: [PATCH 2/3] Fix failing indent tests in CI --- .github/workflows/clojure.yml | 2 +- clj/vim/test-runtime.vim | 15 ++------------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/.github/workflows/clojure.yml b/.github/workflows/clojure.yml index 533318c..0d443d0 100644 --- a/.github/workflows/clojure.yml +++ b/.github/workflows/clojure.yml @@ -29,7 +29,7 @@ jobs: env: cache-name: cache-m2 with: - path: ~/.m2 + path: ~/.m2/repository key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/project.clj') }} restore-keys: | ${{ runner.os }}-build-${{ env.cache-name }}- diff --git a/clj/vim/test-runtime.vim b/clj/vim/test-runtime.vim index 102de0a..d276c50 100644 --- a/clj/vim/test-runtime.vim +++ b/clj/vim/test-runtime.vim @@ -1,10 +1,9 @@ " Authors: Sung Pae -execute 'set rtp=' . expand('%:p:h:h:h') . ',$VIMRUNTIME' +let &rtp = getcwd() . '/..,' . &rtp filetype plugin indent on -syntax on +syntax enable set synmaxcol=0 backspace=2 -setfiletype clojure function! EDN(value) " Changing the quotes may make this valid EDN @@ -20,16 +19,6 @@ function! ClojureSynIDNames() return EDN(names) endfunction -function! TypeKeys(keys) - execute 'normal! ' . a:keys - write -endfunction - -function! IndentFile() - normal! gg=G - write -endfunction - function! Time(n, expr) let start = reltime() let i = 0 From aee8c8dbd7d4dc570e02c837dc04925e9b478643 Mon Sep 17 00:00:00 2001 From: Alex Vear Date: Mon, 8 Aug 2022 20:09:55 +0100 Subject: [PATCH 3/3] Remove global `test-case-dir` var --- clj/test/vim/indent_test.clj | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/clj/test/vim/indent_test.clj b/clj/test/vim/indent_test.clj index bd8ed68..2aa63e9 100644 --- a/clj/test/vim/indent_test.clj +++ b/clj/test/vim/indent_test.clj @@ -5,8 +5,6 @@ [vim.helpers :as h]) (:import [java.io File])) -(def ^:private test-case-dir (io/file (io/resource "indent-test-cases"))) - (defn get-test-cases [^File test-case-dir] (into [] (comp @@ -14,7 +12,7 @@ (map #(.getName ^File %))) (.listFiles test-case-dir))) -(defn run-test-case [test-case] +(defn run-test-case [test-case-dir test-case] (testing (str "Preparation for " test-case) (let [input (io/file test-case-dir test-case "in.clj") expected (io/file test-case-dir test-case "out.clj") @@ -36,8 +34,10 @@ ;; TODO: do this parallisation more intelligently with agents. (deftest test-indent "Runs all indentation tests in parallel" - (doseq [{:keys [test-case expected expected-file actual actual-file]} - (pmap run-test-case (get-test-cases test-case-dir))] - (testing test-case - (is (= expected actual) - (format "(not= \"%s\"\n \"%s\")" expected-file actual-file))))) + (let [test-case-dir (io/file (io/resource "indent-test-cases")) + test-cases (get-test-cases test-case-dir)] + (doseq [{:keys [test-case expected expected-file actual actual-file]} + (pmap (partial run-test-case test-case-dir) test-cases)] + (testing test-case + (is (= expected actual) + (format "(not= \"%s\"\n \"%s\")" expected-file actual-file))))))