From 39a5a17de9693b9412f9d28baf0e202534e78338 Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Sun, 1 Feb 2026 01:02:46 +0000 Subject: [PATCH 01/22] Fix ActiveSupport::Autoload error for ActiveSupport 7.0+ Require 'active_support' before 'active_support/core_ext' to ensure the autoloading mechanism is initialized first. --- lib/infinity_test.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/infinity_test.rb b/lib/infinity_test.rb index 99b581e..89dcd3a 100644 --- a/lib/infinity_test.rb +++ b/lib/infinity_test.rb @@ -1,4 +1,5 @@ require 'optparse' +require 'active_support' require 'active_support/core_ext' require 'active_support/deprecation' require 'hike' From a76a3e96221437f00ec7dd5844df350790d1a3da Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Sun, 1 Feb 2026 01:08:13 +0000 Subject: [PATCH 02/22] Update specs and code for modern Ruby/RSpec/ActiveSupport compatibility - Use ActiveSupport::Deprecation.new.warn instead of class method (AS 7.x) - Replace be_true/be_false with be true/be false (RSpec 3+ syntax) - Add silence_stream helper to replace removed ActiveSupport method - Stub Watchr.handler to avoid Config::CONFIG deprecation in Ruby 3.x - Remove outdated pending blocks from specs that now pass --- lib/infinity_test/core/base.rb | 8 ++++---- spec/infinity_test/core/base_spec.rb | 9 ++++----- spec/infinity_test/core/command_builder_spec.rb | 4 ++-- spec/infinity_test/core/configuration_merge_spec.rb | 2 +- spec/infinity_test/core/load_configuration_spec.rb | 2 +- spec/infinity_test/framework/padrino_spec.rb | 1 - spec/infinity_test/framework/rails_spec.rb | 2 -- spec/infinity_test/observer/watchr_spec.rb | 6 ++++-- spec/spec_helper.rb | 11 +++++++++++ 9 files changed, 27 insertions(+), 18 deletions(-) diff --git a/lib/infinity_test/core/base.rb b/lib/infinity_test/core/base.rb index 7ccf6c0..772762f 100644 --- a/lib/infinity_test/core/base.rb +++ b/lib/infinity_test/core/base.rb @@ -247,7 +247,7 @@ def self.notifications(notification_name = nil, &block) config.notifications = :growl end MESSAGE - ActiveSupport::Deprecation.warn(message) + ActiveSupport::Deprecation.new.warn(message) self.notifications = notification_name self.instance_eval(&block) if block_given? end @@ -270,7 +270,7 @@ def self.show_images(options) config.mode = 'infinity_test_dir_that_contain_images' end MESSAGE - ActiveSupport::Deprecation.warn(message) + ActiveSupport::Deprecation.new.warn(message) self.success_image = options[:success_image] || options[:sucess_image] if options[:success_image].present? || options[:sucess_image].present? # for fail typo in earlier versions. self.pending_image = options[:pending_image] if options[:pending_image].present? self.failure_image = options[:failure_image] if options[:failure_image].present? @@ -298,7 +298,7 @@ def self.use(options) config.gemset = :some_gemset end MESSAGE - ActiveSupport::Deprecation.warn(message) + ActiveSupport::Deprecation.new.warn(message) self.rubies = options[:rubies] if options[:rubies].present? self.specific_options = options[:specific_options] if options[:specific_options].present? self.test_framework = options[:test_framework] if options[:test_framework].present? @@ -311,7 +311,7 @@ def self.use(options) # def self.clear(option) message = '.clear(:terminal) is DEPRECATED. Please use .clear_terminal instead.' - ActiveSupport::Deprecation.warn(message) + ActiveSupport::Deprecation.new.warn(message) clear_terminal end diff --git a/spec/infinity_test/core/base_spec.rb b/spec/infinity_test/core/base_spec.rb index f652f3d..51810a7 100644 --- a/spec/infinity_test/core/base_spec.rb +++ b/spec/infinity_test/core/base_spec.rb @@ -1,5 +1,4 @@ require "spec_helper" -require 'active_support/core_ext/kernel' module InfinityTest describe Base do @@ -160,14 +159,14 @@ module InfinityTest end describe ".heuristics" do - it "should need to see." do - pending 'Need to see what to do with the patterns to watch' + it "should accept a block" do + expect { Base.heuristics {} }.to_not raise_exception end end describe ".replace_patterns" do - it "should need to see" do - pending 'Need to see how improve this method.' + it "should accept a block" do + expect { Base.replace_patterns {} }.to_not raise_exception end end diff --git a/spec/infinity_test/core/command_builder_spec.rb b/spec/infinity_test/core/command_builder_spec.rb index 5139a22..880c8e5 100644 --- a/spec/infinity_test/core/command_builder_spec.rb +++ b/spec/infinity_test/core/command_builder_spec.rb @@ -29,8 +29,8 @@ module Core describe "#respond_to?" do it "should respond to enything because missing methods will build the command" do - expect(subject.respond_to?(:foo)).to be_true - expect(subject.respond_to?(:bar)).to be_true + expect(subject.respond_to?(:foo)).to be true + expect(subject.respond_to?(:bar)).to be true end end end diff --git a/spec/infinity_test/core/configuration_merge_spec.rb b/spec/infinity_test/core/configuration_merge_spec.rb index 5af7e98..9e41b36 100644 --- a/spec/infinity_test/core/configuration_merge_spec.rb +++ b/spec/infinity_test/core/configuration_merge_spec.rb @@ -99,7 +99,7 @@ module Core it "should keep the verbose mode when verbose mode is blank" do subject.merge! - expect(base.verbose).to be_true + expect(base.verbose).to be true end it "should merge the verbose mode" do diff --git a/spec/infinity_test/core/load_configuration_spec.rb b/spec/infinity_test/core/load_configuration_spec.rb index 4ecdfb8..abe9481 100644 --- a/spec/infinity_test/core/load_configuration_spec.rb +++ b/spec/infinity_test/core/load_configuration_spec.rb @@ -30,7 +30,7 @@ module InfinityTest it "should load if file exist" do expect(File).to receive(:exist?).with('bar').and_return(true) expect(subject).to receive(:load).with('bar').and_return(true) - expect(subject.load_file('bar')).to be_true + expect(subject.load_file('bar')).to be true end it "should not load if file dont exist" do diff --git a/spec/infinity_test/framework/padrino_spec.rb b/spec/infinity_test/framework/padrino_spec.rb index 45ccdf0..e42b533 100644 --- a/spec/infinity_test/framework/padrino_spec.rb +++ b/spec/infinity_test/framework/padrino_spec.rb @@ -5,7 +5,6 @@ module Framework describe Padrino do subject { Padrino.new(Core::Base) } describe "#heuristics" do - before { pending } it "should add heuristics" do expect { subject.heuristics }.to_not raise_exception end diff --git a/spec/infinity_test/framework/rails_spec.rb b/spec/infinity_test/framework/rails_spec.rb index 1b72af5..6e7d9bc 100644 --- a/spec/infinity_test/framework/rails_spec.rb +++ b/spec/infinity_test/framework/rails_spec.rb @@ -6,8 +6,6 @@ module Framework subject { Rails.new(Core::Base) } describe "#heuristics" do - before { pending } - it "should add heuristics" do expect { subject.heuristics }.to_not raise_exception end diff --git a/spec/infinity_test/observer/watchr_spec.rb b/spec/infinity_test/observer/watchr_spec.rb index 3b977a4..79779eb 100644 --- a/spec/infinity_test/observer/watchr_spec.rb +++ b/spec/infinity_test/observer/watchr_spec.rb @@ -34,9 +34,11 @@ module Observer describe "#start" do it "should initialize an watchr controller passing the #observer" do + handler_class = double handler = double - controller = controller - expect(::Watchr.handler).to receive(:new).and_return(handler) + controller = double + allow(::Watchr).to receive(:handler).and_return(handler_class) + expect(handler_class).to receive(:new).and_return(handler) expect(::Watchr::Controller).to receive(:new).with(subject.observer, handler).and_return(controller) expect(controller).to receive(:run).and_return(:running) expect(subject.start).to equal :running diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index eb6a0fd..4616ddd 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -34,3 +34,14 @@ def initialize(options={}) @test_framework = options[:test_framework] end end + +# Replacement for deprecated silence_stream from ActiveSupport +def silence_stream(stream) + old_stream = stream.dup + stream.reopen(File::NULL) + stream.sync = true + yield +ensure + stream.reopen(old_stream) + old_stream.close +end From 8dccc27b9b836d131a61c0394601d190cdc19db3 Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Sun, 1 Feb 2026 01:11:34 +0000 Subject: [PATCH 03/22] Updating to the latest versions --- Gemfile | 2 +- infinity_test.gemspec | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Gemfile b/Gemfile index cd8aa9e..fa75df1 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,3 @@ source 'https://rubygems.org' -gemspec \ No newline at end of file +gemspec diff --git a/infinity_test.gemspec b/infinity_test.gemspec index 92ec8d2..6cac69e 100644 --- a/infinity_test.gemspec +++ b/infinity_test.gemspec @@ -18,14 +18,14 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] - spec.add_dependency 'activesupport', '>= 3.2.0' + spec.add_dependency 'activesupport' spec.add_dependency 'watchr' - spec.add_dependency 'hike', '~> 1.2' - spec.add_dependency 'notifiers', '>= 1.2.2' + spec.add_dependency 'hike' + spec.add_dependency 'notifiers' - spec.add_development_dependency 'bundler', '~> 1.3' + spec.add_development_dependency 'bundler' spec.add_development_dependency 'rake' - spec.add_development_dependency 'rspec', '>= 2.14' + spec.add_development_dependency 'rspec' spec.add_development_dependency 'simplecov' spec.add_development_dependency 'pry' From d0e3030179c79e90ee73901705c643ebaabd3d71 Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Sun, 1 Feb 2026 01:12:57 +0000 Subject: [PATCH 04/22] Improve test descriptions by removing 'should' prefix Update all spec descriptions to use present tense instead of 'it should...' style. For example: - "should return true" -> "returns true" - "should set the mode" -> "sets the mode" - "should respond to" -> "responds to" This follows modern RSpec best practices for clearer, more readable test output. --- lib/infinity_test/framework/shared_example.rb | 16 +++---- lib/infinity_test/observer/shared_example.rb | 10 ++-- lib/infinity_test/strategy/shared_example.rb | 8 ++-- .../test_framework/shared_example.rb | 20 ++++---- spec/infinity_test/core/base_spec.rb | 48 +++++++++---------- .../core/command_builder_spec.rb | 12 ++--- .../core/configuration_merge_spec.rb | 36 +++++++------- .../core/load_configuration_spec.rb | 12 ++--- spec/infinity_test/core/options_spec.rb | 40 ++++++++-------- spec/infinity_test/framework/padrino_spec.rb | 8 ++-- spec/infinity_test/framework/rails_spec.rb | 8 ++-- spec/infinity_test/framework/rubygems_spec.rb | 6 +-- spec/infinity_test/observer/watchr_spec.rb | 10 ++-- spec/infinity_test/strategy/rbenv_spec.rb | 4 +- .../strategy/ruby_default_spec.rb | 6 +-- spec/infinity_test/strategy/rvm_spec.rb | 8 ++-- .../test_framework/rspec_spec.rb | 8 ++-- .../test_framework/test_unit_spec.rb | 8 ++-- 18 files changed, 134 insertions(+), 134 deletions(-) diff --git a/lib/infinity_test/framework/shared_example.rb b/lib/infinity_test/framework/shared_example.rb index 4d4f648..4e7aa7d 100644 --- a/lib/infinity_test/framework/shared_example.rb +++ b/lib/infinity_test/framework/shared_example.rb @@ -14,34 +14,34 @@ module SharedExample # end # shared_examples_for 'an infinity test framework' do - it 'should respond to #heuristics' do + it 'responds to #heuristics' do expect(subject).to respond_to(:heuristics) end - it 'should respond to #heuristics' do + it 'responds to #heuristics!' do expect(subject).to respond_to(:heuristics!) end - it 'should respond to .run?' do + it 'responds to .run?' do expect(subject.class).to respond_to(:run?) end - it 'should respond to #base' do + it 'responds to #base' do expect(subject).to respond_to(:base) end - it 'should respond to #test_framework' do + it 'responds to #test_framework' do expect(subject).to respond_to(:test_framework) end - it 'should respond to #strategy' do + it 'responds to #strategy' do expect(subject).to respond_to(:strategy) end - it 'should respond to #observer' do + it 'responds to #observer' do expect(subject).to respond_to(:observer) end end end end -end \ No newline at end of file +end diff --git a/lib/infinity_test/observer/shared_example.rb b/lib/infinity_test/observer/shared_example.rb index 35900ef..159270f 100644 --- a/lib/infinity_test/observer/shared_example.rb +++ b/lib/infinity_test/observer/shared_example.rb @@ -14,22 +14,22 @@ module SharedExample # end # shared_examples_for 'an infinity test observer' do - it 'should respond to #observer' do + it 'responds to #observer' do expect(subject).to respond_to(:observer) end - it 'should respond to #watch_dir' do + it 'responds to #watch_dir' do expect(subject).to respond_to(:watch_dir) end - it 'should respond to #watch' do + it 'responds to #watch' do expect(subject).to respond_to(:watch) end - it 'should respond to #start' do + it 'responds to #start' do expect(subject).to respond_to(:start) end end end end -end \ No newline at end of file +end diff --git a/lib/infinity_test/strategy/shared_example.rb b/lib/infinity_test/strategy/shared_example.rb index 48131ab..0974965 100644 --- a/lib/infinity_test/strategy/shared_example.rb +++ b/lib/infinity_test/strategy/shared_example.rb @@ -15,18 +15,18 @@ module SharedExample # end # shared_examples_for 'a infinity test strategy' do - it 'should respond to #run!' do + it 'responds to #run!' do expect(subject).to respond_to(:run) end - it 'should respond to .run?' do + it 'responds to .run?' do expect(subject.class).to respond_to(:run?) end - it 'should have Strategy::Base as superclass' do + it 'has Strategy::Base as superclass' do expect(subject.class.superclass).to be InfinityTest::Strategy::Base end end end end -end \ No newline at end of file +end diff --git a/lib/infinity_test/test_framework/shared_example.rb b/lib/infinity_test/test_framework/shared_example.rb index 1f4849b..547a518 100644 --- a/lib/infinity_test/test_framework/shared_example.rb +++ b/lib/infinity_test/test_framework/shared_example.rb @@ -15,42 +15,42 @@ module SharedExample # end # shared_examples_for 'a infinity test test framework' do - it 'should respond to #test_message=' do + it 'responds to #test_message=' do expect(subject).to respond_to(:test_message=) end - it 'should respond to #test_message' do + it 'responds to #test_message' do expect(subject).to respond_to(:test_message) end - it 'should respond to #succeed?' do + it 'responds to #succeed?' do expect(subject).to respond_to(:success?) end - it 'should respond to #failure?' do + it 'responds to #failure?' do expect(subject).to respond_to(:failure?) end - it 'should respond to #pending?' do + it 'responds to #pending?' do expect(subject).to respond_to(:pending?) end - it 'should respond to #test_helper_file' do + it 'responds to #test_helper_file' do expect(subject).to respond_to(:test_helper_file) end - it 'should respond to #test_dir' do + it 'responds to #test_dir' do expect(subject).to respond_to(:test_dir) end - it 'should respond to #test_dir=' do + it 'responds to #test_dir=' do expect(subject).to respond_to(:test_dir=) end - it 'should respond to #binary' do + it 'responds to #binary' do expect(subject).to respond_to(:binary) end end end end -end \ No newline at end of file +end diff --git a/spec/infinity_test/core/base_spec.rb b/spec/infinity_test/core/base_spec.rb index 51810a7..94268c7 100644 --- a/spec/infinity_test/core/base_spec.rb +++ b/spec/infinity_test/core/base_spec.rb @@ -3,7 +3,7 @@ module InfinityTest describe Base do describe ".setup" do - it "should yield self" do + it "yields self" do Base.setup do |config| expect(config).to be InfinityTest::Base end @@ -11,31 +11,31 @@ module InfinityTest end describe ".using_bundler?" do - it "should return the same bundler accessor" do + it "returns the same bundler accessor" do expect(Base.using_bundler?).to equal Base.bundler end end describe "#verbose?" do - it "should return the same verbose accessor" do + it "returns the same verbose accessor" do expect(Base.verbose?).to equal Base.verbose end end describe ".observer" do - it "should have watchr as default observer" do + it "has watchr as default observer" do expect(Base.observer).to equal :watchr end end describe ".ignore_test_files" do - it "should not have test files to ignore as default" do + it "does not have test files to ignore as default" do expect(Base.ignore_test_files).to eql [] end end describe "#ignore_test_folders" do - it "should not ignore test folders as default" do + it "does not ignore test folders as default" do expect(Base.ignore_test_folders).to eql [] end end @@ -44,7 +44,7 @@ module InfinityTest before { pending } let(:proc) { Proc.new { 'To Infinity and beyond!' } } - it "should create before callback instance and push to the callback accessor" do + it "creates before callback instance and pushes to the callback accessor" do expect(BeforeCallback).to receive(:new).with(:all, &proc).once.and_return(:foo) before_callback = Base.before(:all, &proc) expect(before_callback).to be :foo @@ -56,7 +56,7 @@ module InfinityTest before { pending } let(:proc) { Proc.new {}} - it "should create before callback instance and push to the callback accessor" do + it "creates after callback instance and pushes to the callback accessor" do expect(AfterCallback).to receive(:new).with(:each, &proc).once.and_return(:foo) after_callback = Base.after(:each, &proc) expect(after_callback).to be :foo @@ -65,14 +65,14 @@ module InfinityTest end describe ".notifications" do - it "should set the notification class accessor" do + it "sets the notification class accessor" do silence_stream(STDOUT) do Base.notifications(:growl) expect(Base.notifications).to be :growl end end - it "should set the images" do + it "sets the images" do silence_stream(STDOUT) do Base.notifications(:growl) do show_images :success_image => 'foo', :failure_image => 'bar', :pending_image => 'baz' @@ -86,7 +86,7 @@ module InfinityTest Base.pending_image = nil end - it "should set the mode" do + it "sets the mode" do silence_stream(STDOUT) do Base.notifications(:growl) do show_images :mode => :mortal_kombat @@ -115,42 +115,42 @@ module InfinityTest Base.gemset = @gemset end - it "should set the rubies" do + it "sets the rubies" do silence_stream(STDOUT) do Base.use :rubies => %w(foo bar) end expect(Base.rubies).to eql %w(foo bar) end - it "should set the specific options" do + it "sets the specific options" do silence_stream(STDOUT) do Base.use :specific_options => '-J -Ilib -Itest' end expect(Base.specific_options).to eql '-J -Ilib -Itest' end - it "should set the test framework" do + it "sets the test framework" do silence_stream(STDOUT) do Base.use :test_framework => :rspec end expect(Base.test_framework).to be :rspec end - it "should set the app framework" do + it "sets the app framework" do silence_stream(STDOUT) do Base.use :app_framework => :rails end expect(Base.framework).to be :rails end - it "should set the verbose mode" do + it "sets the verbose mode" do silence_stream(STDOUT) do Base.use :verbose => false end - expect(Base.verbose).to equal false # I choose to don't use should be_false + expect(Base.verbose).to equal false end - it "should set the gemset" do + it "sets the gemset" do silence_stream(STDOUT) do Base.use :gemset => 'infinity_test' end @@ -159,13 +159,13 @@ module InfinityTest end describe ".heuristics" do - it "should accept a block" do + it "accepts a block" do expect { Base.heuristics {} }.to_not raise_exception end end describe ".replace_patterns" do - it "should accept a block" do + it "accepts a block" do expect { Base.replace_patterns {} }.to_not raise_exception end end @@ -174,7 +174,7 @@ module InfinityTest let(:options) { Object.new } let(:configuration_merge) { Object.new } - it "should call merge on the configuration merge object" do + it "calls merge on the configuration merge object" do expect(ConfigurationMerge).to receive(:new).with(Core::Base, options).and_return(configuration_merge) expect(configuration_merge).to receive(:merge!) Core::Base.merge!(options) @@ -182,7 +182,7 @@ module InfinityTest end describe ".clear" do - it "should call clear_terminal method" do + it "calls clear_terminal method" do silence_stream(STDOUT) do expect(Base).to receive(:clear_terminal).and_return(true) Base.clear(:terminal) @@ -191,7 +191,7 @@ module InfinityTest end describe ".clear_terminal" do - it "should call system clear" do + it "calls system clear" do silence_stream(STDOUT) do expect(Base).to receive(:system).with('clear').and_return(true) Base.clear_terminal @@ -199,4 +199,4 @@ module InfinityTest end end end -end \ No newline at end of file +end diff --git a/spec/infinity_test/core/command_builder_spec.rb b/spec/infinity_test/core/command_builder_spec.rb index 880c8e5..4c0538a 100644 --- a/spec/infinity_test/core/command_builder_spec.rb +++ b/spec/infinity_test/core/command_builder_spec.rb @@ -4,35 +4,35 @@ module InfinityTest module Core describe CommandBuilder do describe "#add" do - it "should add the argument in the command" do + it "adds the argument in the command" do expect(subject.ruby.option(:S).add(:rspec).add(:spec)).to eq 'ruby -S rspec spec' end end describe "#option" do - it "should put options with one dash and be possible to add more keywords" do + it "puts options with one dash and allows adding more keywords" do expect(subject.bundle.exec.ruby.option(:S).rspec).to eq 'bundle exec ruby -S rspec' end end describe "#opt" do - it "should put double dash options and be possible to add keywords" do + it "puts double dash options and allows adding keywords" do expect(subject.ruby.opt(:copyright)).to eq 'ruby --copyright' end end describe '#method_missing' do - it "should put space after every keyword" do + it "puts space after every keyword" do expect(subject.bundle.exec.ruby.some_file).to eq 'bundle exec ruby some_file' end end describe "#respond_to?" do - it "should respond to enything because missing methods will build the command" do + it "responds to anything because missing methods will build the command" do expect(subject.respond_to?(:foo)).to be true expect(subject.respond_to?(:bar)).to be true end end end end -end \ No newline at end of file +end diff --git a/spec/infinity_test/core/configuration_merge_spec.rb b/spec/infinity_test/core/configuration_merge_spec.rb index 9e41b36..2762797 100644 --- a/spec/infinity_test/core/configuration_merge_spec.rb +++ b/spec/infinity_test/core/configuration_merge_spec.rb @@ -21,99 +21,99 @@ module Core subject { ConfigurationMerge.new(base, options) } describe "#merge!" do - it "should keep the strategy when options strategy is blank" do + it "keeps the strategy when options strategy is blank" do subject.merge! expect(base.strategy).to be :auto_discover end - it "should merge the strategies" do + it "merges the strategies" do expect(options).to receive(:strategy).twice.and_return(:rbenv) subject.merge! expect(base.strategy).to be :rbenv end - it "should keep the rubies when options rubies is nil" do + it "keeps the rubies when options rubies is nil" do base.rubies = %w(jruby ree) expect(options).to receive(:rubies).and_return(nil) subject.merge! expect(base.rubies).to eq %w(jruby ree) end - it "should overwrite the rubies when options rubies is empty" do + it "overwrites the rubies when options rubies is empty" do base.rubies = %w(jruby ree) expect(options).to receive(:rubies).twice.and_return([]) subject.merge! expect(base.rubies).to be_blank end - it "should merge the rubies" do + it "merges the rubies" do expect(options).to receive(:rubies).twice.and_return(%w(ree jruby)) subject.merge! expect(base.rubies).to eql %w(ree jruby) end - it "should keep the test framework when options test framework is blank" do + it "keeps the test framework when options test framework is blank" do subject.merge! expect(base.test_framework).to be :auto_discover end - it "should merge the test library" do + it "merges the test library" do expect(options).to receive(:test_framework).twice.and_return(:rspec) subject.merge! expect(base.test_framework).to be :rspec end - it "should keep the framework when options framework is blank" do + it "keeps the framework when options framework is blank" do subject.merge! expect(base.framework).to be :auto_discover end - it "should merge the framework" do + it "merges the framework" do expect(options).to receive(:framework).twice.and_return(:rails) subject.merge! expect(base.framework).to be :rails end - it "should keep the specific option when options specific is blank" do + it "keeps the specific option when options specific is blank" do subject.merge! expect(base.specific_options).to eql '' end - it "should merge the specific options" do + it "merges the specific options" do expect(options).to receive(:specific_options).twice.and_return('-J -Ilib -Itest') subject.merge! expect(base.specific_options).to eql '-J -Ilib -Itest' end - it "should merge with the infinity test and beyond" do + it "merges with the infinity test and beyond" do expect(options).to receive(:infinity_and_beyond).twice.and_return(false) subject.merge! expect(base.infinity_and_beyond).to be false end - it "should keep the base default if option infinity test and beyond is nil" do + it "keeps the base default if option infinity test and beyond is nil" do expect(options).to receive(:infinity_and_beyond).and_return(nil) subject.merge! expect(base.infinity_and_beyond).to be true end - it "should keep the verbose mode when verbose mode is blank" do + it "keeps the verbose mode when verbose mode is blank" do subject.merge! expect(base.verbose).to be true end - it "should merge the verbose mode" do + it "merges the verbose mode" do expect(options).to receive(:verbose).twice.and_return(false) subject.merge! expect(base.verbose).to be false end - it "should keep the bundler default when bundler is blank" do + it "keeps the bundler default when bundler is blank" do subject.merge! expect(base.bundler).to be true end - it "should merge the verbose mode" do + it "merges the bundler mode" do expect(options).to receive(:bundler).twice.and_return(false) subject.merge! expect(base.bundler).to be false @@ -121,4 +121,4 @@ module Core end end end -end \ No newline at end of file +end diff --git a/spec/infinity_test/core/load_configuration_spec.rb b/spec/infinity_test/core/load_configuration_spec.rb index abe9481..dc7a5d1 100644 --- a/spec/infinity_test/core/load_configuration_spec.rb +++ b/spec/infinity_test/core/load_configuration_spec.rb @@ -3,7 +3,7 @@ module InfinityTest describe LoadConfiguration do describe "#load!" do - it "should load global file and project file" do + it "loads global file and project file" do expect(subject).to receive(:load_global_file!).and_return(true) expect(subject).to receive(:load_project_file!).and_return(true) subject.load! @@ -11,7 +11,7 @@ module InfinityTest end describe "#load_global_file!" do - it "should load the global file" do + it "loads the global file" do subject.global_file = 'foo' expect(subject).to receive(:load_file).with('foo').and_return(true) subject.load_global_file! @@ -19,7 +19,7 @@ module InfinityTest end describe "#load_project_file!" do - it "should load the project file" do + it "loads the project file" do subject.project_file = 'bar' expect(subject).to receive(:load_file).with('bar').and_return(true) subject.load_project_file! @@ -27,17 +27,17 @@ module InfinityTest end describe "#load_file" do - it "should load if file exist" do + it "loads if file exists" do expect(File).to receive(:exist?).with('bar').and_return(true) expect(subject).to receive(:load).with('bar').and_return(true) expect(subject.load_file('bar')).to be true end - it "should not load if file dont exist" do + it "does not load if file does not exist" do expect(File).to receive(:exist?).with('baz').and_return(false) expect(subject).to_not receive(:load) subject.load_file('baz') end end end -end \ No newline at end of file +end diff --git a/spec/infinity_test/core/options_spec.rb b/spec/infinity_test/core/options_spec.rb index e6dad18..8b42550 100644 --- a/spec/infinity_test/core/options_spec.rb +++ b/spec/infinity_test/core/options_spec.rb @@ -4,94 +4,94 @@ module InfinityTest describe Options do describe "#parse!" do describe "#strategy" do - it "should parse the --ruby options with rvm" do + it "parses the --ruby options with rvm" do expect(parse('--ruby', 'rvm').strategy).to be :rvm end - it "should parse the --ruby options with rbenv" do + it "parses the --ruby options with rbenv" do expect(parse('--ruby', 'rbenv').strategy).to be :rbenv end end describe "#rubies" do - it "should pass the ruby versions" do + it "passes the ruby versions" do expect(parse('--rubies=ree,jruby').rubies).to eql %w(ree jruby) end - it "should have empty rubies when pass rubies= without versions" do + it "has empty rubies when pass rubies= without versions" do expect(parse('--rubies=').rubies).to eql [] end - it "should be nil when don't pass the rubies option" do + it "is nil when rubies option is not passed" do expect(parse.rubies).to be_nil end end describe "#infinity_and_beyond" do - it "should return false when setting --no-infinity-and-beyond" do + it "returns false when setting --no-infinity-and-beyond" do expect(parse('--no-infinity-and-beyond').infinity_and_beyond).to equal false expect(parse('-n').infinity_and_beyond).to equal false end - it "should return nil when not setting the --no-infinity-and-beyond" do + it "returns nil when not setting the --no-infinity-and-beyond" do expect(parse.infinity_and_beyond).to be_nil end end describe "#specific_options" do - it "should parse the options" do + it "parses the options" do expect(parse('--options=-J-Ilib-Itest').specific_options).to eql '-J -Ilib -Itest' end end describe "#test_framework" do - it "should parse the test framework as rspec" do + it "parses the test framework as rspec" do expect(parse('--test', 'rspec').test_framework).to be :rspec end - it "should parse the test framework as bacon" do + it "parses the test framework as bacon" do expect(parse('--test', 'bacon').test_framework).to be :bacon end - it "should parse the test framework as test_unit" do + it "parses the test framework as test_unit" do expect(parse('--test', 'test_unit').test_framework).to be :test_unit end - it "should parse the test framework as other" do + it "parses the test framework as other" do expect(parse('--test', 'other').test_framework).to be :other end end describe "#framework" do - it "should parse the app framework as rails" do + it "parses the app framework as rails" do expect(parse('--framework', 'rails').framework).to be :rails end - it "should parse the app framework as rubygems" do + it "parses the app framework as rubygems" do expect(parse('--framework', 'rubygems').framework).to be :rubygems end - it "should parse the app framework as other" do + it "parses the app framework as other" do expect(parse('--framework', 'other').framework).to be :other end end describe "#verbose?" do - it "should return nil when dont pass nothing" do + it "returns nil when nothing is passed" do expect(parse.verbose?).to be_nil end - it "should not be verbose whe pass the option --no-verbose" do + it "is not verbose when passing the option --no-verbose" do expect(parse('--no-verbose')).not_to be_verbose end end describe "#bundler?" do - it "should not use bundler when passing this option" do + it "does not use bundler when passing this option" do expect(parse('--no-bundler')).not_to be_bundler end - it "should return nil when dont pass nothing" do + it "returns nil when nothing is passed" do expect(parse.bundler?).to be_nil end end @@ -101,4 +101,4 @@ def parse(*args) InfinityTest::Options.new(args.flatten).parse! end end -end \ No newline at end of file +end diff --git a/spec/infinity_test/framework/padrino_spec.rb b/spec/infinity_test/framework/padrino_spec.rb index e42b533..18cf7c2 100644 --- a/spec/infinity_test/framework/padrino_spec.rb +++ b/spec/infinity_test/framework/padrino_spec.rb @@ -5,22 +5,22 @@ module Framework describe Padrino do subject { Padrino.new(Core::Base) } describe "#heuristics" do - it "should add heuristics" do + it "adds heuristics" do expect { subject.heuristics }.to_not raise_exception end end describe ".run?" do - it "should return true if find the config/apps.rb" do + it "returns true if config/apps.rb exists" do expect(File).to receive(:exist?).with(File.expand_path('./config/apps.rb')).and_return(true) expect(Padrino).to be_run end - it "should return false if don't find the config/apps.rb" do + it "returns false if config/apps.rb does not exist" do expect(File).to receive(:exist?).with(File.expand_path('./config/apps.rb')).and_return(false) expect(Padrino).not_to be_run end end end end -end \ No newline at end of file +end diff --git a/spec/infinity_test/framework/rails_spec.rb b/spec/infinity_test/framework/rails_spec.rb index 6e7d9bc..fcf9002 100644 --- a/spec/infinity_test/framework/rails_spec.rb +++ b/spec/infinity_test/framework/rails_spec.rb @@ -6,22 +6,22 @@ module Framework subject { Rails.new(Core::Base) } describe "#heuristics" do - it "should add heuristics" do + it "adds heuristics" do expect { subject.heuristics }.to_not raise_exception end end describe ".run?" do - it "should return true if exist the config/enviroment.rb file" do + it "returns true if config/environment.rb exists" do expect(File).to receive(:exist?).with(File.expand_path('./config/environment.rb')).and_return(true) expect(Rails).to be_run end - it "should return false if don't exist the config/enviroment.rb file" do + it "returns false if config/environment.rb does not exist" do expect(File).to receive(:exist?).with(File.expand_path('./config/environment.rb')).and_return(false) expect(Rails).not_to be_run end end end end -end \ No newline at end of file +end diff --git a/spec/infinity_test/framework/rubygems_spec.rb b/spec/infinity_test/framework/rubygems_spec.rb index dde3e14..32476be 100644 --- a/spec/infinity_test/framework/rubygems_spec.rb +++ b/spec/infinity_test/framework/rubygems_spec.rb @@ -9,7 +9,7 @@ module Framework subject { Rubygems.new(continuous_test_server) } describe "#heuristics" do - it "should add heuristics" do + it "adds heuristics" do expect(observer).to receive(:watch_dir).exactly(2) expect(observer).to receive(:watch) expect(test_framework).to receive(:test_helper_file) @@ -19,12 +19,12 @@ module Framework end describe ".run?" do - it "should return true if have a .gemspec in the user current dir" do + it "returns true if there is a .gemspec in the user current dir" do expect(Dir).to receive(:[]).with('*.gemspec').and_return(['infinity_test.gemspec']) expect(Rubygems).to be_run end - it "should return false if don't have a .gemspec in the user current dir" do + it "returns false if there is no .gemspec in the user current dir" do expect(Dir).to receive(:[]).with('*.gemspec').and_return([]) expect(Rubygems).not_to be_run end diff --git a/spec/infinity_test/observer/watchr_spec.rb b/spec/infinity_test/observer/watchr_spec.rb index 79779eb..dc8790a 100644 --- a/spec/infinity_test/observer/watchr_spec.rb +++ b/spec/infinity_test/observer/watchr_spec.rb @@ -8,32 +8,32 @@ module Observer it_should_behave_like 'an infinity test observer' describe "#observer" do - it "should be instance of watchr script" do + it "is instance of watchr script" do expect(subject.observer).to be_instance_of(::Watchr::Script) end end describe "#watch" do - it "should pass the args to the observer" do + it "passes the args to the observer" do expect(subject.observer).to receive(:watch).with('lib') subject.watch(:lib) end end describe "#watch_dir" do - it "should pass the pattern to the observer" do + it "passes the pattern to the observer" do expect(subject.observer).to receive(:watch).with("^spec/*/(.*).rb") subject.watch_dir(:spec) end - it "should pass the pattern and the extension to the observer" do + it "passes the pattern and the extension to the observer" do expect(subject.observer).to receive(:watch).with("^spec/*/(.*).py") subject.watch_dir(:spec, :py) end end describe "#start" do - it "should initialize an watchr controller passing the #observer" do + it "initializes a watchr controller passing the #observer" do handler_class = double handler = double controller = double diff --git a/spec/infinity_test/strategy/rbenv_spec.rb b/spec/infinity_test/strategy/rbenv_spec.rb index b6d0f54..f89007d 100644 --- a/spec/infinity_test/strategy/rbenv_spec.rb +++ b/spec/infinity_test/strategy/rbenv_spec.rb @@ -8,12 +8,12 @@ module Strategy describe ".run?" do let(:rbenv_dir) { File.expand_path('~/.rbenv') } - it "should return true if the user had the rbenv installed" do + it "returns true if the user has rbenv installed" do expect(File).to receive(:exist?).with(rbenv_dir).and_return(true) Rbenv.run? end - it "should return false if the user don't had the rbenv installed" do + it "returns false if the user does not have rbenv installed" do expect(File).to receive(:exist?).with(rbenv_dir).and_return(false) Rbenv.run? end diff --git a/spec/infinity_test/strategy/ruby_default_spec.rb b/spec/infinity_test/strategy/ruby_default_spec.rb index da287e7..3e18324 100644 --- a/spec/infinity_test/strategy/ruby_default_spec.rb +++ b/spec/infinity_test/strategy/ruby_default_spec.rb @@ -9,12 +9,12 @@ module Strategy it_should_behave_like 'a infinity test strategy' describe ".run?" do - it "should return true when don't pass any ruby versions to run tests" do + it "returns true when no ruby versions are passed to run tests" do Core::Base.stub(:rubies).and_return([]) expect(RubyDefault).to be_run end - it "should return false when pass some ruby version to run tests" do + it "returns false when some ruby version is passed to run tests" do Core::Base.stub(:rubies).and_return(['ree', 'jruby']) expect(RubyDefault).not_to be_run end @@ -29,4 +29,4 @@ module Strategy end end end -end \ No newline at end of file +end diff --git a/spec/infinity_test/strategy/rvm_spec.rb b/spec/infinity_test/strategy/rvm_spec.rb index 9b27e13..6cbfbc1 100644 --- a/spec/infinity_test/strategy/rvm_spec.rb +++ b/spec/infinity_test/strategy/rvm_spec.rb @@ -7,18 +7,18 @@ module Strategy it_should_behave_like 'a infinity test strategy' describe ".run?" do - it "should return true if the user had the RVM installed in users home" do + it "returns true if the user has RVM installed in users home" do expect(Rvm).to receive(:installed_users_home?).and_return(true) expect(Rvm).to be_run end - it "should return true if the user had the RVM installed in system wid" do + it "returns true if the user has RVM installed system wide" do expect(Rvm).to receive(:installed_users_home?).and_return(false) expect(Rvm).to receive(:installed_system_wide?).and_return(true) expect(Rvm).to be_run end - it "should return false if the user don't had the RVM installed in users home" do + it "returns false if the user does not have RVM installed" do expect(Rvm).to receive(:installed_users_home?).and_return(false) expect(Rvm).to receive(:installed_system_wide?).and_return(false) expect(Rvm).not_to be_run @@ -26,4 +26,4 @@ module Strategy end end end -end \ No newline at end of file +end diff --git a/spec/infinity_test/test_framework/rspec_spec.rb b/spec/infinity_test/test_framework/rspec_spec.rb index dd5ba9f..875f041 100644 --- a/spec/infinity_test/test_framework/rspec_spec.rb +++ b/spec/infinity_test/test_framework/rspec_spec.rb @@ -6,7 +6,7 @@ module TestFramework it_should_behave_like 'a infinity test test framework' describe "#test_dir" do - it "should return spec as test dir" do + it "returns spec as test dir" do expect(subject.test_dir).to eq 'spec' end end @@ -27,13 +27,13 @@ module TestFramework end describe "#test_helper_file" do - it "should be the spec helper" do + it "is the spec helper" do expect(subject.test_helper_file).to eq 'spec/spec_helper.rb' end end describe "#binary" do - it "should return rspec as binary" do + it "returns rspec as binary" do expect(subject.binary).to eq 'rspec' end end @@ -116,4 +116,4 @@ module TestFramework end end end -end \ No newline at end of file +end diff --git a/spec/infinity_test/test_framework/test_unit_spec.rb b/spec/infinity_test/test_framework/test_unit_spec.rb index b255b25..990261e 100644 --- a/spec/infinity_test/test_framework/test_unit_spec.rb +++ b/spec/infinity_test/test_framework/test_unit_spec.rb @@ -6,20 +6,20 @@ module TestFramework it_should_behave_like 'a infinity test test framework' describe "#test_dir" do - it "should return spec as test dir" do + it "returns test as test dir" do expect(subject.test_dir).to eq 'test' end end describe "#test_helper_file" do - it "should be the spec helper" do + it "is the test helper" do expect(subject.test_helper_file).to eq 'test/test_helper.rb' end end describe '#binary' do - it 'should see the binary with the strategy instance' + it 'sees the binary with the strategy instance' end end end -end \ No newline at end of file +end From 91fd876b6a6d136887f13896d60f64b8266b7819 Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Sun, 1 Feb 2026 01:16:50 +0000 Subject: [PATCH 05/22] Replace deprecated stub with allow().to receive() syntax Update rspec-mocks usage from old :should syntax to modern :expect syntax to remove deprecation warnings. --- spec/infinity_test/core/auto_discover_spec.rb | 28 +++++++++---------- .../core/continuous_test_server_spec.rb | 20 ++++++------- .../strategy/ruby_default_spec.rb | 4 +-- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/spec/infinity_test/core/auto_discover_spec.rb b/spec/infinity_test/core/auto_discover_spec.rb index 054921a..6fc0241 100644 --- a/spec/infinity_test/core/auto_discover_spec.rb +++ b/spec/infinity_test/core/auto_discover_spec.rb @@ -7,7 +7,7 @@ module InfinityTest let(:auto_discover) { AutoDiscover.new(base) } describe '#discover_libraries' do - it 'discover strategy, framework and test framework' do + it 'discovers strategy, framework and test framework' do expect(auto_discover).to receive(:discover_strategy) expect(auto_discover).to receive(:discover_framework) expect(auto_discover).to receive(:discover_test_framework) @@ -19,11 +19,11 @@ module InfinityTest context 'when strategy is auto discover' do before do base.strategy = :auto_discover - Strategy::RubyDefault.stub(:run?).and_return(false) + allow(Strategy::RubyDefault).to receive(:run?).and_return(false) expect(Strategy::Rvm).to receive(:run?).and_return(true) end - it 'change the base strategy' do + it 'changes the base strategy' do auto_discover.discover_strategy expect(base.strategy).to be :rvm end @@ -32,7 +32,7 @@ module InfinityTest context 'when strategy is different from auto discover' do before { base.strategy = :ruby_default } - it 'change anything' do + it 'does not change anything' do auto_discover.discover_strategy expect(base.strategy).to be :ruby_default end @@ -41,9 +41,9 @@ module InfinityTest context 'when do not find any strategy' do before do base.strategy = :auto_discover - Strategy::RubyDefault.stub(:run?).and_return(false) + allow(Strategy::RubyDefault).to receive(:run?).and_return(false) expect(Strategy::Rvm).to receive(:run?).and_return(false) - Strategy::Rbenv.stub(:run?).and_return(false) + allow(Strategy::Rbenv).to receive(:run?).and_return(false) end it 'raises exception' do @@ -56,11 +56,11 @@ module InfinityTest context 'when framework is auto discover' do before do base.framework = :auto_discover - Framework::Rubygems.stub(:run?).and_return(false) + allow(Framework::Rubygems).to receive(:run?).and_return(false) expect(Framework::Rails).to receive(:run?).and_return(true) end - it 'change the base framework' do + it 'changes the base framework' do auto_discover.discover_framework expect(base.framework).to be :rails end @@ -69,7 +69,7 @@ module InfinityTest context 'when framework is different from auto discover' do before { base.framework = :padrino } - it 'change anything' do + it 'does not change anything' do auto_discover.discover_framework expect(base.framework).to be :padrino end @@ -80,12 +80,12 @@ module InfinityTest context 'when framework is auto discover' do before do base.test_framework = :auto_discover - TestFramework::TestUnit.stub(:run?).and_return(false) - TestFramework::Rspec.stub(:run?).and_return(false) + allow(TestFramework::TestUnit).to receive(:run?).and_return(false) + allow(TestFramework::Rspec).to receive(:run?).and_return(false) expect(TestFramework::Bacon).to receive(:run?).and_return(true) end - it 'change the base framework' do + it 'changes the base framework' do auto_discover.discover_test_framework expect(base.test_framework).to be :bacon end @@ -94,11 +94,11 @@ module InfinityTest context 'when framework is different from auto discover' do before { base.test_framework = :test_unit } - it 'change anything' do + it 'does not change anything' do auto_discover.discover_test_framework expect(base.test_framework).to be :test_unit end end end end -end \ No newline at end of file +end diff --git a/spec/infinity_test/core/continuous_test_server_spec.rb b/spec/infinity_test/core/continuous_test_server_spec.rb index dee5a51..c853fd8 100644 --- a/spec/infinity_test/core/continuous_test_server_spec.rb +++ b/spec/infinity_test/core/continuous_test_server_spec.rb @@ -7,7 +7,7 @@ module Core let(:continuous_test_server) { ContinuousTestServer.new(base) } describe '#start!' do - it 'run strategy, start observer' do + it 'runs strategy and starts observer' do expect(continuous_test_server).to receive(:run_strategy) expect(continuous_test_server).to receive(:start_observer) continuous_test_server.start @@ -18,11 +18,11 @@ module Core context 'when base configuration as infinity and beyond' do before do expect(base).to receive(:infinity_and_beyond).and_return(true) - base.stub(:framework).and_return(:rails) - base.stub(:observer).and_return(:watchr) + allow(base).to receive(:framework).and_return(:rails) + allow(base).to receive(:observer).and_return(:watchr) end - it 'add framework heuristics and start the observer' do + it 'adds framework heuristics and starts the observer' do expect(continuous_test_server.framework).to receive(:heuristics!) expect(continuous_test_server.observer).to receive(:start!) continuous_test_server.start_observer @@ -32,10 +32,10 @@ module Core context 'when base configuration is not infinity and beyond' do before do expect(base).to receive(:infinity_and_beyond).and_return(false) - base.stub(:framework).and_return(:rails) + allow(base).to receive(:framework).and_return(:rails) end - it 'do not start the observer' do + it 'does not start the observer' do expect(continuous_test_server.framework).to_not receive(:heuristics!) expect(continuous_test_server.observer).to_not receive(:start!) continuous_test_server.start_observer @@ -56,7 +56,7 @@ module Core expect(test_framework).to receive(:test_message=).with(test_message) end - it 'instantiante a notifier passing the strategy result and the continuous server' do + it 'instantiates a notifier passing the strategy result and the continuous server' do expect(Core::Notifier).to receive(:new).and_return(double(notify: true)) continuous_test_server.notify(test_message) end @@ -65,7 +65,7 @@ module Core context 'when do not have notification library' do let(:base) { double(notifications: nil) } - it 'instantiante a notifier passing the strategy result and the continuous server' do + it 'does not instantiate a notifier' do expect(Core::Notifier).to_not receive(:new) continuous_test_server.notify(test_message) end @@ -79,7 +79,7 @@ module Core expect(continuous_test_server).to receive(:test_framework).twice.and_return(test_framework) end - it 'run strategy agains and ensure the test files is nil after' do + it 'runs strategy again and ensures the test files is nil after' do expect(test_framework).to receive(:test_files=).twice expect(continuous_test_server).to receive(:run_strategy) continuous_test_server.rerun_strategy('base_test.py') @@ -113,4 +113,4 @@ module Core end end end -end \ No newline at end of file +end diff --git a/spec/infinity_test/strategy/ruby_default_spec.rb b/spec/infinity_test/strategy/ruby_default_spec.rb index 3e18324..f134e72 100644 --- a/spec/infinity_test/strategy/ruby_default_spec.rb +++ b/spec/infinity_test/strategy/ruby_default_spec.rb @@ -10,12 +10,12 @@ module Strategy describe ".run?" do it "returns true when no ruby versions are passed to run tests" do - Core::Base.stub(:rubies).and_return([]) + allow(Core::Base).to receive(:rubies).and_return([]) expect(RubyDefault).to be_run end it "returns false when some ruby version is passed to run tests" do - Core::Base.stub(:rubies).and_return(['ree', 'jruby']) + allow(Core::Base).to receive(:rubies).and_return(['ree', 'jruby']) expect(RubyDefault).not_to be_run end end From 38d5792968cbf4683d87235e6c8e39183c378516 Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Sun, 1 Feb 2026 01:31:58 +0000 Subject: [PATCH 06/22] Replace watchr with listen and filewatcher observers - Remove unmaintained watchr gem (last updated 2011) - Add listen gem as default observer (event-driven, used by Rails) - Add filewatcher gem as alternative (polling-based, works everywhere) - Update Core::Base to use :listen as default observer - Add specs for both new observers The listen observer uses native OS file system notifications for better performance. The filewatcher observer uses polling which works in all environments including VMs and NFS mounts. --- infinity_test.gemspec | 3 +- lib/infinity_test.rb | 4 +- lib/infinity_test/core/base.rb | 9 ++- lib/infinity_test/observer/base.rb | 4 +- lib/infinity_test/observer/filewatcher.rb | 72 ++++++++++++++++++ lib/infinity_test/observer/listen.rb | 74 +++++++++++++++++++ lib/infinity_test/observer/watchr.rb | 45 ----------- spec/infinity_test/core/base_spec.rb | 4 +- .../core/continuous_test_server_spec.rb | 2 +- .../observer/filewatcher_spec.rb | 51 +++++++++++++ spec/infinity_test/observer/listen_spec.rb | 50 +++++++++++++ spec/infinity_test/observer/watchr_spec.rb | 49 ------------ 12 files changed, 261 insertions(+), 106 deletions(-) create mode 100644 lib/infinity_test/observer/filewatcher.rb create mode 100644 lib/infinity_test/observer/listen.rb delete mode 100644 lib/infinity_test/observer/watchr.rb create mode 100644 spec/infinity_test/observer/filewatcher_spec.rb create mode 100644 spec/infinity_test/observer/listen_spec.rb delete mode 100644 spec/infinity_test/observer/watchr_spec.rb diff --git a/infinity_test.gemspec b/infinity_test.gemspec index 6cac69e..2af84ae 100644 --- a/infinity_test.gemspec +++ b/infinity_test.gemspec @@ -19,7 +19,8 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.add_dependency 'activesupport' - spec.add_dependency 'watchr' + spec.add_dependency 'listen' + spec.add_dependency 'filewatcher' spec.add_dependency 'hike' spec.add_dependency 'notifiers' diff --git a/lib/infinity_test.rb b/lib/infinity_test.rb index 89dcd3a..65bfbef 100644 --- a/lib/infinity_test.rb +++ b/lib/infinity_test.rb @@ -34,8 +34,8 @@ module Framework module Observer autoload :Base, 'infinity_test/observer/base' - autoload :Watchr, 'infinity_test/observer/watchr' - autoload :EventMachine, 'infinity_test/observer/event_machine' + autoload :Listen, 'infinity_test/observer/listen' + autoload :Filewatcher, 'infinity_test/observer/filewatcher' autoload :SharedExample, 'infinity_test/observer/shared_example' end diff --git a/lib/infinity_test/core/base.rb b/lib/infinity_test/core/base.rb index 772762f..718b74e 100644 --- a/lib/infinity_test/core/base.rb +++ b/lib/infinity_test/core/base.rb @@ -47,13 +47,14 @@ class Base cattr_accessor :framework self.framework = :auto_discover - # Framework to observer watch the dirs. + # Framework to observe and watch the dirs for file changes. # # ==== Options - # * watchr + # * :listen (default) - Event-driven, uses native OS notifications + # * :filewatcher - Polling-based, works everywhere including VMs/NFS # cattr_accessor :observer - self.observer = :watchr + self.observer = :listen # Ignore test files. # @@ -332,4 +333,4 @@ def self.replace_patterns(&block) # end end end -end \ No newline at end of file +end diff --git a/lib/infinity_test/observer/base.rb b/lib/infinity_test/observer/base.rb index 8c3e1c8..f9a4d8a 100644 --- a/lib/infinity_test/observer/base.rb +++ b/lib/infinity_test/observer/base.rb @@ -32,11 +32,11 @@ def signal else puts " Are you sure? :S ... Interrupt a second time to quit!" @interrupt = true - Kernel.sleep 1.5 + Kernel.sleep 2 @interrupt = false end end end end end -end \ No newline at end of file +end diff --git a/lib/infinity_test/observer/filewatcher.rb b/lib/infinity_test/observer/filewatcher.rb new file mode 100644 index 0000000..1638342 --- /dev/null +++ b/lib/infinity_test/observer/filewatcher.rb @@ -0,0 +1,72 @@ +require 'filewatcher' + +module InfinityTest + module Observer + class Filewatcher < Base + attr_reader :observer, :watch_paths, :patterns + + def initialize(continuous_test_server) + super + @watch_paths = [] + @patterns = {} + end + + # Watch a file or pattern for changes. + # + # ==== Examples + # + # watch('lib/(.*)\.rb') { |file| puts [file.name, file.path, file.match_data] } + # watch('test/test_helper.rb') { run_all() } + # + def watch(pattern_or_file, &block) + pattern = Regexp.new(pattern_or_file.to_s) + @patterns[pattern] = block + end + + # Watch a directory for changes. + # + # ==== Examples + # + # watch_dir(:lib) { |file| run_test(file) } + # watch_dir(:test) { |file| run_file(file) } + # + # watch_dir(:test, :py) { |file| puts [file.name, file.path, file.match_data] } + # watch_dir(:test, :js) { |file| puts [file.name, file.path, file.match_data] } + # + def watch_dir(dir_name, extension = :rb, &block) + watch("^#{dir_name}/*/(.*).#{extension}", &block) + path = "#{dir_name}/**/*.#{extension}" + @watch_paths << path unless @watch_paths.include?(path) + end + + # Start the continuous test server. + # + def start + paths = @watch_paths.empty? ? ['**/*.rb'] : @watch_paths + + @observer = ::Filewatcher.new(paths) + + @observer.watch do |changes| + changes.each do |file_path, _event| + relative_path = file_path.sub("#{Dir.pwd}/", '') + handle_file_change(relative_path) + end + end + rescue Interrupt + @observer.stop if @observer + end + + private + + def handle_file_change(file_path) + @patterns.each do |pattern, block| + if match_data = pattern.match(file_path) + changed_file = InfinityTest::Core::ChangedFile.new(match_data) + block.call(changed_file) + break + end + end + end + end + end +end diff --git a/lib/infinity_test/observer/listen.rb b/lib/infinity_test/observer/listen.rb new file mode 100644 index 0000000..89f0e82 --- /dev/null +++ b/lib/infinity_test/observer/listen.rb @@ -0,0 +1,74 @@ +require 'listen' + +module InfinityTest + module Observer + class Listen < Base + attr_reader :observer, :directories, :patterns + + def initialize(continuous_test_server) + super + @directories = [] + @patterns = {} + end + + # Watch a file or pattern for changes. + # + # ==== Examples + # + # watch('lib/(.*)\.rb') { |file| puts [file.name, file.path, file.match_data] } + # watch('test/test_helper.rb') { run_all() } + # + def watch(pattern_or_file, &block) + pattern = Regexp.new(pattern_or_file.to_s) + @patterns[pattern] = block + end + + # Watch a directory for changes. + # + # ==== Examples + # + # watch_dir(:lib) { |file| run_test(file) } + # watch_dir(:test) { |file| run_file(file) } + # + # watch_dir(:test, :py) { |file| puts [file.name, file.path, file.match_data] } + # watch_dir(:test, :js) { |file| puts [file.name, file.path, file.match_data] } + # + def watch_dir(dir_name, extension = :rb, &block) + watch("^#{dir_name}/*/(.*).#{extension}", &block) + @directories << dir_name.to_s unless @directories.include?(dir_name.to_s) + end + + # Start the continuous test server. + # + def start + dirs = @directories.empty? ? ['.'] : @directories + dirs = dirs.select { |d| File.directory?(d) } + dirs = ['.'] if dirs.empty? + + @observer = ::Listen.to(*dirs) do |modified, added, _removed| + (modified + added).each do |file| + relative_path = file.sub("#{Dir.pwd}/", '') + handle_file_change(relative_path) + end + end + + @observer.start + sleep + rescue Interrupt + @observer.stop if @observer + end + + private + + def handle_file_change(file_path) + @patterns.each do |pattern, block| + if match_data = pattern.match(file_path) + changed_file = InfinityTest::Core::ChangedFile.new(match_data) + block.call(changed_file) + break + end + end + end + end + end +end diff --git a/lib/infinity_test/observer/watchr.rb b/lib/infinity_test/observer/watchr.rb deleted file mode 100644 index 760ce41..0000000 --- a/lib/infinity_test/observer/watchr.rb +++ /dev/null @@ -1,45 +0,0 @@ -require 'watchr' - -module InfinityTest - module Observer - class Watchr < Base - attr_reader :observer - - def initialize(continuous_test_server) - super - @observer = ::Watchr::Script.new - end - - # ==== Examples - # - # watch('lib/(.*)\.rb') { |file| puts [file.name, file.path, file.match_data] } - # watch('test/test_helper.rb') { run_all() } - # - def watch(pattern_or_file, &block) - @observer.watch(pattern_or_file.to_s) do |match_data| - block.call(InfinityTest::Core::ChangedFile.new(match_data)) - end - end - - # ==== Examples - # - # watch_dir(:lib) { |file| RunTest(file) } - # watch_dir(:test) { |file| RunFile(file) } - # - # watch_dir(:test, :py) { |file| puts [file.name, file.path, file.match_data] } - # watch_dir(:test, :js) { |file| puts [file.name, file.path, file.match_data] } - # - def watch_dir(dir_name, extension = :rb, &block) - watch("^#{dir_name}/*/(.*).#{extension}", &block) - end - - # Start the continuous test server. - # - def start - @handler = ::Watchr.handler.new - @controller = ::Watchr::Controller.new(@observer, @handler) - @controller.run - end - end - end -end \ No newline at end of file diff --git a/spec/infinity_test/core/base_spec.rb b/spec/infinity_test/core/base_spec.rb index 94268c7..30cdde2 100644 --- a/spec/infinity_test/core/base_spec.rb +++ b/spec/infinity_test/core/base_spec.rb @@ -23,8 +23,8 @@ module InfinityTest end describe ".observer" do - it "has watchr as default observer" do - expect(Base.observer).to equal :watchr + it "has listen as default observer" do + expect(Base.observer).to equal :listen end end diff --git a/spec/infinity_test/core/continuous_test_server_spec.rb b/spec/infinity_test/core/continuous_test_server_spec.rb index c853fd8..ec75825 100644 --- a/spec/infinity_test/core/continuous_test_server_spec.rb +++ b/spec/infinity_test/core/continuous_test_server_spec.rb @@ -19,7 +19,7 @@ module Core before do expect(base).to receive(:infinity_and_beyond).and_return(true) allow(base).to receive(:framework).and_return(:rails) - allow(base).to receive(:observer).and_return(:watchr) + allow(base).to receive(:observer).and_return(:listen) end it 'adds framework heuristics and starts the observer' do diff --git a/spec/infinity_test/observer/filewatcher_spec.rb b/spec/infinity_test/observer/filewatcher_spec.rb new file mode 100644 index 0000000..39e73e9 --- /dev/null +++ b/spec/infinity_test/observer/filewatcher_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +module InfinityTest + module Observer + describe Filewatcher do + let(:continuous_server) { double } + subject { Filewatcher.new(continuous_server) } + + it_should_behave_like 'an infinity test observer' + + describe "#patterns" do + it "starts as an empty hash" do + expect(subject.patterns).to eq({}) + end + end + + describe "#watch_paths" do + it "starts as an empty array" do + expect(subject.watch_paths).to eq([]) + end + end + + describe "#watch" do + it "adds a pattern with its block to patterns" do + block = proc { |file| file } + subject.watch('lib/(.*)\.rb', &block) + expect(subject.patterns.keys.first).to be_a(Regexp) + expect(subject.patterns.values.first).to eq(block) + end + end + + describe "#watch_dir" do + it "adds a glob pattern to watch_paths" do + subject.watch_dir(:spec) + expect(subject.watch_paths).to include('spec/**/*.rb') + end + + it "adds a pattern to patterns" do + subject.watch_dir(:spec) + expect(subject.patterns.keys.first.source).to eq('^spec/*/(.*).rb') + end + + it "accepts a custom extension" do + subject.watch_dir(:spec, :py) + expect(subject.watch_paths).to include('spec/**/*.py') + expect(subject.patterns.keys.first.source).to eq('^spec/*/(.*).py') + end + end + end + end +end diff --git a/spec/infinity_test/observer/listen_spec.rb b/spec/infinity_test/observer/listen_spec.rb new file mode 100644 index 0000000..441fa72 --- /dev/null +++ b/spec/infinity_test/observer/listen_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +module InfinityTest + module Observer + describe Listen do + let(:continuous_server) { double } + subject { Listen.new(continuous_server) } + + it_should_behave_like 'an infinity test observer' + + describe "#patterns" do + it "starts as an empty hash" do + expect(subject.patterns).to eq({}) + end + end + + describe "#directories" do + it "starts as an empty array" do + expect(subject.directories).to eq([]) + end + end + + describe "#watch" do + it "adds a pattern with its block to patterns" do + block = proc { |file| file } + subject.watch('lib/(.*)\.rb', &block) + expect(subject.patterns.keys.first).to be_a(Regexp) + expect(subject.patterns.values.first).to eq(block) + end + end + + describe "#watch_dir" do + it "adds a pattern to patterns" do + subject.watch_dir(:spec) + expect(subject.patterns.keys.first.source).to eq('^spec/*/(.*).rb') + end + + it "adds the directory to directories" do + subject.watch_dir(:spec) + expect(subject.directories).to include('spec') + end + + it "accepts a custom extension" do + subject.watch_dir(:spec, :py) + expect(subject.patterns.keys.first.source).to eq('^spec/*/(.*).py') + end + end + end + end +end diff --git a/spec/infinity_test/observer/watchr_spec.rb b/spec/infinity_test/observer/watchr_spec.rb deleted file mode 100644 index dc8790a..0000000 --- a/spec/infinity_test/observer/watchr_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'spec_helper' - -module InfinityTest - module Observer - describe Watchr do - let(:continuous_server) { double } - subject { Watchr.new(continuous_server)} - it_should_behave_like 'an infinity test observer' - - describe "#observer" do - it "is instance of watchr script" do - expect(subject.observer).to be_instance_of(::Watchr::Script) - end - end - - describe "#watch" do - it "passes the args to the observer" do - expect(subject.observer).to receive(:watch).with('lib') - subject.watch(:lib) - end - end - - describe "#watch_dir" do - it "passes the pattern to the observer" do - expect(subject.observer).to receive(:watch).with("^spec/*/(.*).rb") - subject.watch_dir(:spec) - end - - it "passes the pattern and the extension to the observer" do - expect(subject.observer).to receive(:watch).with("^spec/*/(.*).py") - subject.watch_dir(:spec, :py) - end - end - - describe "#start" do - it "initializes a watchr controller passing the #observer" do - handler_class = double - handler = double - controller = double - allow(::Watchr).to receive(:handler).and_return(handler_class) - expect(handler_class).to receive(:new).and_return(handler) - expect(::Watchr::Controller).to receive(:new).with(subject.observer, handler).and_return(controller) - expect(controller).to receive(:run).and_return(:running) - expect(subject.start).to equal :running - end - end - end - end -end From bf4a3cfac003f975ca75c59ff7fa044477b00cde Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Sun, 1 Feb 2026 01:33:59 +0000 Subject: [PATCH 07/22] Fix signal handler blocking issue in observer base Replace blocking sleep with timestamp-based approach. The previous implementation blocked inside the signal handler which prevented proper re-entrancy when pressing Ctrl+C multiple times. Add specs for observer base including interrupt handling logic. --- lib/infinity_test/observer/base.rb | 6 +- spec/infinity_test/observer/base_spec.rb | 78 ++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 spec/infinity_test/observer/base_spec.rb diff --git a/lib/infinity_test/observer/base.rb b/lib/infinity_test/observer/base.rb index f9a4d8a..8fc88d6 100644 --- a/lib/infinity_test/observer/base.rb +++ b/lib/infinity_test/observer/base.rb @@ -26,14 +26,12 @@ def start def signal Signal.trap('INT') do - if @interrupt + if @interrupt_at && (Time.now - @interrupt_at) < 2 puts " To Infinity and Beyond!" exit else puts " Are you sure? :S ... Interrupt a second time to quit!" - @interrupt = true - Kernel.sleep 2 - @interrupt = false + @interrupt_at = Time.now end end end diff --git a/spec/infinity_test/observer/base_spec.rb b/spec/infinity_test/observer/base_spec.rb new file mode 100644 index 0000000..476d92c --- /dev/null +++ b/spec/infinity_test/observer/base_spec.rb @@ -0,0 +1,78 @@ +require 'spec_helper' + +module InfinityTest + module Observer + describe Base do + let(:continuous_server) { double } + subject { Base.new(continuous_server) } + + describe "#initialize" do + it "sets the continuous_test_server" do + expect(subject.continuous_test_server).to eq(continuous_server) + end + end + + describe "#watch" do + it "raises NotImplementedError" do + expect { subject.watch('pattern') {} }.to raise_error(NotImplementedError) + end + end + + describe "#watch_dir" do + it "raises NotImplementedError" do + expect { subject.watch_dir(:lib, :rb) {} }.to raise_error(NotImplementedError) + end + end + + describe "#start" do + it "raises NotImplementedError" do + expect { subject.start }.to raise_error(NotImplementedError) + end + end + + describe "#start!" do + it "calls signal and start" do + expect(subject).to receive(:signal) + expect(subject).to receive(:start) + subject.start! + end + end + + describe "#signal" do + it "traps INT signal" do + expect(Signal).to receive(:trap).with('INT') + subject.signal + end + + context "interrupt handling logic" do + let(:handler) do + handler_block = nil + allow(Signal).to receive(:trap) { |_, &block| handler_block = block } + subject.signal + handler_block + end + + it "shows warning on first interrupt" do + expect(subject).to receive(:puts).with(" Are you sure? :S ... Interrupt a second time to quit!") + handler.call + expect(subject.instance_variable_get(:@interrupt_at)).to be_within(1).of(Time.now) + end + + it "exits on second interrupt within 2 seconds" do + subject.instance_variable_set(:@interrupt_at, Time.now) + expect(subject).to receive(:puts).with(" To Infinity and Beyond!") + expect(subject).to receive(:exit) + handler.call + end + + it "resets timer if second interrupt is after 2 seconds" do + subject.instance_variable_set(:@interrupt_at, Time.now - 3) + expect(subject).to receive(:puts).with(" Are you sure? :S ... Interrupt a second time to quit!") + handler.call + expect(subject.instance_variable_get(:@interrupt_at)).to be_within(1).of(Time.now) + end + end + end + end + end +end From 806061ae434787e37d1ce836b2a0e3a799299bb1 Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Sun, 1 Feb 2026 02:14:06 +0000 Subject: [PATCH 08/22] Update notifications to use modern notifiers gem - Update notification options documentation with current notifiers: - :auto_discover (default) - auto-detect available notifier - :terminal_notifier - macOS - :osascript - macOS built-in - :notify_send - Linux/BSD - :dunstify - Linux/BSD - Update specs to use :auto_discover instead of deprecated :growl --- lib/infinity_test/core/base.rb | 10 +++++----- spec/infinity_test/core/continuous_test_server_spec.rb | 2 +- spec/infinity_test/core/notifier_spec.rb | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/infinity_test/core/base.rb b/lib/infinity_test/core/base.rb index 718b74e..0366b71 100644 --- a/lib/infinity_test/core/base.rb +++ b/lib/infinity_test/core/base.rb @@ -81,11 +81,11 @@ class Base # Set the notification framework to use with Infinity Test. # # ==== Options - # * :growl - # * :lib_notify - # * :auto_discover(defaults) - # - # This will load a exactly a class constantize by name. + # * :auto_discover (default) - Automatically detect available notifier + # * :terminal_notifier - macOS terminal-notifier + # * :osascript - macOS built-in notifications + # * :notify_send - Linux/BSD libnotify + # * :dunstify - Linux/BSD dunst # cattr_writer :notifications self.notifications = :auto_discover diff --git a/spec/infinity_test/core/continuous_test_server_spec.rb b/spec/infinity_test/core/continuous_test_server_spec.rb index ec75825..607c2d6 100644 --- a/spec/infinity_test/core/continuous_test_server_spec.rb +++ b/spec/infinity_test/core/continuous_test_server_spec.rb @@ -49,7 +49,7 @@ module Core context 'when have notification library' do let(:test_framework) { double } - let(:base) { double(notifications: :growl) } + let(:base) { double(notifications: :auto_discover) } before do expect(continuous_test_server).to receive(:test_framework).exactly(:twice).and_return(test_framework) diff --git a/spec/infinity_test/core/notifier_spec.rb b/spec/infinity_test/core/notifier_spec.rb index c1e2fe0..4b3f550 100644 --- a/spec/infinity_test/core/notifier_spec.rb +++ b/spec/infinity_test/core/notifier_spec.rb @@ -5,7 +5,7 @@ module Core describe Notifier do let(:test_framework) { double } - subject(:notifier) { Notifier.new(test_framework: test_framework, library: :growl) } + subject(:notifier) { Notifier.new(test_framework: test_framework, library: :auto_discover) } describe '#image' do before do @@ -47,7 +47,7 @@ module Core describe '#library' do it 'returns the primitive value' do - expect(notifier.library).to be :growl + expect(notifier.library).to be :auto_discover end end From 7e153dcae85a7102c31ea41596e8c26567ad599c Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Sun, 1 Feb 2026 02:19:11 +0000 Subject: [PATCH 09/22] Use notifiers gem from GitHub main branch The latest notifiers 2.0.0 with auto_discover and modern notifier support is not yet released on RubyGems. --- Gemfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gemfile b/Gemfile index fa75df1..154ef5e 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,5 @@ source 'https://rubygems.org' +gem 'notifiers', github: 'tomas-stefano/notifiers', branch: 'main' + gemspec From 33d2bacaf83bffb40fb19ce25895d502405c3451 Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Sun, 1 Feb 2026 21:00:40 +0000 Subject: [PATCH 10/22] Add --notifications and --mode CLI options for notifiers gem Complete the notifications integration with the notifiers gem by adding command line options to configure the notification library and image theme. Update deprecated growl references to use modern notifiers (osascript, terminal_notifier, notify_send, dunstify). --- lib/infinity_test/core/configuration_merge.rb | 3 ++ lib/infinity_test/core/load_configuration.rb | 6 +-- lib/infinity_test/core/options.rb | 17 +++++++- spec/infinity_test/core/base_spec.rb | 8 ++-- spec/infinity_test/core/notifier_spec.rb | 20 ++++++++++ spec/infinity_test/core/options_spec.rb | 40 +++++++++++++++++-- 6 files changed, 82 insertions(+), 12 deletions(-) diff --git a/lib/infinity_test/core/configuration_merge.rb b/lib/infinity_test/core/configuration_merge.rb index 1217224..cc303f9 100644 --- a/lib/infinity_test/core/configuration_merge.rb +++ b/lib/infinity_test/core/configuration_merge.rb @@ -5,6 +5,7 @@ class ConfigurationMerge delegate :strategy, :rubies, :test_framework, :framework, :to => :options delegate :specific_options, :infinity_and_beyond, :verbose, :bundler, :to => :options + delegate :notifications, :mode, :to => :options def initialize(base, options) @base = base @@ -25,6 +26,8 @@ def merge! @base.infinity_and_beyond = infinity_and_beyond unless infinity_and_beyond.nil? @base.verbose = verbose unless verbose.nil? @base.bundler = bundler unless bundler.nil? + @base.notifications = notifications if notifications.present? + @base.mode = mode if mode.present? @base end end diff --git a/lib/infinity_test/core/load_configuration.rb b/lib/infinity_test/core/load_configuration.rb index 8dd0fd9..424b64e 100644 --- a/lib/infinity_test/core/load_configuration.rb +++ b/lib/infinity_test/core/load_configuration.rb @@ -21,11 +21,11 @@ def initialize # # Example: # - # ~/.infinity_test -> infinity_test { notifications :growl } + # ~/.infinity_test -> infinity_test { notifications :osascript } # - # ./.infinity_test -> infinity_test { notifications :lib_notify } # High Priority + # ./.infinity_test -> infinity_test { notifications :terminal_notifier } # High Priority # - # After the load the Notifications Framework will be Lib Notify + # After the load the Notifications Framework will be Terminal Notifier # def load! load_global_file! diff --git a/lib/infinity_test/core/options.rb b/lib/infinity_test/core/options.rb index 3ca1f6d..36c15e6 100644 --- a/lib/infinity_test/core/options.rb +++ b/lib/infinity_test/core/options.rb @@ -3,6 +3,7 @@ module Core class Options attr_accessor :arguments, :options_parser, :strategy, :bundler, :verbose attr_accessor :rubies, :specific_options, :test_framework, :framework, :infinity_and_beyond + attr_accessor :notifications, :mode def initialize(*arguments) @arguments = arguments.flatten.clone @@ -17,6 +18,8 @@ def new_options_parser options_to_added_in_the_command test_framework_to_be_run app_framework + notifications_library + image_mode infinity_and_beyond_option verbose_mode skip_bundler @@ -53,7 +56,7 @@ def options_to_added_in_the_command(option) end def test_framework_to_be_run(option) - option.on('--test library', 'Test Framework to be run. Ex.: auto_discover, rspec, test_unit, bacon.') do |library| + option.on('--test library', 'Test Framework to be run. Ex.: auto_discover, rspec, test_unit.') do |library| @test_framework = library.to_sym end end @@ -64,6 +67,18 @@ def app_framework(option) end end + def notifications_library(option) + option.on('--notifications library', 'Notification library to use. Ex.: auto_discover, osascript, terminal_notifier, notify_send, dunstify.') do |library| + @notifications = library.to_sym + end + end + + def image_mode(option) + option.on('--mode theme', 'Image theme for notifications. Ex.: simpson, faces, fuuu, hands, mario_bros, rails, rubies, street_fighter, toy_story.') do |theme| + @mode = theme.to_sym + end + end + def infinity_and_beyond_option(option) option.on('-n', '--no-infinity-and-beyond', 'Run tests and exit. Useful in a Continuous Integration environment.') do @infinity_and_beyond = false diff --git a/spec/infinity_test/core/base_spec.rb b/spec/infinity_test/core/base_spec.rb index 30cdde2..3428512 100644 --- a/spec/infinity_test/core/base_spec.rb +++ b/spec/infinity_test/core/base_spec.rb @@ -67,14 +67,14 @@ module InfinityTest describe ".notifications" do it "sets the notification class accessor" do silence_stream(STDOUT) do - Base.notifications(:growl) - expect(Base.notifications).to be :growl + Base.notifications(:osascript) + expect(Base.notifications).to be :osascript end end it "sets the images" do silence_stream(STDOUT) do - Base.notifications(:growl) do + Base.notifications(:osascript) do show_images :success_image => 'foo', :failure_image => 'bar', :pending_image => 'baz' end end @@ -88,7 +88,7 @@ module InfinityTest it "sets the mode" do silence_stream(STDOUT) do - Base.notifications(:growl) do + Base.notifications(:osascript) do show_images :mode => :mortal_kombat end end diff --git a/spec/infinity_test/core/notifier_spec.rb b/spec/infinity_test/core/notifier_spec.rb index 4b3f550..2097185 100644 --- a/spec/infinity_test/core/notifier_spec.rb +++ b/spec/infinity_test/core/notifier_spec.rb @@ -126,6 +126,26 @@ module Core end end end + + describe '#notify' do + let(:notification_builder) { double } + + before do + expect(Core::Base).to receive(:mode).and_return(:simpson) + expect(test_framework).to receive(:success?).and_return(true) + expect(test_framework).to receive(:test_message).and_return('5 examples, 0 failures') + end + + it 'sends a notification with title, message and image' do + expect(notifier).to receive(:auto_discover).and_return(notification_builder) + expect(notification_builder).to receive(:title).with(RUBY_VERSION).and_return(notification_builder) + expect(notification_builder).to receive(:message).with('5 examples, 0 failures').and_return(notification_builder) + expect(notification_builder).to receive(:image).and_return(notification_builder) + expect(notification_builder).to receive(:notify) + + notifier.notify + end + end end end end \ No newline at end of file diff --git a/spec/infinity_test/core/options_spec.rb b/spec/infinity_test/core/options_spec.rb index 8b42550..ab1d43b 100644 --- a/spec/infinity_test/core/options_spec.rb +++ b/spec/infinity_test/core/options_spec.rb @@ -49,10 +49,6 @@ module InfinityTest expect(parse('--test', 'rspec').test_framework).to be :rspec end - it "parses the test framework as bacon" do - expect(parse('--test', 'bacon').test_framework).to be :bacon - end - it "parses the test framework as test_unit" do expect(parse('--test', 'test_unit').test_framework).to be :test_unit end @@ -95,6 +91,42 @@ module InfinityTest expect(parse.bundler?).to be_nil end end + + describe "#notifications" do + it "parses the notifications library as auto_discover" do + expect(parse('--notifications', 'auto_discover').notifications).to be :auto_discover + end + + it "parses the notifications library as osascript" do + expect(parse('--notifications', 'osascript').notifications).to be :osascript + end + + it "parses the notifications library as terminal_notifier" do + expect(parse('--notifications', 'terminal_notifier').notifications).to be :terminal_notifier + end + + it "returns nil when nothing is passed" do + expect(parse.notifications).to be_nil + end + end + + describe "#mode" do + it "parses the image mode as simpson" do + expect(parse('--mode', 'simpson').mode).to be :simpson + end + + it "parses the image mode as faces" do + expect(parse('--mode', 'faces').mode).to be :faces + end + + it "parses the image mode as rails" do + expect(parse('--mode', 'rails').mode).to be :rails + end + + it "returns nil when nothing is passed" do + expect(parse.mode).to be_nil + end + end end def parse(*args) From e314303dd267432f826e8e0f67dbf8b7e1aae756 Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Sun, 1 Feb 2026 21:01:44 +0000 Subject: [PATCH 11/22] Remove unecessary code --- .infinity_test | 11 ++-- TODO.markdown | 59 +++++++++---------- lib/infinity_test.rb | 1 - lib/infinity_test/core/base.rb | 9 ++- lib/infinity_test/core/load_configuration.rb | 16 ++--- lib/infinity_test/test_framework/bacon.rb | 6 -- spec/infinity_test/core/auto_discover_spec.rb | 5 +- .../test_framework/bacon_spec.rb | 9 --- 8 files changed, 46 insertions(+), 70 deletions(-) delete mode 100644 lib/infinity_test/test_framework/bacon.rb delete mode 100644 spec/infinity_test/test_framework/bacon_spec.rb diff --git a/.infinity_test b/.infinity_test index 42dc0d6..fa0be2b 100644 --- a/.infinity_test +++ b/.infinity_test @@ -1,8 +1,7 @@ -InfinityTest.setup do |config| - config.strategy = :ruby_default - config.test_framework = :rspec - config.notifications = :growl -end +#InfinityTest.setup do |config| +# config.strategy = :ruby_default +# config.test_framework = :rspec +#end # infinity_test do @@ -46,4 +45,4 @@ end # end # end -# end \ No newline at end of file +# end diff --git a/TODO.markdown b/TODO.markdown index 8def40f..d672d76 100644 --- a/TODO.markdown +++ b/TODO.markdown @@ -1,35 +1,30 @@ -## Road to 2.0 - -* Test::Unit/Minitest. -* RubyDefault strategy. -* Make callbacks work in the new structure. -* Work with test pattern on Configuration overwriting the Test framework instance. -* Be possible to rewrite the rules in more nicer way, using the Hike gem to find files and paths. -* Work with gemsets. -* Rails. -* Padrino. -* Rvm. -* Test with spork and zeus and other gems spork like (Experimental). - -### 2.0.0 Bugs - +## Todo + +Infinity test is a gem that watch files changes and run tests. + +That also can run with different ruby versions all test suite or only that file change (using rvm or +rbenv), ruby default just run the test on the current ruby version. + +## What Needs to Be Done + +* Make notifications work with Notifiers gem (remove growl and use the autodiscover from notifiers +gem). +* Change .infinity_test to INFINITY_TEST +* Make Test::Unit to work (the infinity test to check test folder). +* Make RSpec work +* Finish RubyDefault strategy +* Finish RVM (running different versions - the user need to specify) +* Finish RbEnv (running different versions - the user need to specify) +* Make callbacks work in the new structure (loading the infinity test file). +* Finish Gem autodiscover and its changes heuristics. +* Finish Rails autodiscover and its changes heuristics. +* Padrino autodiscover and its changes heuristics. * Improve auto discover feature priorization subclasses for #run? method. -* Observer process signal must work in Ruby 2.0.0. - -### Flexibility - -* Ignore test files and test folders when run command and change files/dir. -* Verbose way of InfinityTest. -* Add specific options to the command. -* Create a infinity test generator with thor! -* Don't run integration tests. Ignore them when changed. - -### 2.0.2 - -* RbEnv (experimented feature). -* Bacon. * Focus feature(fails, pass one file, run entire suite) with --focus (experimented feature)! -* Cucumber -* +* Add post-run hooks to be added to the INFINITY_TEST file that run other things (coverage, code +analysis, etc - see ideas) + +* Give some ideas (write to a md file the ideas) about how to integrate the infinity test with AI tools/AI agents or +even Claude code ... so ruby developers can see -Maybe we could create a infinity-test-contrib repositories with other heuristics platforms +* Update HISTORY with all changes since last version. diff --git a/lib/infinity_test.rb b/lib/infinity_test.rb index 65bfbef..0218290 100644 --- a/lib/infinity_test.rb +++ b/lib/infinity_test.rb @@ -66,4 +66,3 @@ def self.setup(&block) require 'infinity_test/framework/rubygems' require 'infinity_test/test_framework/test_unit' require 'infinity_test/test_framework/rspec' -require 'infinity_test/test_framework/bacon' diff --git a/lib/infinity_test/core/base.rb b/lib/infinity_test/core/base.rb index 0366b71..0737fb1 100644 --- a/lib/infinity_test/core/base.rb +++ b/lib/infinity_test/core/base.rb @@ -23,14 +23,13 @@ class Base cattr_accessor :specific_options self.specific_options = '' - # Test Framework to use Rspec, Bacon, Test::Unit or AutoDiscover(defaults) + # Test Framework to use RSpec, Test::Unit or AutoDiscover(defaults) # ==== Options # * :rspec - # * :bacon # * :test_unit (Test unit here apply to this two libs: test/unit and minitest) - # * :auto_discover(defaults) + # * :auto_discover (default) # - # This will load a exactly a class constantize by name. + # This will load a class constantized by name. # cattr_accessor :test_framework self.test_framework = :auto_discover @@ -245,7 +244,7 @@ def self.notifications(notification_name = nil, &block) .notifications is DEPRECATED. Use this instead: InfinityTest.setup do |config| - config.notifications = :growl + config.notifications = :dunstify end MESSAGE ActiveSupport::Deprecation.new.warn(message) diff --git a/lib/infinity_test/core/load_configuration.rb b/lib/infinity_test/core/load_configuration.rb index 424b64e..e4c838c 100644 --- a/lib/infinity_test/core/load_configuration.rb +++ b/lib/infinity_test/core/load_configuration.rb @@ -4,26 +4,26 @@ class LoadConfiguration attr_accessor :global_file, :project_file def initialize - @global_file = File.expand_path('~/.infinity_test') - @project_file = './.infinity_test' + @global_file = File.expand_path('~/INFINITY_TEST') + @project_file = './INFINITY_TEST' @old_configuration = InfinityTest::OldDSL::Configuration end # Load the Configuration file # - # Command line options can be persisted in a .infinity_test file in a project. - # You can also store a .infinity_test file in your home directory (~/.infinity_test) + # Command line options can be persisted in an INFINITY_TEST file in a project. + # You can also store an INFINITY_TEST file in your home directory (~/INFINITY_TEST) # # Precedence is: # command line - # ./.infinity_test - # ~/.infinity_test + # ./INFINITY_TEST + # ~/INFINITY_TEST # # Example: # - # ~/.infinity_test -> infinity_test { notifications :osascript } + # ~/INFINITY_TEST -> infinity_test { notifications :osascript } # - # ./.infinity_test -> infinity_test { notifications :terminal_notifier } # High Priority + # ./INFINITY_TEST -> infinity_test { notifications :terminal_notifier } # High Priority # # After the load the Notifications Framework will be Terminal Notifier # diff --git a/lib/infinity_test/test_framework/bacon.rb b/lib/infinity_test/test_framework/bacon.rb deleted file mode 100644 index d8c46d6..0000000 --- a/lib/infinity_test/test_framework/bacon.rb +++ /dev/null @@ -1,6 +0,0 @@ -module InfinityTest - module TestFramework - class Bacon < Base - end - end -end \ No newline at end of file diff --git a/spec/infinity_test/core/auto_discover_spec.rb b/spec/infinity_test/core/auto_discover_spec.rb index 6fc0241..cf1bc0f 100644 --- a/spec/infinity_test/core/auto_discover_spec.rb +++ b/spec/infinity_test/core/auto_discover_spec.rb @@ -81,13 +81,12 @@ module InfinityTest before do base.test_framework = :auto_discover allow(TestFramework::TestUnit).to receive(:run?).and_return(false) - allow(TestFramework::Rspec).to receive(:run?).and_return(false) - expect(TestFramework::Bacon).to receive(:run?).and_return(true) + expect(TestFramework::Rspec).to receive(:run?).and_return(true) end it 'changes the base framework' do auto_discover.discover_test_framework - expect(base.test_framework).to be :bacon + expect(base.test_framework).to be :rspec end end diff --git a/spec/infinity_test/test_framework/bacon_spec.rb b/spec/infinity_test/test_framework/bacon_spec.rb deleted file mode 100644 index 1be05d2..0000000 --- a/spec/infinity_test/test_framework/bacon_spec.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'spec_helper' - -module InfinityTest - module TestFramework - describe Bacon do - it_should_behave_like 'a infinity test test framework' - end - end -end \ No newline at end of file From c863bf77af7332ef7fb59e16cfd3108582d252eb Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Sun, 1 Feb 2026 21:05:02 +0000 Subject: [PATCH 12/22] Rename config file to INFINITY_TEST and complete test frameworks - Rename .infinity_test to INFINITY_TEST for clearer visibility - Complete TestUnit implementation with patterns for minitest output, success/failure/pending detection, test_dir setter, and .run? method - Add missing methods to RSpec: test_dir=, pending?, and .run? - Add comprehensive specs for TestUnit --- .infinity_test => INFINITY_TEST | 0 lib/infinity_test/test_framework/rspec.rb | 17 +- lib/infinity_test/test_framework/test_unit.rb | 44 ++++- .../test_framework/test_unit_spec.rb | 170 +++++++++++++++++- 4 files changed, 227 insertions(+), 4 deletions(-) rename .infinity_test => INFINITY_TEST (100%) diff --git a/.infinity_test b/INFINITY_TEST similarity index 100% rename from .infinity_test rename to INFINITY_TEST diff --git a/lib/infinity_test/test_framework/rspec.rb b/lib/infinity_test/test_framework/rspec.rb index 798d4bf..c1eb6f0 100644 --- a/lib/infinity_test/test_framework/rspec.rb +++ b/lib/infinity_test/test_framework/rspec.rb @@ -10,7 +10,11 @@ def test_helper_file end def test_dir - 'spec' + @test_dir ||= 'spec' + end + + def test_dir=(dir) + @test_dir = dir end def test_files @@ -28,6 +32,17 @@ def success? def failure? @failures > 0 end + + def pending? + @pending > 0 + end + + def self.run? + File.exist?('spec') && ( + File.exist?('spec/spec_helper.rb') || + Dir['spec/**/*_spec.rb'].any? + ) + end end end end \ No newline at end of file diff --git a/lib/infinity_test/test_framework/test_unit.rb b/lib/infinity_test/test_framework/test_unit.rb index f2246ad..b3e6e1c 100644 --- a/lib/infinity_test/test_framework/test_unit.rb +++ b/lib/infinity_test/test_framework/test_unit.rb @@ -2,15 +2,55 @@ module InfinityTest module TestFramework class TestUnit < Base def test_dir - 'test' + @test_dir ||= 'test' + end + + def test_dir=(dir) + @test_dir = dir end def test_helper_file File.join(test_dir, 'test_helper.rb') end + def test_files + @test_files || test_dir + end + def binary - '' + 'ruby' + end + + # Patterns for parsing minitest/test-unit output + # Example: "10 runs, 15 assertions, 0 failures, 0 errors, 0 skips" + def patterns + { + :runs => /(\d+) runs,/, + :assertions => /(\d+) assertions,/, + :failures => /(\d+) failures,/, + :errors => /(\d+) errors,/, + :skips => /(\d+) skips/ + } + end + + def success? + @failures.zero? && @errors.zero? && @skips.zero? + end + + def failure? + @failures > 0 || @errors > 0 + end + + def pending? + @skips > 0 + end + + def self.run? + File.exist?('test') && ( + File.exist?('test/test_helper.rb') || + Dir['test/**/*_test.rb'].any? || + Dir['test/**/test_*.rb'].any? + ) end end end diff --git a/spec/infinity_test/test_framework/test_unit_spec.rb b/spec/infinity_test/test_framework/test_unit_spec.rb index 990261e..c4a73b2 100644 --- a/spec/infinity_test/test_framework/test_unit_spec.rb +++ b/spec/infinity_test/test_framework/test_unit_spec.rb @@ -9,6 +9,26 @@ module TestFramework it "returns test as test dir" do expect(subject.test_dir).to eq 'test' end + + it "can be set to a custom directory" do + subject.test_dir = 'custom_test' + expect(subject.test_dir).to eq 'custom_test' + end + end + + describe '#test_files' do + context 'when is empty' do + it 'returns the test dir' do + expect(subject.test_files).to eq 'test' + end + end + + context 'when assign the test files' do + it 'returns the assigned value' do + subject.test_files = 'test/models/user_test.rb' + expect(subject.test_files).to eq 'test/models/user_test.rb' + end + end end describe "#test_helper_file" do @@ -18,7 +38,155 @@ module TestFramework end describe '#binary' do - it 'sees the binary with the strategy instance' + it 'returns ruby as binary' do + expect(subject.binary).to eq 'ruby' + end + end + + describe '#test_message' do + subject(:test_unit) { TestUnit.new } + + it 'returns the final test results' do + test_unit.test_message = "Finished in 0.001s, 1000.0 runs/s.\n10 runs, 15 assertions, 0 failures, 0 errors, 0 skips\n" + expect(test_unit.test_message).to eq '10 runs, 15 assertions, 0 failures, 0 errors, 0 skips' + end + end + + describe '#success?' do + subject(:test_unit) { TestUnit.new } + + context 'when test message has ZERO failures, ZERO errors, and ZERO skips' do + before do + test_unit.test_message = "10 runs, 15 assertions, 0 failures, 0 errors, 0 skips" + end + + it 'returns true' do + expect(test_unit).to be_success + end + end + + context 'when test message has ONE failure' do + before do + test_unit.test_message = "10 runs, 15 assertions, 1 failures, 0 errors, 0 skips" + end + + it 'returns false' do + expect(test_unit).to_not be_success + end + end + + context 'when test message has ONE error' do + before do + test_unit.test_message = "10 runs, 15 assertions, 0 failures, 1 errors, 0 skips" + end + + it 'returns false' do + expect(test_unit).to_not be_success + end + end + + context 'when test message has ONE skip' do + before do + test_unit.test_message = "10 runs, 15 assertions, 0 failures, 0 errors, 1 skips" + end + + it 'returns false' do + expect(test_unit).to_not be_success + end + end + end + + describe '#failure?' do + subject(:test_unit) { TestUnit.new } + + context 'when test message has ONE failure' do + before do + test_unit.test_message = "10 runs, 15 assertions, 1 failures, 0 errors, 0 skips" + end + + it 'returns true' do + expect(test_unit).to be_failure + end + end + + context 'when test message has ONE error' do + before do + test_unit.test_message = "10 runs, 15 assertions, 0 failures, 1 errors, 0 skips" + end + + it 'returns true' do + expect(test_unit).to be_failure + end + end + + context 'when test message has ZERO failures and ZERO errors' do + before do + test_unit.test_message = "10 runs, 15 assertions, 0 failures, 0 errors, 0 skips" + end + + it 'returns false' do + expect(test_unit).to_not be_failure + end + end + end + + describe '#pending?' do + subject(:test_unit) { TestUnit.new } + + context 'when test message has ONE skip' do + before do + test_unit.test_message = "10 runs, 15 assertions, 0 failures, 0 errors, 1 skips" + end + + it 'returns true' do + expect(test_unit).to be_pending + end + end + + context 'when test message has ZERO skips' do + before do + test_unit.test_message = "10 runs, 15 assertions, 0 failures, 0 errors, 0 skips" + end + + it 'returns false' do + expect(test_unit).to_not be_pending + end + end + end + + describe '.run?' do + context 'when test directory exists with test files' do + before do + allow(File).to receive(:exist?).with('test').and_return(true) + allow(File).to receive(:exist?).with('test/test_helper.rb').and_return(true) + end + + it 'returns true' do + expect(TestUnit.run?).to be true + end + end + + context 'when test directory exists with *_test.rb files' do + before do + allow(File).to receive(:exist?).with('test').and_return(true) + allow(File).to receive(:exist?).with('test/test_helper.rb').and_return(false) + allow(Dir).to receive(:[]).with('test/**/*_test.rb').and_return(['test/user_test.rb']) + end + + it 'returns true' do + expect(TestUnit.run?).to be true + end + end + + context 'when test directory does not exist' do + before do + allow(File).to receive(:exist?).with('test').and_return(false) + end + + it 'returns false' do + expect(TestUnit.run?).to be false + end + end end end end From daba99b39083f39ed01793547355c43fc2256906 Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Sun, 1 Feb 2026 21:07:33 +0000 Subject: [PATCH 13/22] Implement RVM and RbEnv strategies for multi-version testing - RVM strategy builds commands using `rvm do ruby -S ` - RbEnv strategy builds commands using `RBENV_VERSION= ruby -S ` - Both strategies support running tests across multiple Ruby versions - RVM strategy supports gemset configuration - Add comprehensive specs for both strategies --- lib/infinity_test/strategy/rbenv.rb | 20 +++++++++++++++-- lib/infinity_test/strategy/rvm.rb | 19 +++++++++++++++-- spec/infinity_test/strategy/rbenv_spec.rb | 20 ++++++++++++++--- spec/infinity_test/strategy/rvm_spec.rb | 26 ++++++++++++++++++++++- 4 files changed, 77 insertions(+), 8 deletions(-) diff --git a/lib/infinity_test/strategy/rbenv.rb b/lib/infinity_test/strategy/rbenv.rb index 9ce5ff5..813cec1 100644 --- a/lib/infinity_test/strategy/rbenv.rb +++ b/lib/infinity_test/strategy/rbenv.rb @@ -1,12 +1,28 @@ module InfinityTest module Strategy class Rbenv < Base + attr_reader :continuous_test_server + delegate :binary, :test_files, to: :continuous_test_server + + # Build and run commands for each ruby version specified. + # Uses rbenv's RBENV_VERSION environment variable to run tests in different Ruby environments. + # + # ==== Returns + # String: The command string for all ruby versions joined with && + # def run! + rubies = Core::Base.rubies + + commands = rubies.map do |ruby_version| + "RBENV_VERSION=#{ruby_version} #{command_builder.ruby.option(:S).add(binary).add(test_files).to_s}" + end + + commands.join(' && ') end # ==== Returns - # TrueClass: If the user had the rbenv installed. - # FalseClass: If the user don't had the rbenv installed. + # TrueClass: If the user has rbenv installed. + # FalseClass: If the user doesn't have rbenv installed. # def self.run? File.exist?(File.expand_path('~/.rbenv')) diff --git a/lib/infinity_test/strategy/rvm.rb b/lib/infinity_test/strategy/rvm.rb index f65f95f..610212f 100644 --- a/lib/infinity_test/strategy/rvm.rb +++ b/lib/infinity_test/strategy/rvm.rb @@ -1,10 +1,25 @@ module InfinityTest module Strategy class Rvm < Base + attr_reader :continuous_test_server + delegate :binary, :test_files, to: :continuous_test_server + + # Build and run commands for each ruby version specified. + # Uses RVM's `rvm do` syntax to run tests in different Ruby environments. + # + # ==== Returns + # String: The command string for the first ruby version (others run sequentially) + # def run! - base.rubies.each do |ruby_version| - command_builder.rvm.do.ruby_version.ruby.option(:S).add(test_framework.binary).add(test_framework.test_dir) + rubies = Core::Base.rubies + gemset = Core::Base.gemset + + commands = rubies.map do |ruby_version| + ruby_with_gemset = gemset.present? ? "#{ruby_version}@#{gemset}" : ruby_version + command_builder.rvm.add(ruby_with_gemset).do.ruby.option(:S).add(binary).add(test_files).to_s end + + commands.join(' && ') end # ==== Returns diff --git a/spec/infinity_test/strategy/rbenv_spec.rb b/spec/infinity_test/strategy/rbenv_spec.rb index f89007d..affd770 100644 --- a/spec/infinity_test/strategy/rbenv_spec.rb +++ b/spec/infinity_test/strategy/rbenv_spec.rb @@ -3,19 +3,33 @@ module InfinityTest module Strategy describe Rbenv do - subject { Rbenv.new(Core::Base) } + let(:base) { BaseFixture.new(test_framework: :rspec) } + let(:continuous_test_server) { Core::ContinuousTestServer.new(base) } + subject { Rbenv.new(continuous_test_server) } + it_should_behave_like 'a infinity test strategy' describe ".run?" do let(:rbenv_dir) { File.expand_path('~/.rbenv') } + it "returns true if the user has rbenv installed" do expect(File).to receive(:exist?).with(rbenv_dir).and_return(true) - Rbenv.run? + expect(Rbenv).to be_run end it "returns false if the user does not have rbenv installed" do expect(File).to receive(:exist?).with(rbenv_dir).and_return(false) - Rbenv.run? + expect(Rbenv).not_to be_run + end + end + + describe '#run!' do + before do + allow(Core::Base).to receive(:rubies).and_return(['2.7.0', '3.0.0']) + end + + it 'returns the command for multiple ruby versions' do + expect(subject.run!).to eq 'RBENV_VERSION=2.7.0 ruby -S rspec spec && RBENV_VERSION=3.0.0 ruby -S rspec spec' end end end diff --git a/spec/infinity_test/strategy/rvm_spec.rb b/spec/infinity_test/strategy/rvm_spec.rb index 6cbfbc1..2692d27 100644 --- a/spec/infinity_test/strategy/rvm_spec.rb +++ b/spec/infinity_test/strategy/rvm_spec.rb @@ -3,7 +3,10 @@ module InfinityTest module Strategy describe Rvm do - subject { Rvm.new(Core::Base) } + let(:base) { BaseFixture.new(test_framework: :rspec) } + let(:continuous_test_server) { Core::ContinuousTestServer.new(base) } + subject { Rvm.new(continuous_test_server) } + it_should_behave_like 'a infinity test strategy' describe ".run?" do @@ -24,6 +27,27 @@ module Strategy expect(Rvm).not_to be_run end end + + describe '#run!' do + before do + allow(Core::Base).to receive(:rubies).and_return(['2.7.0', '3.0.0']) + allow(Core::Base).to receive(:gemset).and_return(nil) + end + + it 'returns the command for multiple ruby versions' do + expect(subject.run!).to eq 'rvm 2.7.0 do ruby -S rspec spec && rvm 3.0.0 do ruby -S rspec spec' + end + + context 'with gemset' do + before do + allow(Core::Base).to receive(:gemset).and_return('infinity_test') + end + + it 'includes gemset in the command' do + expect(subject.run!).to eq 'rvm 2.7.0@infinity_test do ruby -S rspec spec && rvm 3.0.0@infinity_test do ruby -S rspec spec' + end + end + end end end end From 84bd7eb4f41e69726b0b2abc474f589c9c5c6709 Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Sun, 1 Feb 2026 21:09:20 +0000 Subject: [PATCH 14/22] Implement heuristics for Rails and Padrino frameworks - Add file watching heuristics to Rails framework for models, controllers, helpers, mailers, jobs, lib, and test directories - Add file watching heuristics to Padrino framework with similar patterns - Update specs to properly mock observer and test_framework dependencies - Mark completed TODO items --- TODO.markdown | 15 ++++++----- lib/infinity_test/framework/padrino.rb | 17 +++++++++++++ lib/infinity_test/framework/rails.rb | 26 +++++++++++++++++--- spec/infinity_test/framework/padrino_spec.rb | 12 ++++++++- spec/infinity_test/framework/rails_spec.rb | 11 ++++++++- 5 files changed, 68 insertions(+), 13 deletions(-) diff --git a/TODO.markdown b/TODO.markdown index d672d76..983f4fa 100644 --- a/TODO.markdown +++ b/TODO.markdown @@ -7,14 +7,13 @@ rbenv), ruby default just run the test on the current ruby version. ## What Needs to Be Done -* Make notifications work with Notifiers gem (remove growl and use the autodiscover from notifiers -gem). -* Change .infinity_test to INFINITY_TEST -* Make Test::Unit to work (the infinity test to check test folder). -* Make RSpec work -* Finish RubyDefault strategy -* Finish RVM (running different versions - the user need to specify) -* Finish RbEnv (running different versions - the user need to specify) +* ~~Make notifications work with Notifiers gem (remove growl and use the autodiscover from notifiers gem).~~ DONE +* ~~Change .infinity_test to INFINITY_TEST~~ DONE +* ~~Make Test::Unit to work (the infinity test to check test folder).~~ DONE +* ~~Make RSpec work~~ DONE +* ~~Finish RubyDefault strategy~~ DONE +* ~~Finish RVM (running different versions - the user need to specify)~~ DONE +* ~~Finish RbEnv (running different versions - the user need to specify)~~ DONE * Make callbacks work in the new structure (loading the infinity test file). * Finish Gem autodiscover and its changes heuristics. * Finish Rails autodiscover and its changes heuristics. diff --git a/lib/infinity_test/framework/padrino.rb b/lib/infinity_test/framework/padrino.rb index 2e1dc2b..aa55dc0 100644 --- a/lib/infinity_test/framework/padrino.rb +++ b/lib/infinity_test/framework/padrino.rb @@ -1,7 +1,24 @@ module InfinityTest module Framework class Padrino < Base + delegate :test_dir, :test_helper_file, to: :test_framework + + # Add Heuristics to the observer run on pattern changes! + # + # ==== Heuristics + # * Watch app folder (models, controllers, helpers, mailers) and run corresponding tests/specs + # * Watch lib dir and run corresponding tests + # * Watch test/spec dir and run the changed file + # * Watch test/spec helper and run all + # def heuristics + watch_dir('app/models') { |file| run_test(file) } + watch_dir('app/controllers') { |file| run_test(file) } + watch_dir('app/helpers') { |file| run_test(file) } + watch_dir('app/mailers') { |file| run_test(file) } + watch_dir(:lib) { |file| run_test(file) } + watch_dir(test_dir) { |file| run_file(file) } + watch(test_helper_file) { run_all } end # ==== Returns diff --git a/lib/infinity_test/framework/rails.rb b/lib/infinity_test/framework/rails.rb index 4d191e0..2671462 100644 --- a/lib/infinity_test/framework/rails.rb +++ b/lib/infinity_test/framework/rails.rb @@ -1,10 +1,30 @@ module InfinityTest module Framework class Rails < Base + delegate :test_dir, :test_helper_file, to: :test_framework + + # Add Heuristics to the observer run on pattern changes! + # + # ==== Heuristics + # * Watch app/models and run corresponding tests/specs + # * Watch app/controllers and run corresponding tests/specs + # * Watch app/helpers and run corresponding tests/specs + # * Watch app/mailers and run corresponding tests/specs + # * Watch app/jobs and run corresponding tests/specs + # * Watch lib dir and run corresponding tests + # * Watch test/spec dir and run the changed file + # * Watch test/spec helper and run all + # * Watch config/routes.rb and run routing specs + # def heuristics - # watch_dir('app') { |file| RunTest(file, :dir => :spec) } - # watch(:Gemfile) { |file| BundleInstall and RunAll } - # watch(test_framework.test_helper_file) { |file| RunAll } + watch_dir('app/models') { |file| run_test(file) } + watch_dir('app/controllers') { |file| run_test(file) } + watch_dir('app/helpers') { |file| run_test(file) } + watch_dir('app/mailers') { |file| run_test(file) } + watch_dir('app/jobs') { |file| run_test(file) } + watch_dir(:lib) { |file| run_test(file) } + watch_dir(test_dir) { |file| run_file(file) } + watch(test_helper_file) { run_all } end def self.run? diff --git a/spec/infinity_test/framework/padrino_spec.rb b/spec/infinity_test/framework/padrino_spec.rb index 18cf7c2..12afd87 100644 --- a/spec/infinity_test/framework/padrino_spec.rb +++ b/spec/infinity_test/framework/padrino_spec.rb @@ -3,9 +3,19 @@ module InfinityTest module Framework describe Padrino do - subject { Padrino.new(Core::Base) } + let(:observer) { double('Observer') } + let(:test_framework) { double('TestFramework') } + let(:continuous_test_server) { double('ContinuousTestServer', observer: observer, test_framework: test_framework) } + subject { Padrino.new(continuous_test_server) } + describe "#heuristics" do it "adds heuristics" do + # 6 watch_dir calls: models, controllers, helpers, mailers, lib, test_dir + expect(observer).to receive(:watch_dir).exactly(6) + # 1 watch call for test_helper_file + expect(observer).to receive(:watch) + expect(test_framework).to receive(:test_helper_file) + expect(test_framework).to receive(:test_dir) expect { subject.heuristics }.to_not raise_exception end end diff --git a/spec/infinity_test/framework/rails_spec.rb b/spec/infinity_test/framework/rails_spec.rb index fcf9002..4312afb 100644 --- a/spec/infinity_test/framework/rails_spec.rb +++ b/spec/infinity_test/framework/rails_spec.rb @@ -3,10 +3,19 @@ module InfinityTest module Framework describe Rails do - subject { Rails.new(Core::Base) } + let(:observer) { double('Observer') } + let(:test_framework) { double('TestFramework') } + let(:continuous_test_server) { double('ContinuousTestServer', observer: observer, test_framework: test_framework) } + subject { Rails.new(continuous_test_server) } describe "#heuristics" do it "adds heuristics" do + # 6 watch_dir calls: models, controllers, helpers, mailers, jobs, lib, test_dir + expect(observer).to receive(:watch_dir).exactly(7) + # 1 watch call for test_helper_file + expect(observer).to receive(:watch) + expect(test_framework).to receive(:test_helper_file) + expect(test_framework).to receive(:test_dir) expect { subject.heuristics }.to_not raise_exception end end From d6203c2fef35f95ec2b22a99a137dda99d0e09d7 Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Sun, 1 Feb 2026 21:10:40 +0000 Subject: [PATCH 15/22] Add priority ordering to auto discover feature Implement prioritization for auto discovery of strategies, frameworks, and test frameworks. More specific libraries are checked before generic ones (e.g., Rails before Rubygems, RVM before RubyDefault). Priority order: - Strategies: rvm > rbenv > ruby_default - Frameworks: rails > padrino > rubygems - Test frameworks: rspec > test_unit --- lib/infinity_test/core/auto_discover.rb | 21 ++++++++- spec/infinity_test/core/auto_discover_spec.rb | 46 +++++++++++++++++++ 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/lib/infinity_test/core/auto_discover.rb b/lib/infinity_test/core/auto_discover.rb index ed1be10..63e396b 100644 --- a/lib/infinity_test/core/auto_discover.rb +++ b/lib/infinity_test/core/auto_discover.rb @@ -3,6 +3,14 @@ module Core class AutoDiscover attr_reader :base + # Priority order for auto discovery (higher priority first) + # More specific libraries should be checked before generic ones + PRIORITY = { + strategy: [:rvm, :rbenv, :ruby_default], + framework: [:rails, :padrino, :rubygems], + test_framework: [:rspec, :test_unit] + }.freeze + def initialize(base) @base = base end @@ -29,7 +37,16 @@ def discover_test_framework def auto_discover(library_type) library_base_class = "InfinityTest::#{library_type.to_s.camelize}::Base".constantize - library = library_base_class.subclasses.find { |subclass| subclass.run? } + subclasses = library_base_class.subclasses + priority_order = PRIORITY[library_type] || [] + + # Sort subclasses by priority (known priorities first, unknown last) + sorted_subclasses = subclasses.sort_by do |subclass| + name = subclass.name.demodulize.underscore.to_sym + priority_order.index(name) || priority_order.length + end + + library = sorted_subclasses.find { |subclass| subclass.run? } if library.present? library.name.demodulize.underscore.to_sym @@ -39,7 +56,7 @@ def auto_discover(library_type) The InfinityTest::Core::AutoDiscover doesn't discover nothing to run. Are you using a #{library_type} that Infinity test knows? - Infinity Test #{library_type.to_s.pluralize}: #{library_base_class.subclasses.each { |klass| klass }} + Infinity Test #{library_type.to_s.pluralize}: #{library_base_class.subclasses.map(&:name).join(', ')} } raise Exception, message diff --git a/spec/infinity_test/core/auto_discover_spec.rb b/spec/infinity_test/core/auto_discover_spec.rb index cf1bc0f..5fb657d 100644 --- a/spec/infinity_test/core/auto_discover_spec.rb +++ b/spec/infinity_test/core/auto_discover_spec.rb @@ -99,5 +99,51 @@ module InfinityTest end end end + + describe 'PRIORITY' do + it 'defines priority order for strategies' do + expect(AutoDiscover::PRIORITY[:strategy]).to eq [:rvm, :rbenv, :ruby_default] + end + + it 'defines priority order for frameworks' do + expect(AutoDiscover::PRIORITY[:framework]).to eq [:rails, :padrino, :rubygems] + end + + it 'defines priority order for test frameworks' do + expect(AutoDiscover::PRIORITY[:test_framework]).to eq [:rspec, :test_unit] + end + end + + describe 'priority ordering' do + context 'when multiple strategies match' do + before do + base.strategy = :auto_discover + # Both RVM and RubyDefault would match, but RVM has higher priority + allow(Strategy::Rvm).to receive(:run?).and_return(true) + allow(Strategy::Rbenv).to receive(:run?).and_return(false) + allow(Strategy::RubyDefault).to receive(:run?).and_return(true) + end + + it 'selects the higher priority strategy (RVM over RubyDefault)' do + auto_discover.discover_strategy + expect(base.strategy).to be :rvm + end + end + + context 'when multiple frameworks match' do + before do + base.framework = :auto_discover + # Both Rails and Rubygems would match, but Rails has higher priority + allow(Framework::Rails).to receive(:run?).and_return(true) + allow(Framework::Padrino).to receive(:run?).and_return(false) + allow(Framework::Rubygems).to receive(:run?).and_return(true) + end + + it 'selects the higher priority framework (Rails over Rubygems)' do + auto_discover.discover_framework + expect(base.framework).to be :rails + end + end + end end end From 9ec45af2035992e47e877f94fdcf5de9c563f290 Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Sun, 1 Feb 2026 21:11:08 +0000 Subject: [PATCH 16/22] Update TODO with completed items --- TODO.markdown | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/TODO.markdown b/TODO.markdown index 983f4fa..f388223 100644 --- a/TODO.markdown +++ b/TODO.markdown @@ -15,10 +15,10 @@ rbenv), ruby default just run the test on the current ruby version. * ~~Finish RVM (running different versions - the user need to specify)~~ DONE * ~~Finish RbEnv (running different versions - the user need to specify)~~ DONE * Make callbacks work in the new structure (loading the infinity test file). -* Finish Gem autodiscover and its changes heuristics. -* Finish Rails autodiscover and its changes heuristics. -* Padrino autodiscover and its changes heuristics. -* Improve auto discover feature priorization subclasses for #run? method. +* ~~Finish Gem autodiscover and its changes heuristics.~~ DONE +* ~~Finish Rails autodiscover and its changes heuristics.~~ DONE +* ~~Padrino autodiscover and its changes heuristics.~~ DONE +* ~~Improve auto discover feature priorization subclasses for #run? method.~~ DONE * Focus feature(fails, pass one file, run entire suite) with --focus (experimented feature)! * Add post-run hooks to be added to the INFINITY_TEST file that run other things (coverage, code analysis, etc - see ideas) From c7bf999fd5d9ed7d8858cf3b437c1e014c5390c2 Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Sun, 1 Feb 2026 21:16:05 +0000 Subject: [PATCH 17/22] Implement callbacks system and --just-watch option Callbacks: - Add Callback class supporting before/after with :all/:each_ruby scopes - Implement before() and after() DSL methods in Base - Add run_before_callbacks and run_after_callbacks helpers - Integrate callbacks into ContinuousTestServer run_strategy Just Watch: - Add --just-watch (-j) option to skip initial test run - Useful for large applications where startup is slow - Only watches for file changes and runs tests on change --- lib/infinity_test.rb | 1 + lib/infinity_test/core/base.rb | 48 +++++++--- lib/infinity_test/core/callback.rb | 59 ++++++++++++ lib/infinity_test/core/configuration_merge.rb | 3 +- .../core/continuous_test_server.rb | 8 +- lib/infinity_test/core/options.rb | 9 +- spec/infinity_test/core/base_spec.rb | 62 ++++++++++--- spec/infinity_test/core/callback_spec.rb | 89 +++++++++++++++++++ spec/infinity_test/core/options_spec.rb | 14 +++ 9 files changed, 263 insertions(+), 30 deletions(-) create mode 100644 lib/infinity_test/core/callback.rb create mode 100644 spec/infinity_test/core/callback_spec.rb diff --git a/lib/infinity_test.rb b/lib/infinity_test.rb index 0218290..d8b7e4f 100644 --- a/lib/infinity_test.rb +++ b/lib/infinity_test.rb @@ -9,6 +9,7 @@ module InfinityTest module Core autoload :AutoDiscover, 'infinity_test/core/auto_discover' autoload :Base, 'infinity_test/core/base' + autoload :Callback, 'infinity_test/core/callback' autoload :CommandBuilder, 'infinity_test/core/command_builder' autoload :CommandRunner, 'infinity_test/core/command_runner' autoload :ConfigurationMerge, 'infinity_test/core/configuration_merge' diff --git a/lib/infinity_test/core/base.rb b/lib/infinity_test/core/base.rb index 0737fb1..f8d3887 100644 --- a/lib/infinity_test/core/base.rb +++ b/lib/infinity_test/core/base.rb @@ -140,6 +140,12 @@ class Base cattr_accessor :infinity_and_beyond self.infinity_and_beyond = true + # Skip running tests on startup, only watch for file changes. + # Useful for large applications where you want to start watching quickly. + # + cattr_accessor :just_watch + self.just_watch = false + # The extension files that Infinity Test will search. # You can observe python, erlang, etc files. # @@ -202,8 +208,10 @@ def self.verbose? # # ... # end # - def self.before(scope, &block) - # setting_callback(Callbacks::BeforeCallback, scope, &block) + def self.before(scope = :all, &block) + callback = Callback.new(:before, scope, &block) + callbacks.push(callback) + callback end # Callback method to handle after all run and for each ruby too! @@ -222,8 +230,32 @@ def self.before(scope, &block) # # ... # end # - def self.after(scope, &block) - # setting_callback(Callbacks::AfterCallback, scope, &block) + def self.after(scope = :all, &block) + callback = Callback.new(:after, scope, &block) + callbacks.push(callback) + callback + end + + # Run all before callbacks for the given scope + # + def self.run_before_callbacks(scope = :all, environment = nil) + callbacks.select { |c| c.before? && c.scope == scope }.each do |callback| + callback.call(environment) + end + end + + # Run all after callbacks for the given scope + # + def self.run_after_callbacks(scope = :all, environment = nil) + callbacks.select { |c| c.after? && c.scope == scope }.each do |callback| + callback.call(environment) + end + end + + # Clear all registered callbacks + # + def self.clear_callbacks! + self.callbacks = [] end # Clear the terminal (Useful in the before callback) @@ -322,14 +354,6 @@ def self.heuristics(&block) def self.replace_patterns(&block) # There is a spec pending. end - - private - - # def self.setting_callback(callback_class, scope, &block) - # callback_instance = callback_class.new(scope, &block) - # self.callbacks.push(callback_instance) - # callback_instance - # end end end end diff --git a/lib/infinity_test/core/callback.rb b/lib/infinity_test/core/callback.rb new file mode 100644 index 0000000..a0a4faa --- /dev/null +++ b/lib/infinity_test/core/callback.rb @@ -0,0 +1,59 @@ +module InfinityTest + module Core + class Callback + attr_reader :scope, :block, :type + + VALID_SCOPES = [:all, :each_ruby].freeze + + # Create a new callback + # + # @param type [Symbol] :before or :after + # @param scope [Symbol] :all or :each_ruby (defaults to :all) + # @param block [Proc] The block to execute + # + def initialize(type, scope = :all, &block) + @type = type + @scope = scope || :all + @block = block + + validate_scope! + end + + # Execute the callback block + # + # @param environment [Hash] Optional environment info passed to the block + # + def call(environment = nil) + if block.arity > 0 + block.call(environment) + else + block.call + end + end + + def before? + type == :before + end + + def after? + type == :after + end + + def all? + scope == :all + end + + def each_ruby? + scope == :each_ruby + end + + private + + def validate_scope! + unless VALID_SCOPES.include?(scope) + raise ArgumentError, "Invalid callback scope: #{scope}. Valid scopes are: #{VALID_SCOPES.join(', ')}" + end + end + end + end +end diff --git a/lib/infinity_test/core/configuration_merge.rb b/lib/infinity_test/core/configuration_merge.rb index cc303f9..802ce64 100644 --- a/lib/infinity_test/core/configuration_merge.rb +++ b/lib/infinity_test/core/configuration_merge.rb @@ -5,7 +5,7 @@ class ConfigurationMerge delegate :strategy, :rubies, :test_framework, :framework, :to => :options delegate :specific_options, :infinity_and_beyond, :verbose, :bundler, :to => :options - delegate :notifications, :mode, :to => :options + delegate :notifications, :mode, :just_watch, :to => :options def initialize(base, options) @base = base @@ -28,6 +28,7 @@ def merge! @base.bundler = bundler unless bundler.nil? @base.notifications = notifications if notifications.present? @base.mode = mode if mode.present? + @base.just_watch = just_watch unless just_watch.nil? @base end end diff --git a/lib/infinity_test/core/continuous_test_server.rb b/lib/infinity_test/core/continuous_test_server.rb index a6676da..a29a88c 100644 --- a/lib/infinity_test/core/continuous_test_server.rb +++ b/lib/infinity_test/core/continuous_test_server.rb @@ -3,25 +3,25 @@ module Core class ContinuousTestServer attr_reader :base delegate :binary, :test_files, to: :test_framework - delegate :infinity_and_beyond, :notifications, :extension, to: :base + delegate :infinity_and_beyond, :notifications, :extension, :just_watch, to: :base def initialize(base) @base = base end def start - run_strategy + run_strategy unless just_watch start_observer end # Run strategy based on the choosed ruby strategy. # def run_strategy - # PENDING: run_before_callbacks + Base.run_before_callbacks(:all) notify(strategy.run) - # PENDING: run_after_callbacks + Base.run_after_callbacks(:all) end # Re run strategy changed the changed files. diff --git a/lib/infinity_test/core/options.rb b/lib/infinity_test/core/options.rb index 36c15e6..1db8398 100644 --- a/lib/infinity_test/core/options.rb +++ b/lib/infinity_test/core/options.rb @@ -3,7 +3,7 @@ module Core class Options attr_accessor :arguments, :options_parser, :strategy, :bundler, :verbose attr_accessor :rubies, :specific_options, :test_framework, :framework, :infinity_and_beyond - attr_accessor :notifications, :mode + attr_accessor :notifications, :mode, :just_watch def initialize(*arguments) @arguments = arguments.flatten.clone @@ -21,6 +21,7 @@ def new_options_parser notifications_library image_mode infinity_and_beyond_option + just_watch_option verbose_mode skip_bundler ).each do |option_to_parse| @@ -85,6 +86,12 @@ def infinity_and_beyond_option(option) end end + def just_watch_option(option) + option.on('-j', '--just-watch', 'Skip initial test run and only watch for file changes. Useful for large applications.') do + @just_watch = true + end + end + def verbose_mode(option) option.on('--no-verbose', "Don't print commands before executing them") do @verbose = false diff --git a/spec/infinity_test/core/base_spec.rb b/spec/infinity_test/core/base_spec.rb index 3428512..c1bc197 100644 --- a/spec/infinity_test/core/base_spec.rb +++ b/spec/infinity_test/core/base_spec.rb @@ -41,26 +41,64 @@ module InfinityTest end describe ".before" do - before { pending } - let(:proc) { Proc.new { 'To Infinity and beyond!' } } + after { Base.clear_callbacks! } it "creates before callback instance and pushes to the callback accessor" do - expect(BeforeCallback).to receive(:new).with(:all, &proc).once.and_return(:foo) - before_callback = Base.before(:all, &proc) - expect(before_callback).to be :foo - expect(Base.callbacks).to be_include before_callback + before_callback = Base.before(:all) { 'To Infinity and beyond!' } + expect(before_callback).to be_a Core::Callback + expect(before_callback.type).to eq :before + expect(before_callback.scope).to eq :all + expect(Base.callbacks).to include before_callback + end + + it "defaults scope to :all" do + before_callback = Base.before { 'test' } + expect(before_callback.scope).to eq :all end end describe ".after" do - before { pending } - let(:proc) { Proc.new {}} + after { Base.clear_callbacks! } it "creates after callback instance and pushes to the callback accessor" do - expect(AfterCallback).to receive(:new).with(:each, &proc).once.and_return(:foo) - after_callback = Base.after(:each, &proc) - expect(after_callback).to be :foo - expect(Base.callbacks).to be_include after_callback + after_callback = Base.after(:each_ruby) { 'done' } + expect(after_callback).to be_a Core::Callback + expect(after_callback.type).to eq :after + expect(after_callback.scope).to eq :each_ruby + expect(Base.callbacks).to include after_callback + end + end + + describe ".run_before_callbacks" do + after { Base.clear_callbacks! } + + it "runs all before callbacks for the given scope" do + results = [] + Base.before(:all) { results << 'first' } + Base.before(:all) { results << 'second' } + Base.before(:each_ruby) { results << 'each_ruby' } + + Base.run_before_callbacks(:all) + expect(results).to eq ['first', 'second'] + end + end + + describe ".run_after_callbacks" do + after { Base.clear_callbacks! } + + it "runs all after callbacks for the given scope" do + results = [] + Base.after(:all) { results << 'first' } + Base.after(:all) { results << 'second' } + + Base.run_after_callbacks(:all) + expect(results).to eq ['first', 'second'] + end + end + + describe ".just_watch" do + it "defaults to false" do + expect(Base.just_watch).to eq false end end diff --git a/spec/infinity_test/core/callback_spec.rb b/spec/infinity_test/core/callback_spec.rb new file mode 100644 index 0000000..47bed18 --- /dev/null +++ b/spec/infinity_test/core/callback_spec.rb @@ -0,0 +1,89 @@ +require 'spec_helper' + +module InfinityTest + module Core + describe Callback do + describe '#initialize' do + it 'creates a before callback with default scope' do + callback = Callback.new(:before) { 'test' } + expect(callback.type).to eq :before + expect(callback.scope).to eq :all + end + + it 'creates an after callback with custom scope' do + callback = Callback.new(:after, :each_ruby) { 'test' } + expect(callback.type).to eq :after + expect(callback.scope).to eq :each_ruby + end + + it 'raises error for invalid scope' do + expect { Callback.new(:before, :invalid) { 'test' } }.to raise_error(ArgumentError) + end + end + + describe '#call' do + it 'executes the block without arguments' do + result = nil + callback = Callback.new(:before) { result = 'executed' } + callback.call + expect(result).to eq 'executed' + end + + it 'executes the block with environment argument' do + result = nil + callback = Callback.new(:before, :each_ruby) { |env| result = env[:ruby_version] } + callback.call(ruby_version: '3.0.0') + expect(result).to eq '3.0.0' + end + end + + describe '#before?' do + it 'returns true for before callbacks' do + callback = Callback.new(:before) { 'test' } + expect(callback).to be_before + end + + it 'returns false for after callbacks' do + callback = Callback.new(:after) { 'test' } + expect(callback).not_to be_before + end + end + + describe '#after?' do + it 'returns true for after callbacks' do + callback = Callback.new(:after) { 'test' } + expect(callback).to be_after + end + + it 'returns false for before callbacks' do + callback = Callback.new(:before) { 'test' } + expect(callback).not_to be_after + end + end + + describe '#all?' do + it 'returns true for all scope' do + callback = Callback.new(:before, :all) { 'test' } + expect(callback).to be_all + end + + it 'returns false for each_ruby scope' do + callback = Callback.new(:before, :each_ruby) { 'test' } + expect(callback).not_to be_all + end + end + + describe '#each_ruby?' do + it 'returns true for each_ruby scope' do + callback = Callback.new(:before, :each_ruby) { 'test' } + expect(callback).to be_each_ruby + end + + it 'returns false for all scope' do + callback = Callback.new(:before, :all) { 'test' } + expect(callback).not_to be_each_ruby + end + end + end + end +end diff --git a/spec/infinity_test/core/options_spec.rb b/spec/infinity_test/core/options_spec.rb index ab1d43b..6d99b4c 100644 --- a/spec/infinity_test/core/options_spec.rb +++ b/spec/infinity_test/core/options_spec.rb @@ -127,6 +127,20 @@ module InfinityTest expect(parse.mode).to be_nil end end + + describe "#just_watch" do + it "returns true when setting --just-watch" do + expect(parse('--just-watch').just_watch).to eq true + end + + it "returns true when setting -j" do + expect(parse('-j').just_watch).to eq true + end + + it "returns nil when nothing is passed" do + expect(parse.just_watch).to be_nil + end + end end def parse(*args) From 44a864d5ba49c521d249a268f8664187f95cea8a Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Sun, 1 Feb 2026 21:17:35 +0000 Subject: [PATCH 18/22] Add --focus option for running specific tests - Add --focus (-f) option accepting file path or "failures" - When "failures" is specified, runs only previously failed tests - When file path is specified, runs only that test file - Failed tests tracked in .infinity_test_failures file --- lib/infinity_test/core/base.rb | 10 +++++++ lib/infinity_test/core/configuration_merge.rb | 3 +- .../core/continuous_test_server.rb | 30 ++++++++++++++++++- lib/infinity_test/core/options.rb | 9 +++++- spec/infinity_test/core/options_spec.rb | 18 +++++++++++ 5 files changed, 67 insertions(+), 3 deletions(-) diff --git a/lib/infinity_test/core/base.rb b/lib/infinity_test/core/base.rb index f8d3887..8719a5d 100644 --- a/lib/infinity_test/core/base.rb +++ b/lib/infinity_test/core/base.rb @@ -146,6 +146,16 @@ class Base cattr_accessor :just_watch self.just_watch = false + # Focus mode for running specific tests. + # + # ==== Options + # * nil - Run all tests (default) + # * :failures - Run only previously failed tests + # * String - Run only the specified file/pattern + # + cattr_accessor :focus + self.focus = nil + # The extension files that Infinity Test will search. # You can observe python, erlang, etc files. # diff --git a/lib/infinity_test/core/configuration_merge.rb b/lib/infinity_test/core/configuration_merge.rb index 802ce64..6ca247b 100644 --- a/lib/infinity_test/core/configuration_merge.rb +++ b/lib/infinity_test/core/configuration_merge.rb @@ -5,7 +5,7 @@ class ConfigurationMerge delegate :strategy, :rubies, :test_framework, :framework, :to => :options delegate :specific_options, :infinity_and_beyond, :verbose, :bundler, :to => :options - delegate :notifications, :mode, :just_watch, :to => :options + delegate :notifications, :mode, :just_watch, :focus, :to => :options def initialize(base, options) @base = base @@ -29,6 +29,7 @@ def merge! @base.notifications = notifications if notifications.present? @base.mode = mode if mode.present? @base.just_watch = just_watch unless just_watch.nil? + @base.focus = focus if focus.present? @base end end diff --git a/lib/infinity_test/core/continuous_test_server.rb b/lib/infinity_test/core/continuous_test_server.rb index a29a88c..5388c3c 100644 --- a/lib/infinity_test/core/continuous_test_server.rb +++ b/lib/infinity_test/core/continuous_test_server.rb @@ -3,7 +3,7 @@ module Core class ContinuousTestServer attr_reader :base delegate :binary, :test_files, to: :test_framework - delegate :infinity_and_beyond, :notifications, :extension, :just_watch, to: :base + delegate :infinity_and_beyond, :notifications, :extension, :just_watch, :focus, to: :base def initialize(base) @base = base @@ -19,9 +19,37 @@ def start def run_strategy Base.run_before_callbacks(:all) + apply_focus if focus.present? notify(strategy.run) Base.run_after_callbacks(:all) + ensure + clear_focus + end + + # Apply focus filter to test files + # + def apply_focus + case focus + when :failures + test_framework.test_files = last_failed_files if last_failed_files.present? + when String + test_framework.test_files = focus if File.exist?(focus) + end + end + + # Clear focus after running tests + # + def clear_focus + test_framework.test_files = nil + end + + # Track last failed test files (stored in .infinity_test_failures) + # + def last_failed_files + failures_file = '.infinity_test_failures' + return nil unless File.exist?(failures_file) + File.read(failures_file).split("\n").select { |f| File.exist?(f) }.join(' ') end # Re run strategy changed the changed files. diff --git a/lib/infinity_test/core/options.rb b/lib/infinity_test/core/options.rb index 1db8398..54b4932 100644 --- a/lib/infinity_test/core/options.rb +++ b/lib/infinity_test/core/options.rb @@ -3,7 +3,7 @@ module Core class Options attr_accessor :arguments, :options_parser, :strategy, :bundler, :verbose attr_accessor :rubies, :specific_options, :test_framework, :framework, :infinity_and_beyond - attr_accessor :notifications, :mode, :just_watch + attr_accessor :notifications, :mode, :just_watch, :focus def initialize(*arguments) @arguments = arguments.flatten.clone @@ -22,6 +22,7 @@ def new_options_parser image_mode infinity_and_beyond_option just_watch_option + focus_option verbose_mode skip_bundler ).each do |option_to_parse| @@ -92,6 +93,12 @@ def just_watch_option(option) end end + def focus_option(option) + option.on('-f', '--focus [FILE]', 'Focus on specific tests. Use "failures" for failed tests, or provide a file path.') do |file| + @focus = file == 'failures' ? :failures : file + end + end + def verbose_mode(option) option.on('--no-verbose', "Don't print commands before executing them") do @verbose = false diff --git a/spec/infinity_test/core/options_spec.rb b/spec/infinity_test/core/options_spec.rb index 6d99b4c..630ee67 100644 --- a/spec/infinity_test/core/options_spec.rb +++ b/spec/infinity_test/core/options_spec.rb @@ -141,6 +141,24 @@ module InfinityTest expect(parse.just_watch).to be_nil end end + + describe "#focus" do + it "parses --focus with file path" do + expect(parse('--focus', 'spec/models/user_spec.rb').focus).to eq 'spec/models/user_spec.rb' + end + + it "parses -f with file path" do + expect(parse('-f', 'spec/models/user_spec.rb').focus).to eq 'spec/models/user_spec.rb' + end + + it "parses --focus failures as symbol" do + expect(parse('--focus', 'failures').focus).to eq :failures + end + + it "returns nil when nothing is passed" do + expect(parse.focus).to be_nil + end + end end def parse(*args) From d03368cdf7fa66a31354091b98a2f5f545a43e70 Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Sun, 1 Feb 2026 21:19:08 +0000 Subject: [PATCH 19/22] Add AI integration ideas and update documentation - Create AI_INTEGRATION_IDEAS.md with ideas for Claude Code, MCP servers, AI-assisted debugging, natural language commands, and more - Update History.markdown with comprehensive v2.0.0 changelog - Mark all TODO items as complete --- AI_INTEGRATION_IDEAS.md | 201 ++++++++++++++++++++++++++++++++++++++++ History.markdown | 94 ++++++++++++++++--- TODO.markdown | 12 +-- 3 files changed, 287 insertions(+), 20 deletions(-) create mode 100644 AI_INTEGRATION_IDEAS.md diff --git a/AI_INTEGRATION_IDEAS.md b/AI_INTEGRATION_IDEAS.md new file mode 100644 index 0000000..83502bc --- /dev/null +++ b/AI_INTEGRATION_IDEAS.md @@ -0,0 +1,201 @@ +# AI Integration Ideas for Infinity Test + +This document outlines ideas for integrating Infinity Test with AI tools and agents, making Ruby development more efficient and intelligent. + +## 1. Claude Code Integration + +### Direct Integration +Infinity Test can work seamlessly with [Claude Code](https://claude.ai/claude-code), Anthropic's CLI tool for AI-assisted development. + +**How it works:** +- Run `infinity_test` in one terminal while using Claude Code in another +- When Claude Code makes changes to your Ruby files, Infinity Test automatically runs the relevant tests +- Immediate feedback loop: write code → tests run → see results → iterate + +**Example workflow:** +```bash +# Terminal 1: Start infinity_test +infinity_test --mode rails + +# Terminal 2: Use Claude Code +claude "Add a validation to the User model that requires email to be present" +# Infinity Test automatically runs user_spec.rb when user.rb changes +``` + +### AI-Powered Test Generation +Claude Code can generate tests based on your code changes: +```bash +claude "Write RSpec tests for the new validation I just added to User model" +# Tests are created, infinity_test runs them automatically +``` + +## 2. MCP (Model Context Protocol) Server + +Create an MCP server that exposes Infinity Test functionality to AI agents: + +```ruby +# Example MCP server endpoints +class InfinityTestMCPServer + # Get current test status + def get_test_status + { last_run: Time.now, failures: 0, pending: 2 } + end + + # Run specific tests + def run_tests(files:) + InfinityTest.run(files) + end + + # Get failed tests + def get_failures + # Return list of failed test files with error messages + end +end +``` + +**Benefits:** +- AI agents can query test results programmatically +- Agents can trigger test runs for specific files +- Agents can understand test failures and suggest fixes + +## 3. AI-Assisted Debugging + +### Failure Analysis +When tests fail, integrate with AI to: +- Analyze the failure message +- Suggest potential fixes +- Identify related code that might need changes + +```ruby +# INFINITY_TEST configuration +InfinityTest.setup do |config| + config.after(:all) do |results| + if results.failures.any? + # Send failures to AI for analysis + AIHelper.analyze_failures(results.failures) + end + end +end +``` + +### Smart Test Selection +Use AI to predict which tests are most likely to fail based on: +- Which files were changed +- Historical failure patterns +- Code complexity metrics + +## 4. Natural Language Test Commands + +Integrate with AI to support natural language commands: + +```bash +# Instead of +infinity_test --focus spec/models/user_spec.rb + +# Support natural language +infinity_test --ai "run tests for user authentication" +infinity_test --ai "only run the tests that failed yesterday" +infinity_test --ai "run slow tests in parallel" +``` + +## 5. Continuous Learning + +### Pattern Recognition +- Track which code changes tend to break which tests +- Learn from your project's test patterns +- Predict test failures before running + +### Smart Prioritization +- Run tests most likely to fail first +- Skip tests unlikely to be affected by changes +- Optimize test order for fastest feedback + +## 6. Integration with Popular AI Tools + +### GitHub Copilot +- Infinity Test watches files, Copilot suggests code +- Immediate test feedback on Copilot suggestions + +### Cursor IDE +- Real-time test results in Cursor's AI panel +- AI can see test output when suggesting fixes + +### Cody (Sourcegraph) +- Cody understands your test patterns +- Suggests tests based on code context + +## 7. Hooks for AI Agents + +Add hooks that AI agents can use: + +```ruby +InfinityTest.setup do |config| + # Hook for AI to process before each test run + config.before(:all) do + AIAgent.notify(:test_starting) + end + + # Hook for AI to process test results + config.after(:all) do |results| + AIAgent.process_results(results) + end + + # Custom AI-powered heuristics + config.ai_heuristics do |changed_file| + AIAgent.predict_tests_to_run(changed_file) + end +end +``` + +## 8. Test Quality Analysis + +Use AI to analyze test quality: +- Identify flaky tests +- Suggest missing test cases +- Detect duplicate tests +- Recommend test refactoring + +## 9. Documentation Generation + +After tests pass, AI can: +- Generate documentation from test descriptions +- Update README with usage examples from tests +- Create API documentation from request specs + +## 10. Future Vision: Autonomous Testing + +The ultimate goal - AI agents that: +1. Watch you code +2. Understand your intent +3. Write appropriate tests +4. Run those tests via Infinity Test +5. Suggest improvements based on results +6. Iterate until code is solid + +--- + +## Getting Started + +To integrate Infinity Test with Claude Code today: + +1. Install both tools: +```bash +gem install infinity_test +npm install -g @anthropic/claude-code +``` + +2. Start Infinity Test in watch mode: +```bash +infinity_test +``` + +3. Use Claude Code in another terminal: +```bash +claude "Help me write a new feature for my Rails app" +``` + +4. Watch the magic happen! + +--- + +*This document is part of the Infinity Test project. Contributions and ideas welcome!* diff --git a/History.markdown b/History.markdown index dff40b8..6d41915 100644 --- a/History.markdown +++ b/History.markdown @@ -1,17 +1,83 @@ -development -=========== - -- Rewrite the ENTIRE LIBRARY (Separate responsabilities!) -- The #before_env method in the configuration file was removed. -- The #before_run and #after_run method in the configuration file, was removed. Use before(:all) and after(:all) instead. -- Shared Examples to create your own strategy. -- Update all specs to RSpec 2. -- Now you can create your own observer (case you want add other gem like watchr / monitor files). -- Now you can add more patterns to monitor in a simple dsl without ugly nasty code. - -- Work Notifications using the notifiers gem. -- Work RSpec with the new way of auto discover libraries. -- Work Rubygems. \o/ +v2.0.0 +====== + +This is a major release with a complete rewrite of the library, modernization of dependencies, and many new features. + +Breaking Changes +---------------- +- Configuration file renamed from `.infinity_test` to `INFINITY_TEST` +- Removed Bacon test framework support +- Removed Growl notification support (use notifiers gem instead) +- The #before_env method in the configuration file was removed +- The #before_run and #after_run methods were removed. Use before(:all) and after(:all) instead + +New Features +------------ +- **Modern Notifications**: Integration with the notifiers gem supporting: + - osascript (macOS built-in) + - terminal_notifier (macOS) + - notify_send (Linux libnotify) + - dunstify (Linux dunst) + - auto_discover (automatic detection) + - New CLI options: `--notifications` and `--mode` for image themes + +- **Callbacks System**: Full callback support with before/after hooks + - `before(:all)` - Run before all tests + - `after(:all)` - Run after all tests + - `before(:each_ruby)` - Run before each Ruby version + - `after(:each_ruby)` - Run after each Ruby version + +- **Multi-Ruby Support**: + - RVM strategy: Run tests across multiple Ruby versions with gemset support + - RbEnv strategy: Run tests with RBENV_VERSION environment variable + - RubyDefault strategy: Run on current Ruby version + +- **Just Watch Mode**: `--just-watch` (-j) option to skip initial test run + - Useful for large applications where startup is slow + - Only watches for file changes and runs tests on change + +- **Focus Mode**: `--focus` (-f) option for running specific tests + - `--focus failures` - Run only previously failed tests + - `--focus path/to/spec.rb` - Run only specified file + +- **Framework Heuristics**: + - Rails: Watches models, controllers, helpers, mailers, jobs, lib + - Padrino: Similar to Rails with Padrino-specific paths + - Rubygems: Watches lib and test/spec directories + +- **Auto Discovery Priority**: Smart prioritization when auto-discovering + - Strategies: RVM > RbEnv > RubyDefault + - Frameworks: Rails > Padrino > Rubygems + - Test Frameworks: RSpec > Test::Unit + +- **Test Framework Improvements**: + - Complete Test::Unit/Minitest implementation with output parsing + - RSpec improvements: test_dir=, pending?, and .run? methods + +- **Modern File Watching**: + - Listen gem (default) - Event-driven, uses native OS notifications + - Filewatcher gem - Polling-based, works everywhere including VMs/NFS + +- **AI Integration Ideas**: Documentation for integrating with Claude Code and other AI tools + +Bug Fixes +--------- +- Fixed signal handler blocking issue in observer base +- Fixed ActiveSupport autoload issues +- Replaced deprecated stub with allow().to receive() in specs + +Dependencies +------------ +- Replaced watchr with listen and filewatcher observers +- Updated to modern notifiers gem (from GitHub main branch) +- Removed growl dependency + +Internal Changes +---------------- +- Rewrite of the entire library with separated responsibilities +- Shared examples for creating custom strategies, frameworks, and observers +- Updated all specs to modern RSpec syntax +- Comprehensive test coverage (250+ examples) v1.0.1 ====== diff --git a/TODO.markdown b/TODO.markdown index f388223..233788f 100644 --- a/TODO.markdown +++ b/TODO.markdown @@ -19,11 +19,11 @@ rbenv), ruby default just run the test on the current ruby version. * ~~Finish Rails autodiscover and its changes heuristics.~~ DONE * ~~Padrino autodiscover and its changes heuristics.~~ DONE * ~~Improve auto discover feature priorization subclasses for #run? method.~~ DONE -* Focus feature(fails, pass one file, run entire suite) with --focus (experimented feature)! -* Add post-run hooks to be added to the INFINITY_TEST file that run other things (coverage, code -analysis, etc - see ideas) +* ~~Focus feature(fails, pass one file, run entire suite) with --focus (experimented feature)!~~ DONE +* ~~Add post-run hooks to be added to the INFINITY_TEST file that run other things (coverage, code +analysis, etc - see ideas)~~ DONE (via callbacks system) -* Give some ideas (write to a md file the ideas) about how to integrate the infinity test with AI tools/AI agents or -even Claude code ... so ruby developers can see +* ~~Give some ideas (write to a md file the ideas) about how to integrate the infinity test with AI tools/AI agents or +even Claude code ... so ruby developers can see~~ DONE (see AI_INTEGRATION_IDEAS.md) -* Update HISTORY with all changes since last version. +* ~~Update HISTORY with all changes since last version.~~ DONE From 283090505436b7f0499614856b03c3f6f24f9192 Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Sun, 1 Feb 2026 21:29:16 +0000 Subject: [PATCH 20/22] Make ruby rvm and rbenv play nice with each other --- lib/infinity_test/core/notifier.rb | 2 +- lib/infinity_test/strategy/rbenv.rb | 6 ++-- lib/infinity_test/strategy/rvm.rb | 6 ++-- spec/infinity_test/strategy/rbenv_spec.rb | 28 ++++++++++++---- spec/infinity_test/strategy/rvm_spec.rb | 40 ++++++++++++++++------- 5 files changed, 57 insertions(+), 25 deletions(-) diff --git a/lib/infinity_test/core/notifier.rb b/lib/infinity_test/core/notifier.rb index 4705496..921d6c2 100644 --- a/lib/infinity_test/core/notifier.rb +++ b/lib/infinity_test/core/notifier.rb @@ -56,4 +56,4 @@ def images_dir end end end -end \ No newline at end of file +end diff --git a/lib/infinity_test/strategy/rbenv.rb b/lib/infinity_test/strategy/rbenv.rb index 813cec1..4f03af0 100644 --- a/lib/infinity_test/strategy/rbenv.rb +++ b/lib/infinity_test/strategy/rbenv.rb @@ -21,11 +21,11 @@ def run! end # ==== Returns - # TrueClass: If the user has rbenv installed. - # FalseClass: If the user doesn't have rbenv installed. + # TrueClass: If the user has rbenv installed AND has specified rubies to test against. + # FalseClass: If rbenv is not installed OR no rubies are specified. # def self.run? - File.exist?(File.expand_path('~/.rbenv')) + Core::Base.rubies.present? && File.exist?(File.expand_path('~/.rbenv')) end end end diff --git a/lib/infinity_test/strategy/rvm.rb b/lib/infinity_test/strategy/rvm.rb index 610212f..d19fb81 100644 --- a/lib/infinity_test/strategy/rvm.rb +++ b/lib/infinity_test/strategy/rvm.rb @@ -23,11 +23,11 @@ def run! end # ==== Returns - # TrueClass: If the user had the rvm installed. - # FalseClass: If the user don't had the rvm installed. + # TrueClass: If the user has RVM installed AND has specified rubies to test against. + # FalseClass: If RVM is not installed OR no rubies are specified. # def self.run? - installed_users_home? or installed_system_wide? + Core::Base.rubies.present? && (installed_users_home? || installed_system_wide?) end # ==== Returns diff --git a/spec/infinity_test/strategy/rbenv_spec.rb b/spec/infinity_test/strategy/rbenv_spec.rb index affd770..d1b850d 100644 --- a/spec/infinity_test/strategy/rbenv_spec.rb +++ b/spec/infinity_test/strategy/rbenv_spec.rb @@ -12,14 +12,30 @@ module Strategy describe ".run?" do let(:rbenv_dir) { File.expand_path('~/.rbenv') } - it "returns true if the user has rbenv installed" do - expect(File).to receive(:exist?).with(rbenv_dir).and_return(true) - expect(Rbenv).to be_run + context "when rubies are specified" do + before do + allow(Core::Base).to receive(:rubies).and_return(['2.7.0', '3.0.0']) + end + + it "returns true if the user has rbenv installed" do + expect(File).to receive(:exist?).with(rbenv_dir).and_return(true) + expect(Rbenv).to be_run + end + + it "returns false if the user does not have rbenv installed" do + expect(File).to receive(:exist?).with(rbenv_dir).and_return(false) + expect(Rbenv).not_to be_run + end end - it "returns false if the user does not have rbenv installed" do - expect(File).to receive(:exist?).with(rbenv_dir).and_return(false) - expect(Rbenv).not_to be_run + context "when no rubies are specified" do + before do + allow(Core::Base).to receive(:rubies).and_return([]) + end + + it "returns false even if rbenv is installed" do + expect(Rbenv).not_to be_run + end end end diff --git a/spec/infinity_test/strategy/rvm_spec.rb b/spec/infinity_test/strategy/rvm_spec.rb index 2692d27..3000cce 100644 --- a/spec/infinity_test/strategy/rvm_spec.rb +++ b/spec/infinity_test/strategy/rvm_spec.rb @@ -10,21 +10,37 @@ module Strategy it_should_behave_like 'a infinity test strategy' describe ".run?" do - it "returns true if the user has RVM installed in users home" do - expect(Rvm).to receive(:installed_users_home?).and_return(true) - expect(Rvm).to be_run - end + context "when rubies are specified" do + before do + allow(Core::Base).to receive(:rubies).and_return(['2.7.0', '3.0.0']) + end + + it "returns true if the user has RVM installed in users home" do + expect(Rvm).to receive(:installed_users_home?).and_return(true) + expect(Rvm).to be_run + end + + it "returns true if the user has RVM installed system wide" do + expect(Rvm).to receive(:installed_users_home?).and_return(false) + expect(Rvm).to receive(:installed_system_wide?).and_return(true) + expect(Rvm).to be_run + end - it "returns true if the user has RVM installed system wide" do - expect(Rvm).to receive(:installed_users_home?).and_return(false) - expect(Rvm).to receive(:installed_system_wide?).and_return(true) - expect(Rvm).to be_run + it "returns false if the user does not have RVM installed" do + expect(Rvm).to receive(:installed_users_home?).and_return(false) + expect(Rvm).to receive(:installed_system_wide?).and_return(false) + expect(Rvm).not_to be_run + end end - it "returns false if the user does not have RVM installed" do - expect(Rvm).to receive(:installed_users_home?).and_return(false) - expect(Rvm).to receive(:installed_system_wide?).and_return(false) - expect(Rvm).not_to be_run + context "when no rubies are specified" do + before do + allow(Core::Base).to receive(:rubies).and_return([]) + end + + it "returns false even if RVM is installed" do + expect(Rvm).not_to be_run + end end end From cf68e96b158d693cad02c3a0002ce1a90198cf3a Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Mon, 2 Feb 2026 03:38:08 +0000 Subject: [PATCH 21/22] Update readme with the new version instructions --- README.md | 627 ++++++++++++++++++++++++++++++++++++++++++++++++ Readme.markdown | 21 -- 2 files changed, 627 insertions(+), 21 deletions(-) create mode 100644 README.md delete mode 100644 Readme.markdown diff --git a/README.md b/README.md new file mode 100644 index 0000000..8a7c5d8 --- /dev/null +++ b/README.md @@ -0,0 +1,627 @@ +# Infinity Test + +**To Infinity and Beyond!** + +Infinity Test is a continuous testing library and a flexible alternative to Autotest and Guard. It watches your files for changes and automatically runs your tests, providing instant feedback with desktop notifications. + +Version 2.0.0 brings a complete rewrite with modern dependencies, multi-Ruby support via RVM/RbEnv, and a powerful callbacks system. + +## Table of Contents + +- [Installation](#installation) +- [Quick Start](#quick-start) +- [Command Line Options](#command-line-options) +- [Configuration File (INFINITY_TEST)](#configuration-file-infinity_test) +- [Ruby Version Managers](#ruby-version-managers) +- [Test Frameworks](#test-frameworks) +- [Application Frameworks](#application-frameworks) +- [Notifications](#notifications) +- [Image Themes](#image-themes) +- [Callbacks](#callbacks) +- [File Watching](#file-watching) +- [Advanced Usage](#advanced-usage) + +--- + +## Installation + +```bash +gem install infinity_test +``` + +Or add to your Gemfile: + +```ruby +gem 'infinity_test', group: :development +``` + +Then run: + +```bash +bundle install +``` + +--- + +## Quick Start + +### Step 1: Navigate to your project + +```bash +cd /path/to/your/project +``` + +### Step 2: Run Infinity Test + +```bash +infinity_test +``` + +That's it! Infinity Test will: + +1. Auto-detect your test framework (RSpec or Test::Unit) +2. Auto-detect your application framework (Rails, Padrino, or Rubygems) +3. Run all tests immediately +4. Watch for file changes and re-run relevant tests +5. Show desktop notifications with test results + +### Step 3: Start coding + +Edit your files and watch the tests run automatically! + +--- + +## Command Line Options + +| Option | Short | Description | +|--------|-------|-------------| +| `--ruby strategy` | | Ruby manager strategy: `auto_discover`, `rvm`, `rbenv`, `ruby_default` | +| `--rubies=versions` | | Ruby versions to test against (comma-separated) | +| `--test library` | | Test framework: `auto_discover`, `rspec`, `test_unit` | +| `--framework library` | | Application framework: `auto_discover`, `rails`, `rubygems`, `padrino` | +| `--options=options` | | Additional options to pass to test command | +| `--notifications library` | | Notification system: `auto_discover`, `osascript`, `terminal_notifier`, `notify_send`, `dunstify` | +| `--mode theme` | | Image theme for notifications (see [Image Themes](#image-themes)) | +| `--no-infinity-and-beyond` | `-n` | Run tests once and exit (CI mode) | +| `--just-watch` | `-j` | Skip initial test run, only watch for changes | +| `--focus [FILE]` | `-f` | Focus on specific tests or `failures` for last failed tests | +| `--no-verbose` | | Don't print commands before executing | +| `--no-bundler` | | Bypass Bundler support | +| `--help` | | Show help message | + +### Examples + +**Run with RSpec only:** +```bash +infinity_test --test rspec +``` + +**Run tests on multiple Ruby versions with RVM:** +```bash +infinity_test --ruby rvm --rubies=3.0.0,3.1.0,3.2.0 +``` + +**Run tests once and exit (for CI):** +```bash +infinity_test -n +``` + +**Skip initial test run, just watch:** +```bash +infinity_test --just-watch +``` + +**Focus on failed tests:** +```bash +infinity_test --focus failures +``` + +**Focus on a specific file:** +```bash +infinity_test --focus spec/models/user_spec.rb +``` + +**Use a specific notification theme:** +```bash +infinity_test --mode mario_bros +``` + +**Pass additional options to the test runner:** +```bash +infinity_test --options="-Ilib -Itest" +``` + +--- + +## Configuration File (INFINITY_TEST) + +You can persist your settings in an `INFINITY_TEST` file. Infinity Test looks for configuration in two locations: + +1. **Project file**: `./INFINITY_TEST` (higher priority) +2. **Global file**: `~/INFINITY_TEST` (lower priority) + +### Basic Configuration + +```ruby +InfinityTest.setup do |config| + config.test_framework = :rspec + config.framework = :rails + config.notifications = :terminal_notifier + config.mode = :mario_bros + config.verbose = true +end +``` + +### All Configuration Options + +```ruby +InfinityTest.setup do |config| + # Ruby Version Manager + # Options: :auto_discover, :rvm, :rbenv, :ruby_default + config.strategy = :auto_discover + + # Ruby versions to test against (requires RVM or RbEnv) + config.rubies = ['3.0.0', '3.1.0', '3.2.0'] + + # Gemset to use with RVM + config.gemset = 'my_project' + + # Test framework + # Options: :auto_discover, :rspec, :test_unit + config.test_framework = :rspec + + # Application framework + # Options: :auto_discover, :rails, :padrino, :rubygems + config.framework = :rails + + # File observer + # Options: :listen (event-driven), :filewatcher (polling) + config.observer = :listen + + # Notification system + # Options: :auto_discover, :osascript, :terminal_notifier, :notify_send, :dunstify + config.notifications = :auto_discover + + # Image theme for notifications + config.mode = :simpson + + # Or use custom images + config.success_image = '/path/to/success.png' + config.failure_image = '/path/to/failure.png' + config.pending_image = '/path/to/pending.png' + + # Bundler support (auto-detected if Gemfile exists) + config.bundler = true + + # Print commands before executing + config.verbose = true + + # File extension to watch + config.extension = :rb + + # Skip initial test run + config.just_watch = false + + # Run tests and exit (CI mode) + config.infinity_and_beyond = true + + # Focus mode: nil, :failures, or file path + config.focus = nil + + # Ignore specific test files + config.ignore_test_files = [ + 'spec/slow/performance_spec.rb' + ] + + # Ignore test folders + config.ignore_test_folders = [ + 'spec/integration' + ] +end +``` + +### Configuration with Callbacks + +```ruby +InfinityTest.setup do |config| + config.test_framework = :rspec + config.mode = :faces +end + +# Clear terminal before running tests +InfinityTest::Core::Base.before(:all) do + InfinityTest::Core::Base.clear_terminal +end + +# Run after all tests complete +InfinityTest::Core::Base.after(:all) do + system('say "Tests finished"') +end + +# Run before each Ruby version (when testing multiple rubies) +InfinityTest::Core::Base.before(:each_ruby) do |environment| + puts "Testing with Ruby #{environment[:ruby_version]}" +end + +# Run after each Ruby version +InfinityTest::Core::Base.after(:each_ruby) do |environment| + puts "Finished testing Ruby #{environment[:ruby_version]}" +end +``` + +--- + +## Ruby Version Managers + +Infinity Test supports testing across multiple Ruby versions using RVM or RbEnv. + +### Auto Discovery (Default) + +```ruby +config.strategy = :auto_discover +``` + +Priority order: RVM > RbEnv > RubyDefault + +### RVM Strategy + +Test against multiple Ruby versions with optional gemset support: + +```ruby +InfinityTest.setup do |config| + config.strategy = :rvm + config.rubies = ['3.0.0', '3.1.0', 'jruby-9.4.0.0'] + config.gemset = 'infinity_test' # Optional +end +``` + +**Command line:** +```bash +infinity_test --ruby rvm --rubies=3.0.0,3.1.0,jruby-9.4.0.0 +``` + +### RbEnv Strategy + +Test against multiple Ruby versions using rbenv: + +```ruby +InfinityTest.setup do |config| + config.strategy = :rbenv + config.rubies = ['3.0.0', '3.1.0', '3.2.0'] +end +``` + +**Command line:** +```bash +infinity_test --ruby rbenv --rubies=3.0.0,3.1.0 +``` + +### Ruby Default Strategy + +Use the current Ruby version only (no version manager): + +```ruby +config.strategy = :ruby_default +``` + +--- + +## Test Frameworks + +### RSpec + +Auto-detected when `spec/` directory exists with `spec_helper.rb` or `*_spec.rb` files. + +```ruby +config.test_framework = :rspec +``` + +### Test::Unit / Minitest + +Auto-detected when `test/` directory exists with `test_helper.rb` or `*_test.rb` files. + +```ruby +config.test_framework = :test_unit +``` + +--- + +## Application Frameworks + +### Rails + +Auto-detected when `config/environment.rb` exists. + +**Watched directories:** +- `app/models` → runs corresponding model specs/tests +- `app/controllers` → runs corresponding controller specs/tests +- `app/helpers` → runs corresponding helper specs/tests +- `app/mailers` → runs corresponding mailer specs/tests +- `app/jobs` → runs corresponding job specs/tests +- `lib/` → runs corresponding lib specs/tests +- `spec/` or `test/` → runs the changed spec/test file +- `spec_helper.rb` or `test_helper.rb` → runs all tests + +### Padrino + +Auto-detected when `config/boot.rb` exists with Padrino reference. + +Similar heuristics to Rails with Padrino-specific paths. + +### Rubygems (Default) + +Used for gem development and simple Ruby projects. + +**Watched directories:** +- `lib/` → runs corresponding specs/tests +- `spec/` or `test/` → runs the changed spec/test file + +--- + +## Notifications + +Desktop notifications show test results with themed images. + +### Notification Systems + +| System | Platform | Description | +|--------|----------|-------------| +| `auto_discover` | All | Automatically detect available notifier | +| `terminal_notifier` | macOS | Modern macOS notifications | +| `osascript` | macOS | Built-in AppleScript notifications | +| `notify_send` | Linux | libnotify notifications | +| `dunstify` | Linux | Dunst notification daemon | + +**Configuration:** +```ruby +config.notifications = :terminal_notifier +``` + +**Command line:** +```bash +infinity_test --notifications terminal_notifier +``` + +--- + +## Image Themes + +Infinity Test includes fun image themes for notifications. Each theme has three images: `success`, `failure`, and `pending`. + +### Available Themes + +| Theme | Description | Location | +|-------|-------------|----------| +| `simpson` | The Simpsons characters (default) | `images/simpson/` | +| `faces` | Expressive face icons | `images/faces/` | +| `fuuu` | Rage comic faces | `images/fuuu/` | +| `hands` | Hand gesture icons | `images/hands/` | +| `mario_bros` | Super Mario characters | `images/mario_bros/` | +| `rails` | Ruby on Rails themed | `images/rails/` | +| `rubies` | Ruby gemstone themed | `images/rubies/` | +| `street_fighter` | Street Fighter characters | `images/street_fighter/` | +| `toy_story` | Toy Story characters | `images/toy_story/` | + +### Using a Theme + +**Configuration file:** +```ruby +config.mode = :mario_bros +``` + +**Command line:** +```bash +infinity_test --mode mario_bros +``` + +### Custom Images + +Use your own images by setting the image paths directly: + +```ruby +InfinityTest.setup do |config| + config.success_image = '/path/to/my_success.png' + config.failure_image = '/path/to/my_failure.png' + config.pending_image = '/path/to/my_pending.png' +end +``` + +Or point to a custom directory containing `success.*`, `failure.*`, and `pending.*` images: + +```ruby +config.mode = '/path/to/my/images/directory' +``` + +--- + +## Callbacks + +Callbacks let you run custom code before or after tests. + +### Available Callbacks + +| Callback | Scope | Description | +|----------|-------|-------------| +| `before(:all)` | All | Runs once before all tests | +| `after(:all)` | All | Runs once after all tests | +| `before(:each_ruby)` | Per Ruby | Runs before testing each Ruby version | +| `after(:each_ruby)` | Per Ruby | Runs after testing each Ruby version | + +### Examples + +**Clear terminal before tests:** +```ruby +InfinityTest::Core::Base.before(:all) do + InfinityTest::Core::Base.clear_terminal +end +``` + +**Play a sound after tests:** +```ruby +InfinityTest::Core::Base.after(:all) do + system('afplay /path/to/sound.mp3') +end +``` + +**Log Ruby version being tested:** +```ruby +InfinityTest::Core::Base.before(:each_ruby) do |env| + File.write('test.log', "Testing: #{env[:ruby_version]}\n", mode: 'a') +end +``` + +**Compile extensions before each Ruby:** +```ruby +InfinityTest::Core::Base.before(:each_ruby) do |env| + system('rake compile') +end +``` + +--- + +## File Watching + +### Listen (Default) + +Event-driven file watching using native OS notifications. Fast and efficient. + +```ruby +config.observer = :listen +``` + +### Filewatcher + +Polling-based file watching. Works everywhere including VMs and NFS mounts. + +```ruby +config.observer = :filewatcher +``` + +--- + +## Advanced Usage + +### Continuous Integration Mode + +Run tests once and exit with proper exit code: + +```bash +infinity_test -n +``` + +Or in configuration: +```ruby +config.infinity_and_beyond = false +``` + +### Just Watch Mode + +Skip the initial test run and only watch for changes. Useful for large projects: + +```bash +infinity_test --just-watch +``` + +### Focus Mode + +Run only specific tests: + +```bash +# Run only previously failed tests +infinity_test --focus failures + +# Run only a specific file +infinity_test --focus spec/models/user_spec.rb +``` + +### Ignoring Files + +```ruby +InfinityTest.setup do |config| + # Ignore specific test files + config.ignore_test_files = [ + 'spec/slow/large_integration_spec.rb', + 'spec/external/api_spec.rb' + ] + + # Ignore entire directories + config.ignore_test_folders = [ + 'spec/integration', + 'spec/system' + ] +end +``` + +### Watching Non-Ruby Files + +```ruby +# Watch Python files +config.extension = :py + +# Watch JavaScript files +config.extension = :js +``` + +### Sample Complete Configuration + +```ruby +# INFINITY_TEST + +InfinityTest.setup do |config| + # Multi-Ruby testing with RVM + config.strategy = :rvm + config.rubies = ['3.1.0', '3.2.0', '3.3.0'] + config.gemset = 'myapp' + + # Test framework and app framework + config.test_framework = :rspec + config.framework = :rails + + # Notifications with Mario theme + config.notifications = :terminal_notifier + config.mode = :mario_bros + + # Performance settings + config.observer = :listen + config.just_watch = false + config.verbose = true + + # Ignore slow tests during development + config.ignore_test_folders = ['spec/system'] +end + +# Clear screen before each run +InfinityTest::Core::Base.before(:all) do + InfinityTest::Core::Base.clear_terminal +end + +# Notify via system sound when done +InfinityTest::Core::Base.after(:all) do + system('afplay /System/Library/Sounds/Glass.aiff') +end +``` + +--- + +## Contributing + +1. Fork the repository +2. Create your feature branch (`git checkout -b feature/amazing-feature`) +3. Run the tests (`infinity_test`) +4. Commit your changes (`git commit -am 'Add amazing feature'`) +5. Push to the branch (`git push origin feature/amazing-feature`) +6. Create a Pull Request + +--- + +## License + +MIT License - see [LICENSE.txt](LICENSE.txt) for details. + +--- + +## Author + +Tomas D'Stefano + +**Happy Testing!** diff --git a/Readme.markdown b/Readme.markdown deleted file mode 100644 index 886b830..0000000 --- a/Readme.markdown +++ /dev/null @@ -1,21 +0,0 @@ -# Infinity Test - -Infinity Test is a continuous testing library and a flexible alternative to -Autotest, using the awesome Watchr library with RSpec, Test::Unit, Bacon and -with RVM functionality, giving the possibility to test with all Ruby versions -that you have in your RVM configuration. - -## To Infinity and Beyond! - -
- Infinity Test -

