Skip to content

Commit

Permalink
refactoring namespaces, and add nicer console reporter
Browse files Browse the repository at this point in the history
  • Loading branch information
Alfred Xiao committed Aug 5, 2019
1 parent fc95ff1 commit 9cd9978
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 75 deletions.
2 changes: 1 addition & 1 deletion project.clj
@@ -1,4 +1,4 @@
(defproject canvas "0.1.1"
(defproject canvas "0.1.2"
:description "Clojure plugin for leiningen that reports on test coverage."
:url "https://github.com/alfredxiao/canvas"
:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
Expand Down
86 changes: 12 additions & 74 deletions src/canvas/core.clj
@@ -1,89 +1,27 @@
(ns canvas.core
(:require [clojure.test]
[clojure.string :refer [starts-with? ends-with?]]
[clojure.tools.namespace.find :as ns-find]
[clojure.pprint :refer [pprint]]
[clojure.java.io :as io]))

(defn- log-info [& msgs]
#_(apply println msgs))

(defn- instrument
[f v]
(fn [& args]
(alter-meta! v assoc ::tested? true)
(alter-meta! v update ::hit inc)
(apply f args)))

(defn- instrumentable?
[v]
(and (.isBound v)
(fn? @v)
(not (:macro (meta v)))
(not (:test (meta v)))))

(defn instrument-var! [v]
(when (instrumentable? v)
(log-info "canvas: instrumenting function " v)
(alter-meta! v assoc ::tested? false)
(alter-meta! v assoc ::hit 0)
(alter-meta! v assoc ::original @v)
(alter-var-root v instrument v)))

(defn uninstrument-var! [v]
(when (instrumentable? v)
(let [root (::original (meta v))]
(assert root "No root binding to restore!")
(alter-meta! v dissoc ::tested?)
(alter-meta! v dissoc ::hit)
(alter-meta! v dissoc ::original)
(alter-var-root v (constantly root)))))

(defn apply-to-each-var [f ns]
(doseq [[_ v] (ns-interns ns)]
(f v)))

(defn instrument-ns! [& nz]
(doseq [n nz]
(log-info "canvas: instrumenting publics of ns:" (ns-name n))
(apply-to-each-var instrument-var! n)))

(defn uninstrument-ns! [& nss]
(doseq [ns nss]
(apply-to-each-var uninstrument-var! ns)))

(defn- user? [ns]
(= "user" (name (ns-name ns))))

(defn report [nz]
(into {}
(for [n nz :when (not (user? n))]
[(ns-name n)
(into {}
(for [[_ v] (ns-interns n) :when (instrumentable? v)]
[(:name (meta v)) (let [mt (meta v)]
{:scope (if (:private mt) :private :public)
:line (:line mt)
:tested? (::tested? mt)
:hit (::hit mt)})]))])))
[clojure.java.io :as io]
[canvas.instrument :as ins]
[canvas.report :refer [report]]))

(defn- doseq-with [values op]
(doseq [v values]
(op v)))

(defn evaluate-test-coverage
[{:keys [source-paths test-paths] :as opts}]
(let [target-nz (apply concat (->> source-paths
[{:keys [source-paths test-paths reporter]}]
(let [target-nss (apply concat (->> source-paths
(map io/file)
(map ns-find/find-namespaces-in-dir)))
test-nz (apply concat (->> test-paths
test-nss (apply concat (->> test-paths
(map io/file)
(map ns-find/find-namespaces-in-dir)))]
(doseq-with target-nz require)
(doseq-with test-nz require)
(doseq-with target-nss require)
(doseq-with test-nss require)
(try
(doseq-with target-nz instrument-ns!)
(apply clojure.test/run-tests test-nz)
(pprint (report target-nz))
(doseq-with target-nss ins/instrument-ns!)
(apply clojure.test/run-tests test-nss)
(report target-nss reporter)
(finally
(mapv uninstrument-ns! target-nz)))))
(mapv ins/uninstrument-ns! target-nss)))))
43 changes: 43 additions & 0 deletions src/canvas/instrument.clj
@@ -0,0 +1,43 @@
(ns canvas.instrument)

(defn- instrument
[f v]
(fn [& args]
(alter-meta! v assoc ::tested? true)
(alter-meta! v update ::hit inc)
(apply f args)))

(defn instrumentable?
[v]
(and (.isBound v)
(fn? @v)
(not (:macro (meta v)))
(not (:test (meta v)))))

(defn instrument-var! [v]
(when (instrumentable? v)
(alter-meta! v assoc ::tested? false)
(alter-meta! v assoc ::hit 0)
(alter-meta! v assoc ::original @v)
(alter-var-root v instrument v)))

(defn uninstrument-var! [v]
(when (instrumentable? v)
(let [root (::original (meta v))]
(assert root "No root binding to restore!")
(alter-meta! v dissoc ::tested?)
(alter-meta! v dissoc ::hit)
(alter-meta! v dissoc ::original)
(alter-var-root v (constantly root)))))

(defn apply-to-each-var [f ns]
(doseq [[_ v] (ns-interns ns)]
(f v)))

(defn instrument-ns! [& nz]
(doseq [n nz]
(apply-to-each-var instrument-var! n)))

(defn uninstrument-ns! [& nss]
(doseq [ns nss]
(apply-to-each-var uninstrument-var! ns)))
36 changes: 36 additions & 0 deletions src/canvas/report.clj
@@ -0,0 +1,36 @@
(ns canvas.report
(:require [canvas.instrument :as ins]))

(defn- user? [ns]
(= "user" (name (ns-name ns))))

(defn- console [stats]
(let [max-nsname-len (->> stats keys
(map name)
(map count)
(apply max))
nsname-fmt-str (str " %s %-" (inc max-nsname-len) "s%s")
max-chars-per-line (+ max-nsname-len 20)]
(println (apply str (repeat max-chars-per-line "=")))
(doseq [[nsname ns-stats] stats]
(println nsname)
(doseq [[fn-name fn-stat] ns-stats]
(let [visibility-indicator (if (:private fn-stat) "-" "+")
tested-indicator (if (:tested? fn-stat) "T" " ")]
(println (format nsname-fmt-str visibility-indicator fn-name
(format "%s %d" tested-indicator (:hit fn-stat)))))))
(println (apply str (repeat max-chars-per-line "=")))))

(defn report [nss reporter]
(let [stats (into {}
(for [n nss :when (not (user? n))]
[(ns-name n)
(into {}
(for [[_ v] (ns-interns n) :when (ins/instrumentable? v)]
[(:name (meta v)) (let [mt (meta v)]
{:private (:private mt)
:line (:line mt)
:tested? (:canvas.instrument/tested? mt)
:hit (:canvas.instrument/hit mt)})]))]))]
(case reporter
(console stats))))

0 comments on commit 9cd9978

Please sign in to comment.