Skip to content

Commit

Permalink
orchard.xref: include info for test vars (#177)
Browse files Browse the repository at this point in the history
Fixes #176
  • Loading branch information
vemv authored Aug 9, 2023
1 parent b98aa2d commit 178c7e3
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 32 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## master (unreleased)

### Changes

- [#176](https://github.com/clojure-emacs/orchard/issues/176): `orchard.xref`: include info for test vars.
- `orchard.xref`: avoid duplicate vars that might appear following REPL re-evaluation.

## 0.14.1 (2023-08-05)

### Changes
Expand Down
38 changes: 30 additions & 8 deletions src/orchard/xref.clj
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,27 @@
[clojure.string :as string]
[orchard.query :as q]))

(defn- var->symbol
;; TODO: use `symbol` once we start targeting Clojure >= 1.10 after CIDER 1.8 is released.
"Normally one could just use `(symbol var-ref)`,
but that doesn't work in older Clojures."
[var-ref]
(let [{:keys [ns name]} (meta var-ref)]
(symbol (str (ns-name ns))
(str name))))

(defn- var->fn [var-ref]
(let [{:keys [test]} (meta var-ref)]
(if (fn? test)
test ;; for deftests, :test metadata contains the actual test implementation, with all the interesting contents.
(var-get var-ref))))

(defn- to-fn
"Convert `thing` to a function value."
[thing]
(cond
(var? thing) (var-get thing)
(symbol? thing) (var-get (find-var thing))
(var? thing) (var->fn thing)
(symbol? thing) (var->fn (find-var thing))
(fn? thing) thing))

(defn- fn-name [^java.lang.Class f]
Expand Down Expand Up @@ -71,12 +86,19 @@
(map (fn [[_k value]]
(.get ^java.lang.ref.Reference value)))
(mapcat fn-deps-class))
class-cache)]
;; if there's no deps the class is most likely AoT compiled,
;; try to access it directly
(if (empty? deps)
(-> v .getClass fn-deps-class)
deps))))
class-cache)
result
;; if there's no deps the class is most likely AoT compiled,
;; try to access it directly
(if (empty? deps)
(-> v .getClass fn-deps-class)
deps)]
(into #{}
(map resolve) ;; choose the freshest one
;; group duplicates. This is important
;; because there can be two seemingly equal #'foo.bar/baz var objects in the result.
;; That can happen as one re-evaluates code and the old var hasn't been GC'd yet.
(keys (group-by var->symbol result))))))

