Last active
November 30, 2022 11:09
-
-
Save ianmuge/bfc1e036de7294ad7cd55329268b4ce8 to your computer and use it in GitHub Desktop.
Messing around with Babashka support Code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Build and Test | |
on: | |
push: | |
paths-ignore: | |
- '*.md' | |
jobs: | |
test: | |
runs-on: ubuntu-22.04 | |
name: "Runner #${{ matrix.ci_node_index }}: Run test suite in parallel" | |
strategy: | |
fail-fast: false | |
matrix: | |
ci_node_total: [5] | |
ci_node_index: [0,1,2,3,4] | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v2 | |
- name: Setup java | |
uses: actions/setup-java@v3 | |
with: | |
java-version: '11' | |
distribution: 'adopt' | |
- name: Install clojure tools | |
uses: DeLaGuardo/setup-clojure@9.5 | |
with: | |
lein: 2.9.8 # Leiningen | |
clj-kondo: 2021.04.23 # Clj-kondo | |
cli: 1.11.1.1105 | |
bb: 1.0.165 | |
- name: Cached Dependencies | |
uses: actions/cache@v3 | |
with: | |
path: | | |
~/.m2/repository | |
key: ${{ runner.os }}-project-${{ hashFiles('**/project.clj') }} | |
restore-keys: ${{ runner.os }}-project | |
- name: Install dependencies | |
run: lein deps | |
- name: Download artifact | |
id: download-artifact | |
uses: dawidd6/action-download-artifact@v2 | |
continue-on-error: true | |
with: | |
github_token: ${{secrets.GITHUB_TOKEN}} | |
workflow: build-and-test.yml | |
workflow_conclusion: "completed" | |
name: test-artifacts | |
path: ./target/test2junit | |
check_artifacts: true | |
search_artifacts: true | |
if_no_artifact_found: warn | |
- name: Split timing | |
id: split-tests | |
run: | | |
bb split --workers ${{ matrix.ci_node_total }} --index ${{ matrix.ci_node_index }} --plan | |
echo ::set-output name=test-suite::$(bb split --workers ${{ matrix.ci_node_total }} --index ${{ matrix.ci_node_index }}) | |
- name: Cleanup previous tests | |
run: | | |
lein clean | |
- name: Run tests | |
env: | |
_JAVA_OPTIONS: '-XX:MaxRAMPercentage=80' | |
run: | | |
echo "${{ steps.split-tests.outputs.test-suite }}" | |
echo "${{ steps.split-tests.outputs.test-suite }}" | xargs lein test2junit | |
- name: Test report | |
uses: dorny/test-reporter@v1 | |
if: always() # run this step even if previous step failed | |
with: | |
name: "Runner #${{ matrix.ci_node_index }}: Clojure tests results" | |
path: target/test2junit/xml/*.xml | |
reporter: java-junit | |
fail-on-error: false | |
- name: Upload test artifact | |
uses: actions/upload-artifact@v3 | |
if: always() | |
with: | |
name: test-artifacts | |
path: | | |
./target/test2junit | |
retention-days: 7 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{:paths ["."] | |
:min-bb-version "1.0.165" | |
:tasks | |
{:requires ([ci.test-helper :as cth]) | |
split {:docs "Split tests based on timings" | |
:task (cth/run-split *command-line-args*)} | |
}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bb | |
(ns ci.test-helper | |
(:require | |
[babashka.fs :as fs] | |
[clojure.data.xml :refer :all] | |
[clojure.java.shell :refer :all] | |
[clojure.string :as str] | |
[clojure.tools.cli :as cli] | |
[clojure.repl :as repl])) | |
(def cli-options | |
;; Options with a required argument | |
[["-w" "--workers WORKERS" "Number of Workers/Runners" | |
:default 30 | |
:parse-fn parse-long] | |
["-i" "--index INDEX" "Runner Index" | |
:parse-fn parse-long] | |
["-b" "--test-results-base-path TEST_RESULTS_BASE_PATH" "Test results base path" | |
:default "./target/test2junit/" | |
:parse-fn str] | |
["-p" "--test-results-pattern TEST_RESULTS_PATTERN" "Test results pattern" | |
:default "**/*.xml" | |
:parse-fn str] | |
["-t" "--tests-path TESTS_PATH" "Tests path" | |
:default "test/" | |
:parse-fn str] | |
["-h" "--help"] | |
["-p" "--plan" "Execution plan" | |
:default false]]) | |
(defn filename-to-classname [filename] | |
(-> filename | |
(str/replace #"^target/test2junit/xml/(.+?)\.xml$" "$1") | |
(str/replace #"^test/(.+?)\.clj$" "$1") | |
(str/replace #"/" ".") | |
repl/demunge)) | |
(defn add-to-bin [bin [_ size :as item]] | |
{:size (+ (:size bin) size) | |
:items (conj (:items bin) item)}) | |
(defn select-smallest-bin [bins] | |
(first (apply min-key | |
(fn [[_ bin]] (:size bin)) | |
(map-indexed vector bins)))) | |
(defn pack | |
"Simple first fit packing algorithm." | |
[items no-fit-fn] | |
(let [init-bins [{:size 0 :items []}]] | |
(reduce no-fit-fn init-bins items))) | |
(defn grow-bins [bins item] | |
(conj bins (add-to-bin {:size 0 :items []} item))) | |
(defn add-to-smallest-bin [n bins item] | |
(if (< (count bins) n) | |
(grow-bins bins item) | |
(update-in bins [(select-smallest-bin bins)] | |
#(add-to-bin % item)))) | |
(defn pack-n-bins | |
[items n] | |
(pack items (partial add-to-smallest-bin n))) | |
(defn parse-to-double [string] | |
"Decimal seperator varies depending on system locale, string replacement standardizes this" | |
(-> string | |
(str/replace #"," ".") | |
parse-double)) | |
(defn try-parse-xml [xml-content filename] | |
(try | |
(parse-str xml-content) | |
(catch Exception _ | |
{:tag :testsuite | |
:attrs {:name (filename-to-classname(str filename)) | |
:errors 1 | |
:failures 1 | |
:tests 0 | |
:time 1 | |
:timestamp 0} | |
:content []} | |
))) | |
(defn read-file [file] | |
(-> file | |
str | |
slurp)) | |
(defn parse-test-results [file] | |
(-> (read-file file) | |
(try-parse-xml file) | |
:attrs | |
((juxt :name :time)))) | |
(defn parse-test-files [file test-results] | |
(let [file-content (read-file file)] | |
(when (str/includes? file-content "deftest") | |
{:filename (str file) | |
:line-count (count (str/split-lines file-content)) | |
:test-time ((keyword (filename-to-classname file)) test-results 1) | |
}))) | |
(defn run-split [arglist] | |
(if-let [errors (:errors (cli/parse-opts arglist cli-options))] | |
(print errors) | |
(let [options (:options (cli/parse-opts arglist cli-options)) | |
{:keys [workers index tests-path test-results-base-path test-results-pattern]} options | |
test-results (when (fs/exists? test-results-base-path) | |
(->> (fs/glob test-results-base-path test-results-pattern {:recursive true}) | |
(map parse-test-results) | |
(into {}) | |
(#(update-vals % parse-to-double)) | |
(#(update-keys % keyword)))) | |
aggregated-results (->> (fs/glob tests-path "**/*.clj" {:recursive true}) | |
(keep #(parse-test-files % test-results)) | |
(sort-by :test-time #(compare %2 %1))) | |
items (->> (pack-n-bins | |
(map vector | |
(map (comp filename-to-classname :filename) aggregated-results) | |
(map :test-time aggregated-results)) | |
workers)) | |
runner-spec (-> items | |
(get index)) | |
runner-items (->> runner-spec | |
(:items) | |
(into {}) | |
keys)] | |
(if (:plan options) | |
(do | |
(println (str "Number of workers: " workers)) | |
(println (str "Runner Index: " index)) | |
(println (str "Estimated runtime: " (:size runner-spec))) | |
(println (str "Planned classes: " runner-items))) | |
(doseq [item runner-items] | |
(print (str item " "))))))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment