TestFileFinder

A Ruby gem for detecting test files associated with input files.

Installation

Add this line to your application's Gemfile:

gem 'test_file_finder', git: 'https://gitlab.com/gitlab-org/ci-cd/test_file_finder.git'

And then execute:

bundle install

Usage

Ruby

The FileFinder class has exactly one method (#test_files) that returns existing test files likely to be associated with the files you passed into #initialize:

TestFileFinder::FileFinder.new(paths: file_paths).test_files

Command Line

TestFileFinder is packed up into the tff executable, which can be used from the command line:

$ file_paths="app/models/widget.rb"
$ tff $file_paths
spec/models/widget_spec.rb

You can use it in a script, for example:

$ rspec $(tff $(git diff --name-only master..head))

Options

-f mapping

TestFileFinder can be used with an optional YAML mapping file to specify the mapping from a source file to a test file. Both, source and test can be lists to map multiple files.

The mapping file is a yaml file containing to entries to match source file patterns to its test files.

The source file pattern may be an exact file path or a regular expression. The regular expression may include capturing groups to be used to identify the test file. To refer to a captured value in the test file, use the %s placeholder. For example:

mapping:
  # maps `app/file.rb` to `spec/file_spec.rb`
  - source: 'app/(.+)\.rb'
    test: 'spec/%s_spec.rb'
  # maps `db/schema.rb` and `db/migrate/*` to `spec/db/schema_spec.rb`
  - source:
      - 'db/schema.rb'
      - 'db/migrate/(.+)'
    test: spec/db/schema_spec.rb
  # maps `ee/app/models/ee/user.rb` to `ee/spec/models/user_spec.rb` and `spec/models/user_spec.rb`
  - source: ee/app/(.*/)ee/(.+)\.rb
    test:
      - 'spec/%s%s_spec.rb'
      - 'ee/spec/%s%s_spec.rb'

The patterns may include named captures in test files and referenced by its name in source files. For example:

mapping:
  # maps `lib/api/issues.rb` to `spec/requests/api/issues/issues_spec.rb`
  - source: 'lib/api/(?<name>.*)\.rb'
    test: 'spec/requests/api/%{name}/%{name}_spec.rb'

Numbered and named captures cannot be mixed in a single pattern.

A test file containing metacharacters like *, {}, [], or ? is considered a file name pattern and globbing is used to match the resulting test files.

For example:

mapping:
  # maps `lib/api/issues.rb` to tests following this pattern
  # `spec/requests/api/issues/*_spec.rb`
  - source: 'lib/api/(.*)\.rb'
    test: 'spec/requests/api/%s/*_spec.rb'

Command line example:

$ file_paths="app/models/widget.rb"
$ tff -f mapping.yml $file_paths
spec/models/widget_spec.rb

Ruby example:

tff = TestFileFinder::FileFinder.new(paths: file_paths)
tff.use TestFileFinder::MappingStrategies::PatternMatching.load('mapping.yml')
tff.test_files

An example mapping file is available in fixtures/mapping.yml.

--json mapping

TestFileFinder can be used with an optional JSON mapping file to specify the mapping from a source file to test files.

The mapping file is a JSON file containing a JSON object. The keys in the JSON are the source files, and the values are arrays containing the test files. For example:

{
  "app/models/project.rb": [
    "spec/models/project_spec.rb",
    "spec/controllers/projects_controller_spec.rb"
  ],
  "app/controllers/projects_controller.rb": [
    "spec/controllers/projects_controller_spec.rb"
  ]
}

Command line example:

$ file_paths="app/models/project.rb"
$ tff --json mapping.json $file_paths
spec/models/project_spec.rb
spec/controllers/projects_controller_spec.rb

Ruby example:

tff = TestFileFinder::FileFinder.new(paths: file_paths)
tff.use TestFileFinder::MappingStrategies::DirectMatching.load('mapping.json')
tff.test_files

An example mapping file is available in fixtures/mapping.json.

It's also possible to return a percentage of test files with the limit_percentage and limit_min arguments (only available in ruby):

tff = TestFileFinder::FileFinder.new(paths: file_paths)
# Return 50% of the test files, with a minimum of 14 test files.
tff.use TestFileFinder::MappingStrategies::DirectMatching.load('mapping.json', limit_percentage: 50, limit_min: 14)
tff.test_files
--project-path project_path and --merge-request-iid merge_request_iid

TestFileFinder can be used with both GitLab project path and merge request IID to get the test files that failed in the project merge request.

Command line example:

$ tff --project-path project/path --merge-request-iid 123
spec/models/widget_spec.rb

Ruby example:

tff = TestFileFinder::FileFinder.new(paths: file_paths)
tff.use TestFileFinder::MappingStrategies::GitlabMergeRequestRspecFailure.new(project_path: 'project/path', merge_request_iid: 123)
tff.test_files

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install.

Activate lefthook locally

lefthook install

Release Process

We release test_file_finder on an ad-hoc basis. There is no regularity to when we release, we just release when we make a change - no matter the size of the change.

To release a new version:

  1. Create a Merge Request.
  2. Use Merge Request template Release.md.
  3. Follow the instructions.
  4. After the Merge Request has been merged, a new gem version is published automatically.

See !49 as an example.

Contributing

Bug reports and merge requests are welcome in GitLab at https://gitlab.com/gitlab-org/ci-cd/test_file_finder.