From f344fd084f5e26485700d9fefc7fa7bd668f01dc Mon Sep 17 00:00:00 2001 From: Peter Boling Date: Sat, 11 Jan 2025 09:23:56 +0700 Subject: [PATCH 01/23] =?UTF-8?q?=F0=9F=93=8C=20Ruby=203.4.1=20for=20devel?= =?UTF-8?q?opment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .tool-versions | 1 + 1 file changed, 1 insertion(+) create mode 100644 .tool-versions diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..041df9a --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +ruby 3.4.1 From 090880b68abf2d0f627976fe6591a66222e6309d Mon Sep 17 00:00:00 2001 From: Peter Boling Date: Sat, 11 Jan 2025 09:24:32 +0700 Subject: [PATCH 02/23] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Latest=20dependencie?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gemfile.lock | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index a95169f..3615397 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -9,28 +9,28 @@ GEM remote: https://rubygems.org/ specs: coderay (1.1.3) - diff-lcs (1.5.0) - method_source (1.0.0) - parallel (1.22.1) - parallel_tests (4.2.0) + diff-lcs (1.5.1) + method_source (1.1.0) + parallel (1.26.3) + parallel_tests (4.9.0) parallel - pry (0.14.2) + pry (0.15.2) coderay (~> 1.1) method_source (~> 1.0) - rake (13.0.6) - rspec (3.12.0) - rspec-core (~> 3.12.0) - rspec-expectations (~> 3.12.0) - rspec-mocks (~> 3.12.0) - rspec-core (3.12.1) - rspec-support (~> 3.12.0) - rspec-expectations (3.12.2) + rake (13.2.1) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.2) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.3) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-mocks (3.12.4) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.2) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-support (3.12.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.2) PLATFORMS ruby @@ -41,4 +41,4 @@ DEPENDENCIES turbo_tests! BUNDLED WITH - 2.4.19 + 2.6.2 From 99c84d767562254438934e36af5c778cbee1f996 Mon Sep 17 00:00:00 2001 From: Peter Boling Date: Sat, 11 Jan 2025 09:38:44 +0700 Subject: [PATCH 03/23] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Upgrade=20to=20actio?= =?UTF-8?q?ns/checkout@v4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 657d595..e552093 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,7 +17,7 @@ jobs: os: [ ubuntu-latest, windows-latest ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{matrix.ruby}} From 4c06920fadf4524f2216b60b5466ba866b54cd4e Mon Sep 17 00:00:00 2001 From: Peter Boling Date: Sat, 11 Jan 2025 10:00:52 +0700 Subject: [PATCH 04/23] =?UTF-8?q?=E2=9E=95=20appraisals?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gemfile.lock | 6 ++++++ turbo_tests.gemspec | 1 + 2 files changed, 7 insertions(+) diff --git a/Gemfile.lock b/Gemfile.lock index 3615397..897849f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -8,6 +8,10 @@ PATH GEM remote: https://rubygems.org/ specs: + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) coderay (1.1.3) diff-lcs (1.5.1) method_source (1.1.0) @@ -31,11 +35,13 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-support (3.13.2) + thor (1.3.2) PLATFORMS ruby DEPENDENCIES + appraisal (~> 2.5) pry (~> 0.14) rake (~> 13.0) turbo_tests! diff --git a/turbo_tests.gemspec b/turbo_tests.gemspec index 52064e1..cb0fd36 100644 --- a/turbo_tests.gemspec +++ b/turbo_tests.gemspec @@ -22,6 +22,7 @@ Gem::Specification.new do |spec| spec.add_dependency "rspec", ">= 3.10" spec.add_dependency "parallel_tests", ">= 3.3.0", "< 5" + spec.add_development_dependency("appraisal", "~> 2.5") spec.add_development_dependency "pry", "~> 0.14" # Specify which files should be added to the gem when it is released. From 53b22cd8d148c050db4d75e06f54838cdcf3bc06 Mon Sep 17 00:00:00 2001 From: Peter Boling Date: Tue, 14 Jan 2025 10:49:43 +0700 Subject: [PATCH 05/23] =?UTF-8?q?=F0=9F=94=A7=20Setup=20appraisals?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/dependabot.yml | 13 ++ .github/workflows/codeql-analysis.yml | 70 +++++++++ .github/workflows/coverage.yml | 125 ++++++++++++++++ .github/workflows/dependency-review.yml | 20 +++ .github/workflows/heads.yml | 87 +++++++++++ .github/workflows/legacy.yml | 71 +++++++++ .github/workflows/style.yml | 68 +++++++++ .github/workflows/supported.yml | 94 ++++++++++++ .github/workflows/tag_and_release.yml | 2 +- .github/workflows/tests.yml | 2 + .github/workflows/unsupported.yml | 71 +++++++++ .gitignore | 4 + .rubocop.yml | 5 + .rubocop_gradual.lock | 63 ++++++++ Appraisal.boot.gemfile | 8 + Appraisal.root.gemfile | 16 ++ Appraisals | 67 +++++++++ Gemfile | 20 ++- Gemfile.lock | 120 +++++++++++++++ README.md | 11 +- Rakefile | 62 +++++++- appraisal-hack.gemfile | 40 +++++ bin/turbo_tests | 2 +- .../rspec/errors_outside_of_examples_spec.rb | 4 +- fixtures/rspec/failing_spec.rb | 6 +- fixtures/rspec/no_method_error_spec.rb | 6 +- fixtures/rspec/pending_exceptions_spec.rb | 8 +- gemfiles/appraisal-hack.gemfile | 40 +++++ gemfiles/audit.gemfile | 12 ++ gemfiles/audit.gemfile.lock | 65 ++++++++ gemfiles/coverage.gemfile | 12 ++ gemfiles/coverage.gemfile.lock | 88 +++++++++++ gemfiles/jruby_head.gemfile | 10 ++ gemfiles/modular/audit.gemfile | 5 + gemfiles/modular/coverage.gemfile | 6 + gemfiles/modular/style.gemfile | 12 ++ gemfiles/ruby_2_7.gemfile | 10 ++ gemfiles/ruby_3_0.gemfile | 10 ++ gemfiles/ruby_3_1.gemfile | 10 ++ gemfiles/ruby_3_2.gemfile | 10 ++ gemfiles/ruby_3_3.gemfile | 10 ++ gemfiles/ruby_3_4.gemfile | 10 ++ gemfiles/ruby_head.gemfile | 10 ++ gemfiles/style.gemfile | 12 ++ gemfiles/style.gemfile.lock | 141 ++++++++++++++++++ gemfiles/truffleruby_head.gemfile | 10 ++ lib/turbo_tests.rb | 52 ++++--- lib/turbo_tests/cli.rb | 28 ++-- lib/turbo_tests/json_rows_formatter.rb | 46 +++--- lib/turbo_tests/reporter.rb | 52 ++++--- lib/turbo_tests/runner.rb | 41 +++-- lib/utils/hash_extension.rb | 4 +- spec/cli_spec.rb | 48 +++--- spec/spec_helper.rb | 1 - spec/turbo_tests_spec.rb | 2 +- turbo_tests.gemspec | 12 +- 56 files changed, 1674 insertions(+), 160 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/codeql-analysis.yml create mode 100644 .github/workflows/coverage.yml create mode 100644 .github/workflows/dependency-review.yml create mode 100644 .github/workflows/heads.yml create mode 100644 .github/workflows/legacy.yml create mode 100644 .github/workflows/style.yml create mode 100644 .github/workflows/supported.yml create mode 100644 .github/workflows/unsupported.yml create mode 100644 .rubocop.yml create mode 100644 .rubocop_gradual.lock create mode 100644 Appraisal.boot.gemfile create mode 100644 Appraisal.root.gemfile create mode 100644 Appraisals create mode 100644 appraisal-hack.gemfile create mode 100644 gemfiles/appraisal-hack.gemfile create mode 100644 gemfiles/audit.gemfile create mode 100644 gemfiles/audit.gemfile.lock create mode 100644 gemfiles/coverage.gemfile create mode 100644 gemfiles/coverage.gemfile.lock create mode 100644 gemfiles/jruby_head.gemfile create mode 100644 gemfiles/modular/audit.gemfile create mode 100644 gemfiles/modular/coverage.gemfile create mode 100644 gemfiles/modular/style.gemfile create mode 100644 gemfiles/ruby_2_7.gemfile create mode 100644 gemfiles/ruby_3_0.gemfile create mode 100644 gemfiles/ruby_3_1.gemfile create mode 100644 gemfiles/ruby_3_2.gemfile create mode 100644 gemfiles/ruby_3_3.gemfile create mode 100644 gemfiles/ruby_3_4.gemfile create mode 100644 gemfiles/ruby_head.gemfile create mode 100644 gemfiles/style.gemfile create mode 100644 gemfiles/style.gemfile.lock create mode 100644 gemfiles/truffleruby_head.gemfile diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..46f1c90 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +version: 2 +updates: + - package-ecosystem: bundler + directory: "/" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + ignore: + - dependency-name: "rubocop-lts" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..502c2a0 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,70 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ main, "*-stable" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ main, "*-stable" ] + schedule: + - cron: '35 1 * * 5' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'ruby' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://git.io/codeql-language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000..c4e8f52 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,125 @@ +name: Test Coverage + +env: + K_SOUP_COV_MIN_BRANCH: 80 + K_SOUP_COV_MIN_LINE: 91 + K_SOUP_COV_MIN_HARD: true + K_SOUP_COV_DO: true + K_SOUP_COV_COMMAND_NAME: "RSpec Coverage" + +on: + push: + branches: + - 'main' + tags: + - '!*' # Do not execute on tags + pull_request: + branches: + - '*' + # Allow manually triggering the workflow. + workflow_dispatch: + +permissions: + contents: read + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + # The concurrency group contains the workflow name and the branch name. + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + test: + name: Specs with Coverage - Ruby ${{ matrix.ruby }} ${{ matrix.name_extra || '' }} + if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} + env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile + strategy: + fail-fast: false + matrix: + include: + # Coverage + - ruby: "3.4" + appraisal: "coverage" + exec_cmd: "turbo_tests -n4" + gemfile: "Appraisal.root" + rubygems: latest + bundler: latest + + steps: + - uses: amancevice/setup-code-climate@v2 + name: CodeClimate Install + if: ${{ github.event_name != 'pull_request' }} + with: + cc_test_reporter_id: ${{ secrets.CC_TEST_REPORTER_ID }} + + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Ruby & RubyGems + uses: ruby/setup-ruby@v1 + with: + ruby-version: "${{ matrix.ruby }}" + rubygems: "${{ matrix.rubygems }}" + bundler: "${{ matrix.bundler }}" + bundler-cache: false + + - name: CodeClimate Pre-build Notification + run: cc-test-reporter before-build + if: ${{ github.event_name != 'pull_request' }} + continue-on-error: ${{ matrix.experimental != 'false' }} + + # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) + # We need to do this first to get appraisal installed. + # NOTE: This does not use the root Gemfile at all. + # However, because we hack appraisal in our root gemfile, + # we need a separate Gemfile to install it first, which is Appraisal.boot. + - name: Install Appraisal + run: BUNDLE_GEMFILE=Appraisal.boot.gemfile bundle + - name: Bundle for Appraisal ${{ matrix.appraisal }} + run: bundle + - name: Install Appraisal ${{ matrix.appraisal }} dependencies + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + - name: Run ${{ matrix.appraisal }} tests via ${{ matrix.exec_cmd }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} + + - name: CodeClimate Post-build Notification + run: cc-test-reporter after-build + if: ${{ github.event_name != 'pull_request' }} + continue-on-error: ${{ matrix.experimental != 'false' }} + + - name: Code Coverage Summary Report + uses: irongut/CodeCoverageSummary@v1.3.0 + if: ${{ github.event_name == 'pull_request' }} + with: + filename: ./coverage/coverage.xml + badge: true + fail_below_min: true + format: markdown + hide_branch_rate: false + hide_complexity: true + indicators: true + output: both + thresholds: '69 80' + continue-on-error: ${{ matrix.experimental != 'false' }} + + - name: Add Coverage PR Comment + uses: marocchino/sticky-pull-request-comment@v2 + if: ${{ github.event_name == 'pull_request' }} + with: + recreate: true + path: code-coverage-results.md + continue-on-error: ${{ matrix.experimental != 'false' }} + + - name: Coveralls + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + continue-on-error: ${{ matrix.experimental != 'false' }} + + - name: Upload results to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000..0d4a013 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,20 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement +name: 'Dependency Review' +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: 'Checkout Repository' + uses: actions/checkout@v4 + - name: 'Dependency Review' + uses: actions/dependency-review-action@v4 diff --git a/.github/workflows/heads.yml b/.github/workflows/heads.yml new file mode 100644 index 0000000..0201dda --- /dev/null +++ b/.github/workflows/heads.yml @@ -0,0 +1,87 @@ +name: Heads Compat Matrix + +env: + K_SOUP_COV_DO: false + +on: + push: + branches: + - 'main' + tags: + - '!*' # Do not execute on tags + pull_request: + branches: + - '*' + # Allow manually triggering the workflow. + workflow_dispatch: + +permissions: + contents: read + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + # The concurrency group contains the workflow name and the branch name. + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + test: + name: Specs - Ruby ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} + if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} + env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile + strategy: + fail-fast: true + matrix: + include: + # ruby-head + - ruby: "ruby-head" + appraisal: "ruby-head" + exec_cmd: "turbo_tests -n4" + gemfile: "Appraisal.root" + rubygems: latest + bundler: latest + + # truffleruby-head + - ruby: "truffleruby-head" + appraisal: "truffleruby-head" + exec_cmd: "turbo_tests -n4" + gemfile: "Appraisal.root" + rubygems: latest + bundler: latest + + # jruby-head + - ruby: "jruby-head" + appraisal: "jruby-head" + exec_cmd: "turbo_tests -n4" + gemfile: "Appraisal.root" + rubygems: latest + bundler: latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Ruby & RubyGems + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + rubygems: ${{ matrix.rubygems }} + bundler: ${{ matrix.bundler }} + bundler-cache: false + + # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) + # We need to do this first to get appraisal installed. + # NOTE: This does not use the root Gemfile at all. + # However, because we hack appraisal in our root gemfile, + # we need a separate Gemfile to install it first, which is Appraisal.boot. + - name: Install Appraisal + run: BUNDLE_GEMFILE=Appraisal.boot.gemfile bundle + - name: Bundle for Appraisal ${{ matrix.appraisal }} + run: bundle + - name: Install Appraisal ${{ matrix.appraisal }} dependencies + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + - name: Run ${{ matrix.appraisal }} tests via ${{ matrix.exec_cmd }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} diff --git a/.github/workflows/legacy.yml b/.github/workflows/legacy.yml new file mode 100644 index 0000000..dd200de --- /dev/null +++ b/.github/workflows/legacy.yml @@ -0,0 +1,71 @@ +name: Legacy Compat (EOL, Ruby 3.0) Matrix + +env: + K_SOUP_COV_DO: false + +on: + push: + branches: + - 'main' + tags: + - '!*' # Do not execute on tags + pull_request: + branches: + - '*' + # Allow manually triggering the workflow. + workflow_dispatch: + +permissions: + contents: read + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + # The concurrency group contains the workflow name and the branch name. + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + test: + name: Specs - Ruby ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} + if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" + runs-on: ubuntu-22.04 + continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} + env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile + strategy: + fail-fast: false + matrix: + include: + # Ruby 3.0 + - ruby: "3.0" + appraisal: "ruby-3-0" + exec_cmd: "turbo_tests -n4" + gemfile: "Appraisal.root" + rubygems: '3.5.23' + bundler: '2.5.23' + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Ruby & RubyGems + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + rubygems: ${{ matrix.rubygems }} + bundler: ${{ matrix.bundler }} + bundler-cache: false + + # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) + # We need to do this first to get appraisal installed. + # NOTE: This does not use the root Gemfile at all. + # However, because we hack appraisal in our root gemfile, + # we need a separate Gemfile to install it first, which is Appraisal.boot. + - name: Install Appraisal + run: BUNDLE_GEMFILE=Appraisal.boot.gemfile bundle + - name: Bundle for Appraisal ${{ matrix.appraisal }} + run: bundle + - name: Install Appraisal ${{ matrix.appraisal }} dependencies + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + - name: Run ${{ matrix.appraisal }} tests via ${{ matrix.exec_cmd }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml new file mode 100644 index 0000000..24e656a --- /dev/null +++ b/.github/workflows/style.yml @@ -0,0 +1,68 @@ +name: Ruby - Style + +on: + push: + branches: + - 'main' + tags: + - '!*' # Do not execute on tags + pull_request: + branches: + - '*' + # Allow manually triggering the workflow. + workflow_dispatch: + +permissions: + contents: read + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + # The concurrency group contains the workflow name and the branch name. + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + rubocop: + name: RuboCop Gradual + if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} + env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile + strategy: + fail-fast: false + matrix: + include: + # Style + - ruby: "3.4" + appraisal: "style" + exec_cmd: "rake rubocop_gradual:check" + gemfile: "Appraisal.root" + rubygems: latest + bundler: latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Ruby & RubyGems + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + rubygems: ${{ matrix.rubygems }} + bundler: ${{ matrix.bundler }} + bundler-cache: false + + # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) + # We need to do this first to get appraisal installed. + # NOTE: This does not use the root Gemfile at all. + # However, because we hack appraisal in our root gemfile, + # we need a separate Gemfile to install it first, which is Appraisal.boot. + - name: Install Appraisal + run: BUNDLE_GEMFILE=Appraisal.boot.gemfile bundle + - name: Bundle for Appraisal ${{ matrix.appraisal }} + run: bundle + - name: Install Appraisal ${{ matrix.appraisal }} dependencies + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + - name: Run ${{ matrix.appraisal }} tests via ${{ matrix.exec_cmd }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} diff --git a/.github/workflows/supported.yml b/.github/workflows/supported.yml new file mode 100644 index 0000000..353488b --- /dev/null +++ b/.github/workflows/supported.yml @@ -0,0 +1,94 @@ +name: Supported Compat Matrix + +env: + K_SOUP_COV_DO: false + +on: + push: + branches: + - 'main' + tags: + - '!*' # Do not execute on tags + pull_request: + branches: + - '*' + # Allow manually triggering the workflow. + workflow_dispatch: + +permissions: + contents: read + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + # The concurrency group contains the workflow name and the branch name. + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + test: + name: Specs - Ruby ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} + if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} + env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile + strategy: + matrix: + include: + # Ruby 3.1 + - ruby: "3.1" + appraisal: "ruby-3-1" + exec_cmd: "turbo_tests -n4" + gemfile: "Appraisal.root" + rubygems: latest + bundler: latest + + # Ruby 3.2 + - ruby: "3.2" + appraisal: "ruby-3-2" + exec_cmd: "turbo_tests -n4" + gemfile: "Appraisal.root" + rubygems: latest + bundler: latest + + # Ruby 3.3 + - ruby: "3.3" + appraisal: "ruby-3-3" + exec_cmd: "turbo_tests -n4" + gemfile: "Appraisal.root" + rubygems: latest + bundler: latest + + # Ruby 3.4 + - ruby: "3.4" + appraisal: "ruby-3-4" + exec_cmd: "turbo_tests -n4" + gemfile: "Appraisal.root" + rubygems: latest + bundler: latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Ruby & RubyGems + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + rubygems: ${{ matrix.rubygems }} + bundler: ${{ matrix.bundler }} + bundler-cache: false + + # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) + # We need to do this first to get appraisal installed. + # NOTE: This does not use the root Gemfile at all. + # However, because we hack appraisal in our root gemfile, + # we need a separate Gemfile to install it first, which is Appraisal.boot. + - name: Install Appraisal + run: BUNDLE_GEMFILE=Appraisal.boot.gemfile bundle + - name: Bundle for Appraisal ${{ matrix.appraisal }} + run: bundle + - name: Install Appraisal ${{ matrix.appraisal }} dependencies + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + - name: Run ${{ matrix.appraisal }} tests via ${{ matrix.exec_cmd }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} diff --git a/.github/workflows/tag_and_release.yml b/.github/workflows/tag_and_release.yml index 9d0264a..11eff4c 100644 --- a/.github/workflows/tag_and_release.yml +++ b/.github/workflows/tag_and_release.yml @@ -20,7 +20,7 @@ jobs: packages: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Ruby 2.7 uses: ruby/setup-ruby@v1 with: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e552093..4f56ce7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,6 +2,8 @@ name: Tests on: pull_request: + branches: + - '!*' # Ignore this workflow; Remove workflow once done pulling in upstream PRs permissions: contents: read diff --git a/.github/workflows/unsupported.yml b/.github/workflows/unsupported.yml new file mode 100644 index 0000000..6fb1c6c --- /dev/null +++ b/.github/workflows/unsupported.yml @@ -0,0 +1,71 @@ +name: Unsupported Compat (EOL, Ruby 2.7) Matrix + +env: + K_SOUP_COV_DO: false + +on: + push: + branches: + - 'main' + tags: + - '!*' # Do not execute on tags + pull_request: + branches: + - '*' + # Allow manually triggering the workflow. + workflow_dispatch: + +permissions: + contents: read + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + # The concurrency group contains the workflow name and the branch name. + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + test: + name: Specs - Ruby ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} + if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" + runs-on: ubuntu-22.04 + continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} + env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile + strategy: + fail-fast: false + matrix: + include: + # Ruby 2.7 + - ruby: "2.7" + appraisal: "ruby-2-7" + exec_cmd: "turbo_tests -n4" + gemfile: "Appraisal.root" + rubygems: '3.4.22' + bundler: '2.4.22' + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Ruby & RubyGems + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + rubygems: ${{ matrix.rubygems }} + bundler: ${{ matrix.bundler }} + bundler-cache: false + + # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) + # We need to do this first to get appraisal installed. + # NOTE: This does not use the root Gemfile at all. + # However, because we hack appraisal in our root gemfile, + # we need a separate Gemfile to install it first, which is Appraisal.boot. + - name: Install Appraisal + run: BUNDLE_GEMFILE=Appraisal.boot.gemfile bundle + - name: Bundle for Appraisal ${{ matrix.appraisal }} + run: bundle + - name: Install Appraisal ${{ matrix.appraisal }} dependencies + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + - name: Run ${{ matrix.appraisal }} tests via ${{ matrix.exec_cmd }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} diff --git a/.gitignore b/.gitignore index 2a71158..2fde0d5 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,7 @@ tags # End of https://www.toptal.com/developers/gitignore/api/macos,vim +# Appraisals Bundles (only commit the coverage, audit, and style lockfiles) +gemfiles/ruby_*.gemfile.lock +gemfiles/*_head.gemfile.lock +Appraisal.root.gemfile.lock \ No newline at end of file diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..eb389b5 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,5 @@ +inherit_gem: + rubocop-lts: config/rubygem_rspec.yml + +require: + - 'rubocop-packaging' diff --git a/.rubocop_gradual.lock b/.rubocop_gradual.lock new file mode 100644 index 0000000..0a6ba3c --- /dev/null +++ b/.rubocop_gradual.lock @@ -0,0 +1,63 @@ +{ + "fixtures/rspec/errors_outside_of_examples_spec.rb:2409688709": [ + [1, 16, 54, "RSpec/DescribeClass: The first argument to describe should be the class or module being tested.", 3626965792], + [2, 35, 3, "RSpec/BeEql: Prefer `be` over `eql`.", 193405885] + ], + "fixtures/rspec/failing_spec.rb:1154565131": [ + [1, 16, 23, "RSpec/DescribeClass: The first argument to describe should be the class or module being tested.", 986317040], + [11, 12, 1, "RSpec/ExpectActual: Provide the actual value you are testing to `expect(...)`.", 177559] + ], + "fixtures/rspec/no_method_error_spec.rb:47614094": [ + [1, 16, 20, "RSpec/DescribeClass: The first argument to describe should be the class or module being tested.", 4112385446] + ], + "fixtures/rspec/pending_exceptions_spec.rb:2868747541": [ + [1, 16, 51, "RSpec/DescribeClass: The first argument to describe should be the class or module being tested.", 4038399051], + [5, 12, 1, "RSpec/ExpectActual: Provide the actual value you are testing to `expect(...)`.", 177559], + [9, 12, 3, "RSpec/ExpectActual: Provide the actual value you are testing to `expect(...)`.", 193358996], + [12, 3, 43, "RSpec/PendingWithoutReason: Give the reason for xit.", 1479957056], + [13, 12, 1, "RSpec/ExpectActual: Provide the actual value you are testing to `expect(...)`.", 177556] + ], + "lib/turbo_tests.rb:2945832194": [ + [23, 5, 323, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 166166262], + [50, 5, 269, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 4255608859], + [71, 5, 614, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 574527918] + ], + "lib/turbo_tests/reporter.rb:1386902608": [ + [7, 5, 400, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2092085575] + ], + "lib/turbo_tests/runner.rb:816710941": [ + [12, 5, 798, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2396047272], + [190, 11, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [208, 47, 6, "Style/GlobalStdStream: Use `$stderr` instead of `STDERR`.", 3356712163], + [210, 21, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [219, 7, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [279, 9, 6, "Style/GlobalStdStream: Use `$stdout` instead of `STDOUT`.", 3356722952] + ], + "spec/cli_spec.rb:43879232": [ + [1, 1, 30, "RSpec/SpecFilePathFormat: Spec path should end with `turbo_tests/cli*_spec.rb`.", 965721356], + [11, 13, 28, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 2146945865], + [30, 7, 12, "RSpec/MultipleExpectations: Example has too many expectations [3/1].", 2388739333], + [31, 34, 3, "RSpec/BeEql: Prefer `be` over `eql`.", 193405885], + [38, 13, 20, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 812172126], + [41, 7, 632, "RSpec/ExampleLength: Example has too many lines. [10/5]", 4177603105], + [42, 34, 3, "RSpec/BeEql: Prefer `be` over `eql`.", 193405885], + [60, 13, 28, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 2146945865], + [75, 7, 12, "RSpec/MultipleExpectations: Example has too many expectations [3/1].", 2388739333], + [76, 34, 3, "RSpec/BeEql: Prefer `be` over `eql`.", 193405885], + [87, 13, 20, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 812172126], + [90, 7, 600, "RSpec/ExampleLength: Example has too many lines. [10/5]", 1660729462], + [91, 34, 3, "RSpec/BeEql: Prefer `be` over `eql`.", 193405885], + [111, 5, 32, "RSpec/MultipleExpectations: Example has too many expectations [2/1].", 281328851], + [112, 32, 3, "RSpec/BeEql: Prefer `be` over `eql`.", 193405885], + [121, 5, 38, "RSpec/MultipleExpectations: Example has too many expectations [2/1].", 3463001811], + [121, 5, 384, "RSpec/ExampleLength: Example has too many lines. [9/5]", 4122755410], + [122, 32, 3, "RSpec/BeEql: Prefer `be` over `eql`.", 193405885] + ], + "spec/doc_formatter_spec.rb:233381593": [ + [5, 16, 19, "RSpec/DescribeClass: The first argument to describe should be the class or module being tested.", 879596202] + ], + "turbo_tests.gemspec:2105011563": [ + [30, 16, 36, "ThreadSafety/DirChdir: Avoid using `Dir.chdir` due to its process-wide effect.", 3576345059], + [31, 5, 19, "Packaging/GemspecGit: Avoid using git to produce lists of files. Downstreams often need to build your package in an environment that does not have git (on purpose). Use some pure Ruby alternative, like `Dir` or `Dir.glob`.", 3879951891] + ] +} diff --git a/Appraisal.boot.gemfile b/Appraisal.boot.gemfile new file mode 100644 index 0000000..b4f9d66 --- /dev/null +++ b/Appraisal.boot.gemfile @@ -0,0 +1,8 @@ +git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } + +source "https://rubygems.org" + +# Appraisal Boot Gemfile is for installing appraisal only (not running it). +# We do not load the standard Gemfile, as it is tailored for local development. + +gemspec diff --git a/Appraisal.root.gemfile b/Appraisal.root.gemfile new file mode 100644 index 0000000..c678dc6 --- /dev/null +++ b/Appraisal.root.gemfile @@ -0,0 +1,16 @@ +git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } + +source "https://rubygems.org" + +# Appraisal Root Gemfile is for running appraisal to generate the Appraisal Gemfiles +# in gemfiles/*gemfile. It is not loaded on CI. +# On CI we only run it for the Appraisal-based builds. +# We do not load the standard Gemfile, as it is tailored for local development. + +gemspec + +# Allow usage of eval_gemfile inside our Appraisal definitions +# Solution is based on this comment: +# https://github.com/thoughtbot/appraisal/issues/154#issuecomment-493804217 +# (and fixed to work in 2025) +eval_gemfile "appraisal-hack.gemfile" diff --git a/Appraisals b/Appraisals new file mode 100644 index 0000000..4bd1503 --- /dev/null +++ b/Appraisals @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +appraise "ruby-2-7" do + gem "mutex_m", "~> 0.2" + gem "stringio", "~> 3.0" +end + +appraise "ruby-3-0" do + gem "mutex_m", "~> 0.2" + gem "stringio", "~> 3.0" +end + +appraise "ruby-3-1" do + gem "mutex_m", "~> 0.2" + gem "stringio", "~> 3.0" +end + +appraise "ruby-3-2" do + gem "mutex_m", "~> 0.2" + gem "stringio", "~> 3.0" +end + +appraise "ruby-3-3" do + gem "mutex_m", "~> 0.2" + gem "stringio", "~> 3.0" +end + +appraise "ruby-3-4" do + gem "mutex_m", "~> 0.2" + gem "stringio", "~> 3.0" +end + +# Only run security audit on latest Ruby version +appraise "audit" do + gem "mutex_m", "~> 0.2" + gem "stringio", "~> 3.0" + eval_gemfile "modular/audit.gemfile" +end + +# Only run coverage on latest Ruby version +appraise "coverage" do + gem "mutex_m", "~> 0.2" + gem "stringio", "~> 3.0" + eval_gemfile "modular/coverage.gemfile" +end + +# Only run linter on latest Ruby version (but, in support of oldest supported Ruby version) +appraise "style" do + gem "mutex_m", "~> 0.2" + gem "stringio", "~> 3.0" + eval_gemfile "modular/style.gemfile" +end + +appraise "ruby-head" do + gem "mutex_m", ">= 0.2" + gem "stringio", ">= 3.0" +end + +appraise "truffleruby-head" do + gem "mutex_m", ">= 0.2" + gem "stringio", ">= 3.0" +end + +appraise "jruby-head" do + gem "mutex_m", ">= 0.2" + gem "stringio", ">= 3.0" +end diff --git a/Gemfile b/Gemfile index 3085f59..47938ce 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,22 @@ source "https://rubygems.org" -# Specify your gem's dependencies in turbo_tests.gemspec +#### IMPORTANT ####################################################### +# Gemfile is for local development ONLY; Gemfile is NOT loaded in CI # +####################################################### IMPORTANT #### + +# Specify your gem's general development dependencies in turbo_tests.gemspec gemspec -gem "rake", "~> 13.0" +# Security Audit +if RUBY_VERSION >= "3" + # NOTE: Audit fails on Ruby 2.7 because nokogiri has dropped support for Ruby < 3 + # See: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-r95h-9x8f-r3f7 + # We can't add upgraded nokogiri here unless we are developing on Ruby 3+ + eval_gemfile "gemfiles/modular/audit.gemfile" +end + +# Code Coverage +eval_gemfile "gemfiles/modular/coverage.gemfile" + +# Linting +eval_gemfile "gemfiles/modular/style.gemfile" diff --git a/Gemfile.lock b/Gemfile.lock index 897849f..b2bd57f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -8,24 +8,52 @@ PATH GEM remote: https://rubygems.org/ specs: + ansi (1.5.0) appraisal (2.5.0) bundler rake thor (>= 0.14.0) + ast (2.4.2) + benchmark (0.4.0) + bundler-audit (0.9.2) + bundler (>= 1.2.0, < 3) + thor (~> 1.0) coderay (1.1.3) diff-lcs (1.5.1) + diffy (3.4.3) + docile (1.4.1) + json (2.9.1) + kettle-soup-cover (1.0.4) + simplecov (~> 0.22) + simplecov-cobertura (~> 2.1) + simplecov-console (~> 0.9, >= 0.9.1) + simplecov-html (~> 0.12) + simplecov-lcov (~> 0.8) + simplecov-rcov (~> 0.3, >= 0.3.3) + simplecov_json_formatter (~> 0.1, >= 0.1.4) + version_gem (~> 1.1, >= 1.1.4) + language_server-protocol (3.17.0.3) + lint_roller (1.1.0) method_source (1.1.0) parallel (1.26.3) parallel_tests (4.9.0) parallel + parser (3.3.6.0) + ast (~> 2.4.1) + racc pry (0.15.2) coderay (~> 1.1) method_source (~> 1.0) + racc (1.8.1) + rainbow (3.1.1) rake (13.2.1) + regexp_parser (2.10.0) + rexml (3.4.0) rspec (3.13.0) rspec-core (~> 3.13.0) rspec-expectations (~> 3.13.0) rspec-mocks (~> 3.13.0) + rspec-block_is_expected (1.0.6) rspec-core (3.13.2) rspec-support (~> 3.13.0) rspec-expectations (3.13.3) @@ -35,15 +63,107 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-support (3.13.2) + rubocop (1.69.2) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.36.2, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.37.0) + parser (>= 3.3.1.0) + rubocop-gradual (0.3.6) + diff-lcs (>= 1.2.0, < 2.0) + diffy (~> 3.0) + parallel (~> 1.10) + rainbow (>= 2.2.2, < 4.0) + rubocop (~> 1.0) + rubocop-lts (18.2.1) + rubocop-ruby2_7 (>= 2.0.4, < 3) + standard-rubocop-lts (>= 1.0.3, < 3) + version_gem (>= 1.1.2, < 3) + rubocop-md (1.2.4) + rubocop (>= 1.45) + rubocop-packaging (0.5.2) + rubocop (>= 1.33, < 2.0) + rubocop-performance (1.23.1) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rake (0.6.0) + rubocop (~> 1.0) + rubocop-rspec (3.3.0) + rubocop (~> 1.61) + rubocop-ruby2_7 (2.0.6) + rubocop-gradual (~> 0.3, >= 0.3.1) + rubocop-md (~> 1.2) + rubocop-rake (~> 0.6) + rubocop-shopify (~> 2.14) + rubocop-thread_safety (~> 0.5, >= 0.5.1) + standard-rubocop-lts (~> 1.0, >= 1.0.7) + version_gem (>= 1.1.3, < 3) + rubocop-shopify (2.15.1) + rubocop (~> 1.51) + rubocop-thread_safety (0.6.0) + rubocop (>= 1.48.1) + ruby-progressbar (1.13.0) + simplecov (0.22.0) + docile (~> 1.1) + simplecov-html (~> 0.11) + simplecov_json_formatter (~> 0.1) + simplecov-cobertura (2.1.0) + rexml + simplecov (~> 0.19) + simplecov-console (0.9.2) + ansi + simplecov + terminal-table + simplecov-html (0.13.1) + simplecov-lcov (0.8.0) + simplecov-rcov (0.3.7) + simplecov (>= 0.4.1) + simplecov_json_formatter (0.1.4) + standard (1.43.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.69.1) + standard-custom (~> 1.0.0) + standard-performance (~> 1.6) + standard-custom (1.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.50) + standard-performance (1.6.0) + lint_roller (~> 1.1) + rubocop-performance (~> 1.23.0) + standard-rubocop-lts (1.0.10) + rspec-block_is_expected (~> 1.0, >= 1.0.5) + standard (>= 1.35.1, < 2) + standard-custom (>= 1.0.2, < 2) + standard-performance (>= 1.3.1, < 2) + version_gem (>= 1.1.4, < 3) + terminal-table (1.6.0) thor (1.3.2) + unicode-display_width (3.1.4) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) + version_gem (1.1.4) PLATFORMS ruby DEPENDENCIES appraisal (~> 2.5) + benchmark (~> 0.4) + bundler-audit (~> 0.9.2) + kettle-soup-cover (~> 1.0, >= 1.0.4) pry (~> 0.14) rake (~> 13.0) + rubocop-lts (~> 18.2, >= 18.2.1) + rubocop-packaging (~> 0.5, >= 0.5.2) + rubocop-rspec (~> 3.2) + standard (>= 1.35.1, != 1.42.0, != 1.41.1) turbo_tests! BUNDLED WITH diff --git a/README.md b/README.md index f5a285a..79ba6aa 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ Finished in 2 minute 25.15 seconds (files took 0 seconds to load) Add this line to your application's `Gemfile`: ```ruby -gem 'turbo_tests' +gem "turbo_tests" ``` And then execute: @@ -105,6 +105,15 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). +### Appraisals + +From time to time the appraisal gemfiles in `gemfiles/` will need to be updated. +They are created and updated with the command: + +```shell +BUNDLE_GEMFILE=Appraisal.root.gemfile appraisal update +``` + ## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/serpapi/turbo_tests. 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/serpapi/turbo_tests/blob/master/CODE_OF_CONDUCT.md). diff --git a/Rakefile b/Rakefile index dbd59ee..c656deb 100644 --- a/Rakefile +++ b/Rakefile @@ -1,7 +1,61 @@ -require "bundler/setup" +# frozen_string_literal: true + require "bundler/gem_tasks" -require "rspec/core/rake_task" -RSpec::Core::RakeTask.new(:spec) +begin + require "rspec/core/rake_task" + + RSpec::Core::RakeTask.new(:spec) +rescue LoadError + task(:spec) do + warn("RSpec is disabled") + end +end + +desc "alias test task to spec" +task test: :spec + +begin + require "reek/rake/task" + Reek::Rake::Task.new do |t| + t.fail_on_error = true + t.verbose = false + t.source_files = "{spec,spec_ignored,spec_orms,lib}/**/*.rb" + end +rescue LoadError + task(:reek) do + warn("reek is disabled") + end +end + +begin + require "yard-junk/rake" + + YardJunk::Rake.define_task +rescue LoadError + task("yard:junk") do + warn("yard:junk is disabled") + end +end + +begin + require "yard" + + YARD::Rake::YardocTask.new(:yard) +rescue LoadError + task(:yard) do + warn("yard is disabled") + end +end + +begin + require "rubocop/lts" + Rubocop::Lts.install_tasks +rescue LoadError + task(:rubocop_gradual) do + warn("RuboCop (Gradual) is disabled") + end +end -task default: :spec +# These tests do not require any services to be running, so this is what we run as default +task default: %i[spec rubocop_gradual:autocorrect yard yard:junk] diff --git a/appraisal-hack.gemfile b/appraisal-hack.gemfile new file mode 100644 index 0000000..b487778 --- /dev/null +++ b/appraisal-hack.gemfile @@ -0,0 +1,40 @@ +# NOTE: There are two identical copies of this file to make this hack work. +# +# Solution is based on this comment: +# https://github.com/thoughtbot/appraisal/issues/154#issuecomment-493804217 +# (and fixed to work in 2025) +# NOTE: We have two copies of this file due to the inability to specify a reliable path, +# that works across varying invocations as indicated in that same comment. +# See: https://github.com/thoughtbot/appraisal/issues/154#issuecomment-2571386411 + +require "appraisal/bundler_dsl" + +if Appraisal::BundlerDSL::PARTS.include?("eval_gemfile") + # When the hack is already active, this code path is run within appraisal, + # so we can't really tell the difference between already hacked, + # and new version of appraisal has added the feature +else + require "appraisal/appraisal" + + Appraisal::Appraisal.class_eval do + def eval_gemfile(*args) + gemfile.eval_gemfile(*args) + end + end + + Appraisal::BundlerDSL.class_eval do + def eval_gemfile(path, contents = nil) + (@eval_gemfile ||= []) << [path, contents] + end + + private + + def eval_gemfile_entry + @eval_gemfile.map { |(p, c)| "eval_gemfile(#{p.inspect}#{", #{c.inspect}" if c})" } * "\n\n" + end + + alias_method :eval_gemfile_entry_for_dup, :eval_gemfile_entry + + self::PARTS << "eval_gemfile" + end +end diff --git a/bin/turbo_tests b/bin/turbo_tests index b67291c..91d4f9d 100755 --- a/bin/turbo_tests +++ b/bin/turbo_tests @@ -3,7 +3,7 @@ # frozen_string_literal: true # Enable local usage from cloned repo -root = File.expand_path("../..", __FILE__) +root = File.expand_path("..", __dir__) $LOAD_PATH << "#{root}/lib" if File.exist?("#{root}/Gemfile") require "turbo_tests" diff --git a/fixtures/rspec/errors_outside_of_examples_spec.rb b/fixtures/rspec/errors_outside_of_examples_spec.rb index 48738ea..9e23f50 100644 --- a/fixtures/rspec/errors_outside_of_examples_spec.rb +++ b/fixtures/rspec/errors_outside_of_examples_spec.rb @@ -1,5 +1,5 @@ -RSpec.describe "Fixture of spec file with errors outside of examples" do - it("passes") { expect(2 * 2).to eql(4) } +RSpec.describe("Fixture of spec file with errors outside of examples") do + it("passes") { expect(2 * 2).to(eql(4)) } 1 / 0 end diff --git a/fixtures/rspec/failing_spec.rb b/fixtures/rspec/failing_spec.rb index 73a8240..4539b59 100644 --- a/fixtures/rspec/failing_spec.rb +++ b/fixtures/rspec/failing_spec.rb @@ -1,5 +1,5 @@ -RSpec.describe "Failing example group" do - after(:each) do |example| +RSpec.describe("Failing example group") do + after do |example| example.metadata[:extra_failure_lines] ||= [] lines = example.metadata[:extra_failure_lines] @@ -8,6 +8,6 @@ end it "fails" do - expect(2).to eq(3) + expect(2).to(eq(3)) end end diff --git a/fixtures/rspec/no_method_error_spec.rb b/fixtures/rspec/no_method_error_spec.rb index c8c9689..86bdbbb 100644 --- a/fixtures/rspec/no_method_error_spec.rb +++ b/fixtures/rspec/no_method_error_spec.rb @@ -1,3 +1,3 @@ -RSpec.describe "NoMethodError spec" do - it("fails") { expect(nil[:key]).to eql("value") } -end \ No newline at end of file +RSpec.describe("NoMethodError spec") do + it("fails") { expect(nil[:key]).to(eql("value")) } +end diff --git a/fixtures/rspec/pending_exceptions_spec.rb b/fixtures/rspec/pending_exceptions_spec.rb index 737005f..a825dc6 100644 --- a/fixtures/rspec/pending_exceptions_spec.rb +++ b/fixtures/rspec/pending_exceptions_spec.rb @@ -1,15 +1,15 @@ -RSpec.describe "Fixture of spec file with pending failed examples" do +RSpec.describe("Fixture of spec file with pending failed examples") do it "is implemented but skipped with 'pending'" do pending("TODO: skipped with 'pending'") - expect(2).to eq(3) + expect(2).to(eq(3)) end it "is implemented but skipped with 'skip'", skip: "TODO: skipped with 'skip'" do - expect(100).to eq(500) + expect(100).to(eq(500)) end xit "is implemented but skipped with 'xit'" do - expect(1).to eq(42) + expect(1).to(eq(42)) end end diff --git a/gemfiles/appraisal-hack.gemfile b/gemfiles/appraisal-hack.gemfile new file mode 100644 index 0000000..b487778 --- /dev/null +++ b/gemfiles/appraisal-hack.gemfile @@ -0,0 +1,40 @@ +# NOTE: There are two identical copies of this file to make this hack work. +# +# Solution is based on this comment: +# https://github.com/thoughtbot/appraisal/issues/154#issuecomment-493804217 +# (and fixed to work in 2025) +# NOTE: We have two copies of this file due to the inability to specify a reliable path, +# that works across varying invocations as indicated in that same comment. +# See: https://github.com/thoughtbot/appraisal/issues/154#issuecomment-2571386411 + +require "appraisal/bundler_dsl" + +if Appraisal::BundlerDSL::PARTS.include?("eval_gemfile") + # When the hack is already active, this code path is run within appraisal, + # so we can't really tell the difference between already hacked, + # and new version of appraisal has added the feature +else + require "appraisal/appraisal" + + Appraisal::Appraisal.class_eval do + def eval_gemfile(*args) + gemfile.eval_gemfile(*args) + end + end + + Appraisal::BundlerDSL.class_eval do + def eval_gemfile(path, contents = nil) + (@eval_gemfile ||= []) << [path, contents] + end + + private + + def eval_gemfile_entry + @eval_gemfile.map { |(p, c)| "eval_gemfile(#{p.inspect}#{", #{c.inspect}" if c})" } * "\n\n" + end + + alias_method :eval_gemfile_entry_for_dup, :eval_gemfile_entry + + self::PARTS << "eval_gemfile" + end +end diff --git a/gemfiles/audit.gemfile b/gemfiles/audit.gemfile new file mode 100644 index 0000000..82f8f72 --- /dev/null +++ b/gemfiles/audit.gemfile @@ -0,0 +1,12 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "mutex_m", "~> 0.2" +gem "stringio", "~> 3.0" + +gemspec path: "../" + +eval_gemfile("appraisal-hack.gemfile") + +eval_gemfile("modular/audit.gemfile") diff --git a/gemfiles/audit.gemfile.lock b/gemfiles/audit.gemfile.lock new file mode 100644 index 0000000..8206e3e --- /dev/null +++ b/gemfiles/audit.gemfile.lock @@ -0,0 +1,65 @@ +PATH + remote: .. + specs: + turbo_tests (2.2.4) + parallel_tests (>= 3.3.0, < 5) + rspec (>= 3.10) + +GEM + remote: https://rubygems.org/ + specs: + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) + bundler-audit (0.9.2) + bundler (>= 1.2.0, < 3) + thor (~> 1.0) + coderay (1.1.3) + diff-lcs (1.5.1) + method_source (1.1.0) + mutex_m (0.3.0) + parallel (1.26.3) + parallel_tests (4.9.0) + parallel + pry (0.15.2) + coderay (~> 1.1) + method_source (~> 1.0) + rake (13.2.1) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.2) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.2) + stringio (3.1.2) + thor (1.3.2) + +PLATFORMS + aarch64-linux-gnu + aarch64-linux-musl + arm-linux-gnu + arm-linux-musl + arm64-darwin + x86_64-darwin + x86_64-linux-gnu + x86_64-linux-musl + +DEPENDENCIES + appraisal (~> 2.5) + bundler-audit (~> 0.9.2) + mutex_m (~> 0.2) + pry (~> 0.14) + rake (~> 13.0) + stringio (~> 3.0) + turbo_tests! + +BUNDLED WITH + 2.6.2 diff --git a/gemfiles/coverage.gemfile b/gemfiles/coverage.gemfile new file mode 100644 index 0000000..2ef804c --- /dev/null +++ b/gemfiles/coverage.gemfile @@ -0,0 +1,12 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "mutex_m", "~> 0.2" +gem "stringio", "~> 3.0" + +gemspec path: "../" + +eval_gemfile("appraisal-hack.gemfile") + +eval_gemfile("modular/coverage.gemfile") diff --git a/gemfiles/coverage.gemfile.lock b/gemfiles/coverage.gemfile.lock new file mode 100644 index 0000000..16b3902 --- /dev/null +++ b/gemfiles/coverage.gemfile.lock @@ -0,0 +1,88 @@ +PATH + remote: .. + specs: + turbo_tests (2.2.4) + parallel_tests (>= 3.3.0, < 5) + rspec (>= 3.10) + +GEM + remote: https://rubygems.org/ + specs: + ansi (1.5.0) + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) + coderay (1.1.3) + diff-lcs (1.5.1) + docile (1.4.1) + kettle-soup-cover (1.0.4) + simplecov (~> 0.22) + simplecov-cobertura (~> 2.1) + simplecov-console (~> 0.9, >= 0.9.1) + simplecov-html (~> 0.12) + simplecov-lcov (~> 0.8) + simplecov-rcov (~> 0.3, >= 0.3.3) + simplecov_json_formatter (~> 0.1, >= 0.1.4) + version_gem (~> 1.1, >= 1.1.4) + method_source (1.1.0) + mutex_m (0.3.0) + parallel (1.26.3) + parallel_tests (4.9.0) + parallel + pry (0.15.2) + coderay (~> 1.1) + method_source (~> 1.0) + rake (13.2.1) + rexml (3.4.0) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.2) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.2) + simplecov (0.22.0) + docile (~> 1.1) + simplecov-html (~> 0.11) + simplecov_json_formatter (~> 0.1) + simplecov-cobertura (2.1.0) + rexml + simplecov (~> 0.19) + simplecov-console (0.9.2) + ansi + simplecov + terminal-table + simplecov-html (0.13.1) + simplecov-lcov (0.8.0) + simplecov-rcov (0.3.7) + simplecov (>= 0.4.1) + simplecov_json_formatter (0.1.4) + stringio (3.1.2) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + thor (1.3.2) + unicode-display_width (2.6.0) + version_gem (1.1.4) + +PLATFORMS + arm64-darwin-24 + ruby + +DEPENDENCIES + appraisal (~> 2.5) + kettle-soup-cover (~> 1.0, >= 1.0.4) + mutex_m (~> 0.2) + pry (~> 0.14) + rake (~> 13.0) + stringio (~> 3.0) + turbo_tests! + +BUNDLED WITH + 2.6.2 diff --git a/gemfiles/jruby_head.gemfile b/gemfiles/jruby_head.gemfile new file mode 100644 index 0000000..288e442 --- /dev/null +++ b/gemfiles/jruby_head.gemfile @@ -0,0 +1,10 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "mutex_m", ">= 0.2" +gem "stringio", ">= 3.0" + +gemspec path: "../" + +eval_gemfile("appraisal-hack.gemfile") diff --git a/gemfiles/modular/audit.gemfile b/gemfiles/modular/audit.gemfile new file mode 100644 index 0000000..e5cc919 --- /dev/null +++ b/gemfiles/modular/audit.gemfile @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +# Many gems are dropping support for Ruby < 3, +# so we only want to run our security audit in CI on Ruby 3+ +gem "bundler-audit", "~> 0.9.2" diff --git a/gemfiles/modular/coverage.gemfile b/gemfiles/modular/coverage.gemfile new file mode 100644 index 0000000..c7756b9 --- /dev/null +++ b/gemfiles/modular/coverage.gemfile @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +# We run code coverage on the latest version of Ruby only. + +# Coverage +gem "kettle-soup-cover", "~> 1.0", ">= 1.0.4" diff --git a/gemfiles/modular/style.gemfile b/gemfiles/modular/style.gemfile new file mode 100644 index 0000000..d222e96 --- /dev/null +++ b/gemfiles/modular/style.gemfile @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +# We run rubocop on the latest version of Ruby, +# but in support of the oldest supported version of Ruby + +gem "rubocop-lts", "~> 18.2", ">= 18.2.1" # For Ruby 2.7+ +gem "rubocop-packaging", "~> 0.5", ">= 0.5.2" +gem "rubocop-rspec", "~> 3.2" +gem "standard", ">= 1.35.1", "!= 1.41.1", "!= 1.42.0" + +# Std Lib extractions +gem "benchmark", "~> 0.4" # Removed from Std Lib in Ruby 3.5 diff --git a/gemfiles/ruby_2_7.gemfile b/gemfiles/ruby_2_7.gemfile new file mode 100644 index 0000000..4c02229 --- /dev/null +++ b/gemfiles/ruby_2_7.gemfile @@ -0,0 +1,10 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "mutex_m", "~> 0.2" +gem "stringio", "~> 3.0" + +gemspec path: "../" + +eval_gemfile("appraisal-hack.gemfile") diff --git a/gemfiles/ruby_3_0.gemfile b/gemfiles/ruby_3_0.gemfile new file mode 100644 index 0000000..4c02229 --- /dev/null +++ b/gemfiles/ruby_3_0.gemfile @@ -0,0 +1,10 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "mutex_m", "~> 0.2" +gem "stringio", "~> 3.0" + +gemspec path: "../" + +eval_gemfile("appraisal-hack.gemfile") diff --git a/gemfiles/ruby_3_1.gemfile b/gemfiles/ruby_3_1.gemfile new file mode 100644 index 0000000..4c02229 --- /dev/null +++ b/gemfiles/ruby_3_1.gemfile @@ -0,0 +1,10 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "mutex_m", "~> 0.2" +gem "stringio", "~> 3.0" + +gemspec path: "../" + +eval_gemfile("appraisal-hack.gemfile") diff --git a/gemfiles/ruby_3_2.gemfile b/gemfiles/ruby_3_2.gemfile new file mode 100644 index 0000000..4c02229 --- /dev/null +++ b/gemfiles/ruby_3_2.gemfile @@ -0,0 +1,10 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "mutex_m", "~> 0.2" +gem "stringio", "~> 3.0" + +gemspec path: "../" + +eval_gemfile("appraisal-hack.gemfile") diff --git a/gemfiles/ruby_3_3.gemfile b/gemfiles/ruby_3_3.gemfile new file mode 100644 index 0000000..4c02229 --- /dev/null +++ b/gemfiles/ruby_3_3.gemfile @@ -0,0 +1,10 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "mutex_m", "~> 0.2" +gem "stringio", "~> 3.0" + +gemspec path: "../" + +eval_gemfile("appraisal-hack.gemfile") diff --git a/gemfiles/ruby_3_4.gemfile b/gemfiles/ruby_3_4.gemfile new file mode 100644 index 0000000..4c02229 --- /dev/null +++ b/gemfiles/ruby_3_4.gemfile @@ -0,0 +1,10 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "mutex_m", "~> 0.2" +gem "stringio", "~> 3.0" + +gemspec path: "../" + +eval_gemfile("appraisal-hack.gemfile") diff --git a/gemfiles/ruby_head.gemfile b/gemfiles/ruby_head.gemfile new file mode 100644 index 0000000..288e442 --- /dev/null +++ b/gemfiles/ruby_head.gemfile @@ -0,0 +1,10 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "mutex_m", ">= 0.2" +gem "stringio", ">= 3.0" + +gemspec path: "../" + +eval_gemfile("appraisal-hack.gemfile") diff --git a/gemfiles/style.gemfile b/gemfiles/style.gemfile new file mode 100644 index 0000000..277ed3b --- /dev/null +++ b/gemfiles/style.gemfile @@ -0,0 +1,12 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "mutex_m", "~> 0.2" +gem "stringio", "~> 3.0" + +gemspec path: "../" + +eval_gemfile("appraisal-hack.gemfile") + +eval_gemfile("modular/style.gemfile") diff --git a/gemfiles/style.gemfile.lock b/gemfiles/style.gemfile.lock new file mode 100644 index 0000000..c128df8 --- /dev/null +++ b/gemfiles/style.gemfile.lock @@ -0,0 +1,141 @@ +PATH + remote: .. + specs: + turbo_tests (2.2.4) + parallel_tests (>= 3.3.0, < 5) + rspec (>= 3.10) + +GEM + remote: https://rubygems.org/ + specs: + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) + ast (2.4.2) + benchmark (0.4.0) + coderay (1.1.3) + diff-lcs (1.5.1) + diffy (3.4.3) + json (2.9.1) + language_server-protocol (3.17.0.3) + lint_roller (1.1.0) + method_source (1.1.0) + mutex_m (0.3.0) + parallel (1.26.3) + parallel_tests (4.9.0) + parallel + parser (3.3.6.0) + ast (~> 2.4.1) + racc + pry (0.15.2) + coderay (~> 1.1) + method_source (~> 1.0) + racc (1.8.1) + rainbow (3.1.1) + rake (13.2.1) + regexp_parser (2.10.0) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-block_is_expected (1.0.6) + rspec-core (3.13.2) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.2) + rubocop (1.69.2) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.36.2, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.37.0) + parser (>= 3.3.1.0) + rubocop-gradual (0.3.6) + diff-lcs (>= 1.2.0, < 2.0) + diffy (~> 3.0) + parallel (~> 1.10) + rainbow (>= 2.2.2, < 4.0) + rubocop (~> 1.0) + rubocop-lts (18.2.1) + rubocop-ruby2_7 (>= 2.0.4, < 3) + standard-rubocop-lts (>= 1.0.3, < 3) + version_gem (>= 1.1.2, < 3) + rubocop-md (1.2.4) + rubocop (>= 1.45) + rubocop-packaging (0.5.2) + rubocop (>= 1.33, < 2.0) + rubocop-performance (1.23.1) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rake (0.6.0) + rubocop (~> 1.0) + rubocop-rspec (3.3.0) + rubocop (~> 1.61) + rubocop-ruby2_7 (2.0.6) + rubocop-gradual (~> 0.3, >= 0.3.1) + rubocop-md (~> 1.2) + rubocop-rake (~> 0.6) + rubocop-shopify (~> 2.14) + rubocop-thread_safety (~> 0.5, >= 0.5.1) + standard-rubocop-lts (~> 1.0, >= 1.0.7) + version_gem (>= 1.1.3, < 3) + rubocop-shopify (2.15.1) + rubocop (~> 1.51) + rubocop-thread_safety (0.6.0) + rubocop (>= 1.48.1) + ruby-progressbar (1.13.0) + standard (1.43.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.69.1) + standard-custom (~> 1.0.0) + standard-performance (~> 1.6) + standard-custom (1.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.50) + standard-performance (1.6.0) + lint_roller (~> 1.1) + rubocop-performance (~> 1.23.0) + standard-rubocop-lts (1.0.10) + rspec-block_is_expected (~> 1.0, >= 1.0.5) + standard (>= 1.35.1, < 2) + standard-custom (>= 1.0.2, < 2) + standard-performance (>= 1.3.1, < 2) + version_gem (>= 1.1.4, < 3) + stringio (3.1.2) + thor (1.3.2) + unicode-display_width (3.1.4) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) + version_gem (1.1.4) + +PLATFORMS + arm64-darwin-24 + ruby + +DEPENDENCIES + appraisal (~> 2.5) + benchmark (~> 0.4) + mutex_m (~> 0.2) + pry (~> 0.14) + rake (~> 13.0) + rubocop-lts (~> 18.2, >= 18.2.1) + rubocop-packaging (~> 0.5, >= 0.5.2) + rubocop-rspec (~> 3.2) + standard (>= 1.35.1, != 1.42.0, != 1.41.1) + stringio (~> 3.0) + turbo_tests! + +BUNDLED WITH + 2.6.2 diff --git a/gemfiles/truffleruby_head.gemfile b/gemfiles/truffleruby_head.gemfile new file mode 100644 index 0000000..288e442 --- /dev/null +++ b/gemfiles/truffleruby_head.gemfile @@ -0,0 +1,10 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "mutex_m", ">= 0.2" +gem "stringio", ">= 3.0" + +gemspec path: "../" + +eval_gemfile("appraisal-hack.gemfile") diff --git a/lib/turbo_tests.rb b/lib/turbo_tests.rb index afb362b..5e2df1d 100644 --- a/lib/turbo_tests.rb +++ b/lib/turbo_tests.rb @@ -21,24 +21,31 @@ module TurboTests FakeException = Struct.new(:backtrace, :message, :cause) class FakeException def self.from_obj(obj) - if obj - klass = - Class.new(FakeException) { - define_singleton_method(:name) do - obj[:class_name] - end - } + return unless obj - klass.new( - obj[:backtrace], - obj[:message], - FakeException.from_obj(obj[:cause]) - ) - end + klass = + Class.new(FakeException) do + define_singleton_method(:name) do + obj[:class_name] + end + end + + klass.new( + obj[:backtrace], + obj[:message], + FakeException.from_obj(obj[:cause]), + ) end end - FakeExecutionResult = Struct.new(:example_skipped?, :pending_message, :status, :pending_fixed?, :exception, :pending_exception) + FakeExecutionResult = Struct.new( + :example_skipped?, + :pending_message, + :status, + :pending_fixed?, + :exception, + :pending_exception, + ) class FakeExecutionResult def self.from_obj(obj) new( @@ -47,12 +54,19 @@ def self.from_obj(obj) obj[:status].to_sym, obj[:pending_fixed?], FakeException.from_obj(obj[:exception]), - FakeException.from_obj(obj[:exception]) + FakeException.from_obj(obj[:exception]), ) end end - FakeExample = Struct.new(:execution_result, :location, :description, :full_description, :metadata, :location_rerun_argument) + FakeExample = Struct.new( + :execution_result, + :location, + :description, + :full_description, + :metadata, + :location_rerun_argument, + ) class FakeExample def self.from_obj(obj) metadata = obj[:metadata] @@ -60,7 +74,7 @@ def self.from_obj(obj) metadata[:shared_group_inclusion_backtrace].map! do |frame| RSpec::Core::SharedExampleGroupInclusionStackFrame.new( frame[:shared_group_name], - frame[:inclusion_location] + frame[:inclusion_location], ) end @@ -72,13 +86,13 @@ def self.from_obj(obj) obj[:description], obj[:full_description], metadata, - obj[:location_rerun_argument] + obj[:location_rerun_argument], ) end def notification RSpec::Core::Notifications::ExampleNotification.for( - self + self, ) end end diff --git a/lib/turbo_tests/cli.rb b/lib/turbo_tests/cli.rb index 6f033cc..bd8364e 100644 --- a/lib/turbo_tests/cli.rb +++ b/lib/turbo_tests/cli.rb @@ -18,7 +18,7 @@ def run fail_fast = nil seed = nil - OptionParser.new { |opts| + OptionParser.new do |opts| opts.banner = <<~BANNER Run all tests in parallel, giving each process ENV['TEST_ENV_NUMBER'] ('1', '2', '3', ...). @@ -40,10 +40,14 @@ def run requires << filename end - opts.on("-f", "--format FORMATTER", "Choose a formatter. Available formatters: progress (p), documentation (d). Default: progress") do |name| + opts.on( + "-f", + "--format FORMATTER", + "Choose a formatter. Available formatters: progress (p), documentation (d). Default: progress", + ) do |name| formatters << { name: name, - outputs: [] + outputs: [], } end @@ -55,7 +59,7 @@ def run if formatters.empty? formatters << { name: "progress", - outputs: [] + outputs: [], } end formatters.last[:outputs] << filename @@ -72,30 +76,28 @@ def run opts.on("--fail-fast=[N]") do |n| n = begin Integer(n) - rescue + rescue StandardError nil end - fail_fast = n.nil? || n < 1 ? 1 : n + fail_fast = (n.nil? || n < 1) ? 1 : n end opts.on("--seed SEED", "Seed for rspec") do |s| seed = s end - }.parse!(@argv) + end.parse!(@argv) requires.each { |f| require(f) } if formatters.empty? formatters << { name: "progress", - outputs: [] + outputs: [], } end formatters.each do |formatter| - if formatter[:outputs].empty? - formatter[:outputs] << "-" - end + formatter[:outputs] << "-" if formatter[:outputs].empty? end exitstatus = TurboTests::Runner.run( @@ -106,11 +108,11 @@ def run verbose: verbose, fail_fast: fail_fast, count: count, - seed: seed + seed: seed, ) # From https://github.com/serpapi/turbo_tests/pull/20/ - exit exitstatus + exit(exitstatus) end end end diff --git a/lib/turbo_tests/json_rows_formatter.rb b/lib/turbo_tests/json_rows_formatter.rb index d0244ca..276d945 100644 --- a/lib/turbo_tests/json_rows_formatter.rb +++ b/lib/turbo_tests/json_rows_formatter.rb @@ -30,7 +30,7 @@ class JsonRowsFormatter :example_group_started, :example_group_finished, :message, - :seed + :seed, ) attr_reader :output @@ -42,76 +42,76 @@ def initialize(output) def start(notification) output_row( type: :load_summary, - summary: load_summary_to_json(notification) + summary: load_summary_to_json(notification), ) end def example_group_started(notification) output_row( type: :group_started, - group: group_to_json(notification) + group: group_to_json(notification), ) end def example_group_finished(notification) output_row( type: :group_finished, - group: group_to_json(notification) + group: group_to_json(notification), ) end def example_passed(notification) output_row( type: :example_passed, - example: example_to_json(notification.example) + example: example_to_json(notification.example), ) end def example_pending(notification) output_row( type: :example_pending, - example: example_to_json(notification.example) + example: example_to_json(notification.example), ) end def example_failed(notification) output_row( type: :example_failed, - example: example_to_json(notification.example) + example: example_to_json(notification.example), ) end def seed(notification) output_row( type: :seed, - seed: notification.seed + seed: notification.seed, ) end - def close(notification) + def close(_notification) output_row( - type: :close + type: :close, ) end def message(notification) output_row( type: :message, - message: notification.message + message: notification.message, ) end private def exception_to_json(exception) - if exception - { - class_name: exception.class.name.to_s, - backtrace: exception.backtrace, - message: exception.message, - cause: exception_to_json(exception.cause) - } - end + return unless exception + + { + class_name: exception.class.name.to_s, + backtrace: exception.backtrace, + message: exception.message, + cause: exception_to_json(exception.cause), + } end def execution_result_to_json(result) @@ -120,14 +120,14 @@ def execution_result_to_json(result) pending_message: result.pending_message, status: result.status, pending_fixed?: result.pending_fixed?, - exception: exception_to_json(result.exception || result.pending_exception) + exception: exception_to_json(result.exception || result.pending_exception), } end def stack_frame_to_json(frame) { shared_group_name: frame.shared_group_name, - inclusion_location: frame.inclusion_location + inclusion_location: frame.inclusion_location, } end @@ -158,8 +158,8 @@ def load_summary_to_json(notification) def group_to_json(notification) { group: { - description: notification.group.description - } + description: notification.group.description, + }, } end diff --git a/lib/turbo_tests/reporter.rb b/lib/turbo_tests/reporter.rb index 417f06a..472e510 100644 --- a/lib/turbo_tests/reporter.rb +++ b/lib/turbo_tests/reporter.rb @@ -11,7 +11,7 @@ def self.from_config(formatter_config, start_time, seed, seed_used) name, outputs = config.values_at(:name, :outputs) outputs.map! do |filename| - filename == "-" ? $stdout : File.open(filename, "w") + (filename == "-") ? $stdout : File.open(filename, "w") end reporter.add(name, outputs) @@ -20,8 +20,7 @@ def self.from_config(formatter_config, start_time, seed, seed_used) reporter end - attr_reader :pending_examples - attr_reader :failed_examples + attr_reader :pending_examples, :failed_examples def initialize(start_time, seed, seed_used) @formatters = [] @@ -63,7 +62,7 @@ def report(example_groups) end end - def start(example_groups, time=RSpec::Core::Time.now) + def start(example_groups, time = RSpec::Core::Time.now) @start = time @load_time = (@start - @start_time).to_f @@ -71,7 +70,10 @@ def start(example_groups, time=RSpec::Core::Time.now) expected_example_count = example_groups.flatten(1).count delegate_to_formatters(:seed, RSpec::Core::Notifications::SeedNotification.new(@seed, @seed_used)) - delegate_to_formatters(:start, RSpec::Core::Notifications::StartNotification.new(expected_example_count, @load_time)) + delegate_to_formatters( + :start, + RSpec::Core::Notifications::StartNotification.new(expected_example_count, @load_time), + ) end def report_number_of_tests(groups) @@ -79,7 +81,7 @@ def report_number_of_tests(groups) num_processes = groups.size num_tests = groups.map(&:size).sum - tests_per_process = (num_processes == 0 ? 0 : num_tests.to_f / num_processes).round + tests_per_process = ((num_processes == 0) ? 0 : num_tests.to_f / num_processes).round puts "#{num_processes} processes for #{num_tests} #{name}s, ~ #{tests_per_process} #{name}s per process" end @@ -119,40 +121,48 @@ def message(message) def error_outside_of_examples(error_message) @errors_outside_of_examples_count += 1 - message error_message + message(error_message) end def finish end_time = RSpec::Core::Time.now @duration = end_time - @start_time - delegate_to_formatters :stop, RSpec::Core::Notifications::ExamplesNotification.new(self) + delegate_to_formatters(:stop, RSpec::Core::Notifications::ExamplesNotification.new(self)) - delegate_to_formatters :start_dump, RSpec::Core::Notifications::NullNotification - delegate_to_formatters(:dump_pending, + delegate_to_formatters(:start_dump, RSpec::Core::Notifications::NullNotification) + delegate_to_formatters( + :dump_pending, RSpec::Core::Notifications::ExamplesNotification.new( - self - )) - delegate_to_formatters(:dump_failures, + self, + ), + ) + delegate_to_formatters( + :dump_failures, RSpec::Core::Notifications::ExamplesNotification.new( - self - )) - delegate_to_formatters(:dump_summary, + self, + ), + ) + delegate_to_formatters( + :dump_summary, RSpec::Core::Notifications::SummaryNotification.new( end_time - @start_time, @all_examples, @failed_examples, @pending_examples, @load_time, - @errors_outside_of_examples_count - )) - delegate_to_formatters(:seed, + @errors_outside_of_examples_count, + ), + ) + delegate_to_formatters( + :seed, RSpec::Core::Notifications::SeedNotification.new( @seed, @seed_used, - )) + ), + ) ensure - delegate_to_formatters :close, RSpec::Core::Notifications::NullNotification + delegate_to_formatters(:close, RSpec::Core::Notifications::NullNotification) end protected diff --git a/lib/turbo_tests/runner.rb b/lib/turbo_tests/runner.rb index 9db297d..bcd745e 100644 --- a/lib/turbo_tests/runner.rb +++ b/lib/turbo_tests/runner.rb @@ -22,9 +22,7 @@ def self.run(opts = {}) seed = opts.fetch(:seed) seed_used = !seed.nil? - if verbose - warn "VERBOSE" - end + warn("VERBOSE") if verbose reporter = Reporter.from_config(formatters, start_time, seed, seed_used) @@ -64,7 +62,7 @@ def initialize(opts) def run @num_processes = [ ParallelTests.determine_number_of_processes(@count), - ParallelTests::RSpec::Runner.tests_with_size(@files, {}).size + ParallelTests::RSpec::Runner.tests_with_size(@files, {}).size, ].min use_runtime_info = @files == ["spec"] @@ -81,7 +79,7 @@ def run ParallelTests::RSpec::Runner.tests_in_groups( @files, @num_processes, - **group_opts + **group_opts, ) setup_tmp_dir @@ -90,7 +88,7 @@ def run record_runtime: use_runtime_info, } - @reporter.report(tests_in_groups) do |reporter| + @reporter.report(tests_in_groups) do |_reporter| wait_threads = tests_in_groups.map.with_index do |tests, process_id| start_regular_subprocess(tests, process_id + 1, **subprocess_opts) end @@ -125,7 +123,7 @@ def start_regular_subprocess(tests, process_id, **opts) @tags.map { |tag| "--tag=#{tag}" }, tests, process_id, - **opts + **opts, ) end @@ -140,17 +138,19 @@ def start_subprocess(env, extra_args, tests, process_id, record_runtime:) env["RUBYOPT"] = ["-I#{File.expand_path("..", __dir__)}", ENV["RUBYOPT"]].compact.join(" ") env["RSPEC_SILENCE_FILTER_ANNOUNCEMENTS"] = "1" - if ENV["BUNDLE_BIN_PATH"] - command_name = [ENV["BUNDLE_BIN_PATH"], "exec", "rspec"] + command_name = if ENV["BUNDLE_BIN_PATH"] + [ENV["BUNDLE_BIN_PATH"], "exec", "rspec"] else - command_name = "rspec" + "rspec" end record_runtime_options = if record_runtime [ - "--format", "ParallelTests::RSpec::RuntimeLogger", - "--out", @runtime_log, + "--format", + "ParallelTests::RSpec::RuntimeLogger", + "--out", + @runtime_log, ] else [] @@ -168,7 +168,8 @@ def start_subprocess(env, extra_args, tests, process_id, record_runtime:) *command_name, *extra_args, *seed_option, - "--format", "TurboTests::JsonRowsFormatter", + "--format", + "TurboTests::JsonRowsFormatter", *record_runtime_options, *tests, ] @@ -179,7 +180,7 @@ def start_subprocess(env, extra_args, tests, process_id, record_runtime:) command.join(" "), ].select { |x| x.size > 0 }.join(" ") - warn "Process #{process_id}: #{command_str}" + warn("Process #{process_id}: #{command_str}") end stdin, stdout, stderr, wait_thr = Open3.popen3(env, *command) @@ -201,15 +202,13 @@ def start_subprocess(env, extra_args, tests, process_id, record_runtime:) @messages << message end - @messages << { type: "exit", process_id: process_id } + @messages << {type: "exit", process_id: process_id} end @threads << start_copy_thread(stderr, STDERR) @threads << Thread.new do - unless wait_thr.value.success? - @messages << { type: "error" } - end + @messages << {type: "error"} unless wait_thr.value.success? end wait_thr @@ -272,11 +271,9 @@ def handle_messages nil when "exit" exited += 1 - if exited == @num_processes - break - end + break if exited == @num_processes else - STDERR.puts("Unhandled message in main process: #{message}") + warn("Unhandled message in main process: #{message}") end STDOUT.flush diff --git a/lib/utils/hash_extension.rb b/lib/utils/hash_extension.rb index c00bf6e..0b1da04 100644 --- a/lib/utils/hash_extension.rb +++ b/lib/utils/hash_extension.rb @@ -1,7 +1,7 @@ module CoreExtensions refine Hash do def to_struct - Struct.new(*self.keys).new(*self.values.map { |value| value.is_a?(Hash) ? value.to_struct : value }) + Struct.new(*keys).new(*values.map { |value| value.is_a?(Hash) ? value.to_struct : value }) end end -end \ No newline at end of file +end diff --git a/spec/cli_spec.rb b/spec/cli_spec.rb index 93fa132..f723c32 100644 --- a/spec/cli_spec.rb +++ b/spec/cli_spec.rb @@ -1,35 +1,28 @@ RSpec.describe TurboTests::CLI do - before { output } + subject(:output) { %x(bundle exec turbo_tests -f d #{fixture}).strip } - subject(:output) { `bundle exec turbo_tests -f d #{fixture}`.strip } + before { output } context "when the 'seed' parameter was used" do - let(:seed) { 1234 } + subject(:output) { %x(bundle exec turbo_tests -f d #{fixture} --seed #{seed}).strip } - subject(:output) { `bundle exec turbo_tests -f d #{fixture} --seed #{seed}`.strip } + let(:seed) { 1234 } context "errors outside of examples" do - let(:expected_start_of_output) { -%( + let(:expected_start_of_output) do + %( 1 processes for 1 specs, ~ 1 specs per process Randomized with seed #{seed} An error occurred while loading #{fixture}. -\e[31mFailure/Error: \e[0m\e[1;34m1\e[0m / \e[1;34m0\e[0m\e[0m -\e[31m\e[0m -\e[31mZeroDivisionError:\e[0m -\e[31m divided by 0\e[0m -\e[36m# #{fixture}:4:in `/'\e[0m -\e[36m# #{fixture}:4:in `block in '\e[0m -\e[36m# #{fixture}:1:in `'\e[0m ).strip - } + end let(:expected_end_of_output) do - "0 examples, 0 failures, 1 error occurred outside of examples\n"\ - "\n"\ - "Randomized with seed #{seed}" + "0 examples, 0 failures, 1 error occurred outside of examples\n" \ + "\n" \ + "Randomized with seed #{seed}" end let(:fixture) { "./fixtures/rspec/errors_outside_of_examples_spec.rb" } @@ -65,20 +58,13 @@ context "when 'seed' parameter was not used" do context "errors outside of examples" do - let(:expected_start_of_output) { -%( + let(:expected_start_of_output) do + %( 1 processes for 1 specs, ~ 1 specs per process An error occurred while loading #{fixture}. -\e[31mFailure/Error: \e[0m\e[1;34m1\e[0m / \e[1;34m0\e[0m\e[0m -\e[31m\e[0m -\e[31mZeroDivisionError:\e[0m -\e[31m divided by 0\e[0m -\e[36m# #{fixture}:4:in `/'\e[0m -\e[36m# #{fixture}:4:in `block in '\e[0m -\e[36m# #{fixture}:1:in `'\e[0m ).strip - } + end let(:expected_end_of_output) do "0 examples, 0 failures, 1 error occurred outside of examples" @@ -94,7 +80,7 @@ end it "exludes the seed message from the output" do - expect(output).to_not include("seed") + expect(output).not_to include("seed") end end @@ -136,9 +122,9 @@ expect($?.exitstatus).to eql(1) [ - "undefined method `[]' for nil", - 'it("fails") { expect(nil[:key]).to eql("value") }', - "# #{fixture}:2:in `block (2 levels) in '", + /undefined method [`']\[\]' for nil/, + 'it("fails") { expect(nil[:key]).to(eql("value")) }', + /# #{fixture}:2:in [`']block \(2 levels\) in '/, "1 example, 1 failure", ].each do |part| expect(output).to include(part) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3e568ab..2ef8fa4 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,4 +1,3 @@ -require "bundler/setup" require "turbo_tests" RSpec.configure do |config| diff --git a/spec/turbo_tests_spec.rb b/spec/turbo_tests_spec.rb index 0ee2784..611fdb6 100644 --- a/spec/turbo_tests_spec.rb +++ b/spec/turbo_tests_spec.rb @@ -1,5 +1,5 @@ RSpec.describe TurboTests do it "has a version number" do - expect(TurboTests::VERSION).not_to be nil + expect(TurboTests::VERSION).not_to be_nil end end diff --git a/turbo_tests.gemspec b/turbo_tests.gemspec index cb0fd36..237aac9 100644 --- a/turbo_tests.gemspec +++ b/turbo_tests.gemspec @@ -4,7 +4,6 @@ Gem::Specification.new do |spec| spec.name = "turbo_tests" spec.version = TurboTests::VERSION spec.platform = Gem::Platform::RUBY - spec.date = Time.now.strftime('%Y-%m-%d') spec.summary = "`turbo_tests` is a drop-in replacement for `grosser/parallel_tests` with incremental summarized output. Source code of `turbo_test` gem is based on Discourse and Rubygems work in this area (see README file of the source repository)." spec.homepage = "https://github.com/serpapi/turbo_tests" @@ -19,16 +18,17 @@ Gem::Specification.new do |spec| spec.required_ruby_version = ">= 2.7" - spec.add_dependency "rspec", ">= 3.10" - spec.add_dependency "parallel_tests", ">= 3.3.0", "< 5" + spec.add_dependency("parallel_tests", ">= 3.3.0", "< 5") + spec.add_dependency("rspec", ">= 3.10") spec.add_development_dependency("appraisal", "~> 2.5") - spec.add_development_dependency "pry", "~> 0.14" + spec.add_development_dependency("pry", "~> 0.14") + spec.add_development_dependency("rake", "~> 13.0") # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. - spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + spec.files = Dir.chdir(File.expand_path(__dir__)) do + %x(git ls-files -z).split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end spec.executables = ["turbo_tests"] From be2da1af0ce4091cb3cbfb9e8e0d1a4d1e19442b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 05:32:17 +0000 Subject: [PATCH 06/23] Bump actions/upload-artifact from 3 to 4 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/tag_and_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tag_and_release.yml b/.github/workflows/tag_and_release.yml index 11eff4c..96712c2 100644 --- a/.github/workflows/tag_and_release.yml +++ b/.github/workflows/tag_and_release.yml @@ -33,7 +33,7 @@ jobs: bundle install bundle exec turbo_tests - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: "ruby-gem" path: "pkg/*.gem" From 6eae1265b64442fec1f032423aabe13accec53e2 Mon Sep 17 00:00:00 2001 From: Peter Boling Date: Sat, 11 Jan 2025 09:16:06 +0700 Subject: [PATCH 07/23] add --create flag --- .rubocop_gradual.lock | 19 ++++++++++++------- README.md | 9 +++++++++ lib/turbo_tests/cli.rb | 9 +++++++++ lib/turbo_tests/runner.rb | 8 ++++++++ spec/turbo_tests_spec.rb | 22 ++++++++++++++++++++++ 5 files changed, 60 insertions(+), 7 deletions(-) diff --git a/.rubocop_gradual.lock b/.rubocop_gradual.lock index 0a6ba3c..24ec251 100644 --- a/.rubocop_gradual.lock +++ b/.rubocop_gradual.lock @@ -25,13 +25,14 @@ "lib/turbo_tests/reporter.rb:1386902608": [ [7, 5, 400, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2092085575] ], - "lib/turbo_tests/runner.rb:816710941": [ - [12, 5, 798, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2396047272], - [190, 11, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [208, 47, 6, "Style/GlobalStdStream: Use `$stderr` instead of `STDERR`.", 3356712163], - [210, 21, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [219, 7, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [279, 9, 6, "Style/GlobalStdStream: Use `$stdout` instead of `STDOUT`.", 3356722952] + "lib/turbo_tests/runner.rb:3148178217": [ + [13, 5, 271, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 21989008], + [20, 5, 798, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2396047272], + [198, 11, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [216, 47, 6, "Style/GlobalStdStream: Use `$stderr` instead of `STDERR`.", 3356712163], + [218, 21, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [227, 7, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [287, 9, 6, "Style/GlobalStdStream: Use `$stdout` instead of `STDOUT`.", 3356722952] ], "spec/cli_spec.rb:43879232": [ [1, 1, 30, "RSpec/SpecFilePathFormat: Spec path should end with `turbo_tests/cli*_spec.rb`.", 965721356], @@ -56,6 +57,10 @@ "spec/doc_formatter_spec.rb:233381593": [ [5, 16, 19, "RSpec/DescribeClass: The first argument to describe should be the class or module being tested.", 879596202] ], + "spec/turbo_tests_spec.rb:811756577": [ + [10, 15, 7, "RSpec/MessageSpies: Prefer `have_received` for setting message expectations. Setup `ParallelTests::Tasks` as a spy using `allow` or `instance_spy`.", 1384559950], + [20, 15, 7, "RSpec/MessageSpies: Prefer `have_received` for setting message expectations. Setup `ParallelTests::Tasks` as a spy using `allow` or `instance_spy`.", 1384559950] + ], "turbo_tests.gemspec:2105011563": [ [30, 16, 36, "ThreadSafety/DirChdir: Avoid using `Dir.chdir` due to its process-wide effect.", 3576345059], [31, 5, 19, "Packaging/GemspecGit: Avoid using git to produce lists of files. Downstreams often need to build your package in an environment that does not have git (on purpose). Use some pure Ruby alternative, like `Dir` or `Dir.glob`.", 3879951891] diff --git a/README.md b/README.md index 79ba6aa..47cc3b8 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,14 @@ Or install it yourself as: $ gem install turbo_tests ``` +## Setup + +Create test databases + +```bash +$ bundle exec turbo_tests --create +``` + ## Usage Execute tests: @@ -97,6 +105,7 @@ Options: -v, --verbose More output --fail-fast=[N] --seed SEED Seed for rspec + --create Create test databases ``` ## Development diff --git a/lib/turbo_tests/cli.rb b/lib/turbo_tests/cli.rb index bd8364e..89c04bb 100644 --- a/lib/turbo_tests/cli.rb +++ b/lib/turbo_tests/cli.rb @@ -17,6 +17,7 @@ def run verbose = false fail_fast = nil seed = nil + create = false OptionParser.new do |opts| opts.banner = <<~BANNER @@ -85,8 +86,16 @@ def run opts.on("--seed SEED", "Seed for rspec") do |s| seed = s end + + opts.on("--create", "Create databases") do + create = true + end end.parse!(@argv) + if create + return TurboTests::Runner.create(count) + end + requires.each { |f| require(f) } if formatters.empty? diff --git a/lib/turbo_tests/runner.rb b/lib/turbo_tests/runner.rb index bcd745e..c2b406d 100644 --- a/lib/turbo_tests/runner.rb +++ b/lib/turbo_tests/runner.rb @@ -2,6 +2,7 @@ require "json" require "parallel_tests/rspec/runner" +require "parallel_tests/tasks" require_relative "../utils/hash_extension" @@ -9,6 +10,13 @@ module TurboTests class Runner using CoreExtensions + def self.create(count) + ENV["PARALLEL_TEST_FIRST_IS_1"] = "true" + command = ["bundle", "exec", "rake", "db:create", "RAILS_ENV=#{ParallelTests::Tasks.rails_env}"] + args = {count: count.to_s} + ParallelTests::Tasks.run_in_parallel(command, args) + end + def self.run(opts = {}) files = opts[:files] formatters = opts[:formatters] diff --git a/spec/turbo_tests_spec.rb b/spec/turbo_tests_spec.rb index 611fdb6..438ccb1 100644 --- a/spec/turbo_tests_spec.rb +++ b/spec/turbo_tests_spec.rb @@ -2,4 +2,26 @@ it "has a version number" do expect(TurboTests::VERSION).not_to be_nil end + + describe "create" do + context "with nil count" do + it "creates databases" do + expect(ParallelTests::Tasks) + .to receive(:run_in_parallel) + .with(["bundle", "exec", "rake", "db:create", "RAILS_ENV=test"], {count: ""}) + + TurboTests::Runner.create(nil) + end + end + + context "with count" do + it "creates databases" do + expect(ParallelTests::Tasks) + .to receive(:run_in_parallel) + .with(["bundle", "exec", "rake", "db:create", "RAILS_ENV=test"], {count: "4"}) + + TurboTests::Runner.create(4) + end + end + end end From 9d5c0c19e07274aca0c825d478306a5c76fb7c3a Mon Sep 17 00:00:00 2001 From: mrudzki Date: Fri, 12 May 2023 09:21:31 +0200 Subject: [PATCH 08/23] Add option to print failing group --- lib/turbo_tests/cli.rb | 6 ++++++ lib/turbo_tests/runner.rb | 18 +++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/turbo_tests/cli.rb b/lib/turbo_tests/cli.rb index 89c04bb..b78b3f0 100644 --- a/lib/turbo_tests/cli.rb +++ b/lib/turbo_tests/cli.rb @@ -17,6 +17,7 @@ def run verbose = false fail_fast = nil seed = nil + print_failed_group = false create = false OptionParser.new do |opts| @@ -90,6 +91,10 @@ def run opts.on("--create", "Create databases") do create = true end + + opts.on("--print_failed_group") do + print_failed_group = true + end end.parse!(@argv) if create @@ -118,6 +123,7 @@ def run fail_fast: fail_fast, count: count, seed: seed, + print_failed_group: print_failed_group ) # From https://github.com/serpapi/turbo_tests/pull/20/ diff --git a/lib/turbo_tests/runner.rb b/lib/turbo_tests/runner.rb index c2b406d..1b22da4 100644 --- a/lib/turbo_tests/runner.rb +++ b/lib/turbo_tests/runner.rb @@ -27,8 +27,9 @@ def self.run(opts = {}) verbose = opts.fetch(:verbose, false) fail_fast = opts.fetch(:fail_fast, nil) count = opts.fetch(:count, nil) - seed = opts.fetch(:seed) + seed = opts.fetch(:seed, nil) || rand(0xFFFF).to_s seed_used = !seed.nil? + print_failed_group = opts.fetch(:print_failed_group, false) warn("VERBOSE") if verbose @@ -44,6 +45,7 @@ def self.run(opts = {}) count: count, seed: seed, seed_used: seed_used, + print_failed_group: print_failed_group ).run end @@ -65,6 +67,7 @@ def initialize(opts) @messages = Thread::Queue.new @threads = [] @error = false + @print_failed_group = opts[:print_failed_group] end def run @@ -96,6 +99,8 @@ def run record_runtime: use_runtime_info, } + @reporter.seed_notification(@seed, @seed_used) + @reporter.report(tests_in_groups) do |_reporter| wait_threads = tests_in_groups.map.with_index do |tests, process_id| start_regular_subprocess(tests, process_id + 1, **subprocess_opts) @@ -105,6 +110,8 @@ def run @threads.each(&:join) + report_failed_group(wait_threads, tests_in_groups) if @print_failed_group + if @reporter.failed_examples.empty? && wait_threads.map(&:value).all?(&:success?) 0 else @@ -292,5 +299,14 @@ def handle_messages def fail_fast_met !@fail_fast.nil? && @failure_count >= @fail_fast end + + def report_failed_group(wait_threads, tests_in_groups) + wait_threads.map(&:value).each_with_index do |value, index| + next if value.success? + + failing_group = tests_in_groups[index].join(" ") + puts "Group that failed: #{failing_group}" + end + end end end From 6b496adca8372c421f120086a1c38e65fff5cc29 Mon Sep 17 00:00:00 2001 From: mrudzki Date: Fri, 12 May 2023 09:32:48 +0200 Subject: [PATCH 09/23] Add change to readme --- README.md | 1 + lib/turbo_tests/cli.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 47cc3b8..47ff3fb 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,7 @@ Options: --fail-fast=[N] --seed SEED Seed for rspec --create Create test databases + --print_failed_group Prints group that had failures in it ``` ## Development diff --git a/lib/turbo_tests/cli.rb b/lib/turbo_tests/cli.rb index b78b3f0..166ef30 100644 --- a/lib/turbo_tests/cli.rb +++ b/lib/turbo_tests/cli.rb @@ -92,7 +92,7 @@ def run create = true end - opts.on("--print_failed_group") do + opts.on("--print_failed_group", "Prints group that had failures in it") do print_failed_group = true end end.parse!(@argv) From c948a71747d6bfa4cbdbb876a94cb7c2e03bdca6 Mon Sep 17 00:00:00 2001 From: Peter Boling Date: Tue, 14 Jan 2025 16:06:12 +0700 Subject: [PATCH 10/23] =?UTF-8?q?=F0=9F=92=9A=20Fix=20CI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/heads.yml | 13 +++++++------ .rubocop_gradual.lock | 18 +++++++++--------- lib/turbo_tests/cli.rb | 2 +- lib/turbo_tests/runner.rb | 6 ++---- spec/cli_spec.rb | 8 ++++---- 5 files changed, 23 insertions(+), 24 deletions(-) diff --git a/.github/workflows/heads.yml b/.github/workflows/heads.yml index 0201dda..e5458e2 100644 --- a/.github/workflows/heads.yml +++ b/.github/workflows/heads.yml @@ -53,12 +53,13 @@ jobs: bundler: latest # jruby-head - - ruby: "jruby-head" - appraisal: "jruby-head" - exec_cmd: "turbo_tests -n4" - gemfile: "Appraisal.root" - rubygems: latest - bundler: latest + # Error output has a significantly different format in JRuby :( + # - ruby: "jruby-head" + # appraisal: "jruby-head" + # exec_cmd: "turbo_tests -n4" + # gemfile: "Appraisal.root" + # rubygems: latest + # bundler: latest steps: - name: Checkout diff --git a/.rubocop_gradual.lock b/.rubocop_gradual.lock index 24ec251..6591d04 100644 --- a/.rubocop_gradual.lock +++ b/.rubocop_gradual.lock @@ -25,16 +25,16 @@ "lib/turbo_tests/reporter.rb:1386902608": [ [7, 5, 400, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2092085575] ], - "lib/turbo_tests/runner.rb:3148178217": [ + "lib/turbo_tests/runner.rb:2204434148": [ [13, 5, 271, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 21989008], - [20, 5, 798, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2396047272], - [198, 11, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [216, 47, 6, "Style/GlobalStdStream: Use `$stderr` instead of `STDERR`.", 3356712163], - [218, 21, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [227, 7, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [287, 9, 6, "Style/GlobalStdStream: Use `$stdout` instead of `STDOUT`.", 3356722952] + [20, 5, 917, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1582439743], + [203, 11, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [221, 47, 6, "Style/GlobalStdStream: Use `$stderr` instead of `STDERR`.", 3356712163], + [223, 21, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [232, 7, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [292, 9, 6, "Style/GlobalStdStream: Use `$stdout` instead of `STDOUT`.", 3356722952] ], - "spec/cli_spec.rb:43879232": [ + "spec/cli_spec.rb:3990998076": [ [1, 1, 30, "RSpec/SpecFilePathFormat: Spec path should end with `turbo_tests/cli*_spec.rb`.", 965721356], [11, 13, 28, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 2146945865], [30, 7, 12, "RSpec/MultipleExpectations: Example has too many expectations [3/1].", 2388739333], @@ -51,7 +51,7 @@ [111, 5, 32, "RSpec/MultipleExpectations: Example has too many expectations [2/1].", 281328851], [112, 32, 3, "RSpec/BeEql: Prefer `be` over `eql`.", 193405885], [121, 5, 38, "RSpec/MultipleExpectations: Example has too many expectations [2/1].", 3463001811], - [121, 5, 384, "RSpec/ExampleLength: Example has too many lines. [9/5]", 4122755410], + [121, 5, 409, "RSpec/ExampleLength: Example has too many lines. [9/5]", 4122755410], [122, 32, 3, "RSpec/BeEql: Prefer `be` over `eql`.", 193405885] ], "spec/doc_formatter_spec.rb:233381593": [ diff --git a/lib/turbo_tests/cli.rb b/lib/turbo_tests/cli.rb index 166ef30..33b2b17 100644 --- a/lib/turbo_tests/cli.rb +++ b/lib/turbo_tests/cli.rb @@ -123,7 +123,7 @@ def run fail_fast: fail_fast, count: count, seed: seed, - print_failed_group: print_failed_group + print_failed_group: print_failed_group, ) # From https://github.com/serpapi/turbo_tests/pull/20/ diff --git a/lib/turbo_tests/runner.rb b/lib/turbo_tests/runner.rb index 1b22da4..efab4b7 100644 --- a/lib/turbo_tests/runner.rb +++ b/lib/turbo_tests/runner.rb @@ -27,7 +27,7 @@ def self.run(opts = {}) verbose = opts.fetch(:verbose, false) fail_fast = opts.fetch(:fail_fast, nil) count = opts.fetch(:count, nil) - seed = opts.fetch(:seed, nil) || rand(0xFFFF).to_s + seed = opts.fetch(:seed, nil) seed_used = !seed.nil? print_failed_group = opts.fetch(:print_failed_group, false) @@ -45,7 +45,7 @@ def self.run(opts = {}) count: count, seed: seed, seed_used: seed_used, - print_failed_group: print_failed_group + print_failed_group: print_failed_group, ).run end @@ -99,8 +99,6 @@ def run record_runtime: use_runtime_info, } - @reporter.seed_notification(@seed, @seed_used) - @reporter.report(tests_in_groups) do |_reporter| wait_threads = tests_in_groups.map.with_index do |tests, process_id| start_regular_subprocess(tests, process_id + 1, **subprocess_opts) diff --git a/spec/cli_spec.rb b/spec/cli_spec.rb index f723c32..06d74fd 100644 --- a/spec/cli_spec.rb +++ b/spec/cli_spec.rb @@ -123,11 +123,11 @@ [ /undefined method [`']\[\]' for nil/, - 'it("fails") { expect(nil[:key]).to(eql("value")) }', - /# #{fixture}:2:in [`']block \(2 levels\) in '/, - "1 example, 1 failure", + /it\("fails"\) \{ expect\(nil\[:key\]\).to\(eql\("value"\)\) \}/, + /# #{Regexp.escape(fixture)}:2:in [`']block \(2 levels\) in '/, + /1 example, 1 failure/, ].each do |part| - expect(output).to include(part) + expect(output).to match(part) end end end From 4723a12a032e62f37d50b9d461536f3fba559e3d Mon Sep 17 00:00:00 2001 From: Sebastien Savater Date: Tue, 19 Sep 2023 15:54:12 +0200 Subject: [PATCH 11/23] Support parallel_tests options --- .rubocop_gradual.lock | 14 +++++++------- README.md | 6 ++++++ lib/turbo_tests/cli.rb | 6 +++++- lib/turbo_tests/runner.rb | 34 +++++++++++++++++++++------------- 4 files changed, 39 insertions(+), 21 deletions(-) diff --git a/.rubocop_gradual.lock b/.rubocop_gradual.lock index 6591d04..7527bd3 100644 --- a/.rubocop_gradual.lock +++ b/.rubocop_gradual.lock @@ -25,14 +25,14 @@ "lib/turbo_tests/reporter.rb:1386902608": [ [7, 5, 400, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2092085575] ], - "lib/turbo_tests/runner.rb:2204434148": [ + "lib/turbo_tests/runner.rb:1170052458": [ [13, 5, 271, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 21989008], - [20, 5, 917, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1582439743], - [203, 11, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [221, 47, 6, "Style/GlobalStdStream: Use `$stderr` instead of `STDERR`.", 3356712163], - [223, 21, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [232, 7, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [292, 9, 6, "Style/GlobalStdStream: Use `$stdout` instead of `STDOUT`.", 3356722952] + [20, 5, 1253, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1925027850], + [211, 11, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [229, 47, 6, "Style/GlobalStdStream: Use `$stderr` instead of `STDERR`.", 3356712163], + [231, 21, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [240, 7, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [300, 9, 6, "Style/GlobalStdStream: Use `$stdout` instead of `STDOUT`.", 3356722952] ], "spec/cli_spec.rb:3990998076": [ [1, 1, 30, "RSpec/SpecFilePathFormat: Spec path should end with `turbo_tests/cli*_spec.rb`.", 965721356], diff --git a/README.md b/README.md index 47ff3fb..a5e30f7 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,12 @@ Options: --print_failed_group Prints group that had failures in it ``` +To pass any options supported by paralell_tests, use `--` : + +```bash +bundle exec turbo_tests -n 4 -- --only-group 1 --pattern spec/system +``` + ## 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. diff --git a/lib/turbo_tests/cli.rb b/lib/turbo_tests/cli.rb index 33b2b17..18c7ac2 100644 --- a/lib/turbo_tests/cli.rb +++ b/lib/turbo_tests/cli.rb @@ -114,16 +114,20 @@ def run formatter[:outputs] << "-" if formatter[:outputs].empty? end + parallel_options = ParallelTests::CLI.new.send(:parse_options!, @argv.unshift("--type", "rspec")) + files = parallel_options.fetch(:files, ["spec"]) + exitstatus = TurboTests::Runner.run( formatters: formatters, tags: tags, - files: @argv.empty? ? ["spec"] : @argv, + files: files, runtime_log: runtime_log, verbose: verbose, fail_fast: fail_fast, count: count, seed: seed, print_failed_group: print_failed_group, + parallel_options: parallel_options, ) # From https://github.com/serpapi/turbo_tests/pull/20/ diff --git a/lib/turbo_tests/runner.rb b/lib/turbo_tests/runner.rb index efab4b7..94fdfa1 100644 --- a/lib/turbo_tests/runner.rb +++ b/lib/turbo_tests/runner.rb @@ -21,6 +21,7 @@ def self.run(opts = {}) files = opts[:files] formatters = opts[:formatters] tags = opts[:tags] + parallel_options = opts[:parallel_options] || {} start_time = opts.fetch(:start_time) { RSpec::Core::Time.now } runtime_log = opts.fetch(:runtime_log, nil) @@ -31,6 +32,14 @@ def self.run(opts = {}) seed_used = !seed.nil? print_failed_group = opts.fetch(:print_failed_group, false) + use_runtime_info = files == ["spec"] + + if use_runtime_info + parallel_options[:runtime_log] = runtime_log + else + parallel_options[:group_by] = :filesize + end + warn("VERBOSE") if verbose reporter = Reporter.from_config(formatters, start_time, seed, seed_used) @@ -46,6 +55,8 @@ def self.run(opts = {}) seed: seed, seed_used: seed_used, print_failed_group: print_failed_group, + use_runtime_info: use_runtime_info, + parallel_options: parallel_options, ).run end @@ -53,17 +64,24 @@ def initialize(opts) @reporter = opts[:reporter] @files = opts[:files] @tags = opts[:tags] - @runtime_log = opts[:runtime_log] || "tmp/turbo_rspec_runtime.log" @verbose = opts[:verbose] @fail_fast = opts[:fail_fast] @count = opts[:count] @seed = opts[:seed] @seed_used = opts[:seed_used] + @use_runtime_info = opts[:use_runtime_info] @load_time = 0 @load_count = 0 @failure_count = 0 + # Supports runtime_log as a top level option, + # but also nested inside parallel_options + @runtime_log = opts[:runtime_log] || "tmp/turbo_rspec_runtime.log" + @parallel_options = opts.fetch(:parallel_options, {}) + @parallel_options[:runtime_log] ||= @runtime_log + @record_runtime = @parallel_options[:group_by] == :runtime + @messages = Thread::Queue.new @threads = [] @error = false @@ -76,27 +94,17 @@ def run ParallelTests::RSpec::Runner.tests_with_size(@files, {}).size, ].min - use_runtime_info = @files == ["spec"] - - group_opts = {} - - if use_runtime_info - group_opts[:runtime_log] = @runtime_log - else - group_opts[:group_by] = :filesize - end - tests_in_groups = ParallelTests::RSpec::Runner.tests_in_groups( @files, @num_processes, - **group_opts, + **@parallel_options, ) setup_tmp_dir subprocess_opts = { - record_runtime: use_runtime_info, + record_runtime: @record_runtime, } @reporter.report(tests_in_groups) do |_reporter| From 28829a141262c148ed72dcb41d6d69908c593dc0 Mon Sep 17 00:00:00 2001 From: Bo Anderson Date: Sun, 17 Dec 2023 02:38:45 +0000 Subject: [PATCH 12/23] Add `--nice` flag --- .rubocop_gradual.lock | 14 +++++++------- lib/turbo_tests/cli.rb | 6 ++++++ lib/turbo_tests/runner.rb | 5 +++++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/.rubocop_gradual.lock b/.rubocop_gradual.lock index 7527bd3..165d079 100644 --- a/.rubocop_gradual.lock +++ b/.rubocop_gradual.lock @@ -25,14 +25,14 @@ "lib/turbo_tests/reporter.rb:1386902608": [ [7, 5, 400, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2092085575] ], - "lib/turbo_tests/runner.rb:1170052458": [ + "lib/turbo_tests/runner.rb:1092969829": [ [13, 5, 271, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 21989008], - [20, 5, 1253, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1925027850], - [211, 11, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [229, 47, 6, "Style/GlobalStdStream: Use `$stderr` instead of `STDERR`.", 3356712163], - [231, 21, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [240, 7, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [300, 9, 6, "Style/GlobalStdStream: Use `$stdout` instead of `STDOUT`.", 3356722952] + [20, 5, 1311, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1925027850], + [216, 11, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [234, 47, 6, "Style/GlobalStdStream: Use `$stderr` instead of `STDERR`.", 3356712163], + [236, 21, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [245, 7, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [305, 9, 6, "Style/GlobalStdStream: Use `$stdout` instead of `STDOUT`.", 3356722952] ], "spec/cli_spec.rb:3990998076": [ [1, 1, 30, "RSpec/SpecFilePathFormat: Spec path should end with `turbo_tests/cli*_spec.rb`.", 965721356], diff --git a/lib/turbo_tests/cli.rb b/lib/turbo_tests/cli.rb index 18c7ac2..ae6f445 100644 --- a/lib/turbo_tests/cli.rb +++ b/lib/turbo_tests/cli.rb @@ -19,6 +19,7 @@ def run seed = nil print_failed_group = false create = false + nice = false OptionParser.new do |opts| opts.banner = <<~BANNER @@ -95,6 +96,10 @@ def run opts.on("--print_failed_group", "Prints group that had failures in it") do print_failed_group = true end + + opts.on("--nice", "execute test commands with low priority") do + nice = true + end end.parse!(@argv) if create @@ -126,6 +131,7 @@ def run fail_fast: fail_fast, count: count, seed: seed, + nice: nice, print_failed_group: print_failed_group, parallel_options: parallel_options, ) diff --git a/lib/turbo_tests/runner.rb b/lib/turbo_tests/runner.rb index 94fdfa1..f271d7c 100644 --- a/lib/turbo_tests/runner.rb +++ b/lib/turbo_tests/runner.rb @@ -31,6 +31,7 @@ def self.run(opts = {}) seed = opts.fetch(:seed, nil) seed_used = !seed.nil? print_failed_group = opts.fetch(:print_failed_group, false) + nice = opts.fetch(:nice, false) use_runtime_info = files == ["spec"] @@ -57,6 +58,7 @@ def self.run(opts = {}) print_failed_group: print_failed_group, use_runtime_info: use_runtime_info, parallel_options: parallel_options, + nice: nice, ).run end @@ -69,6 +71,7 @@ def initialize(opts) @count = opts[:count] @seed = opts[:seed] @seed_used = opts[:seed_used] + @nice = opts[:nice] @use_runtime_info = opts[:use_runtime_info] @load_time = 0 @@ -194,6 +197,8 @@ def start_subprocess(env, extra_args, tests, process_id, record_runtime:) *record_runtime_options, *tests, ] + command.unshift(ENV["BUNDLE_BIN_PATH"], "exec") if ENV["BUNDLE_BIN_PATH"] + command.unshift("nice") if @nice if @verbose command_str = [ From 9f2cece2f775b5bb3effa9615952928c83ca3040 Mon Sep 17 00:00:00 2001 From: Bo Anderson Date: Sun, 17 Dec 2023 02:41:27 +0000 Subject: [PATCH 13/23] Support reading `.rspec_parallel` --- .rubocop_gradual.lock | 12 ++++++------ lib/turbo_tests/runner.rb | 9 +++++++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/.rubocop_gradual.lock b/.rubocop_gradual.lock index 165d079..1656439 100644 --- a/.rubocop_gradual.lock +++ b/.rubocop_gradual.lock @@ -25,14 +25,14 @@ "lib/turbo_tests/reporter.rb:1386902608": [ [7, 5, 400, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2092085575] ], - "lib/turbo_tests/runner.rb:1092969829": [ + "lib/turbo_tests/runner.rb:3421474245": [ [13, 5, 271, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 21989008], [20, 5, 1311, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1925027850], - [216, 11, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [234, 47, 6, "Style/GlobalStdStream: Use `$stderr` instead of `STDERR`.", 3356712163], - [236, 21, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [245, 7, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [305, 9, 6, "Style/GlobalStdStream: Use `$stdout` instead of `STDOUT`.", 3356722952] + [221, 11, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [239, 47, 6, "Style/GlobalStdStream: Use `$stderr` instead of `STDERR`.", 3356712163], + [241, 21, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [250, 7, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [310, 9, 6, "Style/GlobalStdStream: Use `$stdout` instead of `STDOUT`.", 3356722952] ], "spec/cli_spec.rb:3990998076": [ [1, 1, 30, "RSpec/SpecFilePathFormat: Spec path should end with `turbo_tests/cli*_spec.rb`.", 965721356], diff --git a/lib/turbo_tests/runner.rb b/lib/turbo_tests/runner.rb index f271d7c..6195da4 100644 --- a/lib/turbo_tests/runner.rb +++ b/lib/turbo_tests/runner.rb @@ -97,6 +97,8 @@ def run ParallelTests::RSpec::Runner.tests_with_size(@files, {}).size, ].min + options_file = [".rspec_parallel", "spec/parallel_spec.opts", "spec/spec.opts"].detect { |f| File.file?(f) } + tests_in_groups = ParallelTests::RSpec::Runner.tests_in_groups( @files, @@ -108,6 +110,7 @@ def run subprocess_opts = { record_runtime: @record_runtime, + options_file: options_file, } @reporter.report(tests_in_groups) do |_reporter| @@ -151,7 +154,7 @@ def start_regular_subprocess(tests, process_id, **opts) ) end - def start_subprocess(env, extra_args, tests, process_id, record_runtime:) + def start_subprocess(env, extra_args, tests, process_id, record_runtime:, options_file:) if tests.empty? @messages << { type: "exit", @@ -188,6 +191,8 @@ def start_subprocess(env, extra_args, tests, process_id, record_runtime:) [] end + spec_opts = ["-O", options_file] if options_file + command = [ *command_name, *extra_args, @@ -195,9 +200,9 @@ def start_subprocess(env, extra_args, tests, process_id, record_runtime:) "--format", "TurboTests::JsonRowsFormatter", *record_runtime_options, + *spec_opts, *tests, ] - command.unshift(ENV["BUNDLE_BIN_PATH"], "exec") if ENV["BUNDLE_BIN_PATH"] command.unshift("nice") if @nice if @verbose From e814c1da1409230631da88128772fa89f66c38aa Mon Sep 17 00:00:00 2001 From: Bo Anderson Date: Sun, 17 Dec 2023 02:47:21 +0000 Subject: [PATCH 14/23] Better handle interrupts --- lib/turbo_tests/runner.rb | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/lib/turbo_tests/runner.rb b/lib/turbo_tests/runner.rb index 6195da4..7017c81 100644 --- a/lib/turbo_tests/runner.rb +++ b/lib/turbo_tests/runner.rb @@ -87,6 +87,7 @@ def initialize(opts) @messages = Thread::Queue.new @threads = [] + @wait_threads = [] @error = false @print_failed_group = opts[:print_failed_group] end @@ -114,27 +115,47 @@ def run } @reporter.report(tests_in_groups) do |_reporter| - wait_threads = tests_in_groups.map.with_index do |tests, process_id| + old_signal = Signal.trap(:INT) { handle_interrupt } + + @wait_threads = tests_in_groups.map.with_index do |tests, process_id| start_regular_subprocess(tests, process_id + 1, **subprocess_opts) - end + end.compact + @interrupt_handled = false handle_messages @threads.each(&:join) - report_failed_group(wait_threads, tests_in_groups) if @print_failed_group + report_failed_group(tests_in_groups) if @print_failed_group + + Signal.trap(:INT, old_signal) - if @reporter.failed_examples.empty? && wait_threads.map(&:value).all?(&:success?) + if @reporter.failed_examples.empty? && @wait_threads.map(&:value).all?(&:success?) 0 else # From https://github.com/serpapi/turbo_tests/pull/20/ - wait_threads.map { |thread| thread.value.exitstatus }.max + @wait_threads.map { |thread| thread.value.exitstatus }.max end end end private + def handle_interrupt + if @interrupt_handled + Kernel.exit + else + puts "\nShutting down subprocesses..." + @wait_threads.each do |wait_thr| + child_pid = wait_thr.pid + pgid = Process.respond_to?(:getpgid) ? Process.getpgid(child_pid) : 0 + Process.kill(:INT, child_pid) if Process.pid != pgid + rescue Errno::ESRCH, Errno::ENOENT + end + @interrupt_handled = true + end + end + def setup_tmp_dir begin FileUtils.rm_r("tmp/test-pipes") @@ -160,6 +181,8 @@ def start_subprocess(env, extra_args, tests, process_id, record_runtime:, option type: "exit", process_id: process_id, } + + nil else env["RSPEC_FORMATTER_OUTPUT_ID"] = SecureRandom.uuid env["RUBYOPT"] = ["-I#{File.expand_path("..", __dir__)}", ENV["RUBYOPT"]].compact.join(" ") @@ -229,6 +252,7 @@ def start_subprocess(env, extra_args, tests, process_id, record_runtime:, option next unless message message = JSON.parse(message, symbolize_names: true) + message[:process_id] = process_id @messages << message end @@ -316,8 +340,8 @@ def fail_fast_met !@fail_fast.nil? && @failure_count >= @fail_fast end - def report_failed_group(wait_threads, tests_in_groups) - wait_threads.map(&:value).each_with_index do |value, index| + def report_failed_group(tests_in_groups) + @wait_threads.map(&:value).each_with_index do |value, index| next if value.success? failing_group = tests_in_groups[index].join(" ") From ad79f33098d74718cda9bafa901a272048169d2d Mon Sep 17 00:00:00 2001 From: Peter Boling Date: Tue, 4 Feb 2025 14:51:45 +0700 Subject: [PATCH 15/23] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Use=20appraisals=20w?= =?UTF-8?q?ith=20eval=5Fgemfile=20support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/coverage.yml | 8 ++----- .github/workflows/heads.yml | 8 ++----- .github/workflows/legacy.yml | 8 ++----- .github/workflows/style.yml | 8 ++----- .github/workflows/supported.yml | 8 ++----- .github/workflows/unsupported.yml | 8 ++----- .rubocop_gradual.lock | 18 +++++++------- Appraisal.boot.gemfile | 8 ------- Appraisal.root.gemfile | 10 +++----- Appraisals | 12 ++++++++++ Gemfile | 2 ++ Gemfile.lock | 31 ++++++++++++++---------- README.md | 7 ++++++ appraisal-hack.gemfile | 40 ------------------------------- gemfiles/appraisal-hack.gemfile | 40 ------------------------------- gemfiles/audit.gemfile | 2 -- gemfiles/audit.gemfile.lock | 5 ---- gemfiles/coverage.gemfile | 2 -- gemfiles/coverage.gemfile.lock | 14 ++++------- gemfiles/jruby_head.gemfile | 2 -- gemfiles/ruby_2_7.gemfile | 2 -- gemfiles/ruby_3_0.gemfile | 2 -- gemfiles/ruby_3_1.gemfile | 2 -- gemfiles/ruby_3_2.gemfile | 2 -- gemfiles/ruby_3_3.gemfile | 2 -- gemfiles/ruby_3_4.gemfile | 2 -- gemfiles/ruby_head.gemfile | 2 -- gemfiles/style.gemfile | 2 -- gemfiles/style.gemfile.lock | 20 ++++++---------- gemfiles/truffleruby_head.gemfile | 2 -- lib/turbo_tests/runner.rb | 4 ++-- turbo_tests.gemspec | 1 - 32 files changed, 77 insertions(+), 207 deletions(-) delete mode 100644 Appraisal.boot.gemfile delete mode 100644 appraisal-hack.gemfile delete mode 100644 gemfiles/appraisal-hack.gemfile diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index c4e8f52..6306958 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -73,12 +73,8 @@ jobs: # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) # We need to do this first to get appraisal installed. - # NOTE: This does not use the root Gemfile at all. - # However, because we hack appraisal in our root gemfile, - # we need a separate Gemfile to install it first, which is Appraisal.boot. - - name: Install Appraisal - run: BUNDLE_GEMFILE=Appraisal.boot.gemfile bundle - - name: Bundle for Appraisal ${{ matrix.appraisal }} + # NOTE: This does not use the main Gemfile at all. + - name: Bundle install for Appraisal ${{ matrix.appraisal }} run: bundle - name: Install Appraisal ${{ matrix.appraisal }} dependencies run: bundle exec appraisal ${{ matrix.appraisal }} bundle diff --git a/.github/workflows/heads.yml b/.github/workflows/heads.yml index e5458e2..f528289 100644 --- a/.github/workflows/heads.yml +++ b/.github/workflows/heads.yml @@ -75,12 +75,8 @@ jobs: # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) # We need to do this first to get appraisal installed. - # NOTE: This does not use the root Gemfile at all. - # However, because we hack appraisal in our root gemfile, - # we need a separate Gemfile to install it first, which is Appraisal.boot. - - name: Install Appraisal - run: BUNDLE_GEMFILE=Appraisal.boot.gemfile bundle - - name: Bundle for Appraisal ${{ matrix.appraisal }} + # NOTE: This does not use the main Gemfile at all. + - name: Bundle install for Appraisal ${{ matrix.appraisal }} run: bundle - name: Install Appraisal ${{ matrix.appraisal }} dependencies run: bundle exec appraisal ${{ matrix.appraisal }} bundle diff --git a/.github/workflows/legacy.yml b/.github/workflows/legacy.yml index dd200de..f2dc030 100644 --- a/.github/workflows/legacy.yml +++ b/.github/workflows/legacy.yml @@ -58,12 +58,8 @@ jobs: # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) # We need to do this first to get appraisal installed. - # NOTE: This does not use the root Gemfile at all. - # However, because we hack appraisal in our root gemfile, - # we need a separate Gemfile to install it first, which is Appraisal.boot. - - name: Install Appraisal - run: BUNDLE_GEMFILE=Appraisal.boot.gemfile bundle - - name: Bundle for Appraisal ${{ matrix.appraisal }} + # NOTE: This does not use the main Gemfile at all. + - name: Bundle install for Appraisal ${{ matrix.appraisal }} run: bundle - name: Install Appraisal ${{ matrix.appraisal }} dependencies run: bundle exec appraisal ${{ matrix.appraisal }} bundle diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index 24e656a..62cbe81 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -55,12 +55,8 @@ jobs: # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) # We need to do this first to get appraisal installed. - # NOTE: This does not use the root Gemfile at all. - # However, because we hack appraisal in our root gemfile, - # we need a separate Gemfile to install it first, which is Appraisal.boot. - - name: Install Appraisal - run: BUNDLE_GEMFILE=Appraisal.boot.gemfile bundle - - name: Bundle for Appraisal ${{ matrix.appraisal }} + # NOTE: This does not use the main Gemfile at all. + - name: Bundle install for Appraisal ${{ matrix.appraisal }} run: bundle - name: Install Appraisal ${{ matrix.appraisal }} dependencies run: bundle exec appraisal ${{ matrix.appraisal }} bundle diff --git a/.github/workflows/supported.yml b/.github/workflows/supported.yml index 353488b..062faf0 100644 --- a/.github/workflows/supported.yml +++ b/.github/workflows/supported.yml @@ -81,12 +81,8 @@ jobs: # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) # We need to do this first to get appraisal installed. - # NOTE: This does not use the root Gemfile at all. - # However, because we hack appraisal in our root gemfile, - # we need a separate Gemfile to install it first, which is Appraisal.boot. - - name: Install Appraisal - run: BUNDLE_GEMFILE=Appraisal.boot.gemfile bundle - - name: Bundle for Appraisal ${{ matrix.appraisal }} + # NOTE: This does not use the main Gemfile at all. + - name: Bundle install for Appraisal ${{ matrix.appraisal }} run: bundle - name: Install Appraisal ${{ matrix.appraisal }} dependencies run: bundle exec appraisal ${{ matrix.appraisal }} bundle diff --git a/.github/workflows/unsupported.yml b/.github/workflows/unsupported.yml index 6fb1c6c..494818a 100644 --- a/.github/workflows/unsupported.yml +++ b/.github/workflows/unsupported.yml @@ -58,12 +58,8 @@ jobs: # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) # We need to do this first to get appraisal installed. - # NOTE: This does not use the root Gemfile at all. - # However, because we hack appraisal in our root gemfile, - # we need a separate Gemfile to install it first, which is Appraisal.boot. - - name: Install Appraisal - run: BUNDLE_GEMFILE=Appraisal.boot.gemfile bundle - - name: Bundle for Appraisal ${{ matrix.appraisal }} + # NOTE: This does not use the main Gemfile at all. + - name: Bundle install for Appraisal ${{ matrix.appraisal }} run: bundle - name: Install Appraisal ${{ matrix.appraisal }} dependencies run: bundle exec appraisal ${{ matrix.appraisal }} bundle diff --git a/.rubocop_gradual.lock b/.rubocop_gradual.lock index 1656439..4227240 100644 --- a/.rubocop_gradual.lock +++ b/.rubocop_gradual.lock @@ -25,14 +25,14 @@ "lib/turbo_tests/reporter.rb:1386902608": [ [7, 5, 400, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2092085575] ], - "lib/turbo_tests/runner.rb:3421474245": [ + "lib/turbo_tests/runner.rb:2461972978": [ [13, 5, 271, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 21989008], [20, 5, 1311, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1925027850], - [221, 11, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [239, 47, 6, "Style/GlobalStdStream: Use `$stderr` instead of `STDERR`.", 3356712163], - [241, 21, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [250, 7, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [310, 9, 6, "Style/GlobalStdStream: Use `$stdout` instead of `STDOUT`.", 3356722952] + [244, 11, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [263, 47, 6, "Style/GlobalStdStream: Use `$stderr` instead of `STDERR`.", 3356712163], + [265, 21, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [274, 7, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [334, 9, 6, "Style/GlobalStdStream: Use `$stdout` instead of `STDOUT`.", 3356722952] ], "spec/cli_spec.rb:3990998076": [ [1, 1, 30, "RSpec/SpecFilePathFormat: Spec path should end with `turbo_tests/cli*_spec.rb`.", 965721356], @@ -61,8 +61,8 @@ [10, 15, 7, "RSpec/MessageSpies: Prefer `have_received` for setting message expectations. Setup `ParallelTests::Tasks` as a spy using `allow` or `instance_spy`.", 1384559950], [20, 15, 7, "RSpec/MessageSpies: Prefer `have_received` for setting message expectations. Setup `ParallelTests::Tasks` as a spy using `allow` or `instance_spy`.", 1384559950] ], - "turbo_tests.gemspec:2105011563": [ - [30, 16, 36, "ThreadSafety/DirChdir: Avoid using `Dir.chdir` due to its process-wide effect.", 3576345059], - [31, 5, 19, "Packaging/GemspecGit: Avoid using git to produce lists of files. Downstreams often need to build your package in an environment that does not have git (on purpose). Use some pure Ruby alternative, like `Dir` or `Dir.glob`.", 3879951891] + "turbo_tests.gemspec:2921004918": [ + [29, 16, 36, "ThreadSafety/DirChdir: Avoid using `Dir.chdir` due to its process-wide effect.", 3576345059], + [30, 5, 19, "Packaging/GemspecGit: Avoid using git to produce lists of files. Downstreams often need to build your package in an environment that does not have git (on purpose). Use some pure Ruby alternative, like `Dir` or `Dir.glob`.", 3879951891] ] } diff --git a/Appraisal.boot.gemfile b/Appraisal.boot.gemfile deleted file mode 100644 index b4f9d66..0000000 --- a/Appraisal.boot.gemfile +++ /dev/null @@ -1,8 +0,0 @@ -git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } - -source "https://rubygems.org" - -# Appraisal Boot Gemfile is for installing appraisal only (not running it). -# We do not load the standard Gemfile, as it is tailored for local development. - -gemspec diff --git a/Appraisal.root.gemfile b/Appraisal.root.gemfile index c678dc6..c5db382 100644 --- a/Appraisal.root.gemfile +++ b/Appraisal.root.gemfile @@ -3,14 +3,10 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } source "https://rubygems.org" # Appraisal Root Gemfile is for running appraisal to generate the Appraisal Gemfiles -# in gemfiles/*gemfile. It is not loaded on CI. -# On CI we only run it for the Appraisal-based builds. +# in gemfiles/*gemfile. +# On CI, we use it for the Appraisal-based builds. # We do not load the standard Gemfile, as it is tailored for local development. gemspec -# Allow usage of eval_gemfile inside our Appraisal definitions -# Solution is based on this comment: -# https://github.com/thoughtbot/appraisal/issues/154#issuecomment-493804217 -# (and fixed to work in 2025) -eval_gemfile "appraisal-hack.gemfile" +gem "appraisal", github: "pboling/appraisal", branch: "eval_gemfile" diff --git a/Appraisals b/Appraisals index 4bd1503..f24e85e 100644 --- a/Appraisals +++ b/Appraisals @@ -3,31 +3,37 @@ appraise "ruby-2-7" do gem "mutex_m", "~> 0.2" gem "stringio", "~> 3.0" + remove_gem "appraisal" # only present because it must be in the gemfile because we target a git branch end appraise "ruby-3-0" do gem "mutex_m", "~> 0.2" gem "stringio", "~> 3.0" + remove_gem "appraisal" # only present because it must be in the gemfile because we target a git branch end appraise "ruby-3-1" do gem "mutex_m", "~> 0.2" gem "stringio", "~> 3.0" + remove_gem "appraisal" # only present because it must be in the gemfile because we target a git branch end appraise "ruby-3-2" do gem "mutex_m", "~> 0.2" gem "stringio", "~> 3.0" + remove_gem "appraisal" # only present because it must be in the gemfile because we target a git branch end appraise "ruby-3-3" do gem "mutex_m", "~> 0.2" gem "stringio", "~> 3.0" + remove_gem "appraisal" # only present because it must be in the gemfile because we target a git branch end appraise "ruby-3-4" do gem "mutex_m", "~> 0.2" gem "stringio", "~> 3.0" + remove_gem "appraisal" # only present because it must be in the gemfile because we target a git branch end # Only run security audit on latest Ruby version @@ -35,6 +41,7 @@ appraise "audit" do gem "mutex_m", "~> 0.2" gem "stringio", "~> 3.0" eval_gemfile "modular/audit.gemfile" + remove_gem "appraisal" # only present because it must be in the gemfile because we target a git branch end # Only run coverage on latest Ruby version @@ -42,6 +49,7 @@ appraise "coverage" do gem "mutex_m", "~> 0.2" gem "stringio", "~> 3.0" eval_gemfile "modular/coverage.gemfile" + remove_gem "appraisal" # only present because it must be in the gemfile because we target a git branch end # Only run linter on latest Ruby version (but, in support of oldest supported Ruby version) @@ -49,19 +57,23 @@ appraise "style" do gem "mutex_m", "~> 0.2" gem "stringio", "~> 3.0" eval_gemfile "modular/style.gemfile" + remove_gem "appraisal" # only present because it must be in the gemfile because we target a git branch end appraise "ruby-head" do gem "mutex_m", ">= 0.2" gem "stringio", ">= 3.0" + remove_gem "appraisal" # only present because it must be in the gemfile because we target a git branch end appraise "truffleruby-head" do gem "mutex_m", ">= 0.2" gem "stringio", ">= 3.0" + remove_gem "appraisal" # only present because it must be in the gemfile because we target a git branch end appraise "jruby-head" do gem "mutex_m", ">= 0.2" gem "stringio", ">= 3.0" + remove_gem "appraisal" # only present because it must be in the gemfile because we target a git branch end diff --git a/Gemfile b/Gemfile index 47938ce..5963235 100644 --- a/Gemfile +++ b/Gemfile @@ -20,3 +20,5 @@ eval_gemfile "gemfiles/modular/coverage.gemfile" # Linting eval_gemfile "gemfiles/modular/style.gemfile" + +gem "appraisal", path: "/Users/pboling/src/forks/appraisal" diff --git a/Gemfile.lock b/Gemfile.lock index b2bd57f..5db77d4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -5,14 +5,18 @@ PATH parallel_tests (>= 3.3.0, < 5) rspec (>= 3.10) -GEM - remote: https://rubygems.org/ +PATH + remote: /Users/pboling/src/forks/appraisal specs: - ansi (1.5.0) - appraisal (2.5.0) + appraisal (3.0.0.rc1) bundler rake thor (>= 0.14.0) + +GEM + remote: https://rubygems.org/ + specs: + ansi (1.5.0) ast (2.4.2) benchmark (0.4.0) bundler-audit (0.9.2) @@ -32,13 +36,13 @@ GEM simplecov-rcov (~> 0.3, >= 0.3.3) simplecov_json_formatter (~> 0.1, >= 0.1.4) version_gem (~> 1.1, >= 1.1.4) - language_server-protocol (3.17.0.3) + language_server-protocol (3.17.0.4) lint_roller (1.1.0) method_source (1.1.0) parallel (1.26.3) parallel_tests (4.9.0) parallel - parser (3.3.6.0) + parser (3.3.7.0) ast (~> 2.4.1) racc pry (0.15.2) @@ -63,7 +67,7 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-support (3.13.2) - rubocop (1.69.2) + rubocop (1.70.0) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) @@ -73,7 +77,7 @@ GEM rubocop-ast (>= 1.36.2, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.37.0) + rubocop-ast (1.38.0) parser (>= 3.3.1.0) rubocop-gradual (0.3.6) diff-lcs (>= 1.2.0, < 2.0) @@ -94,7 +98,7 @@ GEM rubocop-ast (>= 1.31.1, < 2.0) rubocop-rake (0.6.0) rubocop (~> 1.0) - rubocop-rspec (3.3.0) + rubocop-rspec (3.4.0) rubocop (~> 1.61) rubocop-ruby2_7 (2.0.6) rubocop-gradual (~> 0.3, >= 0.3.1) @@ -125,10 +129,10 @@ GEM simplecov-rcov (0.3.7) simplecov (>= 0.4.1) simplecov_json_formatter (0.1.4) - standard (1.43.0) + standard (1.44.0) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.0) - rubocop (~> 1.69.1) + rubocop (~> 1.70.0) standard-custom (~> 1.0.0) standard-performance (~> 1.6) standard-custom (1.0.2) @@ -143,7 +147,8 @@ GEM standard-custom (>= 1.0.2, < 2) standard-performance (>= 1.3.1, < 2) version_gem (>= 1.1.4, < 3) - terminal-table (1.6.0) + terminal-table (4.0.0) + unicode-display_width (>= 1.1.1, < 4) thor (1.3.2) unicode-display_width (3.1.4) unicode-emoji (~> 4.0, >= 4.0.4) @@ -154,7 +159,7 @@ PLATFORMS ruby DEPENDENCIES - appraisal (~> 2.5) + appraisal! benchmark (~> 0.4) bundler-audit (~> 0.9.2) kettle-soup-cover (~> 1.0, >= 1.0.4) diff --git a/README.md b/README.md index a5e30f7..8c03ca7 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,13 @@ To install this gem onto your local machine, run `bundle exec rake install`. To From time to time the appraisal gemfiles in `gemfiles/` will need to be updated. They are created and updated with the command: +```shell +BUNDLE_GEMFILE=Appraisal.root.gemfile bundle install +BUNDLE_GEMFILE=Appraisal.root.gemfile bundle exec appraisal update +``` + +NOTE: Once `eval_gemfile` support is [merged into appraisal](https://github.com/thoughtbot/appraisal/pull/248) the above commands will be simplified to: + ```shell BUNDLE_GEMFILE=Appraisal.root.gemfile appraisal update ``` diff --git a/appraisal-hack.gemfile b/appraisal-hack.gemfile deleted file mode 100644 index b487778..0000000 --- a/appraisal-hack.gemfile +++ /dev/null @@ -1,40 +0,0 @@ -# NOTE: There are two identical copies of this file to make this hack work. -# -# Solution is based on this comment: -# https://github.com/thoughtbot/appraisal/issues/154#issuecomment-493804217 -# (and fixed to work in 2025) -# NOTE: We have two copies of this file due to the inability to specify a reliable path, -# that works across varying invocations as indicated in that same comment. -# See: https://github.com/thoughtbot/appraisal/issues/154#issuecomment-2571386411 - -require "appraisal/bundler_dsl" - -if Appraisal::BundlerDSL::PARTS.include?("eval_gemfile") - # When the hack is already active, this code path is run within appraisal, - # so we can't really tell the difference between already hacked, - # and new version of appraisal has added the feature -else - require "appraisal/appraisal" - - Appraisal::Appraisal.class_eval do - def eval_gemfile(*args) - gemfile.eval_gemfile(*args) - end - end - - Appraisal::BundlerDSL.class_eval do - def eval_gemfile(path, contents = nil) - (@eval_gemfile ||= []) << [path, contents] - end - - private - - def eval_gemfile_entry - @eval_gemfile.map { |(p, c)| "eval_gemfile(#{p.inspect}#{", #{c.inspect}" if c})" } * "\n\n" - end - - alias_method :eval_gemfile_entry_for_dup, :eval_gemfile_entry - - self::PARTS << "eval_gemfile" - end -end diff --git a/gemfiles/appraisal-hack.gemfile b/gemfiles/appraisal-hack.gemfile deleted file mode 100644 index b487778..0000000 --- a/gemfiles/appraisal-hack.gemfile +++ /dev/null @@ -1,40 +0,0 @@ -# NOTE: There are two identical copies of this file to make this hack work. -# -# Solution is based on this comment: -# https://github.com/thoughtbot/appraisal/issues/154#issuecomment-493804217 -# (and fixed to work in 2025) -# NOTE: We have two copies of this file due to the inability to specify a reliable path, -# that works across varying invocations as indicated in that same comment. -# See: https://github.com/thoughtbot/appraisal/issues/154#issuecomment-2571386411 - -require "appraisal/bundler_dsl" - -if Appraisal::BundlerDSL::PARTS.include?("eval_gemfile") - # When the hack is already active, this code path is run within appraisal, - # so we can't really tell the difference between already hacked, - # and new version of appraisal has added the feature -else - require "appraisal/appraisal" - - Appraisal::Appraisal.class_eval do - def eval_gemfile(*args) - gemfile.eval_gemfile(*args) - end - end - - Appraisal::BundlerDSL.class_eval do - def eval_gemfile(path, contents = nil) - (@eval_gemfile ||= []) << [path, contents] - end - - private - - def eval_gemfile_entry - @eval_gemfile.map { |(p, c)| "eval_gemfile(#{p.inspect}#{", #{c.inspect}" if c})" } * "\n\n" - end - - alias_method :eval_gemfile_entry_for_dup, :eval_gemfile_entry - - self::PARTS << "eval_gemfile" - end -end diff --git a/gemfiles/audit.gemfile b/gemfiles/audit.gemfile index 82f8f72..b93d318 100644 --- a/gemfiles/audit.gemfile +++ b/gemfiles/audit.gemfile @@ -7,6 +7,4 @@ gem "stringio", "~> 3.0" gemspec path: "../" -eval_gemfile("appraisal-hack.gemfile") - eval_gemfile("modular/audit.gemfile") diff --git a/gemfiles/audit.gemfile.lock b/gemfiles/audit.gemfile.lock index 8206e3e..f614ea5 100644 --- a/gemfiles/audit.gemfile.lock +++ b/gemfiles/audit.gemfile.lock @@ -8,10 +8,6 @@ PATH GEM remote: https://rubygems.org/ specs: - appraisal (2.5.0) - bundler - rake - thor (>= 0.14.0) bundler-audit (0.9.2) bundler (>= 1.2.0, < 3) thor (~> 1.0) @@ -53,7 +49,6 @@ PLATFORMS x86_64-linux-musl DEPENDENCIES - appraisal (~> 2.5) bundler-audit (~> 0.9.2) mutex_m (~> 0.2) pry (~> 0.14) diff --git a/gemfiles/coverage.gemfile b/gemfiles/coverage.gemfile index 2ef804c..ec3037b 100644 --- a/gemfiles/coverage.gemfile +++ b/gemfiles/coverage.gemfile @@ -7,6 +7,4 @@ gem "stringio", "~> 3.0" gemspec path: "../" -eval_gemfile("appraisal-hack.gemfile") - eval_gemfile("modular/coverage.gemfile") diff --git a/gemfiles/coverage.gemfile.lock b/gemfiles/coverage.gemfile.lock index 16b3902..2bfc00b 100644 --- a/gemfiles/coverage.gemfile.lock +++ b/gemfiles/coverage.gemfile.lock @@ -9,10 +9,6 @@ GEM remote: https://rubygems.org/ specs: ansi (1.5.0) - appraisal (2.5.0) - bundler - rake - thor (>= 0.14.0) coderay (1.1.3) diff-lcs (1.5.1) docile (1.4.1) @@ -65,10 +61,11 @@ GEM simplecov (>= 0.4.1) simplecov_json_formatter (0.1.4) stringio (3.1.2) - terminal-table (3.0.2) - unicode-display_width (>= 1.1.1, < 3) - thor (1.3.2) - unicode-display_width (2.6.0) + terminal-table (4.0.0) + unicode-display_width (>= 1.1.1, < 4) + unicode-display_width (3.1.4) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) version_gem (1.1.4) PLATFORMS @@ -76,7 +73,6 @@ PLATFORMS ruby DEPENDENCIES - appraisal (~> 2.5) kettle-soup-cover (~> 1.0, >= 1.0.4) mutex_m (~> 0.2) pry (~> 0.14) diff --git a/gemfiles/jruby_head.gemfile b/gemfiles/jruby_head.gemfile index 288e442..49b0671 100644 --- a/gemfiles/jruby_head.gemfile +++ b/gemfiles/jruby_head.gemfile @@ -6,5 +6,3 @@ gem "mutex_m", ">= 0.2" gem "stringio", ">= 3.0" gemspec path: "../" - -eval_gemfile("appraisal-hack.gemfile") diff --git a/gemfiles/ruby_2_7.gemfile b/gemfiles/ruby_2_7.gemfile index 4c02229..ed1eb63 100644 --- a/gemfiles/ruby_2_7.gemfile +++ b/gemfiles/ruby_2_7.gemfile @@ -6,5 +6,3 @@ gem "mutex_m", "~> 0.2" gem "stringio", "~> 3.0" gemspec path: "../" - -eval_gemfile("appraisal-hack.gemfile") diff --git a/gemfiles/ruby_3_0.gemfile b/gemfiles/ruby_3_0.gemfile index 4c02229..ed1eb63 100644 --- a/gemfiles/ruby_3_0.gemfile +++ b/gemfiles/ruby_3_0.gemfile @@ -6,5 +6,3 @@ gem "mutex_m", "~> 0.2" gem "stringio", "~> 3.0" gemspec path: "../" - -eval_gemfile("appraisal-hack.gemfile") diff --git a/gemfiles/ruby_3_1.gemfile b/gemfiles/ruby_3_1.gemfile index 4c02229..ed1eb63 100644 --- a/gemfiles/ruby_3_1.gemfile +++ b/gemfiles/ruby_3_1.gemfile @@ -6,5 +6,3 @@ gem "mutex_m", "~> 0.2" gem "stringio", "~> 3.0" gemspec path: "../" - -eval_gemfile("appraisal-hack.gemfile") diff --git a/gemfiles/ruby_3_2.gemfile b/gemfiles/ruby_3_2.gemfile index 4c02229..ed1eb63 100644 --- a/gemfiles/ruby_3_2.gemfile +++ b/gemfiles/ruby_3_2.gemfile @@ -6,5 +6,3 @@ gem "mutex_m", "~> 0.2" gem "stringio", "~> 3.0" gemspec path: "../" - -eval_gemfile("appraisal-hack.gemfile") diff --git a/gemfiles/ruby_3_3.gemfile b/gemfiles/ruby_3_3.gemfile index 4c02229..ed1eb63 100644 --- a/gemfiles/ruby_3_3.gemfile +++ b/gemfiles/ruby_3_3.gemfile @@ -6,5 +6,3 @@ gem "mutex_m", "~> 0.2" gem "stringio", "~> 3.0" gemspec path: "../" - -eval_gemfile("appraisal-hack.gemfile") diff --git a/gemfiles/ruby_3_4.gemfile b/gemfiles/ruby_3_4.gemfile index 4c02229..ed1eb63 100644 --- a/gemfiles/ruby_3_4.gemfile +++ b/gemfiles/ruby_3_4.gemfile @@ -6,5 +6,3 @@ gem "mutex_m", "~> 0.2" gem "stringio", "~> 3.0" gemspec path: "../" - -eval_gemfile("appraisal-hack.gemfile") diff --git a/gemfiles/ruby_head.gemfile b/gemfiles/ruby_head.gemfile index 288e442..49b0671 100644 --- a/gemfiles/ruby_head.gemfile +++ b/gemfiles/ruby_head.gemfile @@ -6,5 +6,3 @@ gem "mutex_m", ">= 0.2" gem "stringio", ">= 3.0" gemspec path: "../" - -eval_gemfile("appraisal-hack.gemfile") diff --git a/gemfiles/style.gemfile b/gemfiles/style.gemfile index 277ed3b..f75762c 100644 --- a/gemfiles/style.gemfile +++ b/gemfiles/style.gemfile @@ -7,6 +7,4 @@ gem "stringio", "~> 3.0" gemspec path: "../" -eval_gemfile("appraisal-hack.gemfile") - eval_gemfile("modular/style.gemfile") diff --git a/gemfiles/style.gemfile.lock b/gemfiles/style.gemfile.lock index c128df8..d4ff575 100644 --- a/gemfiles/style.gemfile.lock +++ b/gemfiles/style.gemfile.lock @@ -8,24 +8,20 @@ PATH GEM remote: https://rubygems.org/ specs: - appraisal (2.5.0) - bundler - rake - thor (>= 0.14.0) ast (2.4.2) benchmark (0.4.0) coderay (1.1.3) diff-lcs (1.5.1) diffy (3.4.3) json (2.9.1) - language_server-protocol (3.17.0.3) + language_server-protocol (3.17.0.4) lint_roller (1.1.0) method_source (1.1.0) mutex_m (0.3.0) parallel (1.26.3) parallel_tests (4.9.0) parallel - parser (3.3.6.0) + parser (3.3.7.0) ast (~> 2.4.1) racc pry (0.15.2) @@ -49,7 +45,7 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-support (3.13.2) - rubocop (1.69.2) + rubocop (1.70.0) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) @@ -59,7 +55,7 @@ GEM rubocop-ast (>= 1.36.2, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.37.0) + rubocop-ast (1.38.0) parser (>= 3.3.1.0) rubocop-gradual (0.3.6) diff-lcs (>= 1.2.0, < 2.0) @@ -80,7 +76,7 @@ GEM rubocop-ast (>= 1.31.1, < 2.0) rubocop-rake (0.6.0) rubocop (~> 1.0) - rubocop-rspec (3.3.0) + rubocop-rspec (3.4.0) rubocop (~> 1.61) rubocop-ruby2_7 (2.0.6) rubocop-gradual (~> 0.3, >= 0.3.1) @@ -95,10 +91,10 @@ GEM rubocop-thread_safety (0.6.0) rubocop (>= 1.48.1) ruby-progressbar (1.13.0) - standard (1.43.0) + standard (1.44.0) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.0) - rubocop (~> 1.69.1) + rubocop (~> 1.70.0) standard-custom (~> 1.0.0) standard-performance (~> 1.6) standard-custom (1.0.2) @@ -114,7 +110,6 @@ GEM standard-performance (>= 1.3.1, < 2) version_gem (>= 1.1.4, < 3) stringio (3.1.2) - thor (1.3.2) unicode-display_width (3.1.4) unicode-emoji (~> 4.0, >= 4.0.4) unicode-emoji (4.0.4) @@ -125,7 +120,6 @@ PLATFORMS ruby DEPENDENCIES - appraisal (~> 2.5) benchmark (~> 0.4) mutex_m (~> 0.2) pry (~> 0.14) diff --git a/gemfiles/truffleruby_head.gemfile b/gemfiles/truffleruby_head.gemfile index 288e442..49b0671 100644 --- a/gemfiles/truffleruby_head.gemfile +++ b/gemfiles/truffleruby_head.gemfile @@ -6,5 +6,3 @@ gem "mutex_m", ">= 0.2" gem "stringio", ">= 3.0" gemspec path: "../" - -eval_gemfile("appraisal-hack.gemfile") diff --git a/lib/turbo_tests/runner.rb b/lib/turbo_tests/runner.rb index 7017c81..16d651a 100644 --- a/lib/turbo_tests/runner.rb +++ b/lib/turbo_tests/runner.rb @@ -245,8 +245,8 @@ def start_subprocess(env, extra_args, tests, process_id, record_runtime:, option stdout.each_line do |line| result = line.split(env["RSPEC_FORMATTER_OUTPUT_ID"]) - output = result.shift - print(output) unless output.empty? + initial = result.shift + print(initial) unless initial.empty? message = result.shift next unless message diff --git a/turbo_tests.gemspec b/turbo_tests.gemspec index 237aac9..1556a89 100644 --- a/turbo_tests.gemspec +++ b/turbo_tests.gemspec @@ -21,7 +21,6 @@ Gem::Specification.new do |spec| spec.add_dependency("parallel_tests", ">= 3.3.0", "< 5") spec.add_dependency("rspec", ">= 3.10") - spec.add_development_dependency("appraisal", "~> 2.5") spec.add_development_dependency("pry", "~> 0.14") spec.add_development_dependency("rake", "~> 13.0") From ce8b9339cc92b8b6d66839b84e3767e4a87ca542 Mon Sep 17 00:00:00 2001 From: Peter Boling Date: Tue, 4 Feb 2025 15:09:20 +0700 Subject: [PATCH 16/23] =?UTF-8?q?=F0=9F=94=A5=20Use=20parallel-tests=20dir?= =?UTF-8?q?ectly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - https://github.com/serpapi/turbo_tests/pull/45/files#r1456006187 --- .rubocop_gradual.lock | 12 ++++++------ lib/turbo_tests/runner.rb | 7 ++----- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/.rubocop_gradual.lock b/.rubocop_gradual.lock index 4227240..4b2c047 100644 --- a/.rubocop_gradual.lock +++ b/.rubocop_gradual.lock @@ -25,14 +25,14 @@ "lib/turbo_tests/reporter.rb:1386902608": [ [7, 5, 400, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2092085575] ], - "lib/turbo_tests/runner.rb:2461972978": [ + "lib/turbo_tests/runner.rb:2125673792": [ [13, 5, 271, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 21989008], [20, 5, 1311, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1925027850], - [244, 11, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [263, 47, 6, "Style/GlobalStdStream: Use `$stderr` instead of `STDERR`.", 3356712163], - [265, 21, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [274, 7, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [334, 9, 6, "Style/GlobalStdStream: Use `$stdout` instead of `STDOUT`.", 3356722952] + [241, 11, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [260, 47, 6, "Style/GlobalStdStream: Use `$stderr` instead of `STDERR`.", 3356712163], + [262, 21, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [271, 7, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [331, 9, 6, "Style/GlobalStdStream: Use `$stdout` instead of `STDOUT`.", 3356722952] ], "spec/cli_spec.rb:3990998076": [ [1, 1, 30, "RSpec/SpecFilePathFormat: Spec path should end with `turbo_tests/cli*_spec.rb`.", 965721356], diff --git a/lib/turbo_tests/runner.rb b/lib/turbo_tests/runner.rb index 16d651a..dce9a6d 100644 --- a/lib/turbo_tests/runner.rb +++ b/lib/turbo_tests/runner.rb @@ -98,8 +98,6 @@ def run ParallelTests::RSpec::Runner.tests_with_size(@files, {}).size, ].min - options_file = [".rspec_parallel", "spec/parallel_spec.opts", "spec/spec.opts"].detect { |f| File.file?(f) } - tests_in_groups = ParallelTests::RSpec::Runner.tests_in_groups( @files, @@ -111,7 +109,6 @@ def run subprocess_opts = { record_runtime: @record_runtime, - options_file: options_file, } @reporter.report(tests_in_groups) do |_reporter| @@ -175,7 +172,7 @@ def start_regular_subprocess(tests, process_id, **opts) ) end - def start_subprocess(env, extra_args, tests, process_id, record_runtime:, options_file:) + def start_subprocess(env, extra_args, tests, process_id, record_runtime:) if tests.empty? @messages << { type: "exit", @@ -214,7 +211,7 @@ def start_subprocess(env, extra_args, tests, process_id, record_runtime:, option [] end - spec_opts = ["-O", options_file] if options_file + spec_opts = ParallelTests::RSpec::Runner.send(:spec_opts) command = [ *command_name, From c8d91b1508fe7919a110ba79aca9beec225cbb4b Mon Sep 17 00:00:00 2001 From: Peter Boling Date: Tue, 4 Feb 2025 15:18:32 +0700 Subject: [PATCH 17/23] =?UTF-8?q?=E2=9E=95=20yard,=20yard-junk,=20rdoc,=20?= =?UTF-8?q?github-markup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gemfile | 3 +++ Gemfile.lock | 20 ++++++++++++++++++++ README.md | 4 ++-- gemfiles/modular/documentation.gemfile | 5 +++++ 4 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 gemfiles/modular/documentation.gemfile diff --git a/Gemfile b/Gemfile index 5963235..7203c89 100644 --- a/Gemfile +++ b/Gemfile @@ -21,4 +21,7 @@ eval_gemfile "gemfiles/modular/coverage.gemfile" # Linting eval_gemfile "gemfiles/modular/style.gemfile" +# Documentation +eval_gemfile "gemfiles/modular/documentation.gemfile" + gem "appraisal", path: "/Users/pboling/src/forks/appraisal" diff --git a/Gemfile.lock b/Gemfile.lock index 5db77d4..f6383e4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -18,14 +18,17 @@ GEM specs: ansi (1.5.0) ast (2.4.2) + backports (3.25.0) benchmark (0.4.0) bundler-audit (0.9.2) bundler (>= 1.2.0, < 3) thor (~> 1.0) coderay (1.1.3) + date (3.4.1) diff-lcs (1.5.1) diffy (3.4.3) docile (1.4.1) + github-markup (5.0.1) json (2.9.1) kettle-soup-cover (1.0.4) simplecov (~> 0.22) @@ -39,6 +42,7 @@ GEM language_server-protocol (3.17.0.4) lint_roller (1.1.0) method_source (1.1.0) + ostruct (0.6.1) parallel (1.26.3) parallel_tests (4.9.0) parallel @@ -48,9 +52,14 @@ GEM pry (0.15.2) coderay (~> 1.1) method_source (~> 1.0) + psych (5.2.3) + date + stringio racc (1.8.1) rainbow (3.1.1) rake (13.2.1) + rdoc (6.11.0) + psych (>= 4.0.0) regexp_parser (2.10.0) rexml (3.4.0) rspec (3.13.0) @@ -147,6 +156,7 @@ GEM standard-custom (>= 1.0.2, < 2) standard-performance (>= 1.3.1, < 2) version_gem (>= 1.1.4, < 3) + stringio (3.1.2) terminal-table (4.0.0) unicode-display_width (>= 1.1.1, < 4) thor (1.3.2) @@ -154,6 +164,12 @@ GEM unicode-emoji (~> 4.0, >= 4.0.4) unicode-emoji (4.0.4) version_gem (1.1.4) + yard (0.9.37) + yard-junk (0.0.10) + backports (>= 3.18) + ostruct + rainbow + yard PLATFORMS ruby @@ -162,14 +178,18 @@ DEPENDENCIES appraisal! benchmark (~> 0.4) bundler-audit (~> 0.9.2) + github-markup kettle-soup-cover (~> 1.0, >= 1.0.4) pry (~> 0.14) rake (~> 13.0) + rdoc (~> 6.10) rubocop-lts (~> 18.2, >= 18.2.1) rubocop-packaging (~> 0.5, >= 0.5.2) rubocop-rspec (~> 3.2) standard (>= 1.35.1, != 1.42.0, != 1.41.1) turbo_tests! + yard (~> 0.9.34) + yard-junk (~> 0.0.10) BUNDLED WITH 2.6.2 diff --git a/README.md b/README.md index 8c03ca7..f0de0c3 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ -`turbo_tests` is a drop-in replacement for [`grosser/parallel_tests`](https://github.com/grosser/parallel_tests) with incremental summarized output. Source code of this gem is based on [Discourse](https://github.com/discourse/discourse/blob/6b9784cf8a18636bce281a7e4d18e65a0cbc6290/lib/turbo_tests.rb) and [RubyGems](https://github.com/rubygems/rubygems/tree/390335ceb351668cd433bd5bb9823dd021f82533/bundler/tool) work in this area. +`turbo_tests` is a drop-in replacement for [grosser/parallel_tests](https://github.com/grosser/parallel_tests) with incremental summarized output. Source code of this gem is based on [Discourse](https://github.com/discourse/discourse/blob/6b9784cf8a18636bce281a7e4d18e65a0cbc6290/lib/turbo_tests.rb) and [RubyGems](https://github.com/rubygems/rubygems/tree/390335ceb351668cd433bd5bb9823dd021f82533/bundler/tool) work in this area. -Incremental summarized output [doesn't fit vision of `parallel_tests` author](https://github.com/grosser/parallel_tests/issues/708) and [RSpec doesn't support built-in parallel testing yet](https://github.com/rspec/rspec-rails/issues/2104#issuecomment-658474900). This gem will not be useful once one of the issues above will be implemented. +Incremental summarized output doesn't [fit vision](https://github.com/grosser/parallel_tests/issues/708) of `parallel_tests` author and [RSpec doesn't support built-in parallel testing yet](https://github.com/rspec/rspec-rails/issues/2104#issuecomment-658474900). This gem will not be useful once one of the issues above will be implemented. ## Why incremental output? diff --git a/gemfiles/modular/documentation.gemfile b/gemfiles/modular/documentation.gemfile new file mode 100644 index 0000000..c985345 --- /dev/null +++ b/gemfiles/modular/documentation.gemfile @@ -0,0 +1,5 @@ +### Documentation +gem "yard", "~> 0.9.34", require: false +gem "yard-junk", "~> 0.0.10" +gem "github-markup" +gem "rdoc", "~> 6.10" From 96e06c30ae4de2d948b5216fa4b346fa08defb24 Mon Sep 17 00:00:00 2001 From: Sebastien Savater Date: Thu, 11 Apr 2024 15:11:57 +0200 Subject: [PATCH 18/23] Refactor Runner to forward more options to Reporter --- .rubocop_gradual.lock | 17 +++++++---------- lib/turbo_tests/reporter.rb | 27 +++++++++++++++------------ lib/turbo_tests/runner.rb | 8 ++++++-- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/.rubocop_gradual.lock b/.rubocop_gradual.lock index 4b2c047..7d421b0 100644 --- a/.rubocop_gradual.lock +++ b/.rubocop_gradual.lock @@ -22,17 +22,14 @@ [50, 5, 269, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 4255608859], [71, 5, 614, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 574527918] ], - "lib/turbo_tests/reporter.rb:1386902608": [ - [7, 5, 400, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2092085575] - ], - "lib/turbo_tests/runner.rb:2125673792": [ + "lib/turbo_tests/runner.rb:3852406545": [ [13, 5, 271, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 21989008], - [20, 5, 1311, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1925027850], - [241, 11, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [260, 47, 6, "Style/GlobalStdStream: Use `$stderr` instead of `STDERR`.", 3356712163], - [262, 21, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [271, 7, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [331, 9, 6, "Style/GlobalStdStream: Use `$stdout` instead of `STDOUT`.", 3356722952] + [20, 5, 1400, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1925027850], + [245, 11, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [264, 47, 6, "Style/GlobalStdStream: Use `$stderr` instead of `STDERR`.", 3356712163], + [266, 21, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [275, 7, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [335, 9, 6, "Style/GlobalStdStream: Use `$stdout` instead of `STDOUT`.", 3356722952] ], "spec/cli_spec.rb:3990998076": [ [1, 1, 30, "RSpec/SpecFilePathFormat: Spec path should end with `turbo_tests/cli*_spec.rb`.", 965721356], diff --git a/lib/turbo_tests/reporter.rb b/lib/turbo_tests/reporter.rb index 472e510..acd2f76 100644 --- a/lib/turbo_tests/reporter.rb +++ b/lib/turbo_tests/reporter.rb @@ -3,26 +3,27 @@ module TurboTests class Reporter attr_writer :load_time + attr_reader :pending_examples, :failed_examples + + class << self + def from_config(formatter_config, start_time, seed, seed_used, files, parallel_options) + reporter = new(start_time, seed, seed_used, files, parallel_options) - def self.from_config(formatter_config, start_time, seed, seed_used) - reporter = new(start_time, seed, seed_used) + formatter_config.each do |config| + name, outputs = config.values_at(:name, :outputs) - formatter_config.each do |config| - name, outputs = config.values_at(:name, :outputs) + outputs.map! do |filename| + (filename == "-") ? $stdout : File.open(filename, "w") + end - outputs.map! do |filename| - (filename == "-") ? $stdout : File.open(filename, "w") + reporter.add(name, outputs) end - reporter.add(name, outputs) + reporter end - - reporter end - attr_reader :pending_examples, :failed_examples - - def initialize(start_time, seed, seed_used) + def initialize(start_time, seed, seed_used, files, parallel_options) @formatters = [] @pending_examples = [] @failed_examples = [] @@ -33,6 +34,8 @@ def initialize(start_time, seed, seed_used) @seed_used = seed_used @load_time = 0 @errors_outside_of_examples_count = 0 + @files = files + @parallel_options = parallel_options end def add(name, outputs) diff --git a/lib/turbo_tests/runner.rb b/lib/turbo_tests/runner.rb index dce9a6d..1bb7117 100644 --- a/lib/turbo_tests/runner.rb +++ b/lib/turbo_tests/runner.rb @@ -43,10 +43,12 @@ def self.run(opts = {}) warn("VERBOSE") if verbose - reporter = Reporter.from_config(formatters, start_time, seed, seed_used) + reporter = Reporter.from_config(formatters, start_time, seed, seed_used, files, parallel_options) new( reporter: reporter, + formatters: formatters, + start_time: start_time, files: files, tags: tags, runtime_log: runtime_log, @@ -62,12 +64,14 @@ def self.run(opts = {}) ).run end - def initialize(opts) + def initialize(**opts) + @formatters = opts[:formatters] @reporter = opts[:reporter] @files = opts[:files] @tags = opts[:tags] @verbose = opts[:verbose] @fail_fast = opts[:fail_fast] + @start_time = opts[:start_time] @count = opts[:count] @seed = opts[:seed] @seed_used = opts[:seed_used] From b28dbe025b3deeb967fd2d6daeb48a0ac6094120 Mon Sep 17 00:00:00 2001 From: Sebastien Savater Date: Wed, 17 Apr 2024 09:16:47 +0200 Subject: [PATCH 19/23] Add custom formatters to README --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f0de0c3..c23b7c2 100644 --- a/README.md +++ b/README.md @@ -109,12 +109,18 @@ Options: --print_failed_group Prints group that had failures in it ``` -To pass any options supported by paralell_tests, use `--` : +To pass any options supported by paralell_tests, use `--`: ```bash bundle exec turbo_tests -n 4 -- --only-group 1 --pattern spec/system ``` +`turbo_tests` supports custom formatter such as Fuubar, but you might need to require it: + +```bash +bundle exec turbo_tests -r fuubar -f Fuubar spec/whatever +``` + ## 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. From a040af525cb673ea5f11bcf21a9412f0ea1768b9 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 4 Dec 2024 15:58:32 +0900 Subject: [PATCH 20/23] Use RSPEC_EXECUTABLE environmental variable if it's provided - https://github.com/serpapi/turbo_tests/pull/66#issuecomment-2517881281 --- lib/turbo_tests/runner.rb | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/turbo_tests/runner.rb b/lib/turbo_tests/runner.rb index 1bb7117..49564fb 100644 --- a/lib/turbo_tests/runner.rb +++ b/lib/turbo_tests/runner.rb @@ -189,11 +189,14 @@ def start_subprocess(env, extra_args, tests, process_id, record_runtime:) env["RUBYOPT"] = ["-I#{File.expand_path("..", __dir__)}", ENV["RUBYOPT"]].compact.join(" ") env["RSPEC_SILENCE_FILTER_ANNOUNCEMENTS"] = "1" - command_name = if ENV["BUNDLE_BIN_PATH"] - [ENV["BUNDLE_BIN_PATH"], "exec", "rspec"] - else - "rspec" - end + command_name = + if ENV["RSPEC_EXECUTABLE"] + ENV["RSPEC_EXECUTABLE"].split + elsif ENV["BUNDLE_BIN_PATH"] + [ENV["BUNDLE_BIN_PATH"], "exec", "rspec"] + else + "rspec" + end record_runtime_options = if record_runtime From 78bd2a93ea6e7efab6533a059f81c3bee9d0b60c Mon Sep 17 00:00:00 2001 From: Peter Boling Date: Tue, 4 Feb 2025 23:31:36 +0700 Subject: [PATCH 21/23] =?UTF-8?q?=F0=9F=9A=A8=20Update=20lint=20lock?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .rubocop_gradual.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.rubocop_gradual.lock b/.rubocop_gradual.lock index 7d421b0..dac37d7 100644 --- a/.rubocop_gradual.lock +++ b/.rubocop_gradual.lock @@ -22,14 +22,14 @@ [50, 5, 269, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 4255608859], [71, 5, 614, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 574527918] ], - "lib/turbo_tests/runner.rb:3852406545": [ + "lib/turbo_tests/runner.rb:1197951922": [ [13, 5, 271, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 21989008], [20, 5, 1400, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1925027850], - [245, 11, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [264, 47, 6, "Style/GlobalStdStream: Use `$stderr` instead of `STDERR`.", 3356712163], - [266, 21, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [275, 7, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [335, 9, 6, "Style/GlobalStdStream: Use `$stdout` instead of `STDOUT`.", 3356722952] + [248, 11, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [267, 47, 6, "Style/GlobalStdStream: Use `$stderr` instead of `STDERR`.", 3356712163], + [269, 21, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [278, 7, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [338, 9, 6, "Style/GlobalStdStream: Use `$stdout` instead of `STDOUT`.", 3356722952] ], "spec/cli_spec.rb:3990998076": [ [1, 1, 30, "RSpec/SpecFilePathFormat: Spec path should end with `turbo_tests/cli*_spec.rb`.", 965721356], From b9d229c24da217176a98f6d1807905653772b58b Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 12 Dec 2024 13:53:50 +0900 Subject: [PATCH 22/23] tmp/test-pipes is no longer needed --- lib/turbo_tests/runner.rb | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/lib/turbo_tests/runner.rb b/lib/turbo_tests/runner.rb index 49564fb..466b5ad 100644 --- a/lib/turbo_tests/runner.rb +++ b/lib/turbo_tests/runner.rb @@ -109,8 +109,6 @@ def run **@parallel_options, ) - setup_tmp_dir - subprocess_opts = { record_runtime: @record_runtime, } @@ -157,15 +155,6 @@ def handle_interrupt end end - def setup_tmp_dir - begin - FileUtils.rm_r("tmp/test-pipes") - rescue Errno::ENOENT - end - - FileUtils.mkdir_p("tmp/test-pipes/") - end - def start_regular_subprocess(tests, process_id, **opts) start_subprocess( {"TEST_ENV_NUMBER" => process_id.to_s}, From 49f2bcc02ab92df4e9a6965174ca20d7aca0ba1a Mon Sep 17 00:00:00 2001 From: Peter Boling Date: Tue, 4 Feb 2025 23:38:38 +0700 Subject: [PATCH 23/23] =?UTF-8?q?=F0=9F=9A=A8=20Update=20lint=20lock?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .rubocop_gradual.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.rubocop_gradual.lock b/.rubocop_gradual.lock index dac37d7..352c243 100644 --- a/.rubocop_gradual.lock +++ b/.rubocop_gradual.lock @@ -22,14 +22,14 @@ [50, 5, 269, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 4255608859], [71, 5, 614, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 574527918] ], - "lib/turbo_tests/runner.rb:1197951922": [ + "lib/turbo_tests/runner.rb:1439137339": [ [13, 5, 271, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 21989008], [20, 5, 1400, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1925027850], - [248, 11, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [267, 47, 6, "Style/GlobalStdStream: Use `$stderr` instead of `STDERR`.", 3356712163], - [269, 21, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [278, 7, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], - [338, 9, 6, "Style/GlobalStdStream: Use `$stdout` instead of `STDOUT`.", 3356722952] + [237, 11, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [256, 47, 6, "Style/GlobalStdStream: Use `$stderr` instead of `STDERR`.", 3356712163], + [258, 21, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [267, 7, 10, "ThreadSafety/NewThread: Avoid starting new threads.", 3411682361], + [327, 9, 6, "Style/GlobalStdStream: Use `$stdout` instead of `STDOUT`.", 3356722952] ], "spec/cli_spec.rb:3990998076": [ [1, 1, 30, "RSpec/SpecFilePathFormat: Spec path should end with `turbo_tests/cli*_spec.rb`.", 965721356],