From 00baaa54b8c58ed3aee4a8dce8fa9b7d39628f0c Mon Sep 17 00:00:00 2001 From: Cam Saul <1455846+camsaul@users.noreply.github.com> Date: Wed, 11 Sep 2024 10:01:08 -0700 Subject: [PATCH] Add `:only-tags` option (#32) * Improved test partitioning * Update dox * Code cleanup * Modernize GH Actions * Appease Kondo * Ok partitioning does need to sort namespaces (but not vars) * Support :only-tags * Turns out I don't know how to use the GH conflict resolver --------- Co-authored-by: Chris Truter Co-authored-by: Chris Truter --- README.md | 27 ++++++++++++++++-- src/mb/hawk/core.clj | 56 ++++++++++++++++++++------------------ test/mb/hawk/core_test.clj | 21 ++++++++++++++ 3 files changed, 76 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 5e71f36..8b74b57 100644 --- a/README.md +++ b/README.md @@ -153,7 +153,7 @@ please submit a PR. :exec-args {:exclude-directories ["src" "resources" "shared/src"]}}}} ``` -## Skipping namespaces +## Skipping namespaces or vars with tags You can optionally exclude tests in namespaces with certain tags by specifying the `:exclude-tags` option: @@ -177,7 +177,30 @@ And adding it to namespaces like ...) ``` -Currently only supported on namespaces! It would be nice to support this on individual tests as well -- PRs are welcome! +## Only running tests against namespaces or vars with tags + +The opposite of `:exclude-tags` -- you can only run tests against a certain set of tags with `:only-tags`. If multiple +`:only-tags` are specified, only namespaces or vars that have all of those tags will be run. + +`:only-tags` can be combined with `:only` and/or `:exclude-tags`. + +``` +clj -X:test :only-tags [:my-project/e2e-test] +``` + +will only run tests in namespaces like + +```clj +(ns ^:my-project/e2e-test my-namespace + ...) +``` + +or ones individually marked `:my-project/e2e-test` like + +```clj +(deftest ^:my-project/e2e-test my-test + ...) +``` ## Whole-Suite Hooks diff --git a/src/mb/hawk/core.clj b/src/mb/hawk/core.clj index a61ab04..bb7c126 100644 --- a/src/mb/hawk/core.clj +++ b/src/mb/hawk/core.clj @@ -76,36 +76,40 @@ [(or (resolve symb) (throw (ex-info (format "Unable to resolve test named %s" symb) {:test-symbol symb})))]) -(defn- excluded-tags - "Return the set of all tags in an element's metadata that are also in the `:exclude-tags` options." - [element options] - (when-let [excluded-tags (not-empty (set (:exclude-tags options)))] - (let [ns-tags (-> element meta keys set)] - (not-empty (set/intersection excluded-tags ns-tags))))) - -(defn- filter-tests-by-tag - "Filter out the test cases with tags that are also in the `:exclude-tags` options." - [tests options] - (filter (fn [test] - (if-let [excluded-tags (not-empty (excluded-tags test options))] - (println (format - "Skipping test `%s` due to excluded tag(s): %s" - (:name (meta test)) - (->> excluded-tags sort (str/join ",")))) - test)) - tests)) +(defn- skip-by-tags? + "Whether we should skip a namespace or test var because it has tags in `:exclude-tags` or is missing tags in + `:only-tags`. Prints debug message as a side-effect." + [ns-or-var options] + (let [tags-set (fn [ns-or-var] + (not-empty (set (keys (meta ns-or-var))))) + excluded-tag? (when-let [exclude-tags (not-empty (set (:exclude-tags options)))] + (when-let [disallowed-tags (not-empty (set/intersection exclude-tags (tags-set ns-or-var)))] + (printf + "Skipping `%s` due to excluded tag(s): %s\n" + (if (var? ns-or-var) + (:name (meta ns-or-var)) + (ns-name ns-or-var)) + (->> disallowed-tags sort (str/join ","))) + true)) + missing-tag? (when (var? ns-or-var) + (let [varr ns-or-var] + (when-let [only-tags (not-empty (set (:only-tags options)))] + (when-let [missing-tags (not-empty (set/difference only-tags + (tags-set (:ns (meta varr))) + (tags-set varr)))] + (printf + "Skipping `%s` due to missing only tag(s): %s\n" + (:name (meta varr)) + (->> missing-tags sort (str/join ","))) + true))))] + (or excluded-tag? missing-tag?))) (defn- find-tests-for-namespace-symbol [ns-symb options] (load-test-namespace ns-symb) - (if-let [excluded-tags (not-empty (excluded-tags (find-ns ns-symb) options))] - (println (format - "Skipping tests in `%s` due to excluded tag(s): %s" - ns-symb - (->> excluded-tags sort (str/join ",")))) - (filter-tests-by-tag - (eftest.runner/find-tests ns-symb) - options))) + (when-not (skip-by-tags? (find-ns ns-symb) options) + (remove #(skip-by-tags? % options) + (eftest.runner/find-tests ns-symb)))) ;; a test namespace or individual test (defmethod find-tests clojure.lang.Symbol diff --git a/test/mb/hawk/core_test.clj b/test/mb/hawk/core_test.clj index da96693..413fe29 100644 --- a/test/mb/hawk/core_test.clj +++ b/test/mb/hawk/core_test.clj @@ -58,3 +58,24 @@ {:exclude-tags [:exclude-this-test]} {:exclude-tags #{:exclude-this-test}} {:exclude-tags [:exclude-this-test :another/tag]})) + +(deftest only-tags-test + (are [options] (= [] + (hawk/find-tests this-ns options)) + {:only-tags [:another/tag]} + {:only-tags [:another/tag :exclude-tags-test]} + {:only-tags [:another/tag :exclude-this-test]}) + (are [options] (= (hawk/find-tests this-ns {}) + (hawk/find-tests this-ns options)) + {:only-tags [:exclude-tags-test]} + {:only-tags #{:exclude-tags-test}}) + (are [options] (= [#'find-tests-test] + (hawk/find-tests this-ns options)) + {:only-tags [:exclude-this-test]} + {:only-tags #{:exclude-this-test}} + {:only-tags [:exclude-this-test :exclude-tags-test]} + {:only-tags #{:exclude-this-test :exclude-tags-test}}) + (are [options] (= [#'find-tests-test] + (hawk/find-tests this-ns options)) + {:only-tags [:exclude-this-test]} + {:only-tags #{:exclude-this-test}}))