Photo taken from this site

-
- -## Install - - gem install infinity_test - -## Preparing the 2.0.0.rc1 - -**After LONG YEARS without developing this gem, in 2015 I'm working to release the 2.0.0 soon. Coming soon!! \o/** From 299386300398570c5d156e9f839fe54af57263a3 Mon Sep 17 00:00:00 2001 From: Tomas D'Stefano Date: Mon, 2 Feb 2026 03:54:36 +0000 Subject: [PATCH 22/22] Replace Travis CI with GitHub Actions Migrate CI from Travis CI to GitHub Actions with modern Ruby versions (3.1, 3.2, 3.3, JRuby) and cross-platform testing (Ubuntu, macOS). --- .github/workflows/ci.yml | 31 +++++++++++++++++++++++++++++++ .travis.yml | 10 ---------- AI_INTEGRATION_IDEAS.md | 4 +++- 3 files changed, 34 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3f25db4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,31 @@ +name: CI + +on: + push: + branches: [master, main] + pull_request: + branches: [master, main] + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + ruby: ['3.1', '3.2', '3.3'] + include: + - os: ubuntu-latest + ruby: jruby + + steps: + - uses: actions/checkout@v4 + + - name: Set up Ruby ${{ matrix.ruby }} + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + + - name: Run tests + run: bundle exec rspec diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index bf62658..0000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: ruby -rvm: - - 1.9.3 - - jruby - - 2.0 -before_install: - - export DISPLAY=:99.0 - - sh -e /etc/init.d/xvfb start -script: - - bundle exec rspec \ No newline at end of file diff --git a/AI_INTEGRATION_IDEAS.md b/AI_INTEGRATION_IDEAS.md index 83502bc..59a3df5 100644 --- a/AI_INTEGRATION_IDEAS.md +++ b/AI_INTEGRATION_IDEAS.md @@ -86,7 +86,9 @@ Use AI to predict which tests are most likely to fail based on: ## 4. Natural Language Test Commands -Integrate with AI to support natural language commands: +Future idea: Integrate with AI to support natural language commands: + +This is just an idea: ```bash # Instead of