Skip to content

Commit

Permalink
commits for 0.0.3
Browse files Browse the repository at this point in the history
  • Loading branch information
konstan committed Mar 10, 2022
1 parent 5e0a2a1 commit cf4d2d1
Show file tree
Hide file tree
Showing 11 changed files with 427 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
*/target
pom.xml
pom.xml.asc
*.jar
*.class
.lein-*
.nrepl-port
**/test/.output
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,24 @@
# test-report-sonar

Renders test reports results in [Sonar Generic Execution format](https://docs.sonarqube.org/latest/analysis/generic-test/#header-2).

To run the plugin add the following to the `:test` profile

```clojure
:profiles {:test {:plugins [[org.clojars.konstan/lein-test-report-sonar "0.0.2"]]
:test-report-sonar {:output-dir "test-reports"}}
}
```

The reports will be generated in `test-reports/sonar/testExecutions.xml`.

The plugin can generate JUnit XML results as well (internally uses . To do so, add
`:emit-junit-xml true` under `:test-report-sonar` map.

```clojure
:profiles {:test {:plugins [[org.clojars.konstan/lein-test-report-sonar "0.0.2"]]
:test-report-sonar {:output-dir "test-reports"
:emit-junit-xml true}}
}
```
The JUnit XML reports will be generated under `test-reports/xml/`.
18 changes: 18 additions & 0 deletions lein-test-report-sonar/project.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
(defproject org.clojars.konstan/lein-test-report-sonar "0.0.3"
:description "Leiningen plugin providing Sonar Generic execution output for clojure.test"
:url "https://github.com/konstan/test-report-sonar"
:scm {:dir ".."}
:license {:name "Eclipse Public License", :url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[lein-test-report "0.2.0"]]
:deploy-repositories [["clojars" {:sign-releases false
:url "https://clojars.org/repo"
:username :env/clojars_username
:password :env/clojars_password
;;:signing {:gpg-key "release manager key"}
}]
["releases" {:sign-releases false
:url "https://clojars.org/repo"}]
["snapshots" {:sign-releases false
:url "https://clojars.org/repo"}]]
:aliases {"deploy" ["deploy" "clojars"]}
:eval-in-leiningen true)
36 changes: 36 additions & 0 deletions lein-test-report-sonar/src/lein_test_report_sonar/plugin.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
(ns lein-test-report-sonar.plugin
(:require [lein-test-report.utils :refer [add-profile]]))

(def ^:const ver-test-report-junit-xml "0.2.0")
(def ^:const ver-test-report-sonar "0.0.3")
(def ^:const ver-test-report "0.2.0")


(defn middleware [project]
(let [output-dir (or (System/getenv "TEST_REPORT_SONAR_OUTPUT_DIR")
(-> project :test-report-sonar :output-dir)
"target/test-reports")
options (-> project
(:test-report-sonar {})
(dissoc :output-dir :emit-junit-xml))
emit-junit-xml (-> project :test-report-sonar :emit-junit-xml)
dependencies (if emit-junit-xml
[['org.clojars.konstan/test-report-sonar ver-test-report-sonar]
['test-report-junit-xml ver-test-report-junit-xml]]
[['org.clojars.konstan/test-report-sonar ver-test-report-sonar]])
summarizers (if emit-junit-xml
`[#(test-report-sonar.core/write (str ~output-dir "/sonar") % ~options)
#(test-report-junit-xml.core/write (str ~output-dir "/xml") % ~options)]
`[#(test-report-sonar.core/write (str ~output-dir "/sonar") % ~options)])
injections (if emit-junit-xml
`[(require 'test-report-sonar.core)
(require 'test-report-junit-xml.core)
(require 'clojure.java.io)]
`[(require 'test-report-sonar.core)
(require 'clojure.java.io)])]
(add-profile project {:dependencies dependencies
:plugins [['lein-test-report ver-test-report]]
:injections injections
:test-report {:summarizers summarizers}})))


26 changes: 26 additions & 0 deletions test-report-sonar/project.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
(defproject org.clojars.konstan/test-report-sonar "0.0.3"
:description "Library providing Sonar Generic execution output for clojure.test"
:url "https://github.com/konstan/test-report-sonar"
:scm {:dir ".."}
:license {:name "Eclipse Public License", :url "http://www.eclipse.org/legal/epl-v10.html"}
:plugins [[lein-shade "0.4.0"]]
:dependencies [[test-report-junit-xml "0.2.0"]]
:profiles {:test {:resource-paths ["test/resources"]}
:uberjar {:aot :all}
:provided {:dependencies [[org.clojure/clojure "1.10.3"]]}
:shaded {:dependencies [[org.clojure/data.xml "0.2.0-alpha2" :exclusions [org.clojure/clojure]]]
:shade {:namespaces [clojure.data.xml]}}
:unshaded ^:leaky {:dependencies [[test-report "0.2.0"]]}
:default [:leiningen/default :shaded :unshaded]}
:deploy-repositories [["clojars" {:sign-releases false
:url "https://clojars.org/repo"
:username :env/clojars_username
:password :env/clojars_password
;;:signing {:gpg-key "konstan release manager key"}
}]
["releases" {:sign-releases false
:url "https://clojars.org/repo"}]
["snapshots" {:sign-releases false
:url "https://clojars.org/repo"}]]
:aliases {"deploy" ["deploy-shaded-jar" "clojars"]
"install" ["install-shaded-jar"]})
127 changes: 127 additions & 0 deletions test-report-sonar/src/test_report_sonar/core.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
(ns test-report-sonar.core
(:require
[test-report.options :refer [with-options]]
[test-report.summary :refer [summarize]]
[clojure.data.xml :as xml]
[clojure.java.io :as io]
[clojure.stacktrace :as stacktrace]
[clojure.string :as string]
[test-report-sonar.utils :refer :all]))


(defmulti ^:private format-result :type)


(defn- format-stacktrace [error]
(with-out-str (stacktrace/print-cause-trace error)))


(def ^:dynamic *format-result* format-result)
(def ^:dynamic *format-stacktrace* format-stacktrace)


(defn- join-non-blanks [delimiter & strings]
(->> strings (remove string/blank?) (string/join delimiter)))


(defn- context [result]
(->> result :context reverse (string/join " ")))


(defn- error-message [error]
(when (instance? Throwable error)
(.getMessage error)))


(defn- error-cause [error]
(if (instance? Throwable error)
(*format-stacktrace* error)
(prn-str error)))


(defmethod format-result :fail
[result]
(let [message (join-non-blanks ": " (context result) (or (:message result) "failure"))]
{:tag :failure
:attrs {:message message}
:content (join-non-blanks "\n"
message
(str "expected: " (-> result :expected prn-str)
" actual: " (-> result :actual prn-str)
" at: " (:file result) ":" (:line result)))}))


(defmethod format-result :error
[result]
(let [message (join-non-blanks ": " (context result) (or (:message result) "error") (-> result :actual error-message))]
{:tag :error
:attrs {:message message}
:content (join-non-blanks "\n"
message
(str "expected: " (-> result :expected prn-str)
" actual: " (-> result :actual error-cause)))}))


(defmethod format-result :default [result])


(defn- test-case
[test-var]
{:tag :testCase
:attrs {:name (-> test-var :var meta :name)
:duration (-> test-var :time nanos->millis)}
:content (keep *format-result* (:results test-var))})


(defn- file
[test-ns]
{:tag :file
:attrs {:path (-> test-ns :ns ns->path)}
:content (map test-case (:tests test-ns))})


(defn- test-executions
[tested-namespaces]
{:tag :testExecutions
:attrs {:version "1"}
:content (map file tested-namespaces)})


(defn- output-file
[output-dir file-name]
(io/file output-dir file-name))


(defn- write-reports-xml
[output-dir testexecutions]
(.mkdirs (io/file output-dir))
(with-open [writer (io/writer (output-file output-dir "testExecutions.xml"))]
(xml/emit testexecutions writer)))


(defn write
"Writes a Sonar Generic Execution formatted
(https://docs.sonarqube.org/latest/analysis/generic-test/) summary of the
given clojure.test/report messages to the given output directory.
Output may be configured by supplying the following options (or by binding the
corresponding dynamic vars):
:format-result - a function that converts a test result message into an
XML element (a map with keys [:tag :attrs :content]), or
nil if no element should be output (e.g. if the message
:type is :pass)
(default test-report-junit-xml.core/format-result)
:format-stacktrace - a function that takes a Throwable and returns a string
containing the formatted stacktrace (may not be used if
:format-result is configured)
(default test-report-sonar.core/format-stacktrace,
uses clojure.stacktrace/print-cause-trace)"
([output-dir messages]
(write output-dir messages {}))
([output-dir messages options]
(with-options options
(->> messages
summarize
:namespaces
test-executions
(write-reports-xml output-dir)))))
28 changes: 28 additions & 0 deletions test-report-sonar/src/test_report_sonar/utils.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
(ns test-report-sonar.utils
(:require
[clojure.string :as string]))


(defn nanos->millis [nanos]
(->> nanos (* 1e-6) int))


(defn guess-ns-ext
[nsname]
(cond
(string/starts-with? nsname "clj.") ".clj"
(string/starts-with? nsname "cljc.") ".cljc"
(string/starts-with? nsname "cljs.") ".cljs"
(string/starts-with? nsname "cljr.") ".cljr"
:else ".clj"))


(defn ns->path
[ns]
(let [nsname (-> ns ns-name)]
(-> nsname
(string/replace #"-" "_")
(string/replace #"\." "/")
(str (guess-ns-ext nsname)))))


93 changes: 93 additions & 0 deletions test-report-sonar/test/resources/input/messages.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
[{:type :begin-test-ns
:ns (find-ns 'example.first-test)
:time 51705939435917}
{:type :begin-test-var
:var #'example.first-test/nested
:time 51705941156806}
{:type :pass
:expected '(= 0 0)
:actual (list = 0 0)
:message nil
:time 51705942160982
:context (list "inner" "outer")}
{:type :end-test-var
:var #'example.first-test/nested
:time 51705942255394}
{:type :begin-test-var
:var #'example.first-test/passing
:time 51705942304461}
{:type :pass
:expected '(= 0 0)
:actual (list = 0 0)
:message nil
:time 51705942641504
:context ()}
{:type :pass
:expected '(= 0 0)
:actual (list = 0 0)
:message nil
:time 51705942719891
:context ()}
{:type :end-test-var
:var #'example.first-test/passing
:time 51705942761235}
{:type :begin-test-var
:var #'example.first-test/erroring
:time 51705942804163}
{:file "Numbers.java"
:line 158
:type :error
:expected '(= 0 (/ 0 0))
:actual (doto (ArithmeticException. "Divide by zero")
(.setStackTrace (into-array [(StackTraceElement. "clojure.lang.Numbers" "divide" "Numbers.java" 158)
(StackTraceElement. "clojure.lang.Numbers" "divide" "Numbers.java" 3808)
(StackTraceElement. "example.first_test$fn__1305" "invokeStatic" "first_test.clj" 17)
(StackTraceElement. "example.first_test$fn__1305" "invoke" "first_test.clj" 16)])))

:message nil
:time 51705943130574
:context ()}
{:type :end-test-var
:var #'example.first-test/erroring
:time 51705943173760}
{:type :begin-test-var
:var #'example.first-test/failing
:time 51705943223768}
{:file "first_test.clj"
:line 14
:type :fail
:expected '(= 0 1)
:actual '(not (= 0 1))
:message nil
:time 51705943734108
:context ()}
{:type :end-test-var
:var #'example.first-test/failing
:time 51705943780352}
{:type :end-test-ns
:ns (find-ns 'example.first-test)
:time 51705943839193}
{:type :begin-test-ns
:ns (find-ns 'example.second-test)
:time 51705943913222}
{:type :begin-test-var
:var #'example.second-test/passing
:time 51705944232368}
{:type :pass
:expected '(= 0 0)
:actual (list = 0 0)
:message nil
:time 51705944438664
:context ()}
{:type :end-test-var
:var #'example.second-test/passing
:time 51705944538554}
{:type :end-test-ns
:ns (find-ns 'example.second-test)
:time 51705944585988}
{:test 5
:pass 0
:fail 0
:error 0
:type :summary
:time 51705944686193}]
11 changes: 11 additions & 0 deletions test-report-sonar/test/resources/output/testExecutions.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?><testExecutions version="1"><file path="example/first_test.clj"><testCase name="nested" duration="1"></testCase><testCase name="passing" duration="0"></testCase><testCase name="erroring" duration="0"><error message="error: Divide by zero">error: Divide by zero
expected: (= 0 (/ 0 0))
actual: java.lang.ArithmeticException: Divide by zero
at clojure.lang.Numbers.divide (Numbers.java:158)
clojure.lang.Numbers.divide (Numbers.java:3808)
example.first_test$fn__1305.invokeStatic (first_test.clj:17)
example.first_test/fn (first_test.clj:16)
</error></testCase><testCase name="failing" duration="0"><failure message="failure">failure
expected: (= 0 1)
actual: (not (= 0 1))
at: first_test.clj:14</failure></testCase></file><file path="example/second_test.clj"><testCase name="passing" duration="0"></testCase></file></testExecutions>

0 comments on commit cf4d2d1

Please sign in to comment.