From 4ab993a59066f20870df76217a49fe22afdfeae8 Mon Sep 17 00:00:00 2001 From: Marc Daya Date: Sat, 20 Oct 2018 20:48:05 -0600 Subject: [PATCH] Initialise worker pods. The initial pod ID and shutdown hooks are initialised by Boot when the app is launched, but not again when pod pools are spun up. Boot's fileset and tmpfile machinery hooks into the pod lifecycle depends on pods being propertly initialised; the following errors were encounted while testing a task that modifies the Boot fileset. actual: java.lang.NullPointerException: null at clojure.lang.RT.longCast (RT.java:1241) boot.tmpregistry$dir_id.invoke (tmpregistry.clj:31) boot.tmpregistry$registry.invoke (tmpregistry.clj:93) boot.core/fn (core.clj:44) clojure.lang.Delay.deref (Delay.java:37) clojure.core$deref.invokeStatic (core.clj:2228) clojure.core$deref.invoke (core.clj:2214) boot.core$tmp_dir_STAR_.invoke (core.clj:98) boot.core$tmp_dir_STAR__STAR_.doInvoke (core.clj:111) clojure.lang.RestFn.invoke (RestFn.java:423) boot.core$tmp_dir_BANG_.invoke (core.clj:356) actual: java.lang.NullPointerException: null at clojure.lang.Reflector.invokeInstanceMethod (Reflector.java:26) boot.pod$add_shutdown_hook_BANG_.invoke (pod.clj:345) boot.tmpregistry.TmpRegistry._init_BANG_ (tmpregistry.clj:80) boot.tmpregistry$init_BANG_.invoke (tmpregistry.clj:55) boot.core/fn (core.clj:44) clojure.lang.Delay.deref (Delay.java:37) clojure.core$deref.invokeStatic (core.clj:2228) clojure.core$deref.invoke (core.clj:2214) boot.core$tmp_dir_STAR_.invoke (core.clj:98) boot.core$tmp_dir_STAR__STAR_.doInvoke (core.clj:111) clojure.lang.RestFn.invoke (RestFn.java:423) boot.core$tmp_dir_BANG_.invoke (core.clj:356) See: * https://github.com/boot-clj/boot/blob/dce24183711747fc43b6e13c061cd5cb073b4e94/boot/base/src/main/java/boot/App.java#L407 * https://github.com/boot-clj/boot/blob/dce24183711747fc43b6e13c061cd5cb073b4e94/boot/core/src/boot/tmpregistry.clj#L31 --- src/adzerk/boot_test.clj | 103 +++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 47 deletions(-) diff --git a/src/adzerk/boot_test.clj b/src/adzerk/boot_test.clj index 51451e3..00b8ef0 100644 --- a/src/adzerk/boot_test.clj +++ b/src/adzerk/boot_test.clj @@ -9,55 +9,64 @@ '[[org.clojure/tools.namespace "0.2.11" :exclusions [org.clojure/clojure]] [pjstadig/humane-test-output "0.6.0" :exclusions [org.clojure/clojure]]]) +(def last-pod-id (atom (System/currentTimeMillis))) + (defn init [requires fresh-pod] (dorun (map (partial pod/require-in fresh-pod) requires)) - (doto fresh-pod - (pod/with-eval-in - (require '[clojure.test :as t] - '[clojure.java.io :as io] - '[clojure.test.junit :as junit] - '[pjstadig.humane-test-output :refer [activate!]] - '[clojure.tools.namespace.find :refer [find-namespaces-in-dir]]) - (activate!) - - (defn all-ns* [& dirs] - (distinct (mapcat #(find-namespaces-in-dir (io/file %)) dirs))) - - (defn junit-plus-default-report [old-report junit-out m] - (old-report m) - (binding [t/*test-out* junit-out - ;; junit will inc the counters, but old-report is already doing that - ;; so we pass a new counters ref that will be discarded to avoid - ;; duplicate counters - t/*report-counters* (ref {})] - (junit/junit-report m))) - - (defn run-tests-with-junit-reporter [run-tests-fn output-to] - (let [junit-out-filename output-to - old-report t/report] - (with-open [junit-out (io/writer junit-out-filename)] - (binding [junit/*var-context* (list) - junit/*depth* 1 - t/report (partial junit-plus-default-report old-report junit-out)] - (binding [*out* junit-out] - (println "") - (println "")) - (let [result (run-tests-fn)] - (binding [*out* junit-out] - (println "")) - result))))) - - (defn test-ns* [pred junit-output-to ns] - (binding [t/*report-counters* (ref t/*initial-report-counters*)] - (let [ns-obj (the-ns ns) - run-tests* (fn [] - (t/do-report {:type :begin-test-ns :ns ns-obj}) - (t/test-vars (filter pred (vals (ns-publics ns)))) - (t/do-report {:type :end-test-ns :ns ns-obj}) - @t/*report-counters*)] - (if junit-output-to - (run-tests-with-junit-reporter run-tests* (io/file junit-output-to (str (name ns) ".xml"))) - (run-tests*)))))))) + (let [pod-id (swap! last-pod-id inc)] + (doto fresh-pod + (pod/with-eval-in + (require '[clojure.test :as t] + '[clojure.java.io :as io] + '[clojure.test.junit :as junit] + '[boot.pod :as pod] + '[pjstadig.humane-test-output :refer [activate!]] + '[clojure.tools.namespace.find :refer [find-namespaces-in-dir]]) + (import '[java.util.concurrent ConcurrentLinkedQueue]) + (when-not pod/pod-id + (pod/set-pod-id! ~pod-id)) + (when-not @pod/shutdown-hooks + (reset! pod/shutdown-hooks (ConcurrentLinkedQueue.))) + (activate!) + + (defn all-ns* [& dirs] + (distinct (mapcat #(find-namespaces-in-dir (io/file %)) dirs))) + + (defn junit-plus-default-report [old-report junit-out m] + (old-report m) + (binding [t/*test-out* junit-out + ;; junit will inc the counters, but old-report is already doing that + ;; so we pass a new counters ref that will be discarded to avoid + ;; duplicate counters + t/*report-counters* (ref {})] + (junit/junit-report m))) + + (defn run-tests-with-junit-reporter [run-tests-fn output-to] + (let [junit-out-filename output-to + old-report t/report] + (with-open [junit-out (io/writer junit-out-filename)] + (binding [junit/*var-context* (list) + junit/*depth* 1 + t/report (partial junit-plus-default-report old-report junit-out)] + (binding [*out* junit-out] + (println "") + (println "")) + (let [result (run-tests-fn)] + (binding [*out* junit-out] + (println "")) + result))))) + + (defn test-ns* [pred junit-output-to ns] + (binding [t/*report-counters* (ref t/*initial-report-counters*)] + (let [ns-obj (the-ns ns) + run-tests* (fn [] + (t/do-report {:type :begin-test-ns :ns ns-obj}) + (t/test-vars (filter pred (vals (ns-publics ns)))) + (t/do-report {:type :end-test-ns :ns ns-obj}) + @t/*report-counters*)] + (if junit-output-to + (run-tests-with-junit-reporter run-tests* (io/file junit-output-to (str (name ns) ".xml"))) + (run-tests*))))))))) ;;; This prevents a name collision WARNING between the test task and ;;; clojure.core/test, a function that nobody really uses or cares