The project is in a healthy, maintained state
A tool to optimize parallel test execution by analyzing JUnit XML results and distributing test files across buckets.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Runtime

~> 2.1, >= 2.1.0
>= 1.15, < 2.0
~> 1.0
>= 2.4, < 3.0
 Project Readme

JunitTimingSplitter

GemVersion CI

JunitTimingSplitter is a tool written in Ruby designed to parse JUnit XML result files and distribute tests into evenly balanced buckets based on their total execution time. This tool is especially beneficial for optimizing parallel test execution in CI pipelines, drawing inspiration from CircleCI's --split-by=timings feature.


Features

  • Parse JUnit XML files (*.xml) to extract test file execution times.
  • Aggregate timings across multiple XML files.
  • Split tests into evenly distributed buckets based on execution time.
  • Analyze bucket distribution for load balancing.
  • Compatible with glob patterns to process multiple XML files.

Installation

Add this gem to your Gemfile:

gem install junit_timing_splitter

or

# Gemfile
gem 'junit_timing_splitter', '~> 1.0.0'

Then,

bundle install

Usage

  1. Generate Junit XML Results Analyze how test files will be distributed across N buckets:
bundle exec junit_timing_splitter split --files="test/fixtures/results_0[0|1].xml" --buckets=2 --schema="output/buckets.json"
Detected 2 files
Detected file: test/fixtures/results_00.xml
Detected file: test/fixtures/results_01.xml
Buckets written to buckets.json
  1. Get Files for a Specific Bucket
bundle exec junit_timing_splitter show --schema="output/buckets.json" --bucket=0
bundle exec junit_timing_splitter show --schema="output/buckets.json" --bucket=1
# bucket=0
./spec/models/simple_part1_spec.rb

# bucket=1
./spec/models/simple_part2_spec.rb
  1. Find out the missing test cases from the existing schema
bundle exec junit_timing_splitter scan --schema="output/buckets.json" --files="./spec/**/*.rb"
# if no missing file
No missing test files detected.

# if missing exist
Missing test files:
<absolute path>/spec/models/missing_test.rb
  1. Merge the missing test cases from the existing schema
bundle exec junit_timing_splitter merge --schema="output/buckets.json" --files="./spec/**/*_spec.rb"

Development

# rebuild
rm junit_timing_splitter-1.1.0.gem && gem build JunitTimingSplitter.gemspec && gem install junit_timing_splitter-1.1.0.gem
# testing
ruby test/test_junit_timing_splitter.rb --verbose

Run options: --verbose --seed 58633

# Running:

TestJunitTimingSplitter#test_merge_missing_files = 0.00 s = .
TestJunitTimingSplitter#test_simple_case = Detected 2 files
Detected file: test/fixtures/results_00.xml
Detected file: test/fixtures/results_01.xml
0.00 s = .
TestJunitTimingSplitter#test_scan_without_missing_files = 0.00 s = .
TestJunitTimingSplitter#test_all_parsed_files = 0.00 s = .
TestJunitTimingSplitter#test_imbalanced_case = Detected 2 files
Detected file: test/fixtures/results_02.xml
Detected file: test/fixtures/results_03.xml
0.00 s = .
TestJunitTimingSplitter#test_scan_missing_files = 0.00 s = .
TestJunitTimingSplitter#test_files_for_bucket = 0.00 s = .

Finished in 0.008489s, 824.5965 runs/s, 3416.1856 assertions/s.

7 runs, 29 assertions, 0 failures, 0 errors, 0 skips

License

Copyright (c) 2024 Kim Yu Ng, released under the MIT license