Skip to content

Commit

Permalink
More fine grained rerun logic.
Browse files Browse the repository at this point in the history
Instead of a simple path/specs rerun logic, rspectacle remembers
them per example.
  • Loading branch information
netzpirat committed Jan 12, 2012
1 parent cd389c2 commit 729cbc1
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 95 deletions.
32 changes: 18 additions & 14 deletions README.md
Expand Up @@ -35,7 +35,6 @@ Guard::RSpectacle can be adapted to all kind of projects. Please read the
[Guard documentation](https://github.com/guard/guard#readme) for more information about the Guardfile DSL.

```ruby
# NOTE: When using watch with block, you must return all files that should be reloaded.
guard :rspectacle do
watch('spec/spec_helper.rb') { %w(spec/spec_helper spec) }
watch('config/routes.rb') { %w(config/routes.rb spec/routing) }
Expand All @@ -54,6 +53,8 @@ guard :rspectacle do
end
```

**NOTE: When using `watch` with a block, you must return all files that should be reloaded.**

## Options

There are many options that can customize Guard::Jasmine to your needs. Options are simply supplied as hash when
Expand All @@ -70,35 +71,38 @@ end
The general options configures the environment that is needed to run Guard::RSpectacular and RSpec:

```ruby
:cli => '--tag @focus' # RSpec CLI options
# default: ''
:cli => '--tag @focus' # RSpec CLI options
# default: ''
```

### Spec runner options

The spec runner options configures the behavior driven development (or BDD) cycle:

```ruby
:all_on_start => false # Run all specs on start.
# default: true
:all_on_start => false # Run all specs on start.
# default: true

:keep_failed => false # Keep failed examples and add them to the next run again.
# default: true

:keep_failed => false # Keep failed specs and add them to the next run again.
# default: true
:keep_failed => false # Keep pending examples and add them to the next run again.
# default: true

:all_after_pass => false # Run all specs after a suite has passed again after failing.
# default: true
:all_after_pass => false # Run all specs after all examples have passed again after failing.
# default: true
```

### System notifications options

These options affects what system notifications (growl, libnotify or notifu) are shown after a spec run:

```ruby
:notifications => false # Show success and error notifications.
# default: true
:notifications => false # Show success and error notifications.
# default: true

:hide_success => true # Disable successful spec run notification.
# default: false
:hide_success => true # Disable successful spec run notification.
# default: false
```

## Important note on reloading
Expand All @@ -108,7 +112,7 @@ The ability to run specs immediately comes at a cost:
1. In your `Guardfile`, you have to specify which files should be reloaded (apart from specs to be executed). But don't
worry, the default template takes care of it.
2. When a file is changed, it is reloaded the Ruby code with
Kernel#load](http://ruby-doc.org/core-1.9.3/Kernel.html#method-i-load), which only re-interprets the file.
[Kernel#load](http://ruby-doc.org/core-1.9.3/Kernel.html#method-i-load), which only re-interprets the file.

This, for example, means that a method already defined on a class (including `initialize`) will not be removed
simply by deleting that method from the source code:
Expand Down
43 changes: 26 additions & 17 deletions lib/guard/rspectacle.rb
Expand Up @@ -16,14 +16,15 @@ class RSpectacle < Guard
autoload :Reloader, 'guard/rspectacle/reloader'
autoload :Notifier, 'guard/rspectacle/notifier'

attr_accessor :last_run_failed, :last_failed_paths
attr_accessor :last_run_passed, :rerun_examples

DEFAULT_OPTIONS = {
:cli => '',
:notification => true,
:hide_success => false,
:all_on_start => true,
:keep_failed => true,
:keep_pending => true,
:all_after_pass => true,
}

Expand All @@ -35,16 +36,17 @@ class RSpectacle < Guard
# @option options [Boolean] :notification show notifications
# @option options [Boolean] :hide_success hide success message notification
# @option options [Boolean] :all_on_start Run all specs on start
# @option options [Boolean] :keep_failed keep failed suites and add them to the next run again
# @option options [Boolean] :all_after_pass run all suites after a suite has passed again after failing
# @option options [Boolean] :keep_failed keep failed examples and add them to the next run again
# @option options [Boolean] :keep_pending keep pending examples and add them to the next run again
# @option options [Boolean] :all_after_pass run all specs after all examples have passed again after failing
#
def initialize(watchers = [], options = {})
options = DEFAULT_OPTIONS.merge(options)

super(watchers, options)

self.last_run_failed = false
self.last_failed_paths = []
self.last_run_passed = true
self.rerun_examples = []
end

# Gets called once when Guard starts.
Expand All @@ -66,19 +68,24 @@ def start
def reload
Dir.glob('**/*.rb').each { |file| Reloader.reload_file(file) }

self.last_run_failed = false
self.last_failed_paths = []
self.last_run_passed = true
self.rerun_examples = []
end

# Gets called when all specs should be run.
#
# @raise [:task_has_failed] when run_on_change has failed
#
def run_all
passed, failed_specs = Runner.run(['spec'], options)
passed, failed_examples, passed_examples, pending_examples = Runner.run(['spec'], options)

self.last_failed_paths = failed_specs
self.last_run_failed = !passed
if options[:keep_pending]
self.rerun_examples = failed_examples + pending_examples
else
self.rerun_examples = failed_examples
end

self.last_run_passed = passed

throw :task_has_failed unless passed
end
Expand All @@ -92,21 +99,23 @@ def run_on_change(paths)
specs = Inspector.clean(paths)
return false if specs.empty?

specs += self.last_failed_paths if options[:keep_failed]
specs += self.rerun_examples if options[:keep_failed]

# RSpec reloads the files, so reload only non spec files
(paths - specs).each { |path| Reloader.reload_file(path) }

passed, failed_specs = Runner.run(specs, options)
passed, failed_examples, passed_examples, pending_examples = Runner.run(specs, options)

if passed
self.last_failed_paths = self.last_failed_paths - paths
run_all if self.last_run_failed && options[:all_after_pass]
if options[:keep_pending]
self.rerun_examples += failed_examples + pending_examples
else
self.last_failed_paths = self.last_failed_paths + failed_specs
self.rerun_examples += failed_examples
end

self.last_run_failed = !passed
self.rerun_examples -= passed_examples

run_all if passed && !self.last_run_passed && options[:all_after_pass]
self.last_run_passed = passed

throw :task_has_failed unless passed
end
Expand Down
2 changes: 1 addition & 1 deletion lib/guard/rspectacle/templates/Guardfile
@@ -1,4 +1,4 @@
# NOTE: When using watch with block, you must return all files that should be reloaded.
# NOTE: When using watch with a block, you must return all files that should be reloaded.
guard :rspectacle do
watch('spec/spec_helper.rb') { %w(spec/spec_helper spec) }
watch('config/routes.rb') { %w(config/routes.rb spec/routing) }
Expand Down

0 comments on commit 729cbc1

Please sign in to comment.