From 1f2784fb733bab6e689b8011b158d151d8f89dca Mon Sep 17 00:00:00 2001 From: James Couball Date: Tue, 1 Jul 2025 16:19:47 -0700 Subject: [PATCH] feat: ensure windows compatibility for Ruby MRI Add a CI build and make sure all tests pass on Windows or don't run them if not supported on Windows. --- .github/workflows/continuous_integration.yml | 6 +++- spec/rspec/path_matchers/be_dir_spec.rb | 6 ++++ spec/rspec/path_matchers/be_file_spec.rb | 6 ++++ .../path_matchers/failure_messages_spec.rb | 28 +++++++++---------- spec/spec_helper.rb | 15 +++++++--- 5 files changed, 41 insertions(+), 20 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 621687c..f9c13e9 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -7,7 +7,7 @@ on: workflow_dispatch: # Supported platforms / Ruby versions: -# - Ubuntu: MRI (3.2, 3.3, 3.4), TruffleRuby (24), JRuby (9.4) +# - Ubuntu: MRI (3.2, 3.3, 3.4) # - Windows: MRI (3.2) jobs: @@ -30,6 +30,10 @@ jobs: ruby: ["3.2", "3.4"] operating-system: [ubuntu-latest] fail_on_low_coverage: [true] + include: + - # Only test with minimal Ruby version on Windows + ruby: 3.2 + operating-system: windows-latest steps: - name: Checkout diff --git a/spec/rspec/path_matchers/be_dir_spec.rb b/spec/rspec/path_matchers/be_dir_spec.rb index 57a6792..be8809f 100644 --- a/spec/rspec/path_matchers/be_dir_spec.rb +++ b/spec/rspec/path_matchers/be_dir_spec.rb @@ -215,6 +215,12 @@ end describe 'the mode: option' do + before(:all) do + # :nocov: this line is platform-specific + skip 'File mode tests are only applicable on Unix-like platforms' unless UNIX_LIKE_PLATFORM + # :nocov: + end + subject { expect(path).to be_dir(mode: expected_mode) } before { FileUtils.mkdir(path) } diff --git a/spec/rspec/path_matchers/be_file_spec.rb b/spec/rspec/path_matchers/be_file_spec.rb index 28aa09d..794f1de 100644 --- a/spec/rspec/path_matchers/be_file_spec.rb +++ b/spec/rspec/path_matchers/be_file_spec.rb @@ -572,6 +572,12 @@ end describe 'the mode: option' do + before(:all) do + # :nocov: this line is platform-specific + skip 'File mode tests are only applicable on Unix-like platforms' unless UNIX_LIKE_PLATFORM + # :nocov: + end + subject { expect(path).to be_file(mode: expected_mode) } before { FileUtils.touch(path) } diff --git a/spec/rspec/path_matchers/failure_messages_spec.rb b/spec/rspec/path_matchers/failure_messages_spec.rb index 5da7a0d..350b049 100644 --- a/spec/rspec/path_matchers/failure_messages_spec.rb +++ b/spec/rspec/path_matchers/failure_messages_spec.rb @@ -22,13 +22,15 @@ app_path = File.join(base_dir, 'app') Dir.mkdir(app_path) FileUtils.chmod(0o700, app_path) + # Mock the mtime to eliminate the potential for a different timezone output in the failure message + mock_file_stat(app_path, mtime: mocked_now - 200) - matcher = be_dir(mode: '0755') + matcher = be_dir(mtime: be_within(50).of(mocked_now)) matcher.matches?(app_path) expected_message = <<~MSG.chomp #{app_path} was not as expected: - expected mode to be "0755", but it was "0700" + expected mtime to be within 50 of 1967-03-15 00:16:00.000000000 -0700, but it was 1967-03-15 00:12:40 -0700 MSG expect(matcher.failure_message).to eq(expected_message) end @@ -60,20 +62,19 @@ setup_path = File.join(bin_path, 'setup') Dir.mkdir(bin_path) File.write(setup_path, '#!/bin/sh') - FileUtils.chmod(0o755, setup_path) - mock_file_stat(setup_path, uid: 9999, mode: 0o100755) - mock_user_name(9999, 'testuser') + # Mock the mtime to eliminate the potential for a different timezone output in the failure message + mock_file_stat(setup_path, mtime: mocked_now - 1, size: 9) matcher = be_dir.containing( - file('setup', mode: '0644', owner: 'root') + file('setup', mtime: mocked_now, size: be_zero) ) matcher.matches?(bin_path) expected_message = <<~MSG.chomp #{bin_path} was not as expected: - setup - expected mode to be "0644", but it was "0755" - expected owner to be "root", but it was "testuser" + expected mtime to be 1967-03-15 00:16:00 -0700, but it was 1967-03-15 00:15:59 -0700 + expected size to be zero, but it was 9 MSG expect(matcher.failure_message).to eq(expected_message) end @@ -92,14 +93,11 @@ FileUtils.mkdir_p(lib_dir) File.write(setup_path, '#!/bin/sh') File.write(version_path, version_rb_content) - FileUtils.chmod(0o755, setup_path) - - mock_file_stat(setup_path, uid: 9999, mode: 0o100755) - mock_user_name(9999, 'owner') + mock_file_stat(setup_path, mtime: mocked_now - 11, size: 9) matcher = be_dir.containing( dir('bin').containing( - file('setup', mode: '0644', owner: 'root') + file('setup', mtime: be_within(10).of(mocked_now), size: be_zero) ), dir('lib').containing( dir('new_project').containing( @@ -112,8 +110,8 @@ expected_message = <<~MSG.chomp #{base_dir} was not as expected: - bin/setup - expected mode to be "0644", but it was "0755" - expected owner to be "root", but it was "owner" + expected mtime to be within 10 of 1967-03-15 00:16:00.000000000 -0700, but it was 1967-03-15 00:15:49 -0700 + expected size to be zero, but it was 9 - lib/new_project/version.rb expected content to include "VERSION = \\"0.1.1\\"", but it was #{version_rb_content.inspect} MSG diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 73578de..ff1f8af 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -19,7 +19,7 @@ end # Guard for Unix-specific tests, as Windows does not have the same ownership concepts -UNIX_PLATFORM = RUBY_PLATFORM !~ /cygwin|mswin|mingw|bccwin|wince|emx/ +UNIX_LIKE_PLATFORM = RUBY_PLATFORM !~ /cygwin|mswin|mingw|bccwin|wince|emx/ # Helper method to get the current user for ownership tests def current_user @@ -66,13 +66,13 @@ def mock_group_name(gid, name) def mock_file_stat( path, atime: mocked_now, birthtime: mocked_now, ctime: mocked_now, mtime: mocked_now, - uid: 9999, gid: 9999, mode: 0o644 + uid: 9999, gid: 9999, mode: 0o644, size: 0 ) allow(File).to( receive(:stat).with(path).and_return( double( atime:, birthtime:, ctime:, mtime:, - uid:, gid:, mode: + uid:, gid:, mode:, size: ) ) ) @@ -102,7 +102,14 @@ def expectation_not_met_error = RSpec::Expectations::ExpectationNotMetError def ci_build? = ENV.fetch('GITHUB_ACTIONS', 'false') == 'true' SimpleCov::RSpec.start(list_uncovered_lines: ci_build?) do - minimum_coverage line: 100, branch: 100 + minimum_coverage line: 100, branch: 100 if UNIX_LIKE_PLATFORM + + add_filter '/spec/' + add_filter '/vendor/' + add_filter '/lib/rspec/path_matchers/version.rb' + add_filter '/lib/rspec/path_matchers/cli.rb' + add_filter '/lib/rspec/path_matchers/cli_options.rb' + add_filter '/lib/rspec/path_matchers/cli_parser.rb' end require 'rspec/path_matchers'