diff --git a/deps.edn b/deps.edn index d45a9704..bac671ac 100644 --- a/deps.edn +++ b/deps.edn @@ -104,7 +104,9 @@ listora/again {:mvn/version "1.0.0"} org.clojure/tools.gitlibs {:mvn/version "2.4.172"} environ/environ {:mvn/version "1.2.0"} - table/table {:git/url "https://github.com/cldwalker/table", :sha "55aef3d5fced682942af811bf5d642f79fb87688"}} + table/table {:git/url "https://github.com/cldwalker/table", :sha "55aef3d5fced682942af811bf5d642f79fb87688"} + markdown-clj/markdown-clj {:mvn/version "1.10.8"} + org.clojure/tools.namespace {:git/url "https://github.com/babashka/tools.namespace", :sha "a13b037215e21a2e71aa34b27e1dd52c801a2a7b"}} :classpath-overrides {org.clojure/clojure nil org.clojure/spec.alpha nil}} :clj-nvd diff --git a/doc/projects.md b/doc/projects.md index 0a55b0b1..2fc9b06e 100644 --- a/doc/projects.md +++ b/doc/projects.md @@ -56,6 +56,7 @@ The following libraries and projects are known to work with babashka. - [contajners](#contajners) - [dependency](#dependency) - [specmonstah](#specmonstah) + - [markdown-clj](#markdown-clj) - [Pods](#pods) - [Projects](#projects-1) - [babashka-test-action](#babashka-test-action) @@ -792,6 +793,10 @@ Represent dependency graphs as a directed acylic graph. Write concise, maintainable test fixtures with clojure.spec.alpha. +### [markdown-clj](https://github.com/yogthos/markdown-clj) + +Markdown parser that translates markdown to html. + ## Pods [Babashka pods](https://github.com/babashka/babashka.pods) are programs that can diff --git a/test-resources/lib_tests/bb-tested-libs.edn b/test-resources/lib_tests/bb-tested-libs.edn index cbc276be..5d77fd29 100644 --- a/test-resources/lib_tests/bb-tested-libs.edn +++ b/test-resources/lib_tests/bb-tested-libs.edn @@ -16,4 +16,8 @@ org.clojars.askonomm/ruuter {:git-sha "78659212f95cac827efc816dfbdab8181c25fc3d", :git-url "https://github.com/askonomm/ruuter", :test-namespaces (ruuter.core-test)} ;; clojure.data.json-gen-test ommitted from test-namespaces b/c it hangs on stest/check org.clojure/data.json {:git-sha "9f1c9ccf3fd3e5a39cfb7289d3d456e842ddf442", :git-url "https://github.com/clojure/data.json", :test-namespaces (clojure.data.json-test clojure.data.json-test-suite-test clojure.data.json-compat-0-1-test)} - io.replikativ/hasch {:git-sha "04d9c0bd34d86bad79502d8a6963eb2525a22b15", :git-url "https://github.com/replikativ/hasch", :test-namespaces (hasch.test)}} + io.replikativ/hasch {:git-sha "04d9c0bd34d86bad79502d8a6963eb2525a22b15", :git-url "https://github.com/replikativ/hasch", :test-namespaces (hasch.test)} + ;; BB-TEST-PATCH: Removed markdown.md-file-test b/c tests hardcode path to test + ;; files. Removed markdown.benchmark b/c it depends on criterium which isn't bb compatible + markdown-clj/markdown-clj {:git-sha "ac245d3049afa25a6d41fcb5ba5a268f52c610e4", :git-url "https://github.com/yogthos/markdown-clj", :test-namespaces (markdown.md-test)} + org.clojure/tools.namespace {:git-sha "a13b037215e21a2e71aa34b27e1dd52c801a2a7b", :git-url "https://github.com/babashka/tools.namespace", :test-namespaces (clojure.tools.namespace.test-helpers clojure.tools.namespace.dependency-test clojure.tools.namespace.find-test clojure.tools.namespace.move-test clojure.tools.namespace.parse-test clojure.tools.namespace.dir-test), :branch "babashka"}} diff --git a/test-resources/lib_tests/clojure/tools/namespace/dependency_test.clj b/test-resources/lib_tests/clojure/tools/namespace/dependency_test.clj new file mode 100644 index 00000000..5aedb27f --- /dev/null +++ b/test-resources/lib_tests/clojure/tools/namespace/dependency_test.clj @@ -0,0 +1,315 @@ +(ns clojure.tools.namespace.dependency-test + (:use clojure.test + clojure.tools.namespace.dependency)) + +;; building a graph like: +;; +;; :a +;; / | +;; :b | +;; \ | +;; :c +;; | +;; :d +;; +(def g1 (-> (graph) + (depend :b :a) ; "B depends on A" + (depend :c :b) ; "C depends on B" + (depend :c :a) ; "C depends on A" + (depend :d :c))) ; "D depends on C" + +;; 'one 'five +;; | | +;; 'two | +;; / \ | +;; / \ | +;; / \ / +;; 'three 'four +;; | / +;; 'six / +;; | / +;; | / +;; | / +;; 'seven +;; +(def g2 (-> (graph) + (depend 'two 'one) + (depend 'three 'two) + (depend 'four 'two) + (depend 'four 'five) + (depend 'six 'three) + (depend 'seven 'six) + (depend 'seven 'four))) + +;; :level0 +;; / | | \ +;; ----- | | ----- +;; / | | \ +;; :level1a :level1b :level1c :level1d +;; \ | | / +;; ----- | | ----- +;; \ | | / +;; :level2 +;; / | | \ +;; ----- | | ----- +;; / | | \ +;; :level3a :level3b :level3c :level3d +;; \ | | / +;; ----- | | ----- +;; \ | | / +;; :level4 +;; +;; ... and so on in a repeating pattern like that, up to :level26 + +(def g3 (-> (graph) + (depend :level1a :level0) + (depend :level1b :level0) + (depend :level1c :level0) + (depend :level1d :level0) + (depend :level2 :level1a) + (depend :level2 :level1b) + (depend :level2 :level1c) + (depend :level2 :level1d) + + (depend :level3a :level2) + (depend :level3b :level2) + (depend :level3c :level2) + (depend :level3d :level2) + (depend :level4 :level3a) + (depend :level4 :level3b) + (depend :level4 :level3c) + (depend :level4 :level3d) + + (depend :level5a :level4) + (depend :level5b :level4) + (depend :level5c :level4) + (depend :level5d :level4) + (depend :level6 :level5a) + (depend :level6 :level5b) + (depend :level6 :level5c) + (depend :level6 :level5d) + + (depend :level7a :level6) + (depend :level7b :level6) + (depend :level7c :level6) + (depend :level7d :level6) + (depend :level8 :level7a) + (depend :level8 :level7b) + (depend :level8 :level7c) + (depend :level8 :level7d) + + (depend :level9a :level8) + (depend :level9b :level8) + (depend :level9c :level8) + (depend :level9d :level8) + (depend :level10 :level9a) + (depend :level10 :level9b) + (depend :level10 :level9c) + (depend :level10 :level9d) + + (depend :level11a :level10) + (depend :level11b :level10) + (depend :level11c :level10) + (depend :level11d :level10) + (depend :level12 :level11a) + (depend :level12 :level11b) + (depend :level12 :level11c) + (depend :level12 :level11d) + + (depend :level13a :level12) + (depend :level13b :level12) + (depend :level13c :level12) + (depend :level13d :level12) + (depend :level14 :level13a) + (depend :level14 :level13b) + (depend :level14 :level13c) + (depend :level14 :level13d) + + (depend :level15a :level14) + (depend :level15b :level14) + (depend :level15c :level14) + (depend :level15d :level14) + (depend :level16 :level15a) + (depend :level16 :level15b) + (depend :level16 :level15c) + (depend :level16 :level15d) + + (depend :level17a :level16) + (depend :level17b :level16) + (depend :level17c :level16) + (depend :level17d :level16) + (depend :level18 :level17a) + (depend :level18 :level17b) + (depend :level18 :level17c) + (depend :level18 :level17d) + + (depend :level19a :level18) + (depend :level19b :level18) + (depend :level19c :level18) + (depend :level19d :level18) + (depend :level20 :level19a) + (depend :level20 :level19b) + (depend :level20 :level19c) + (depend :level20 :level19d) + + (depend :level21a :level20) + (depend :level21b :level20) + (depend :level21c :level20) + (depend :level21d :level20) + (depend :level22 :level21a) + (depend :level22 :level21b) + (depend :level22 :level21c) + (depend :level22 :level21d) + + (depend :level23a :level22) + (depend :level23b :level22) + (depend :level23c :level22) + (depend :level23d :level22) + (depend :level24 :level23a) + (depend :level24 :level23b) + (depend :level24 :level23c) + (depend :level24 :level23d) + + (depend :level25a :level24) + (depend :level25b :level24) + (depend :level25c :level24) + (depend :level25d :level24) + (depend :level26 :level25a) + (depend :level26 :level25b) + (depend :level26 :level25c) + (depend :level26 :level25d))) + +(deftest t-transitive-dependencies + (is (= #{:a :c :b} + (transitive-dependencies g1 :d))) + (is (= '#{two four six one five three} + (transitive-dependencies g2 'seven)))) + +(deftest t-transitive-dependencies-deep + (is (= #{:level0 + :level1a :level1b :level1c :level1d + :level2 + :level3a :level3b :level3c :level3d + :level4 + :level5a :level5b :level5c :level5d + :level6 + :level7a :level7b :level7c :level7d + :level8 + :level9a :level9b :level9c :level9d + :level10 + :level11a :level11b :level11c :level11d + :level12 + :level13a :level13b :level13c :level13d + :level14 + :level15a :level15b :level15c :level15d + :level16 + :level17a :level17b :level17c :level17d + :level18 + :level19a :level19b :level19c :level19d + :level20 + :level21a :level21b :level21c :level21d + :level22 + :level23a :level23b :level23c :level23d} + (transitive-dependencies g3 :level24))) + (is (= #{:level0 + :level1a :level1b :level1c :level1d + :level2 + :level3a :level3b :level3c :level3d + :level4 + :level5a :level5b :level5c :level5d + :level6 + :level7a :level7b :level7c :level7d + :level8 + :level9a :level9b :level9c :level9d + :level10 + :level11a :level11b :level11c :level11d + :level12 + :level13a :level13b :level13c :level13d + :level14 + :level15a :level15b :level15c :level15d + :level16 + :level17a :level17b :level17c :level17d + :level18 + :level19a :level19b :level19c :level19d + :level20 + :level21a :level21b :level21c :level21d + :level22 + :level23a :level23b :level23c :level23d + :level24 + :level25a :level25b :level25c :level25d} + (transitive-dependencies g3 :level26)))) + + +(deftest t-transitive-dependents + (is (= '#{four seven} + (transitive-dependents g2 'five))) + (is (= '#{four seven six three} + (transitive-dependents g2 'two)))) + +(defn- before? + "True if x comes before y in an ordered collection." + [coll x y] + (loop [[item & more] (seq coll)] + (cond (nil? item) true ; end of the seq + (= x item) true ; x comes first + (= y item) false + :else (recur more)))) + +(deftest t-before + (is (true? (before? [:a :b :c :d] :a :b))) + (is (true? (before? [:a :b :c :d] :b :c))) + (is (false? (before? [:a :b :c :d] :d :c))) + (is (false? (before? [:a :b :c :d] :c :a)))) + +(deftest t-topo-comparator-1 + (let [sorted (sort (topo-comparator g1) [:d :a :b :foo])] + (are [x y] (before? sorted x y) + :a :b + :a :d + :a :foo + :b :d + :b :foo + :d :foo))) + +(deftest t-topo-comparator-2 + (let [sorted (sort (topo-comparator g2) '[three seven nine eight five])] + (are [x y] (before? sorted x y) + 'three 'seven + 'three 'eight + 'three 'nine + 'five 'eight + 'five 'nine + 'seven 'eight + 'seven 'nine))) + +(deftest t-topo-sort + (let [sorted (topo-sort g2)] + (are [x y] (before? sorted x y) + 'one 'two + 'one 'three + 'one 'four + 'one 'six + 'one 'seven + 'two 'three + 'two 'four + 'two 'six + 'two 'seven + 'three 'six + 'three 'seven + 'four 'seven + 'five 'four + 'five 'seven + 'six 'seven))) + +(deftest t-no-cycles + (is (thrown? Exception + (-> (graph) + (depend :a :b) + (depend :b :c) + (depend :c :a))))) + +(deftest t-no-self-cycles + (is (thrown? Exception + (-> (graph) + (depend :a :b) + (depend :a :a))))) diff --git a/test-resources/lib_tests/clojure/tools/namespace/dir_test.clj b/test-resources/lib_tests/clojure/tools/namespace/dir_test.clj new file mode 100644 index 00000000..60e56427 --- /dev/null +++ b/test-resources/lib_tests/clojure/tools/namespace/dir_test.clj @@ -0,0 +1,20 @@ +(ns clojure.tools.namespace.dir-test + (:require [clojure.test :refer [deftest is]] + [clojure.tools.namespace.test-helpers :as help] + [clojure.tools.namespace.dir :as dir]) + (:import + (java.io File))) + +;; Only run this test on Java 1.7+, where java.nio.file.Files is available. +(when (try (Class/forName "java.nio.file.Files") + (catch ClassNotFoundException _ false)) + (deftest t-scan-by-canonical-path + (let [dir (help/create-temp-dir "t-scan-by-canonical-path") + main-clj (help/create-source dir 'example.main :clj '[example.one]) + one-cljc (help/create-source dir 'example.one :clj) + other-dir (help/create-temp-dir "t-scan-by-canonical-path-other") + link (File. other-dir "link")] + (java.nio.file.Files/createSymbolicLink (.toPath link) (.toPath dir) + (make-array java.nio.file.attribute.FileAttribute 0)) + (is (= (::dir/files (dir/scan-dirs {} [dir])) + (::dir/files (dir/scan-dirs {} [link]))))))) diff --git a/test-resources/lib_tests/clojure/tools/namespace/find_test.clj b/test-resources/lib_tests/clojure/tools/namespace/find_test.clj new file mode 100644 index 00000000..d081e84a --- /dev/null +++ b/test-resources/lib_tests/clojure/tools/namespace/find_test.clj @@ -0,0 +1,29 @@ +(ns clojure.tools.namespace.find-test + (:require [clojure.test :refer [deftest is]] + [clojure.tools.namespace.test-helpers :as help] + [clojure.tools.namespace.find :as find]) + (:import (java.io File))) + +(deftest t-find-clj-and-cljc-files + "main.clj depends on one.cljc which depends on two.clj. + two.cljs also exists but should not be returned" + (let [dir (help/create-temp-dir "t-find-clj-and-cljc-files") + main-clj (help/create-source dir 'example.main :clj '[example.one]) + one-cljc (help/create-source dir 'example.one :cljc '[example.two]) + two-clj (help/create-source dir 'example.two :clj) + two-cljs (help/create-source dir 'example.two :cljs)] + (is (help/same-files? + [main-clj one-cljc two-clj] + (find/find-sources-in-dir dir))))) + +(deftest t-find-cljs-and-cljc-files + "main.cljs depends on one.cljc which depends on two.cljs. + two.clj also exists but should not be returned" + (let [dir (help/create-temp-dir "t-find-cljs-and-cljc-files") + main-cljs (help/create-source dir 'example.main :cljs '[example.one]) + one-cljc (help/create-source dir 'example.one :cljc '[example.two]) + two-clj (help/create-source dir 'example.two :clj) + two-cljs (help/create-source dir 'example.two :cljs)] + (is (help/same-files? + [main-cljs one-cljc two-cljs] + (find/find-sources-in-dir dir find/cljs))))) diff --git a/test-resources/lib_tests/clojure/tools/namespace/move_test.clj b/test-resources/lib_tests/clojure/tools/namespace/move_test.clj new file mode 100644 index 00000000..47de7ac4 --- /dev/null +++ b/test-resources/lib_tests/clojure/tools/namespace/move_test.clj @@ -0,0 +1,52 @@ +(ns clojure.tools.namespace.move-test + (:require [clojure.java.io :as io] + [clojure.test :refer [deftest is]] + [clojure.tools.namespace.move :refer [move-ns]] + [clojure.tools.namespace.test-helpers :as help]) + (:import (java.io File))) + +(defn- create-file-one [dir] + (help/create-source dir 'example.one :clj + '[example.two example.three] + '[(defn foo [] + (example.a.four/foo))])) + +(defn- create-file-two [dir] + (help/create-source dir 'example.two :clj + '[example.three example.a.four])) + +(defn- create-file-three [dir] + (help/create-source dir 'example.three :clj + '[example.five])) + +(defn- create-file-four [dir] + (help/create-source dir 'example.a.four :clj)) + +(deftest t-move-ns + (let [temp-dir (help/create-temp-dir "tools-namespace-t-move-ns") + src-dir (io/file temp-dir "src") + example-dir (io/file temp-dir "src" "example") + file-one (create-file-one src-dir) + file-two (create-file-two src-dir) + file-three (create-file-three src-dir) + old-file-four (create-file-four src-dir) + new-file-four (io/file example-dir "b" "four.clj")] + + (let [file-three-last-modified (.lastModified file-three)] + + (Thread/sleep 1500) ;; ensure file timestamps are different + + (move-ns 'example.a.four 'example.b.four src-dir [src-dir]) + + (is (.exists new-file-four) + "new file should exist") + (is (not (.exists old-file-four)) + "old file should not exist") + (is (not (.exists (.getParentFile old-file-four))) + "old empty directory should not exist") + (is (= file-three-last-modified (.lastModified file-three)) + "unaffected file should not have been modified") + (is (not-any? #(.contains (slurp %) "example.a.four") + [file-one file-two file-three new-file-four])) + (is (every? #(.contains (slurp %) "example.b.four") + [file-one file-two new-file-four]))))) diff --git a/test-resources/lib_tests/clojure/tools/namespace/parse_test.clj b/test-resources/lib_tests/clojure/tools/namespace/parse_test.clj new file mode 100644 index 00000000..b6ac6aaa --- /dev/null +++ b/test-resources/lib_tests/clojure/tools/namespace/parse_test.clj @@ -0,0 +1,210 @@ +(ns clojure.tools.namespace.parse-test + (:use [clojure.test :only (deftest is)] + [clojure.tools.namespace.parse :only (deps-from-ns-decl + read-ns-decl)])) + +(def ns-decl-prefix-list + '(ns com.example.one + (:require (com.example two + [three :as three] + [four :refer (a b)]) + (com.example.sub [five :as five] + six)) + (:use (com.example seven + [eight :as eight] + (nine :only (c d)) + [ten])))) + +;; Some people like to write prefix lists as vectors, not lists. The +;; use/require functions accept this form. +(def ns-decl-prefix-list-as-vector + '(ns com.example.one + (:require [com.example + two + [three :as three] + [four :refer (a b)]] + [com.example.sub + [five :as five] + six]) + (:use [com.example + seven + [eight :as eight] + (nine :only (c d)) + [ten]]))) + +(def ns-decl-prefix-list-clauses-as-vectors + "Sometimes people even write the clauses inside ns as vectors, which + clojure.core/ns allows. See TNS-21." + '(ns com.example.one + [:require [com.example + two + [three :as three] + [four :refer (a b)]] + [com.example.sub + [five :as five] + six]] + [:use [com.example + seven + [eight :as eight] + (nine :only (c d)) + [ten]]])) + +(def deps-from-prefix-list + '#{com.example.two + com.example.three + com.example.four + com.example.sub.five + com.example.sub.six + com.example.seven + com.example.eight + com.example.nine + com.example.ten}) + +(deftest t-prefix-list + (is (= deps-from-prefix-list + (deps-from-ns-decl ns-decl-prefix-list)))) + +(deftest t-prefix-list-as-vector + (is (= deps-from-prefix-list + (deps-from-ns-decl ns-decl-prefix-list-as-vector)))) + +(deftest t-prefix-list-clauses-as-vectors + (is (= deps-from-prefix-list + (deps-from-ns-decl ns-decl-prefix-list-clauses-as-vectors)))) + +(deftest t-no-deps-returns-empty-set + (is (= #{} (deps-from-ns-decl '(ns com.example.one))))) + +(def multiple-ns-decls + '((ns one) + (ns two (:require one)) + (ns three (:require [one :as o] [two :as t])))) + +(def multiple-ns-decls-string +" (println \"Code before first ns\") + (ns one) + (println \"Some code\") + (defn hello-world [] \"Hello, World!\") + (ns two (:require one)) + (println \"Some more code\") + (ns three (:require [one :as o] [two :as t]))") + +(deftest t-read-multiple-ns-decls + (with-open [rdr (clojure.lang.LineNumberingPushbackReader. + (java.io.PushbackReader. + (java.io.StringReader. multiple-ns-decls-string)))] + (is (= multiple-ns-decls + (take-while identity (repeatedly #(read-ns-decl rdr))))))) + +(def ns-docstring-example + "The example ns declaration used in the docstring of clojure.core/ns" + '(ns foo.bar + (:refer-clojure :exclude [ancestors printf]) + (:require (clojure.contrib sql combinatorics)) + (:use (my.lib this that)) + (:import (java.util Date Timer Random) + (java.sql Connection Statement)))) + +(def deps-from-ns-docstring-example + '#{clojure.contrib.sql + clojure.contrib.combinatorics + my.lib.this + my.lib.that}) + +(deftest t-ns-docstring-example + (is (= deps-from-ns-docstring-example + (deps-from-ns-decl ns-docstring-example)))) + +(def require-docstring-example + "The example ns declaration used in the docstring of + clojure.core/require" + '(ns (:require (clojure zip [set :as s])))) + +(def deps-from-require-docstring-example + '#{clojure.zip + clojure.set}) + +(deftest t-require-docstring-example + (is (= deps-from-require-docstring-example + (deps-from-ns-decl require-docstring-example)))) + +(def multiple-clauses + "Example showing more than one :require or :use clause in one ns + declaration, which clojure.core/ns allows." + '(ns foo.bar + (:require com.example.one) + (:import java.io.File) + (:require (com.example two three)) + (:use (com.example [four :only [x]])) + (:use (com.example (five :only [x]))))) + +(def deps-from-multiple-clauses + '#{com.example.one + com.example.two + com.example.three + com.example.four + com.example.five}) + +(deftest t-deps-from-multiple-clauses + (is (= deps-from-multiple-clauses + (deps-from-ns-decl multiple-clauses)))) + +(def clauses-without-keywords + "Example of require/use clauses with symbols instead of keywords, + which clojure.core/ns allows." + '(ns foo.bar + (require com.example.one) + (import java.io.File) + (use (com.example (prefixes (two :only [x]) + three))))) + +(def deps-from-clauses-without-keywords + '#{com.example.one + com.example.prefixes.two + com.example.prefixes.three}) + +(deftest t-clauses-without-keywords + (is (= deps-from-clauses-without-keywords + (deps-from-ns-decl clauses-without-keywords)))) + +(def reader-conditionals-string + "(ns com.examples.one + (:require #?(:clj clojure.string + :cljs goog.string)))") + +(deftest t-reader-conditionals + ;; TODO: the predicate wasn't added to bb yet, will come in version after 0.6.7 + (when true #_(resolve 'clojure.core/reader-conditional?) + (let [actual (-> reader-conditionals-string + java.io.StringReader. + java.io.PushbackReader. + clojure.lang.LineNumberingPushbackReader. + read-ns-decl + deps-from-ns-decl)] + (is (= #{'clojure.string} actual))))) + +(def ns-with-npm-dependency + "(ns com.examples.one + (:require [\"foobar\"] [baz]))") + +(deftest npm-dependency + (let [actual (-> ns-with-npm-dependency + java.io.StringReader. + java.io.PushbackReader. + clojure.lang.LineNumberingPushbackReader. + read-ns-decl + deps-from-ns-decl)] + (is (= #{'baz} actual)))) + +(def ns-with-require-macros + "(ns com.examples.one + (:require-macros [foo :refer [bar]]))") + +(deftest require-macros + (let [actual (-> ns-with-require-macros + java.io.StringReader. + java.io.PushbackReader. + clojure.lang.LineNumberingPushbackReader. + read-ns-decl + deps-from-ns-decl)] + (is (= #{'foo} actual)))) diff --git a/test-resources/lib_tests/clojure/tools/namespace/test_helpers.clj b/test-resources/lib_tests/clojure/tools/namespace/test_helpers.clj new file mode 100644 index 00000000..62679e69 --- /dev/null +++ b/test-resources/lib_tests/clojure/tools/namespace/test_helpers.clj @@ -0,0 +1,82 @@ +(ns clojure.tools.namespace.test-helpers + "Utilities to help with testing files and namespaces." + (:require [clojure.java.io :as io] + [clojure.string :as string]) + (:import (java.io Closeable File Writer PrintWriter))) + +(defn create-temp-dir + "Creates and returns a new temporary directory java.io.File." + [name] + (let [temp-file (File/createTempFile name nil)] + (.delete temp-file) + (.mkdirs temp-file) + (println "Temporary directory" (.getAbsolutePath temp-file)) + temp-file)) + +(defn- write-contents + "Writes contents into writer. Strings are written as-is via println, + other types written as by prn." + [^Writer writer contents] + {:pre [(sequential? contents)]} + (binding [*out* (PrintWriter. writer)] + (doseq [content contents] + (if (string? content) + (println content) + (prn content)) + (newline)))) + +(defn create-file + "Creates a file from a vector of path elements. Writes contents into + the file. Elements of contents may be data, written via prn, or + strings, written directly." + [path contents] + {:pre [(vector? path)]} + (let [^File file (apply io/file path)] + (when-let [parent (.getParentFile file)] + (.mkdirs parent)) + (with-open [wtr (io/writer file)] + (write-contents wtr contents)) + file)) + +(defn- sym->path + "Converts a symbol name into a vector of path parts, not including + file extension." + [symbol] + {:pre [(symbol? symbol)] + :post [(vector? %)]} + (-> (name symbol) + (string/replace \- \_) + (string/split #"\."))) + +(defn- source-path + "Returns a vector of path components for namespace named sym, + with given file extension (keyword)." + [sym extension] + (let [path (sym->path sym) + basename (peek path) + filename (str basename \. (name extension))] + (conj (pop path) filename))) + +(defn create-source + "Creates a file at the correct path under base-dir for a namespace + named sym, with file extension (keyword), containing a ns + declaration which :require's the dependencies (symbols). Optional + contents written after the ns declaration as by write-contents." + ([base-dir sym extension] + (create-source base-dir sym extension nil nil)) + ([base-dir sym extension dependencies] + (create-source base-dir sym extension dependencies nil)) + ([base-dir sym extension dependencies contents] + (let [full-path (into [base-dir] (source-path sym extension)) + ns-decl (if (seq dependencies) + (list 'ns sym (list* :require dependencies)) + (list 'ns sym))] + (create-file full-path (into [ns-decl] contents))))) + +(defn same-files? + "True if files-a and files-b contain the same canonical File's, + regardless of order." + [files-a files-b] + (= (sort (map #(.getCanonicalPath ^File %) files-a)) + (sort (map #(.getCanonicalPath ^File %) files-b)))) + diff --git a/test-resources/lib_tests/markdown/md_test.cljc b/test-resources/lib_tests/markdown/md_test.cljc new file mode 100644 index 00000000..b2279d00 --- /dev/null +++ b/test-resources/lib_tests/markdown/md_test.cljc @@ -0,0 +1,450 @@ +(ns markdown.md-test + (:require #?(:cljs [goog.string]) + [clojure.test :refer [deftest is]] + [markdown.core :as markdown] + [markdown.tables :as tables])) + +(def entry-function + #?(:clj markdown/md-to-html-string + :cljs markdown/md->html)) + +(deftest heading1 + (is (= "

Ticket #123

" (entry-function "# Ticket #123"))) + (is (= "

Foo

" (entry-function " # Foo"))) + (is (= "

foo

" (entry-function "#foo"))) + (is (= "

foo

" (entry-function "foo\n==="))) + (is (= "

foo

" (entry-function "#foo#"))) + (is (= "

foo

" (entry-function "#foo#\n"))) + (is (= "

some header with_an_underscore

" + (entry-function "# some header `with_an_underscore`"))) + (is (= "

heading1

" + (entry-function "* one\n\nheading1\n========\n")))) + +(deftest heading2 + (is (= "

foo

" (entry-function "##foo"))) + (is (= "

foo

" (entry-function "foo\n---"))) + (is (= "

foo

" (entry-function "##foo##"))) + (is (= "

foo

" (entry-function "##foo##\n")))) + +(deftest heading-with-complex-anchor + (is (= + "

foo bar BAz

some text

" + (entry-function "###foo bar BAz\nsome text" :heading-anchors true))) + (is (= + "

foo bar BAz

some text

" + (entry-function "###foo bar BAz##\nsome text" :heading-anchors true)))) + +(deftest br + (is (= "

foo

" (entry-function "foo ")))) + +(deftest hr + (is (= "
" (entry-function "***"))) + (is (= "
" (entry-function " * * * "))) + (is (= "
" (entry-function " *****"))) + (is (= "
" (entry-function "- - - ")))) + +(deftest em + (is (= "

foo

" (entry-function "*foo*")))) + +(deftest italics + (is (= "

foo

" (entry-function "_foo_")))) + +(deftest strong + (is (= "

foo

" (entry-function "**foo**")))) + +(deftest bold-italics + (is (= "

foo

" (entry-function "***foo***")))) + +(deftest bold + (is (= "

foo

" (entry-function "__foo__")))) + +(deftest strong-inside-em + (is (= "

foobarbaz

" (entry-function "*foo**bar**baz*")))) + +(deftest bold-inside-a-list + (is (= "
  1. chickens.

See more: Cluck Cluck

" + (entry-function "1. chickens. \n\n **See more: [Cluck Cluck](http://cluck.cluck.com)** \n\n")))) + +(deftest em-inside-strong + (is (= "

foobarbaz

" (entry-function "**foo*bar*baz**")))) + +(deftest paragraph + (is (= "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore

" + (entry-function "\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore")))) + +(deftest paragraph-multiline + (is (= "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore

" + (entry-function "\nLorem ipsum dolor\nsit amet, consectetur adipisicing elit,\nsed do eiusmod tempor incididunt ut labore")))) + +(deftest paragraph-before-codeblock + (is (= "

foo

bar\n

baz

" + (entry-function "foo\n```\nbar\n```\nbaz"))) + (is (= "
foo  \nbar
" (entry-function "```\nfoo \nbar```"))) + (is (= "

" (entry-function "```\n```"))) + (is (= "

" (entry-function "```go\n```"))) + (is (= "
<html>\n</html>\n
" (entry-function "```\n\n\n``` ")))) + +(deftest paragraph-after-codeblock + (is (= "
foo\n

bar baz

" + (entry-function "```\nfoo\n```\nbar\nbaz")))) + +(deftest mulitple-paragraphs + (is (= "

foo bar baz

foo bar baz

" + (entry-function "\nfoo bar baz\n\n\nfoo bar baz")))) + +(deftest ul + (is (= "" + (entry-function "* foo\n* bar\n* baz"))) + (is (= "" + (entry-function "- foo\n- bar\n- baz"))) + (is (= "" + (entry-function "+ foo\n+ bar\n+ baz")))) + +(deftest list-in-a-codeblock + (is + (= "
list:\n- 1\n- 2\n
" + (entry-function "```yaml\nlist:\n- 1\n- 2\n```")))) + +(deftest ul-followed-by-paragraph + (is (= "

paragraph next line

" + (entry-function "* foo\n* bar\n* baz\n\nparagraph\nnext line")))) + +(deftest ul-with-codeblock + (is (= "" + (entry-function + "\n* foo\n* bar\n ```\n (defn foo []\n bar)\n ```\n* baz\n* more text\n"))) + (is (= "" + (entry-function + "\n* foo\n* bar\n ```\n (defn foo []\n bar)\n ```\n text\n* baz\n* more text\n")))) + +(deftest ul-followed-by-multiline-paragraph + (is (= "

paragraph

" + (entry-function "* foo\n* bar\n* baz\n\nparagraph")))) + +(deftest ul-nested + (is (= "" + (entry-function "* first item\n * first sub-item\n * second sub-item\n * third sub-item\n* second item\n * first sub-item\n * second sub-item\n* third item"))) + (is (= "" + (entry-function "* first item\n - first sub-item\n - second sub-item\n - third sub-item\n* second item\n + first sub-item\n + second sub-item\n* third item"))) + (is (= "" (entry-function " * abc\n\n+ def")))) + +(deftest ol + (is (= "
  1. Foo
  2. Bar
  3. Baz
" + (entry-function "1. Foo\n2. Bar\n3. Baz")))) + +(deftest ul-in-ol + (is (= "
  1. Bar
    1. Subbar
      • foo
      • bar
      • baz
  2. Baz
" + (entry-function "1. Bar\n 2. Subbar\n * foo\n * bar\n * baz\n3. Baz")))) + +(deftest ol-in-ul + (is (= "" + (entry-function "* Foo\n 1. Bar\n 1. Subbar\n* Baz"))) + (is (= "" + (entry-function "* Foo\n 1. Bar")))) + +(deftest multilist + (is (= + "" + (entry-function + "* foo +* bar + + * baz + 1. foo + 2. bar + + * fuzz + + * blah + * blue +* brass")))) + +(deftest code + (is (= "

foo bar baz x = y + z; foo

" + (entry-function "foo bar baz `x = y + z;` foo"))) + (is (= "

bar foo --- -- bar foo

" + (entry-function "bar `foo --- -- bar` foo"))) + (is (= "

<?xml version='1.0' encoding='UTF-8'?><channel></channel>

" + (entry-function "``"))) + (is (= "

foo bar baz (fn [x & xs] (str "x:" x)) foo

" + (entry-function "foo bar baz `(fn [x & xs] (str \"x:\" x))` foo"))) + (is (= "
```\nfoo\n```
" + (entry-function " ```\n foo\n ```")))) + +(deftest multiline-code + (is (= "
x = 5\ny = 6\nz = x + y
" + (entry-function " x = 5\n y = 6\n z = x + y"))) + (is (= "
x = 5\ny = 6\nz = x + y\n(fn [x & xs] (str "x"))
" + (entry-function " x = 5\n y = 6\n z = x + y\n (fn [x & xs] (str \"x\"))")))) + +(deftest codeblock + (is (= "
(defn- write^ [writer text]\n  (doseq [c text]\n    (.write writer (int c))))\n
" + (entry-function "```\n(defn- write^ [writer text]\n (doseq [c text]\n (.write writer (int c))))\n```"))) + (is (= "
(fn [x & xs]\n  (str "x"))\n
" + (entry-function "```\n(fn [x & xs]\n (str \"x\"))\n```"))) + (is (= "
(fn [x & xs]\n  (str "x"))\n
" + (entry-function "```\n(fn [x & xs]\n (str \"x\"))\n```"))) + (is (= "
(fn [x & xs]\n  (str "x"))\n
" + (entry-function "```clojure\n(fn [x & xs]\n (str \"x\"))\n```"))) + (is (= "
------------\n============\n    ------------\n    ============\n
" + (entry-function + " +```nohighlight +------------ +============ + ------------ + ============ +``` +")))) + +(deftest indented-codeblock + (is (= "
foo
" + (entry-function " foo"))) + (is (= "
foo

bar

" + (entry-function " foo\n\nbar"))) + (is (= "
foo
bar" + (entry-function " foo\nbar"))) + (is (= "

baz foo

bar

" + (entry-function "baz\n foo\n\nbar"))) + (is (= "

Element #1

" + (entry-function "
\n
\n

Element #1

\n
\n
")))) + +(deftest strikethrough + (is (= "

foo

" + (entry-function "~~foo~~")))) + +(deftest superscript + (is (= "

foobar baz

" + (entry-function "foo^bar baz")))) + +(deftest link + (is (= "

underscoresarefine

" + (entry-function "underscores_are_fine"))) + (is (= "

github

" + (entry-function "[github](http://github.com)"))) + (is (= "

github

" + (entry-function "[github](http://github.com/~)"))) + (is (= "

github

" + (entry-function "[github](http://github.com/^)"))) + (is (= "

github

" + (entry-function "[github](http://github.com/*)"))) + (is (= "" + (entry-function "* [github](http://github.com/*)"))) + (is (= "

a link

" + (entry-function "* hi\n\n[a link](https://see-here)"))) + (is (= "

>!

" + (entry-function "[>!](https://clojure.github.io/core.async/#clojure.core.async/>!)"))) + (is (= "

" + (entry-function "[github

" + (entry-function "[*github*](http://github.com)"))) + (is (= "

github

" + (entry-function "[_github_](http://github.com)"))) + (is (= "

github

" + (entry-function "[__github__](http://github.com)"))) + (is (= "

github

" + (entry-function "[**github**](http://github.com)"))) + (is (= "

github

" + (entry-function "[~~github~~](http://github.com)")))) + +(deftest img + (is (= "

\"Alt

" + (entry-function "![Alt text](/path/to/img.jpg)"))) + (is (= "

\"Alt

" + (entry-function "![Alt text](/path/to/_img_.jpg \"Optional Title\")")))) + +(deftest img-link + (is (= "

\"Continuous

" + (entry-function "[![Continuous Integration status](https://secure.travis-ci.org/yogthos/markdown-clj.png)](http://travis-ci.org/yogthos/markdown-clj)"))) + (is (= "

\"\"

" + (entry-function "![](https://secure.travis-ci.org/yogthos/markdown-clj.png)")))) + +(deftest bad-link + (is (= "

[github](http://github.comfooo

" + (entry-function "[github](http://github.comfooo"))) + (is (= "

[github] no way (http://github.com)

" + (entry-function "[github] no way (http://github.com)")))) + +(deftest bad-link-title + (is (= "

[github(http://github.comfooo)

" + (entry-function "[github(http://github.comfooo)")))) + +(deftest blockquote + (is (= "

Foo bar baz

" + (entry-function ">Foo bar baz")))) + +(deftest blockquote-footer + (is (= "

Foo bar baz

" + (entry-function "> Foo bar baz\n>- Leo Tolstoy")))) + +(deftest blockquote-empty-footer + (is (= "

Foo bar baz

" + (entry-function "> Foo bar baz\n>-")))) + +(deftest blockquote-multiline-without-leading-angle-bracket + (is (= "

Foo bar baz

" + (entry-function "> Foo bar\nbaz")))) + +(deftest blockquote-multiple-paragraphs + (is (= "

Foo bar

baz

" + (entry-function "> Foo bar\n>\n> baz")))) + +(deftest blockquote-bullets + (is (= "

list:

end.

" + (entry-function "> list:\n>* foo\n>* bar\n\nend."))) + (is (= "

" + (entry-function ">* foo\n>* bar\n>* baz")))) + +(deftest blockquote-headings + (is (= "

Foo

bar baz

" + (entry-function "> ## Foo\n>bar baz"))) + (is (= "

Foo

bar

baz

" + (entry-function "> Foo\n>## bar\n> baz")))) + +(deftest escaped-characters + (is + (= "

^*‘_{}[]footestbar{x}[y]

" + (entry-function "\\^\\*\\`\\_\\{\\}\\[\\]*foo*`test`_bar_{x}[y]")))) + +(deftest paragraph-after-list + (is (= "
  1. a
  2. b

test bold and italic

" + (entry-function "1. a\n2. b\n\ntest **bold** and *italic*")))) + +(deftest paragraph-close-before-list + (is (= "

in paragraph

" + (entry-function "in paragraph\n- list")))) + +(deftest autourl + (is (= "

http://example.com/

" + (entry-function ""))) + + (is (= "

Some content with a http://www.google.com/abc__123__efg link it in

" + (entry-function "Some content with a link it in"))) + + (is (= "

http://foo https://bar/baz foo bar

" + (entry-function " foo bar"))) + + #?(:bb nil :org.babashka/nbb nil + :default + (is (= "

abc@google.com

" + (#?(:clj org.apache.commons.lang.StringEscapeUtils/unescapeHtml + :cljs goog.string/unescapeEntities) + (entry-function ""))))) + + #?(:bb nil :org.babashka/nbb nil + :default + (is (= "

abc_def_ghi@google.com

" + (#?(:clj org.apache.commons.lang.StringEscapeUtils/unescapeHtml + :cljs goog.string/unescapeEntities) + (entry-function "")))))) + +(deftest not-a-list + (is (= "

The fish was 192.8 lbs and was amazing to see.

" + (entry-function "The fish was\n192.8 lbs and was amazing to see.")))) + +(deftest dont-encode-chars-in-hrefs + (is (= "

example_link with tilde ~ and carat ^ and splat *

" + (entry-function "[example_link with tilde ~ and carat ^ and splat *](http://www.google.com/example_link_foo~_^*)")))) + +(deftest complex-link-with-terminal-encoding-inside-header + (is (= "

With a link the contents of the_link

" + (entry-function "##With a link [the contents of the_link](http://a.com/under_score_in_the_link/)")))) + +(deftest two-links-tests-link-processing + (is (= "

When you have a pair of links link1 and you want both Wow

" + (entry-function "## When you have a pair of links [link1](http://123.com/1) and you want both [Wow](That%27s%20crazy)")))) + +(deftest link-then-image-processing + (is (= "

You can have a link followed by an image \"\"

" + (entry-function "You can have a [link](github.com) followed by an image ![](img.png)")))) + +(deftest image-then-link-processing + (is (= "

You can have an image \"\" followed by a link

" + (entry-function "You can have an image ![](img.png) followed by a [link](github.com)")))) + +(deftest link-with-optional-title + (is (= "

Cryogens site

" + (entry-function "[Cryogens site](https://github.com/cryogen-project/cryogen \"Cryogen Github\")")))) + +(deftest parse-table-row + (is (= (tables/parse-table-row "| table cell contents |") [{:text "table cell contents"}])) + (is (= (tables/parse-table-row "| contents 1 | contents 2 | contents 3 | contents 4 |") + [{:text "contents 1"} {:text "contents 2"} {:text "contents 3"} {:text "contents 4"}]))) + +(deftest table-row->str + (is (= (tables/table-row->str + [{:text "contents 1"} {:text "contents 2"} {:text "contents 3"} {:text "contents 4"}] + true) + "contents 1contents 2contents 3contents 4")) + (is (= (tables/table-row->str + [{:text "contents 1"} {:text "contents 2"} {:text "contents 3"} {:text "contents 4"}] + false) + "contents 1contents 2contents 3contents 4")) + (is (= (tables/table-row->str + [{:text "contents 1" :alignment :left} + {:text "contents 2" :alignment :center} + {:text "contents 3" :alignment :right} + {:text "contents 4"}] + false) + "contents 1contents 2contents 3contents 4"))) + +(deftest table->str + (is (= (tables/table->str + {:alignment-seq + [{:alignment :left} {:alignment :center} {:alignment :right} {:alignment nil}] + :data [[{:text "Header 1"} + {:text "Header 2"} + {:text "Header 3"} + {:text "Header 4"}] + [{:text "contents 1"} + {:text "contents 2"} + {:text "contents 3"} + {:text "contents 4"}]]}) + "
Header 1Header 2Header 3Header 4
contents 1contents 2contents 3contents 4
"))) + +(deftest divider-seq->alignment + (is (= (tables/divider-seq->alignment + [{:text "-----"} {:text ":-----"} {:text "-----:"} {:text ":-----:"}]) + [nil {:alignment :left} {:alignment :right} {:alignment :center}]))) + +(deftest n-dash + (is (= "

boo – bar

" (entry-function "boo -- bar")))) + +(deftest m-dash + (is (= "

boo — bar

" (entry-function "boo --- bar")))) + +(deftest inhibit-simple + (is (= "

_abc_

" (entry-function "$_abc_$" :inhibit-separator "$")))) + +(deftest inhibit-simple-seq + (is (= "

_abc_

" (entry-function "$_abc_$" :inhibit-separator [\$])))) + +(deftest inhibit-inline-code + (is (= "

`abc`

" (entry-function "$`abc`$" :inhibit-separator [\$])))) + +(deftest inhibit-inside-code + (is (= "

a*b* & dc

" (entry-function "`a$*b* & d$c`" :inhibit-separator "$")))) + +(deftest inhibit-across-backticks + (is (= "

one` `two

" (entry-function "`one$` `$two`" :inhibit-separator "$")))) + +(deftest inhibit-escape + (is (= "

$

" (entry-function "$$" :inhibit-separator [\$])))) + +(deftest inhibit-escape-twice + (is (= "

$$

" (entry-function "$$$$" :inhibit-separator "$")))) + +(deftest img-reprocess + (is (= "

\"Text\" and Edit

" + (entry-function "![Text](img.jpg) and [Edit](#)")))) + +(deftest dont-inhibit-text-within-escapes + (is (= "

$abc$

" (entry-function "$$*abc*$$" :inhibit-separator "$")))) + +(deftest inhibit-escape-inside-code + (is (= "

$

" (entry-function "`$$`" :inhibit-separator "$")))) + +(deftest whitespace-paragraphs + (is (= "

foo

bar

" (entry-function "foo\n \nbar")))) diff --git a/test-resources/lib_tests/markdown/nbb_runner.cljs b/test-resources/lib_tests/markdown/nbb_runner.cljs new file mode 100644 index 00000000..ec9bffa0 --- /dev/null +++ b/test-resources/lib_tests/markdown/nbb_runner.cljs @@ -0,0 +1,10 @@ +(ns nbb-runner + (:require [clojure.string :as str] + [clojure.test :refer [run-tests]] + [nbb.classpath :as cp])) + +(cp/add-classpath (str/join ":" ["src/cljs" "src/cljc" "test"])) + +(require '[markdown.md-test]) + +(run-tests 'markdown.md-test) diff --git a/test-resources/lib_tests/markdown/runner.cljs b/test-resources/lib_tests/markdown/runner.cljs new file mode 100644 index 00000000..aa2cf9f4 --- /dev/null +++ b/test-resources/lib_tests/markdown/runner.cljs @@ -0,0 +1,5 @@ +(ns markdown.runner + (:require [doo.runner :refer-macros [doo-tests]] + [markdown.md-test])) + +(doo-tests 'markdown.md-test)