{"id":15022429,"url":"https://github.com/indydevs/flutter","last_synced_at":"2025-10-24T01:30:38.118Z","repository":{"id":60294683,"uuid":"533390765","full_name":"indydevs/flutter","owner":"indydevs","description":"Intelligent test selection for ruby based on incremental code changes","archived":false,"fork":false,"pushed_at":"2024-02-03T16:49:31.000Z","size":556,"stargazers_count":13,"open_issues_count":1,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-10-10T01:01:09.549Z","etag":null,"topics":["minitest","rspec","ruby","testing"],"latest_commit_sha":null,"homepage":"https://flutter.indydevs.org","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/indydevs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-09-06T15:36:52.000Z","updated_at":"2023-08-15T16:23:20.000Z","dependencies_parsed_at":"2024-09-21T18:30:54.747Z","dependency_job_id":"5fc3336e-87b5-470e-807f-a0fa7107e55d","html_url":"https://github.com/indydevs/flutter","commit_stats":{"total_commits":169,"total_committers":2,"mean_commits":84.5,"dds":"0.011834319526627168","last_synced_commit":"a4bf540d6d925eca95d338d3cc724958d93a641b"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/indydevs%2Fflutter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/indydevs%2Fflutter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/indydevs%2Fflutter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/indydevs%2Fflutter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/indydevs","download_url":"https://codeload.github.com/indydevs/flutter/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219867198,"owners_count":16555821,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["minitest","rspec","ruby","testing"],"created_at":"2024-09-24T19:57:56.372Z","updated_at":"2025-10-24T01:30:32.733Z","avatar_url":"https://github.com/indydevs.png","language":"Ruby","readme":"# Flutter: Intelligent test selection based on incremental code changes\n\n[![CI](https://github.com/indydevs/flutter/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/indydevs/flutter/actions/workflows/main.yml)\n[![codecov](https://codecov.io/github/indydevs/flutter/branch/main/graph/badge.svg?token=XANF37D9C1)](https://codecov.io/github/indydevs/flutter)\n[![Gem](https://img.shields.io/gem/v/flutter)](https://rubygems.org/gems/flutter)\n[![Docs](https://img.shields.io/badge/yard-docs-blue.svg)](https://flutter.indydevs.org)\n\n```\n __   __\n(  \\,/  )\n \\_ | _/\n (_/ \\_)\n\n```\nFlutter plugs in to your [RSpec](https://rspec.info/) or [Minitest](https://github.com/minitest/minitest) test suites to\nrun only the tests that exercise the code that has changed since the last run.\n\nIt can be used in local development as a live incremental test runner in\ncombination with [Guard](https://github.com/guard/guard) (See examples for [minitest](#with-guard) and [rspec](#with-guard-1) respectively)\nor in continuous integration environments to only run the subset of tests affected by a pull request or changeset\n(See [CI Recipes](#configuring-flutter-in-continuous-integration)).\n\n## How?\nFlutter tracks each method call within the context of each test case in your test suite and persists this mapping along with\na signature for all the methods that were exercised. On subsequent runs Flutter intercepts test enumeration and skips any test if\n**ALL** the following conditions are true:\n\n- The test was seen before\n- The source of the test has not changed\n- All the methods exercised in the last recorded run have no changes in their source\n\n## Usage\n\n### Minitest\n\n- Add the gem as a dependency\n\n  ```ruby\n  gem \"flutter\"\n  ```\n- Include it in your `test_helper.rb`:\n\n  ```ruby\n  require 'flutter'\n  ```\n- Enable \u0026 configure it in your `test_helper.rb` (See [Configuration options](#configuration-options) for available options):\n\n  ```ruby\n  Flutter.configure do |config|\n    config.enabled = true\n  end\n  ```\n- Run your test suite the way you normally would (for example: `bundle exec rake test`). The first run will run all\n  tests. After the test run has completed the mapping of test cases to exercised code will be persisted in the `./.flutter`\n  folder.\n- Now make changes and run the test suite again. Only the relevant tests will be executed.\n\n#### With guard\nUsing the same configuration as above (and assuming that the application\nsources are in the `./lib` folder while the tests are in the `./test` folder)\nadd the following to your `Guardfile`:\n\n```ruby\nguard :minitest, test_folders: [\"test\"] do\n  watch(%r{^(test|lib)/(.*/)?([^/]+)\\.rb$}) { \"test\" }\nend\n```\n\n### RSpec\n\n- Add the gem as a dependency:\n\n  ```ruby\n  gem \"flutter\"\n  ```\n- Include the plugin in your `spec_helper.rb`:\n\n  ```ruby\n  require 'flutter'\n  ```\n- Enable \u0026 configure it in your `spec_helper.rb` (See [Configuration options](#configuration-options) for available options):\n\n  ```ruby\n  Flutter.configure do |config|\n    config.enabled = true\n  end\n  ```\n- Run your specs the way you normally would (for example: `bundle exec rspec`). The first run will run all\n  tests. After the test run has completed the mapping of test cases to exercised code will be persisted in the `./.flutter`\n  folder.\n- Now make changes and run rspec again. Only the relevant examples will be executed.\n\n#### With guard\nUsing the same configuration as above (and assuming that the application\nsources are in the `./app` \u0026 `./lib` folders while the specs are in the `./spec` folder)\nadd the following to your `Guardfile`:\n\n```ruby\nguard :rspec, cmd: \"rspec\" do\n  watch(%r{^(spec|app|lib)/(.*/)?([^/]+)\\.rb$}) { \"spec\" }\nend\n```\n\n### Configuration options\n|      option       | Description                                                    |                  Type                   |             Default             |\n|:-----------------:|:---------------------------------------------------------------|:---------------------------------------:|:-------------------------------:|\n|     `enabled`     | Whether flutter is enabled                                     |         `TrueClass, FalseClass`         |             `true`              |\n|     `sources`     | List of glob style expressions to select source files to track |                  `Set`                  |       `[\"#{Dir.pwd}/*\"]`        |\n|   `exclusions`    | List of glob style expressions to exclude sources files        |                  `Set`                  |   `[\"#{Dir.pwd}/vendor}/*\"]`    |\n|  `storage_class`  | The storage class to use for persisting the state              | `Flutter::Persistence::AbstractStorage` | `Flutter::Persistence::Marshal` |\n| `storage_options` | Additional options to pass to the storage class                |                 `Hash`                  |     `{path: './.flutter'}`      |\n|  `reset_storage`  | Whether to clear the persisted state on initialization         |         `TrueClass, FalseClass`         |             `false`             |\n\n\n\n\n\n## Configuring flutter in continuous integration\n\nFlutter can be used in continuous integration environments to speed up the turn\naround time from running tests by only running tests affected by the changes\nin a pull request.\n\n### Github Actions\nThe following example workflow with github actions does the following:\n- Always run all tests on the `main` branch\n- Only run tests affected by the \"current\" commit for CI workflows triggered by a `push` event on other branches\n- If the CI workflow is triggered due to a `pull_request` event, run all tests affected by all commits in the branch\n  (by comparing against the branch point of the pull request)\n\n```yaml\n    # Get the commit where this branch diverges from origin/main\n    - name: Retrieve branch point\n      if: github.event_name == 'pull_request'\n      run: |\n        echo \"::set-output name=KEY::$(diff -u \u003c(git rev-list --first-parent origin/main) \u003c(git rev-list --first-parent HEAD) | sed -ne 's/^ //p' | head -1)\"\n      id: cache_keys\n    # Use the always-upload-cache action to:\n    #  - Restore the flutter state from cache from either the branch point (if it was set in the previous step)\n    #    or the last run in the current branch\n    #  - After the run cache the flutter state using the current commit hash as the hash key\n    - name: Setup flutter state\n      id: flutter-state\n      uses: pat-s/always-upload-cache@v2.1.5\n      env:\n        cache-name: cache-flutter-state\n      with:\n        path: .flutter\n        key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ matrix.ruby-version }}-${{ github.sha }}\n        restore-keys: |\n          ${{ runner.os }}-build-${{ env.cache-name }}-${{ matrix.ruby-version }}-${{ steps.cache_keys.outputs.KEY }}\n    # If this is a push event on the main branch, clear the flutter state\n    # so that all tests are run and a full state is cached on the main branch\n    - name: Clear flutter state\n      if: github.event_name == 'push' \u0026\u0026 startsWith(github.ref, 'refs/heads/main')\n      run: rm -rf .flutter\n```\n\u003e **Note**\n\u003e The exact CI configuration would ofcourse depend on your workflow and confidence in selectively\n\u003e running tests for pull requests.\n\n\u003e **Warning**\n\u003e Selectively running tests in a pull request would show a drop in coverage if you are collecting\n\u003e and/or using code coverage as a \"Check\". One way to make Flutter work hand in hand with code\n\u003e coverage checks is to only validate that the diff in the pull request has a 100% coverage. For\n\u003e example with [codecov](https://docs.codecov.com/docs/commit-status#section-project-status) this can be\n\u003e achieved by only enabling the `project` status for the main branch and `patch` status otherwise.\n\n## Related work\n\nFlutter is heavily inspired by [testmon](https://github.com/tarpas/pytest-testmon)\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nThis project uses [overcommit](https://github.com/sds/overcommit) to enforce standards. Enable the precommit hooks in your local checkout by running: `overcommit --sign`\n\nTo install this gem onto your local machine, run `bundle exec rake install`.\n\n### Releasing a new version\n- Ensure that the [Unreleased](./CHANGELOG.md#Unreleased) section of the changelog is up to date\n  and contains useful details.\n- Create a new release using the `release` rake task as follows (for more details about specifying the version change\n  run `gem bump --help` which is the command used by the task):\n  - Patch release `bundle exec rake release[\"patch\"]`\n  - Minor release `bundle exec rake release[\"minor\"]`\n  - Major release `bundle exec rake release[\"major\"]`\n  \u003e **Note**\n  \u003e The `release` rake task automates updating the changelog \u0026 version, committing the changes \u0026 creating a new tag\n- Push the tag. The CI workflow for tag pushes will take care of publishing the gem \u0026 creating a github release.\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/indydevs/flutter. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/indydevs/flutter/blob/main/CODE_OF_CONDUCT.md).\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n\n## Code of Conduct\n\nEveryone interacting in the Flutter project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/indydevs/flutter/blob/main/CODE_OF_CONDUCT.md).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Findydevs%2Fflutter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Findydevs%2Fflutter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Findydevs%2Fflutter/lists"}