(defn fn-transitive-deps
"Returns a set with all the functions invoked inside `v` or inside those functions.
Expand Down
82 changes: 58 additions & 24 deletions test/orchard/xref_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,38 @@
(defn- dummy-fn [_x]
(map #(fn-dep % 2) (filter even? (range 1 10))))

;; Supports #'fn-deps-test
(deftest sample-test
(is (some? xref/eval-lock))
(is (some? (xref/fn-refs #'dummy-fn))))

(deftest fn-deps-test
(testing "with a fn value"
(is (= (xref/fn-deps dummy-fn)
#{#'clojure.core/map #'clojure.core/filter
#'clojure.core/even? #'clojure.core/range #'orchard.xref-test/fn-dep})))
(is (= #{#'clojure.core/map #'clojure.core/filter
#'clojure.core/even? #'clojure.core/range #'orchard.xref-test/fn-dep}
(xref/fn-deps dummy-fn))))

(testing "with a var"
(is (= (xref/fn-deps #'dummy-fn)
#{#'clojure.core/map #'clojure.core/filter
#'clojure.core/even? #'clojure.core/range #'orchard.xref-test/fn-dep})))
(is (= #{#'clojure.core/map #'clojure.core/filter
#'clojure.core/even? #'clojure.core/range #'orchard.xref-test/fn-dep}
(xref/fn-deps #'dummy-fn))))

(testing "with a var that backs a deftest"
(is (= #{#'orchard.xref-test/dummy-fn
#'orchard.xref/eval-lock
#'clojure.test/do-report
#'clojure.core/cons
#'clojure.core/some?
#'clojure.core/apply
#'orchard.xref/fn-refs
#'clojure.core/list}
(xref/fn-deps #'sample-test))))

(testing "with a symbol"
(is (= (xref/fn-deps 'orchard.xref-test/dummy-fn)
#{#'clojure.core/map #'clojure.core/filter
#'clojure.core/even? #'clojure.core/range #'orchard.xref-test/fn-dep})))
(is (= #{#'clojure.core/map #'clojure.core/filter
#'clojure.core/even? #'clojure.core/range #'orchard.xref-test/fn-dep}
(xref/fn-deps 'orchard.xref-test/dummy-fn))))

(testing "AoT compiled functions return deps"
(is (= #{#'clojure.core/conj}
(xref/fn-deps reverse)))))
Expand All @@ -38,19 +57,6 @@
(def yyy (symbol (str (gensym))
(str (gensym))))

(deftest fn-refs-test
(testing "with a fn value"
(is (= (xref/fn-refs dummy-fn) '()))
(is (contains? (into #{} (xref/fn-refs #'map)) #'orchard.xref-test/dummy-fn)))
(testing "with a var"
(is (= (xref/fn-refs #'dummy-fn) '()))
(is (contains? (into #{} (xref/fn-refs #'map)) #'orchard.xref-test/dummy-fn)))
(testing "with a symbol"
(is (= (xref/fn-refs 'orchard.xref-test/dummy-fn) '()))
(is (contains? (into #{} (xref/fn-refs #'map)) #'orchard.xref-test/dummy-fn)))
(testing "that usage from inside an anonymous function is found"
(is (contains? (into #{} (xref/fn-refs #'fn-dep)) #'orchard.xref-test/dummy-fn))))

(deftest fn-transitive-deps-test
(testing "basics"
(let [expected #{#'orchard.xref-test/fn-deps-test #'orchard.xref-test/fn-dep #'clojure.core/even?
Expand All @@ -59,8 +65,36 @@
(is (contains? expected #'orchard.xref-test/fn-transitive-dep)
"Specifically includes `#'fn-transitive-dep`, which is a transitive dep of `#'dummy-fn` (via `#'fn-dep`)")
(is (contains? expected #'clojure.core/inc')
"Specifically includes `#'clojure.core/inc'`, which is a transitive dep of `#'dummy-fn`
(via `#'clojure.core/range'`). Unlike other AoT compiled core transitive dependancies
"Specifically includes `#'clojure.core/inc'`, which is a transitive dep of `#'dummy-fn`
(via `#'clojure.core/range'`). Unlike other AoT compiled core transitive dependancies
it gets found because its a non `:static` dependancy.")
(is (= expected
(xref/fn-transitive-deps dummy-fn))))))

(deftest fn-refs-test
(testing "with a fn value"
(is (= #{#'orchard.xref-test/fn-deps-test
#'orchard.xref-test/fn-transitive-deps-test
#'orchard.xref-test/sample-test
#'orchard.xref-test/fn-refs-test}
(set (xref/fn-refs dummy-fn))))
(is (contains? (into #{} (xref/fn-refs #'map)) #'orchard.xref-test/dummy-fn)))

(testing "with a var"
(is (= #{#'orchard.xref-test/fn-deps-test
#'orchard.xref-test/fn-transitive-deps-test
#'orchard.xref-test/sample-test
#'orchard.xref-test/fn-refs-test}
(set (xref/fn-refs #'dummy-fn))))
(is (contains? (into #{} (xref/fn-refs #'map)) #'orchard.xref-test/dummy-fn)))

(testing "with a symbol"
(is (= #{#'orchard.xref-test/fn-deps-test
#'orchard.xref-test/fn-transitive-deps-test
#'orchard.xref-test/sample-test
#'orchard.xref-test/fn-refs-test}
(set (xref/fn-refs 'orchard.xref-test/dummy-fn))))
(is (contains? (into #{} (xref/fn-refs #'map)) #'orchard.xref-test/dummy-fn)))

(testing "that usage from inside an anonymous function is found"
(is (contains? (into #{} (xref/fn-refs #'fn-dep)) #'orchard.xref-test/dummy-fn))))

0 comments on commit 178c7e3

Please sign in to comment.