Skip to content

Instantly share code, notes, and snippets.

@mrgenixus
Forked from davidjbeveridge/Gemfile
Created January 31, 2012 04:05
Show Gist options
  • Save mrgenixus/1708710 to your computer and use it in GitHub Desktop.
Save mrgenixus/1708710 to your computer and use it in GitHub Desktop.
Routing
This gist exceeds the recommended number of files (~10). To access all files, please clone this gist.

RSpec-2

Behaviour Driven Development for Ruby

Description

rspec-2.x is a meta-gem, which depends on the rspec-core, rspec-expectations and rspec-mocks gems. Each of these can be installed separately and actived in isolation with the gem command. Among other benefits, this allows you to use rspec-expectations, for example, in Test::Unit::TestCase if you happen to prefer that style.

Conversely, if you like RSpec's approach to declaring example groups and examples (describe and it) but prefer Test::Unit assertions and mocha, rr or flexmock for mocking, you'll be able to do that without having to load the components of rspec that you're not using.

Documentation

rspec-core

rspec-expectations

rspec-mocks

Install

gem install rspec

Contribute

Also see

Routing example

Just so we can play w/ ruby.

rspec-core

gem install rspec      # for rspec-core, rspec-expectations, rspec-mocks
gem install rspec-core # for rspec-core only

overview

rspec-core provides the structure for writing executable examples of how your code should behave. It uses the words "describe" and "it" so we can express concepts like a conversation:

"Describe an order."
"It sums the prices of its line items."

basic structure

describe Order do
  it "sums the prices of its line items" do
    order = Order.new
    order.add_entry(LineItem.new(:item => Item.new(
      :price => Money.new(1.11, :USD)
    )))
    order.add_entry(LineItem.new(:item => Item.new(
      :price => Money.new(2.22, :USD),
      :quantity => 2
    )))
    order.total.should eq(Money.new(5.55, :USD))
  end
end

The describe method creates an ExampleGroup. Within the block passed to describe you can declare examples using the it method.

Under the hood, an example group is a class in which the block passed to describe is evaluated. The blocks passed to it are evaluated in the context of an instance of that class.

nested groups

You can also declare nested nested groups using the describe or context methods:

describe Order do
  context "with no items" do
    it "behaves one way" do
      # ...
    end
  end

  context "with one item" do
    it "behaves another way" do
      # ...
    end
  end
end

aliases

You can declare example groups using either describe or context, though only describe is available at the top level.

You can declare examples within a group using any of it, specify, or example.

shared examples and contexts

Declare a shared example group using shared_examples, and then include it in any group using include_examples.

shared_examples "collections" do |collection_class|
  it "is empty when first created" do
    collection_class.new.should be_empty
  end
end

describe Array do
  include_examples "collections", Array
end

describe Hash do
  include_examples "collections", Hash
end

Nearly anything that can be declared within an example group can be declared within a shared example group. This includes before, after, and around hooks, let declarations, and nested groups/contexts.

You can also use the names shared_context and include_context. These are pretty much the same as shared_examples and include_examples, providing more accurate naming for in which you share hooks, let declarations, helper methods, etc, but no examples.

metadata

rspec-core stores a metadata hash with every example and group, which contains like their descriptions, the locations at which they were declared, etc, etc. This hash powers many of rspec-core's features, including output formatters (which access descriptions and locations), and filtering before and after hooks.

Although you probably won't ever need this unless you are writing an extension, you can access it from an example like this:

it "does something" do
  example.metadata[:description].should eq("does something")
end

described_class

When a class is passed to describe, you can access it from an example using the described_class method, which is a wrapper for example.metadata[:described_class].

describe Widget do
  example do
    described_class.should equal(Widget)
  end
end

This is useful in extensions or shared example groups in which the specific class is unknown. Taking the shared examples example from above, we can clean it up a bit using described_class:

shared_examples "collections" do
  it "is empty when first created" do
    described.new.should be_empty
  end
end

describe Array do
  include_examples "collections"
end

describe Hash do
  include_examples "collections"
end

the rspec command

When you install the rspec-core gem, it installs the rspec executable, which you'll use to run rspec. The rspec comes with many useful options. Run rspec --help to see the complete list.

see also

get started

Start with a simple example of behavior you expect from your system. Do this before you write any implementation code:

# in spec/calculator_spec.rb
describe Calculator do
  it "add(x,y) returns the sum of its arguments" do
    Calculator.new.add(1, 2).should eq(3)
  end
end

Run this with the rspec command, and watch it fail:

$ rspec spec/calculator_spec.rb
./spec/calculator_spec.rb:1: uninitialized constant Calculator

Implement the simplest solution:

# in lib/calculator.rb
class Calculator
  def add(a,b)
    a + b
  end
end

Be sure to require the implementation file in the spec:

# in spec/calculator_spec.rb
# - RSpec adds ./lib to the $LOAD_PATH
require "calculator"

Now run the spec again, and watch it pass:

$ rspec spec/calculator_spec.rb
.

Finished in 0.000315 seconds
1 example, 0 failures

Use the documentation formatter to see the resulting spec:

$ rspec spec/calculator_spec.rb --format doc
Calculator add
  returns the sum of its arguments

Finished in 0.000379 seconds
1 example, 0 failures

Also see

rspec-core provides the structure for RSpec code examples:

describe Account do
  it "has a balance of zero when first opened" do
    # example code goes here - for more on the 
    # code inside the examples, see rspec-expectations 
    # and rspec-mocks
  end
end

Issues

This documentation is open source, and a work in progress. If you find it incomplete or confusing, please submit an issue, or, better yet, a pull request.

The rspec command comes with several options you can use to customize RSpec's behavior, including output formats, filtering examples, etc.

For a full list of options, run the rspec command with the --help flag:

$ rspec --help

Run with ruby

Generally, life is simpler if you just use the rspec command. If you must use the ruby command, however, you'll want to do the following:

  • require 'rspec/autorun'

This tells RSpec to run your examples. Do this in any file that you are passing to the ruby command.

  • Update the LOAD_PATH

It is conventional to put configuration in and require assorted support files from spec/spec_helper.rb. It is also conventional to require that file from the spec files using require 'spec_helper'. This works because RSpec implicitly adds the spec directory to the LOAD_PATH. It also adds lib, so your implementation files will be on the LOAD_PATH as well.

If you're using the ruby command, you'll need to do this yourself:

ruby -Ilib -Ispec path/to/spec.rb

diff-lcs

Description

Diff::LCS is a port of Perl’s Algorithm::Diff that uses the McIlroy-Hunt longest common subsequence (LCS) algorithm to compute intelligent differences between two sequenced enumerable containers. The implementation is based on Mario I. Wolczko’s Smalltalk version 1.2 (1993) and Ned Konz’s Perl version Algorithm::Diff 1.15.

This is release 1.1.3, fixing several small bugs found over the years. Version 1.1.0 added new features, including the ability to #patch and #unpatch changes as well as a new contextual diff callback, Diff::LCS::ContextDiffCallbacks, that should improve the context sensitivity of patching.

This library is called Diff::LCS because of an early version of Algorithm::Diff which was restrictively licensed. This version has seen a minor license change: instead of being under Ruby’s license as an option, the third optional license is the MIT license.

Where

This is the new home of Diff::LCS (diff-lcs). The Ruwiki page still refers to it, but development is not happening there any longer.

Synopsis

Using this module is quite simple. By default, Diff::LCS does not extend objects with the Diff::LCS interface, but will be called as if it were a function:

require 'diff/lcs'

seq1 = %w(a b c e h j l m n p)
seq2 = %w(b c d e f j k l m r s t)

lcs = Diff::LCS.LCS(seq1, seq2)
diffs = Diff::LCS.diff(seq1, seq2)
sdiff = Diff::LCS.sdiff(seq1, seq2)
seq = Diff::LCS.traverse_sequences(seq1, seq2, callback_obj)
bal = Diff::LCS.traverse_balanced(seq1, seq2, callback_obj)
seq2 == Diff::LCS.patch!(seq1, diffs)
seq1 == Diff::LCS.unpatch!(seq2, diffs)
seq2 == Diff::LCS.patch!(seq1, sdiff)
seq1 == Diff::LCS.unpatch!(seq2, sdiff)

Objects can be extended with Diff::LCS:

seq1.extend(Diff::LCS)
lcs = seq1.lcs(seq2)
diffs = seq1.diff(seq2)
sdiff = seq1.sdiff(seq2)
seq = seq1.traverse_sequences(seq2, callback_obj)
bal = seq1.traverse_balanced(seq2, callback_obj)
seq2 == seq1.patch!(diffs)
seq1 == seq2.unpatch!(diffs)
seq2 == seq1.patch!(sdiff)
seq1 == seq2.unpatch!(sdiff)

By requiring ‘diff/lcs/array’ or ‘diff/lcs/string’, Array or String will be extended for use this way.

Note that Diff::LCS requires a sequenced enumerable container, which means that the order of enumeration is both predictable and consistent for the same set of data. While it is theoretically possible to generate a diff for unordereded hash, it will only be meaningful if the enumeration of the hashes is consistent. In general, this will mean that containers that behave like String or Array will perform best.

:include: License.rdoc

Then /^the output should contain all of these:$/ do |table|
table.raw.flatten.each do |string|
assert_partial_output(string, all_output)
end
end
Then /^the output should not contain any of these:$/ do |table|
table.raw.flatten.each do |string|
all_output.should_not =~ regexp(string)
end
end
Then /^the example(?:s)? should(?: all)? pass$/ do
Then %q{the output should contain "0 failures"}
Then %q{the exit status should be 0}
end
Then /^the file "([^"]*)" should contain:$/ do |file, partial_content|
check_file_content(file, partial_content, true)
end
Then /^the backtrace\-normalized output should contain:$/ do |partial_output|
# ruby 1.9 includes additional stuff in the backtrace,
# so we need to normalize it to compare it with our expected output.
normalized_output = all_output.split("\n").map do |line|
line =~ /(^\s+# [^:]+:\d+)/ ? $1 : line # http://rubular.com/r/zDD7DdWyzF
end.join("\n")
normalized_output.should =~ regexp(partial_output)
end
Feature: alias_example_to
Use `config.alias_example_to` to create new example group methods
that define examples with the configured metadata.
If you set the `treat_symbols_as_metadata_keys_with_true_values` config option
to `true`, you can specify metadata using only symbols.
Scenario: Use alias_example_to to define focused example
Given a file named "alias_example_to_spec.rb" with:
"""
RSpec.configure do |c|
c.alias_example_to :fit, :focused => true
c.filter_run :focused => true
end
describe "an example group" do
it "does one thing" do
end
fit "does another thing" do
end
end
"""
When I run `rspec alias_example_to_spec.rb --format doc`
Then the output should contain "does another thing"
And the output should not contain "does one thing"
Scenario: use symbols as metadata
Given a file named "use_symbols_as_metadata_spec.rb" with:
"""
RSpec.configure do |c|
c.treat_symbols_as_metadata_keys_with_true_values = true
c.alias_example_to :fit, :focused
c.filter_run :focused
end
describe "an example group" do
it "does one thing" do
end
fit "does another thing" do
end
end
"""
When I run `rspec use_symbols_as_metadata_spec.rb --format doc`
Then the output should contain "does another thing"
And the output should not contain "does one thing"
Feature: arbitrary file suffix
Scenario: .spec
Given a file named "a.spec" with:
"""
describe "something" do
it "does something" do
3.should eq(3)
end
end
"""
When I run `rspec a.spec`
Then the examples should all pass
Feature: arbitrary helper methods
You can define methods in any example group using Ruby's `def` keyword or
`define_method` method. These _helper_ methods are exposed to examples in the
group in which they are defined and groups nested within that group, but not
parent or sibling groups.
Scenario: use a method defined in the same group
Given a file named "example_spec.rb" with:
"""
describe "an example" do
def help
:available
end
it "has access to methods defined in its group" do
help.should be(:available)
end
end
"""
When I run `rspec example_spec.rb`
Then the examples should all pass
Scenario: use a method defined in a parent group
Given a file named "example_spec.rb" with:
"""
describe "an example" do
def help
:available
end
describe "in a nested group" do
it "has access to methods defined in its parent group" do
help.should be(:available)
end
end
end
"""
When I run `rspec example_spec.rb`
Then the examples should all pass
Feature: around hooks
Around hooks receive the example as a block argument, extended to behave like
a proc. This lets you define code that should be executed before and after
the example. Of course, you can do the same thing with before and after hooks,
and it's often cleaner to do so.
Where around hooks shine is when you want to run an example in a block. For
example, if your database library offers a transaction method that receives
a block, you can use an around hook as described in the first scenario:
WARNING: around hooks do not share state with the example the way before and
after hooks do. This means that you can not share instance variables between
around hooks and examples.
Also, mock frameworks are set up and torn down within the context of running
the example, so you can not interact with them directly in around hooks.
Scenario: use the example as a proc within the block passed to around()
Given a file named "example_spec.rb" with:
"""
class Database
def self.transaction
puts "open transaction"
yield
puts "close transaction"
end
end
describe "around filter" do
around(:each) do |example|
Database.transaction(&example)
end
it "gets run in order" do
puts "run the example"
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain:
"""
open transaction
run the example
close transaction
"""
Scenario: invoke the example using run()
Given a file named "example_spec.rb" with:
"""
describe "around hook" do
around(:each) do |example|
puts "around each before"
example.run
puts "around each after"
end
it "gets run in order" do
puts "in the example"
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain:
"""
around each before
in the example
around each after
"""
Scenario: access the example metadata
Given a file named "example_spec.rb" with:
"""
describe "something" do
around(:each) do |example|
puts example.metadata[:foo]
example.run
end
it "does something", :foo => "this should show up in the output" do
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain "this should show up in the output"
Scenario: define a global around hook
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |c|
c.around(:each) do |example|
puts "around each before"
example.run
puts "around each after"
end
end
describe "around filter" do
it "gets run in order" do
puts "in the example"
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain:
"""
around each before
in the example
around each after
"""
Scenario: before/after(:each) hooks are wrapped by the around hook
Given a file named "example_spec.rb" with:
"""
describe "around filter" do
around(:each) do |example|
puts "around each before"
example.run
puts "around each after"
end
before(:each) do
puts "before each"
end
after(:each) do
puts "after each"
end
it "gets run in order" do
puts "in the example"
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain:
"""
around each before
before each
in the example
after each
around each after
"""
Scenario: before/after(:all) hooks are NOT wrapped by the around hook
Given a file named "example_spec.rb" with:
"""
describe "around filter" do
around(:each) do |example|
puts "around each before"
example.run
puts "around each after"
end
before(:all) do
puts "before all"
end
after(:all) do
puts "after all"
end
it "gets run in order" do
puts "in the example"
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain:
"""
before all
around each before
in the example
around each after
.after all
"""
Scenario: examples run by an around block are run in the configured context
Given a file named "example_spec.rb" with:
"""
module IncludedInConfigureBlock
def included_in_configure_block; true; end
end
Rspec.configure do |c|
c.include IncludedInConfigureBlock
end
describe "around filter" do
around(:each) do |example|
example.run
end
it "runs the example in the correct context" do
included_in_configure_block.should be_true
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain "1 example, 0 failure"
Scenario: implicitly pending examples are detected as Not yet implemented
Given a file named "example_spec.rb" with:
"""
describe "implicit pending example" do
around(:each) do |example|
example.run
end
it "should be detected as Not yet implemented"
end
"""
When I run `rspec example_spec.rb`
Then the output should contain "1 example, 0 failures, 1 pending"
And the output should contain:
"""
Pending:
implicit pending example should be detected as Not yet implemented
# Not yet implemented
"""
Scenario: explicitly pending examples are detected as pending
Given a file named "example_spec.rb" with:
"""
describe "explicit pending example" do
around(:each) do |example|
example.run
end
it "should be detected as pending" do
pending
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain "1 example, 0 failures, 1 pending"
And the output should contain:
"""
explicit pending example should be detected as pending
# No reason given
"""
Scenario: multiple around hooks in the same scope
Given a file named "example_spec.rb" with:
"""
describe "if there are multiple around hooks in the same scope" do
around(:each) do |example|
puts "first around hook before"
example.run
puts "first around hook after"
end
around(:each) do |example|
puts "second around hook before"
example.run
puts "second around hook after"
end
it "they should all be run" do
puts "in the example"
1.should eq(1)
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain "1 example, 0 failure"
And the output should contain:
"""
first around hook before
second around hook before
in the example
second around hook after
first around hook after
"""
Scenario: around hooks in multiple scopes
Given a file named "example_spec.rb" with:
"""
describe "if there are around hooks in an outer scope" do
around(:each) do |example|
puts "first outermost around hook before"
example.run
puts "first outermost around hook after"
end
around(:each) do |example|
puts "second outermost around hook before"
example.run
puts "second outermost around hook after"
end
describe "outer scope" do
around(:each) do |example|
puts "first outer around hook before"
example.run
puts "first outer around hook after"
end
around(:each) do |example|
puts "second outer around hook before"
example.run
puts "second outer around hook after"
end
describe "inner scope" do
around(:each) do |example|
puts "first inner around hook before"
example.run
puts "first inner around hook after"
end
around(:each) do |example|
puts "second inner around hook before"
example.run
puts "second inner around hook after"
end
it "they should all be run" do
puts "in the example"
end
end
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain "1 example, 0 failure"
And the output should contain:
"""
first outermost around hook before
second outermost around hook before
first outer around hook before
second outer around hook before
first inner around hook before
second inner around hook before
in the example
second inner around hook after
first inner around hook after
second outer around hook after
first outer around hook after
second outermost around hook after
first outermost around hook after
"""
#! /usr/env/bin ruby
#--
# Copyright 2004 Austin Ziegler <diff-lcs@halostatue.ca>
# adapted from:
# Algorithm::Diff (Perl) by Ned Konz <perl@bike-nomad.com>
# Smalltalk by Mario I. Wolczko <mario@wolczko.com>
# implements McIlroy-Hunt diff algorithm
#
# This program is free software. It may be redistributed and/or modified under
# the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
# Ruby licence.
#
# $Id$
#++
# Includes Diff::LCS into the Array built-in class.
require 'diff/lcs'
class Array
include Diff::LCS
end
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>The "Artistic License" - dev.perl.org</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<link rel="stylesheet" type="text/css" href="http://cdn.pimg.net/css/leostyle.vffcd481.css">
<link rel="stylesheet" type="text/css" href="http://cdn.pimg.net/css/dev.v5f7fab3.css">
<link rel="shortcut icon" href="http://cdn.pimg.net/favicon.v249dfa7.ico">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="http://cdn.pimg.net/js/jquery.corner.v84b7681.js" type="text/javascript" charset="utf-8"></script>
<script src="http://cdn.pimg.net/js/leo.v9872b9c.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="header_holder">
<div class="sub_holder">
<div id="page_image"></div>
<h1>
The "Artistic License"
</h1>
<div id="logo_holder">
<a href="/"><img src="http://cdn.pimg.net/images/camel_head.v25e738a.png" id="logo" alt="Perl programming" height="65" align="right" /></a>
<span>dev.perl.org</span>
</div>
</div>
</div>
<div id="nav">
<div class="sub_holder">
<ul>
<li>
<a href="/">Home</a>
</li>
<li class="">
<a href="/perl5/">Perl 5</a>
</li>
<li class="">
<a href="/perl6/">Perl 6</a>
</li>
</ul>
</div>
</div>
<div id="crum_holder">
<div id="crum" class="sub_holder">
&nbsp;
</div>
</div>
<div id="content_holder">
<div id="content" class="sub_holder">
<div id="main">
<PRE>
<A style="float:right"
HREF="http://www.opensource.org/licenses/artistic-license.php"
><IMG BORDER=0 SRC="osi-certified-90x75.gif"
></A>
The "Artistic License"
Preamble
The intent of this document is to state the conditions under which a
Package may be copied, such that the Copyright Holder maintains some
semblance of artistic control over the development of the package,
while giving the users of the package the right to use and distribute
the Package in a more-or-less customary fashion, plus the right to make
reasonable modifications.
Definitions:
"Package" refers to the collection of files distributed by the
Copyright Holder, and derivatives of that collection of files
created through textual modification.
"Standard Version" refers to such a Package if it has not been
modified, or has been modified in accordance with the wishes
of the Copyright Holder as specified below.
"Copyright Holder" is whoever is named in the copyright or
copyrights for the package.
"You" is you, if you're thinking about copying or distributing
this Package.
"Reasonable copying fee" is whatever you can justify on the
basis of media cost, duplication charges, time of people involved,
and so on. (You will not be required to justify it to the
Copyright Holder, but only to the computing community at large
as a market that must bear the fee.)
"Freely Available" means that no fee is charged for the item
itself, though there may be fees involved in handling the item.
It also means that recipients of the item may redistribute it
under the same conditions they received it.
1. You may make and give away verbatim copies of the source form of the
Standard Version of this Package without restriction, provided that you
duplicate all of the original copyright notices and associated disclaimers.
2. You may apply bug fixes, portability fixes and other modifications
derived from the Public Domain or from the Copyright Holder. A Package
modified in such a way shall still be considered the Standard Version.
3. You may otherwise modify your copy of this Package in any way, provided
that you insert a prominent notice in each changed file stating how and
when you changed that file, and provided that you do at least ONE of the
following:
a) place your modifications in the Public Domain or otherwise make them
Freely Available, such as by posting said modifications to Usenet or
an equivalent medium, or placing the modifications on a major archive
site such as uunet.uu.net, or by allowing the Copyright Holder to include
your modifications in the Standard Version of the Package.
b) use the modified Package only within your corporation or organization.
c) rename any non-standard executables so the names do not conflict
with standard executables, which must also be provided, and provide
a separate manual page for each non-standard executable that clearly
documents how it differs from the Standard Version.
d) make other distribution arrangements with the Copyright Holder.
4. You may distribute the programs of this Package in object code or
executable form, provided that you do at least ONE of the following:
a) distribute a Standard Version of the executables and library files,
together with instructions (in the manual page or equivalent) on where
to get the Standard Version.
b) accompany the distribution with the machine-readable source of
the Package with your modifications.
c) give non-standard executables non-standard names, and clearly
document the differences in manual pages (or equivalent), together
with instructions on where to get the Standard Version.
d) make other distribution arrangements with the Copyright Holder.
5. You may charge a reasonable copying fee for any distribution of this
Package. You may charge any fee you choose for support of this
Package. You may not charge a fee for this Package itself. However,
you may distribute this Package in aggregate with other (possibly
commercial) programs as part of a larger (possibly commercial) software
distribution provided that you do not advertise this Package as a
product of your own. You may embed this Package's interpreter within
an executable of yours (by linking); this shall be construed as a mere
form of aggregation, provided that the complete Standard Version of the
interpreter is so embedded.
6. The scripts and library files supplied as input to or produced as
output from the programs of this Package do not automatically fall
under the copyright of this Package, but belong to whoever generated
them, and may be sold commercially, and may be aggregated with this
Package. If such scripts or library files are aggregated with this
Package via the so-called "undump" or "unexec" methods of producing a
binary executable image, then distribution of such an image shall
neither be construed as a distribution of this Package nor shall it
fall under the restrictions of Paragraphs 3 and 4, provided that you do
not represent such an executable image as a Standard Version of this
Package.
7. C subroutines (or comparably compiled subroutines in other
languages) supplied by you and linked into this Package in order to
emulate subroutines and variables of the language defined by this
Package shall not be considered part of this Package, but are the
equivalent of input as in Paragraph 6, provided these subroutines do
not change the language in any way that would cause it to fail the
regression tests for the language.
8. Aggregation of this Package with a commercial distribution is always
permitted provided that the use of this Package is embedded; that is,
when no overt attempt is made to make this Package's interfaces visible
to the end user of the commercial distribution. Such use shall not be
construed as a distribution of this Package.
9. The name of the Copyright Holder may not be used to endorse or promote
products derived from this software without specific prior written permission.
10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
The End
</PRE>
</div>
<div id="short_lists" class="short_lists">
<div id="spacer_for_google"></div>
</div>
</div>
</div>
<div style="clear:both"></div>
<div id="footer">
<div class="sub_holder">
<p class="sites">
&nbsp;When you need <em>Perl</em> think <strong>Perl.org</strong>: <a href="http://www.perl.org/">www</a> | <a href="http://blogs.perl.org/">blogs</a> | <a href="http://jobs.perl.org/">jobs</a> | <a href="http://learn.perl.org/">learn</a> <!-- | <a href="http://lists.perl.org/">lists</a> --> | <a href="http://dev.perl.org/">dev</a>
</p>
<p class="copyright">
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/3.0/us/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by-nc-nd/3.0/us/80x15.png"></a> © 2002-2011 Perl.org | <a href="/siteinfo.html">Site Info</a>
</p>
<div style="clear:both"></div>
<div id="google_translate_element"></div><script type="text/javascript">
function googleTranslateElementInit() {
new google.translate.TranslateElement({
pageLanguage: 'en',
autoDisplay: false
,
gaTrack: true,
gaId: 'UA-50555-6'
}, 'google_translate_element');
}
</script><script src="http://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit" type="text/javascript">
</script>
</div>
</div>
<!--[if lt IE 7]>
<div style='border: 1px solid #F7941D; background: #FEEFDA; text-align: center; clear: both; height: 75px; position: relative; margin-top: 2em;'>
<div style='position: absolute; right: 3px; top: 3px; font-family: courier new; font-weight: bold;'><a href='#' onclick='javascript:this.parentNode.parentNode.style.display="none"; return false;'><img src='http://www.ie6nomore.com/files/theme/ie6nomore-cornerx.jpg' style='border: none;' alt='Close this notice'/></a></div>
<div style='width: 640px; margin: 0 auto; text-align: left; padding: 0; overflow: hidden; color: black;'>
<div style='width: 75px; float: left;'><img src='http://www.ie6nomore.com/files/theme/ie6nomore-warning.jpg' alt='Warning!'/></div>
<div style='width: 275px; float: left; font-family: Arial, sans-serif;'>
<div style='font-size: 14px; font-weight: bold; margin-top: 12px;'>You are using an outdated browser</div>
<div style='font-size: 12px; margin-top: 6px; line-height: 12px;'>For a better experience using this site, please upgrade to a modern web browser.</div>
</div>
<div style='width: 75px; float: left;'><a href='http://www.firefox.com' target='_blank'><img src='http://www.ie6nomore.com/files/theme/ie6nomore-firefox.jpg' style='border: none;' alt='Get Firefox 3.5'/></a></div>
<div style='width: 75px; float: left;'><a href='http://www.browserforthebetter.com/download.html' target='_blank'><img src='http://www.ie6nomore.com/files/theme/ie6nomore-ie8.jpg' style='border: none;' alt='Get Internet Explorer 8'/></a></div>
<div style='width: 73px; float: left;'><a href='http://www.apple.com/safari/download/' target='_blank'><img src='http://www.ie6nomore.com/files/theme/ie6nomore-safari.jpg' style='border: none;' alt='Get Safari 4'/></a></div>
<div style='float: left;'><a href='http://www.google.com/chrome' target='_blank'><img src='http://www.ie6nomore.com/files/theme/ie6nomore-chrome.jpg' style='border: none;' alt='Get Google Chrome'/></a></div>
</div>
</div>
<![endif]-->
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-50555-6']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script');
ga.src = ('https:' == document.location.protocol ? 'https://ssl' :
'http://www') + '.google-analytics.com/ga.js';
ga.setAttribute('async', 'true');
document.documentElement.firstChild.appendChild(ga);
})();
</script>
</body>
</html>
Feature: attribute of subject
Use the its() method as a short-hand to generate a nested example group with
a single example that specifies the expected value of an attribute of the
subject. This can be used with an implicit or explicit subject.
its() accepts a symbol or a string, and a block representing the example.
its(:size) { should eq(1) }
its("length") { should eq(1) }
You can use a string with dots to specify a nested attribute (i.e. an
attribute of the attribute of the subject).
its("phone_numbers.size") { should eq(2) }
When the subject is a hash, you can pass in an array with a single key to
access the value at that key in the hash.
its([:key]) { should eq(value) }
Scenario: specify value of an attribute
Given a file named "example_spec.rb" with:
"""
describe Array do
context "when first created" do
its(:size) { should eq(0) }
end
end
"""
When I run `rspec example_spec.rb --format documentation`
Then the output should contain:
"""
Array
when first created
size
should eq 0
"""
Scenario: specify value of a nested attribute
Given a file named "example_spec.rb" with:
"""
class Person
attr_reader :phone_numbers
def initialize
@phone_numbers = []
end
end
describe Person do
context "with one phone number (555-1212)"do
subject do
person = Person.new
person.phone_numbers << "555-1212"
person
end
its("phone_numbers.first") { should eq("555-1212") }
end
end
"""
When I run `rspec example_spec.rb --format documentation`
Then the output should contain:
"""
Person
with one phone number (555-1212)
phone_numbers.first
should eq "555-1212"
"""
Scenario: specify value of an attribute of a hash
Given a file named "example_spec.rb" with:
"""
describe Hash do
context "with two items" do
subject do
{:one => 'one', :two => 'two'}
end
its(:size) { should eq(2) }
end
end
"""
When I run `rspec example_spec.rb`
Then the examples should all pass
Scenario: specify value for key in a hash
Given a file named "example_spec.rb" with:
"""
describe Hash do
context "with keys :one and 'two'" do
subject do
{:one => 1, "two" => 2}
end
its([:one]) { should eq(1) }
its(["two"]) { should eq(2) }
end
end
"""
When I run `rspec example_spec.rb`
Then the examples should all pass
Scenario: specify value for key in a hash-like object
Given a file named "example_spec.rb" with:
"""
require 'matrix'
describe Matrix do
context "with values [[1, 2], [3, 4]]" do
subject do
Matrix[[1, 2], [3, 4]]
end
its([0, 1]) { should eq(2) }
its([1, 0]) { should eq(3) }
its([1, 2]) { should be_nil }
end
end
"""
When I run `rspec example_spec.rb`
Then the examples should all pass
require 'rspec/core'
RSpec::Core::Runner.autorun
#!/usr/bin/env ruby
#
# This file was generated by RubyGems.
#
# The application 'rspec-core' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'rubygems'
version = ">= 0"
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
version = $1
ARGV.shift
end
gem 'rspec-core', version
load Gem.bin_path('rspec-core', 'autospec', version)
#!/usr/bin/env ruby
require 'rspec/core/deprecation'
RSpec.warn_deprecation <<-WARNING
************************************************************
REMOVAL NOTICE: you are using behaviour that has been
removed from rspec-2.
* The 'autospec' command is no longer supported.
* Please use 'autotest' insted.
This message will be removed from a future version of rspec.
************************************************************
WARNING

RSpec ships with a specialized subclass of Autotest. To use it, just add a .rspec file to your project's root directory, and run the autotest command as normal:

$ autotest

Bundler

The autotest command generates a shell command that runs your specs. If you are using Bundler, and you want the shell command to include bundle exec, require the Autotest bundler plugin in a .autotest file in the project's root directory or your home directory:

# in .autotest
require "autotest/bundler"

Upgrading from previous versions of rspec

Previous versions of RSpec used a different mechanism for telling autotest to invoke RSpec's Autotest extension: it generated an autotest/discover.rb file in the project's root directory. This is no longer necessary with the new approach of RSpec looking for a .rspec file, so feel free to delete the autotest/discover.rb file in the project root if you have one.

Gotchas

Invalid Option: --tty

The --tty option was added in rspec-core-2.2.1, and is used internally by RSpec. If you see an error citing it as an invalid option, you'll probably see there are two or more versions of rspec-core in the backtrace: one < 2.2.1 and one >= 2.2.1.

This usually happens because you have a newer rspec-core installed, and an older rspec-core specified in a Bundler Gemfile. If this is the case, you can:

  1. specify the newer version in the Gemfile (recommended)
  2. prefix the autotest command with bundle exec
module RSpec
module Core
# @private
module ConstMissing
# Used to print deprecation warnings for Rspec and Spec constants (use
# RSpec instead)
def const_missing(name)
case name
when :Rspec, :Spec
RSpec.warn_deprecation <<-WARNING
*****************************************************************
DEPRECATION WARNING: you are using a deprecated constant that will
be removed from a future version of RSpec.
#{caller(0)[2]}
* #{name} is deprecated.
* RSpec is the new top-level module in RSpec-2
*****************************************************************
WARNING
RSpec
else
begin
super
rescue Exception => e
e.backtrace.reject! {|l| l =~ Regexp.compile(__FILE__) }
raise e
end
end
end
end
end
module Runner
# @deprecated use RSpec.configure instead.
def self.configure(&block)
RSpec.deprecate("Spec::Runner.configure", "RSpec.configure")
RSpec.configure(&block)
end
end
# @private
module Rake
# Used to print deprecation warnings for Rake::SpecTask constant (use
# RSpec::Core::RakeTask instead)
def self.const_missing(name)
case name
when :SpecTask
RSpec.deprecate("Spec::Rake::SpecTask", "RSpec::Core::RakeTask")
require 'rspec/core/rake_task'
RSpec::Core::RakeTask
else
begin
super
rescue Exception => e
e.backtrace.reject! {|l| l =~ Regexp.compile(__FILE__) }
raise e
end
end
end
end
end
Object.extend(RSpec::Core::ConstMissing)
require 'rspec/core/formatters/helpers'
require 'stringio'
module RSpec
module Core
module Formatters
class BaseFormatter
include Helpers
attr_accessor :example_group
attr_reader :duration, :examples, :output
attr_reader :example_count, :pending_count, :failure_count
attr_reader :failed_examples, :pending_examples
def self.relative_path(line)
line = line.sub(File.expand_path("."), ".")
line = line.sub(/\A([^:]+:\d+)$/, '\\1')
return nil if line == '-e:1'
line
end
def initialize(output)
@output = output || StringIO.new
@example_count = @pending_count = @failure_count = 0
@examples = []
@failed_examples = []
@pending_examples = []
@example_group = nil
end
# This method is invoked before any examples are run, right after
# they have all been collected. This can be useful for special
# formatters that need to provide progress on feedback (graphical ones)
#
# This will only be invoked once, and the next one to be invoked
# is #example_group_started
def start(example_count)
start_sync_output
@example_count = example_count
end
# This method is invoked at the beginning of the execution of each example group.
# +example_group+ is the example_group.
#
# The next method to be invoked after this is +example_passed+,
# +example_pending+, or +example_finished+
def example_group_started(example_group)
@example_group = example_group
end
# This method is invoked at the end of the execution of each example group.
# +example_group+ is the example_group.
def example_group_finished(example_group)
end
def example_started(example)
examples << example
end
def example_passed(example)
end
def example_pending(example)
@pending_examples << example
end
def example_failed(example)
@failed_examples << example
end
def message(message)
end
def stop
end
# This method is invoked after all of the examples have executed. The next method
# to be invoked after this one is #dump_failure (once for each failed example),
def start_dump
end
# Dumps detailed information about each example failure.
def dump_failures
end
# This method is invoked after the dumping of examples and failures.
def dump_summary(duration, example_count, failure_count, pending_count)
@duration = duration
@example_count = example_count
@failure_count = failure_count
@pending_count = pending_count
end
# This gets invoked after the summary if option is set to do so.
def dump_pending
end
def seed(number)
end
# This method is invoked at the very end. Allows the formatter to clean up, like closing open streams.
def close
restore_sync_output
end
def format_backtrace(backtrace, example)
return "" unless backtrace
return backtrace if example.metadata[:full_backtrace] == true
if at_exit_index = backtrace.index(RSpec::Core::Runner::AT_EXIT_HOOK_BACKTRACE_LINE)
backtrace = backtrace[0, at_exit_index]
end
cleansed = backtrace.map { |line| backtrace_line(line) }.compact
cleansed.empty? ? backtrace : cleansed
end
protected
def configuration
RSpec.configuration
end
def backtrace_line(line)
return nil if configuration.cleaned_from_backtrace?(line)
self.class.relative_path(line)
end
def read_failed_line(exception, example)
unless matching_line = find_failed_line(exception.backtrace, example.file_path)
return "Unable to find matching line from backtrace"
end
file_path, line_number = matching_line.match(/(.+?):(\d+)(|:\d+)/)[1..2]
if File.exist?(file_path)
File.readlines(file_path)[line_number.to_i - 1]
else
"Unable to find #{file_path} to read failed line"
end
end
def find_failed_line(backtrace, path)
backtrace.detect { |line|
match = line.match(/(.+?):(\d+)(|:\d+)/)
match && match[1].downcase == path.downcase
}
end
def start_sync_output
@old_sync, output.sync = output.sync, true if output_supports_sync
end
def restore_sync_output
output.sync = @old_sync if output_supports_sync and !output.closed?
end
def output_supports_sync
output.respond_to?(:sync=)
end
def profile_examples?
configuration.profile_examples
end
def color_enabled?
configuration.color_enabled?
end
end
end
end
end
require 'spec_helper'
require 'rspec/core/formatters/base_formatter'
describe RSpec::Core::Formatters::BaseFormatter do
let(:output) { StringIO.new }
let(:formatter) { RSpec::Core::Formatters::BaseFormatter.new(output) }
describe "backtrace_line" do
it "trims current working directory" do
formatter.__send__(:backtrace_line, File.expand_path(__FILE__)).should eq("./spec/rspec/core/formatters/base_formatter_spec.rb")
end
it "leaves the original line intact" do
original_line = File.expand_path(__FILE__)
formatter.__send__(:backtrace_line, original_line)
original_line.should eq(File.expand_path(__FILE__))
end
end
describe "read_failed_line" do
it "deals gracefully with a heterogeneous language stack trace" do
exception = mock(:Exception, :backtrace => [
"at Object.prototypeMethod (foo:331:18)",
"at Array.forEach (native)",
"at a_named_javascript_function (/some/javascript/file.js:39:5)",
"/some/line/of/ruby.rb:14"
])
example = mock(:Example, :file_path => __FILE__)
lambda {
formatter.send(:read_failed_line, exception, example)
}.should_not raise_error
end
context "when String alias to_int to_i" do
before do
String.class_eval do
alias :to_int :to_i
end
end
after do
String.class_eval do
undef to_int
end
end
it "doesn't hang when file exists" do
pending("This issue still exists on JRuby, but should be resolved shortly: https://github.com/rspec/rspec-core/issues/295", :if => RUBY_PLATFORM == 'java')
exception = mock(:Exception, :backtrace => [ "#{__FILE__}:#{__LINE__}"])
example = mock(:Example, :file_path => __FILE__)
formatter.send(:read_failed_line, exception, example).should
eql(%Q{ exception = mock(:Exception, :backtrace => [ "\#{__FILE__}:\#{__LINE__}"])\n})
end
end
end
describe "#format_backtrace" do
let(:rspec_expectations_dir) { "/path/to/rspec-expectations/lib" }
let(:rspec_core_dir) { "/path/to/rspec-core/lib" }
let(:backtrace) do
[
"#{rspec_expectations_dir}/rspec/matchers/operator_matcher.rb:51:in `eval_match'",
"#{rspec_expectations_dir}/rspec/matchers/operator_matcher.rb:29:in `=='",
"./my_spec.rb:5",
"#{rspec_core_dir}/rspec/core/example.rb:52:in `run'",
"#{rspec_core_dir}/rspec/core/runner.rb:37:in `run'",
RSpec::Core::Runner::AT_EXIT_HOOK_BACKTRACE_LINE,
"./my_spec.rb:3"
]
end
it "removes lines from rspec and lines that come before the invocation of the at_exit autorun hook" do
formatter.format_backtrace(backtrace, stub.as_null_object).should eq(["./my_spec.rb:5"])
end
end
end
require 'rspec/core/formatters/base_formatter'
module RSpec
module Core
module Formatters
class BaseTextFormatter < BaseFormatter
def message(message)
output.puts message
end
def dump_failures
return if failed_examples.empty?
output.puts
output.puts "Failures:"
failed_examples.each_with_index do |example, index|
output.puts
pending_fixed?(example) ? dump_pending_fixed(example, index) : dump_failure(example, index)
dump_backtrace(example)
end
end
def colorise_summary(summary)
if failure_count > 0
red(summary)
elsif pending_count > 0
yellow(summary)
else
green(summary)
end
end
def dump_summary(duration, example_count, failure_count, pending_count)
super(duration, example_count, failure_count, pending_count)
# Don't print out profiled info if there are failures, it just clutters the output
dump_profile if profile_examples? && failure_count == 0
output.puts "\nFinished in #{format_seconds(duration)} seconds\n"
output.puts colorise_summary(summary_line(example_count, failure_count, pending_count))
dump_commands_to_rerun_failed_examples
end
def dump_commands_to_rerun_failed_examples
return if failed_examples.empty?
output.puts
output.puts("Failed examples:")
output.puts
failed_examples.each do |example|
output.puts(red("rspec #{BaseFormatter::relative_path(example.location)}") + " " + cyan("# #{example.full_description}"))
end
end
def dump_profile
sorted_examples = examples.sort_by { |example| example.execution_result[:run_time] }.reverse.first(10)
output.puts "\nTop #{sorted_examples.size} slowest examples:\n"
sorted_examples.each do |example|
output.puts " #{example.full_description}"
output.puts cyan(" #{red(format_seconds(example.execution_result[:run_time]))} #{red("seconds")} #{format_caller(example.location)}")
end
end
def summary_line(example_count, failure_count, pending_count)
summary = pluralize(example_count, "example")
summary << ", " << pluralize(failure_count, "failure")
summary << ", #{pending_count} pending" if pending_count > 0
summary
end
def dump_pending
unless pending_examples.empty?
output.puts
output.puts "Pending:"
pending_examples.each do |pending_example|
output.puts yellow(" #{pending_example.full_description}")
output.puts cyan(" # #{pending_example.execution_result[:pending_message]}")
output.puts cyan(" # #{format_caller(pending_example.location)}")
if pending_example.execution_result[:exception] \
&& RSpec.configuration.show_failures_in_pending_blocks?
dump_failure_info(pending_example)
dump_backtrace(pending_example)
end
end
end
end
def seed(number)
output.puts
output.puts "Randomized with seed #{number}"
output.puts
end
def close
output.close if IO === output && output != $stdout
end
protected
def color(text, color_code)
color_enabled? ? "#{color_code}#{text}\e[0m" : text
end
def bold(text)
color(text, "\e[1m")
end
def red(text)
color(text, "\e[31m")
end
def green(text)
color(text, "\e[32m")
end
def yellow(text)
color(text, "\e[33m")
end
def blue(text)
color(text, "\e[34m")
end
def magenta(text)
color(text, "\e[35m")
end
def cyan(text)
color(text, "\e[36m")
end
def white(text)
color(text, "\e[37m")
end
def short_padding
' '
end
def long_padding
' '
end
private
def pluralize(count, string)
"#{count} #{string}#{'s' unless count == 1}"
end
def format_caller(caller_info)
backtrace_line(caller_info.to_s.split(':in `block').first)
end
def dump_backtrace(example)
format_backtrace(example.execution_result[:exception].backtrace, example).each do |backtrace_info|
output.puts cyan("#{long_padding}# #{backtrace_info}")
end
end
def dump_pending_fixed(example, index)
output.puts "#{short_padding}#{index.next}) #{example.full_description} FIXED"
output.puts blue("#{long_padding}Expected pending '#{example.metadata[:execution_result][:pending_message]}' to fail. No Error was raised.")
end
def pending_fixed?(example)
example.execution_result[:exception].pending_fixed?
end
def dump_failure(example, index)
output.puts "#{short_padding}#{index.next}) #{example.full_description}"
dump_failure_info(example)
end
def dump_failure_info(example)
exception = example.execution_result[:exception]
output.puts "#{long_padding}#{red("Failure/Error:")} #{red(read_failed_line(exception, example).strip)}"
output.puts "#{long_padding}#{red(exception.class.name << ":")}" unless exception.class.name =~ /RSpec/
exception.message.split("\n").each { |line| output.puts "#{long_padding} #{red(line)}" } if exception.message
if shared_group = find_shared_group(example)
dump_shared_failure_info(shared_group)
end
end
def dump_shared_failure_info(group)
output.puts "#{long_padding}Shared Example Group: \"#{group.metadata[:shared_group_name]}\" called from " +
"#{backtrace_line(group.metadata[:example_group][:location])}"
end
def find_shared_group(example)
group_and_ancestors(example).find {|group| group.metadata[:shared_group_name]}
end
def group_and_ancestors(example)
example.example_group.ancestors.push(example.example_group)
end
end
end
end
end
Feature: basic structure (describe/it)
RSpec is a DSL for creating executable examples of how code is expected to
behave, organized in groups. It uses the words "describe" and "it" so we can
express concepts like a conversation:
"Describe an account when it is first opened."
"It has a balance of zero."
The `describe` method creates an example group. Within the block passed to
`describe` you can declare nested groups using the `describe` or `context`
methods, or you can declare examples using the `it` or `specify` methods.
Under the hood, an example group is a class in which the block passed to
`describe` or `context` is evaluated. The blocks passed to `it` are evaluated
in the context of an _instance_ of that class.
Scenario: one group, one example
Given a file named "sample_spec.rb" with:
"""
describe "something" do
it "does something" do
end
end
"""
When I run `rspec sample_spec.rb -fn`
Then the output should contain:
"""
something
does something
"""
Scenario: nested example groups (using context)
Given a file named "nested_example_groups_spec.rb" with:
"""
describe "something" do
context "in one context" do
it "does one thing" do
end
end
context "in another context" do
it "does another thing" do
end
end
end
"""
When I run `rspec nested_example_groups_spec.rb -fdoc`
Then the output should contain:
"""
something
in one context
does one thing
in another context
does another thing
"""
Feature: before and after hooks
Use `before` and `after` hooks to execute arbitrary code before and/or
after the body of an example is run:
before(:each) # run before each example
before(:all) # run one time only, before all of the examples in a group
after(:each) # run after each example
after(:all) # run one time only, after all of the examples in a group
Before and after blocks are called in the following order:
before suite
before all
before each
after each
after all
after suite
`before` and `after` hooks can be defined directly in the example groups they
should run in, or in a global RSpec.configure block.
Scenario: define before(:each) block
Given a file named "before_each_spec.rb" with:
"""
require "rspec/expectations"
class Thing
def widgets
@widgets ||= []
end
end
describe Thing do
before(:each) do
@thing = Thing.new
end
describe "initialized in before(:each)" do
it "has 0 widgets" do
@thing.should have(0).widgets
end
it "can get accept new widgets" do
@thing.widgets << Object.new
end
it "does not share state across examples" do
@thing.should have(0).widgets
end
end
end
"""
When I run `rspec before_each_spec.rb`
Then the examples should all pass
Scenario: define before(:all) block in example group
Given a file named "before_all_spec.rb" with:
"""
require "rspec/expectations"
class Thing
def widgets
@widgets ||= []
end
end
describe Thing do
before(:all) do
@thing = Thing.new
end
describe "initialized in before(:all)" do
it "has 0 widgets" do
@thing.should have(0).widgets
end
it "can get accept new widgets" do
@thing.widgets << Object.new
end
it "shares state across examples" do
@thing.should have(1).widgets
end
end
end
"""
When I run `rspec before_all_spec.rb`
Then the examples should all pass
When I run `rspec before_all_spec.rb:15`
Then the examples should all pass
Scenario: failure in before(:all) block
Given a file named "before_all_spec.rb" with:
"""
describe "an error in before(:all)" do
before(:all) do
raise "oops"
end
it "fails this example" do
end
it "fails this example, too" do
end
after(:all) do
puts "after all ran"
end
describe "nested group" do
it "fails this third example" do
end
it "fails this fourth example" do
end
describe "yet another level deep" do
it "fails this last example" do
end
end
end
end
"""
When I run `rspec before_all_spec.rb --format documentation`
Then the output should contain "5 examples, 5 failures"
And the output should contain:
"""
an error in before(:all)
fails this example (FAILED - 1)
fails this example, too (FAILED - 2)
nested group
fails this third example (FAILED - 3)
fails this fourth example (FAILED - 4)
yet another level deep
fails this last example (FAILED - 5)
after all ran
"""
When I run `rspec before_all_spec.rb:9 --format documentation`
Then the output should contain "1 example, 1 failure"
And the output should contain:
"""
an error in before(:all)
fails this example, too (FAILED - 1)
"""
Scenario: failure in after(:all) block
Given a file named "after_all_spec.rb" with:
"""
describe "an error in after(:all)" do
after(:all) do
raise StandardError.new("Boom!")
end
it "passes this example" do
end
it "passes this example, too" do
end
end
"""
When I run `rspec after_all_spec.rb`
Then the examples should all pass
And the output should contain:
"""
An error occurred in an after(:all) hook.
StandardError: Boom!
"""
Scenario: define before and after blocks in configuration
Given a file named "befores_in_configuration_spec.rb" with:
"""
require "rspec/expectations"
RSpec.configure do |config|
config.before(:each) do
@before_each = "before each"
end
config.before(:all) do
@before_all = "before all"
end
end
describe "stuff in before blocks" do
describe "with :all" do
it "should be available in the example" do
@before_all.should eq("before all")
end
end
describe "with :each" do
it "should be available in the example" do
@before_each.should eq("before each")
end
end
end
"""
When I run `rspec befores_in_configuration_spec.rb`
Then the examples should all pass
Scenario: before/after blocks are run in order
Given a file named "ensure_block_order_spec.rb" with:
"""
require "rspec/expectations"
describe "before and after callbacks" do
before(:all) do
puts "before all"
end
before(:each) do
puts "before each"
end
after(:each) do
puts "after each"
end
after(:all) do
puts "after all"
end
it "gets run in order" do
end
end
"""
When I run `rspec ensure_block_order_spec.rb`
Then the output should contain:
"""
before all
before each
after each
.after all
"""
Scenario: before/after blocks defined in config are run in order
Given a file named "configuration_spec.rb" with:
"""
require "rspec/expectations"
RSpec.configure do |config|
config.before(:suite) do
puts "before suite"
end
config.before(:all) do
puts "before all"
end
config.before(:each) do
puts "before each"
end
config.after(:each) do
puts "after each"
end
config.after(:all) do
puts "after all"
end
config.after(:suite) do
puts "after suite"
end
end
describe "ignore" do
example "ignore" do
end
end
"""
When I run `rspec configuration_spec.rb`
Then the output should contain:
"""
before suite
before all
before each
after each
.after all
after suite
"""
Scenario: before/after all blocks are run once
Given a file named "before_and_after_all_spec.rb" with:
"""
describe "before and after callbacks" do
before(:all) do
puts "outer before all"
end
example "in outer group" do
end
after(:all) do
puts "outer after all"
end
describe "nested group" do
before(:all) do
puts "inner before all"
end
example "in nested group" do
end
after(:all) do
puts "inner after all"
end
end
end
"""
When I run `rspec before_and_after_all_spec.rb`
Then the examples should all pass
And the output should contain:
"""
outer before all
.inner before all
.inner after all
outer after all
"""
When I run `rspec before_and_after_all_spec.rb:14`
Then the examples should all pass
And the output should contain:
"""
outer before all
inner before all
.inner after all
outer after all
"""
When I run `rspec before_and_after_all_spec.rb:6`
Then the examples should all pass
And the output should contain:
"""
outer before all
.outer after all
"""
Scenario: nested examples have access to state set in outer before(:all)
Given a file named "before_all_spec.rb" with:
"""
describe "something" do
before :all do
@value = 123
end
describe "nested" do
it "access state set in before(:all)" do
@value.should eq(123)
end
describe "nested more deeply" do
it "access state set in before(:all)" do
@value.should eq(123)
end
end
end
describe "nested in parallel" do
it "access state set in before(:all)" do
@value.should eq(123)
end
end
end
"""
When I run `rspec before_all_spec.rb`
Then the examples should all pass
Scenario: before/after all blocks have access to state
Given a file named "before_and_after_all_spec.rb" with:
"""
describe "before and after callbacks" do
before(:all) do
@outer_state = "set in outer before all"
end
example "in outer group" do
@outer_state.should eq("set in outer before all")
end
describe "nested group" do
before(:all) do
@inner_state = "set in inner before all"
end
example "in nested group" do
@outer_state.should eq("set in outer before all")
@inner_state.should eq("set in inner before all")
end
after(:all) do
@inner_state.should eq("set in inner before all")
end
end
after(:all) do
@outer_state.should eq("set in outer before all")
end
end
"""
When I run `rspec before_and_after_all_spec.rb`
Then the examples should all pass
Scenario: exception in before(:each) is captured and reported as failure
Given a file named "error_in_before_each_spec.rb" with:
"""
describe "error in before(:each)" do
before(:each) do
raise "this error"
end
it "is reported as failure" do
end
end
"""
When I run `rspec error_in_before_each_spec.rb`
Then the output should contain "1 example, 1 failure"
And the output should contain "this error"
#! /usr/env/bin ruby
#--
# Copyright 2004 Austin Ziegler <diff-lcs@halostatue.ca>
# adapted from:
# Algorithm::Diff (Perl) by Ned Konz <perl@bike-nomad.com>
# Smalltalk by Mario I. Wolczko <mario@wolczko.com>
# implements McIlroy-Hunt diff algorithm
#
# This program is free software. It may be redistributed and/or modified under
# the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
# Ruby licence.
#
# $Id$
#++
# Contains Diff::LCS::Block for bin/ldiff.
# A block is an operation removing, adding, or changing a group of items.
# Basically, this is just a list of changes, where each change adds or
# deletes a single item. Used by bin/ldiff.
class Diff::LCS::Block
attr_reader :changes, :insert, :remove
def initialize(chunk)
@changes = []
@insert = []
@remove = []
chunk.each do |item|
@changes << item
@remove << item if item.deleting?
@insert << item if item.adding?
end
end
def diff_size
@insert.size - @remove.size
end
def op
case [@remove.empty?, @insert.empty?]
when [false, false]
'!'
when [false, true]
'-'
when [true, false]
'+'
else # [true, true]
'^'
end
end
end
#! /usr/env/bin ruby
#--
# Copyright 2004 Austin Ziegler <diff-lcs@halostatue.ca>
# adapted from:
# Algorithm::Diff (Perl) by Ned Konz <perl@bike-nomad.com>
# Smalltalk by Mario I. Wolczko <mario@wolczko.com>
# implements McIlroy-Hunt diff algorithm
#
# This program is free software. It may be redistributed and/or modified under
# the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
# Ruby licence.
#
# $Id$
#++
# Contains definitions for all default callback objects.
require 'diff/lcs/change'
module Diff::LCS
# This callback object implements the default set of callback events, which
# only returns the event itself. Note that #finished_a and #finished_b are
# not implemented -- I haven't yet figured out where they would be useful.
#
# Note that this is intended to be called as is, e.g.,
#
# Diff::LCS.LCS(seq1, seq2, Diff::LCS::DefaultCallbacks)
class DefaultCallbacks
class << self
# Called when two items match.
def match(event)
event
end
# Called when the old value is discarded in favour of the new value.
def discard_a(event)
event
end
# Called when the new value is discarded in favour of the old value.
def discard_b(event)
event
end
# Called when both the old and new values have changed.
def change(event)
event
end
private :new
end
end
# An alias for DefaultCallbacks that is used in Diff::LCS#traverse_sequences.
#
# Diff::LCS.LCS(seq1, seq2, Diff::LCS::SequenceCallbacks)
SequenceCallbacks = DefaultCallbacks
# An alias for DefaultCallbacks that is used in Diff::LCS#traverse_balanced.
#
# Diff::LCS.LCS(seq1, seq2, Diff::LCS::BalancedCallbacks)
BalancedCallbacks = DefaultCallbacks
end
# This will produce a compound array of simple diff change objects. Each
# element in the #diffs array is a +hunk+ or +hunk+ array, where each
# element in each +hunk+ array is a single Change object representing the
# addition or removal of a single element from one of the two tested
# sequences. The +hunk+ provides the full context for the changes.
#
# diffs = Diff::LCS.diff(seq1, seq2)
# # This example shows a simplified array format.
# # [ [ [ '-', 0, 'a' ] ], # 1
# # [ [ '+', 2, 'd' ] ], # 2
# # [ [ '-', 4, 'h' ], # 3
# # [ '+', 4, 'f' ] ],
# # [ [ '+', 6, 'k' ] ], # 4
# # [ [ '-', 8, 'n' ], # 5
# # [ '-', 9, 'p' ],
# # [ '+', 9, 'r' ],
# # [ '+', 10, 's' ],
# # [ '+', 11, 't' ] ] ]
#
# There are five hunks here. The first hunk says that the +a+ at position 0
# of the first sequence should be deleted (<tt>'-'</tt>). The second hunk
# says that the +d+ at position 2 of the second sequence should be inserted
# (<tt>'+'</tt>). The third hunk says that the +h+ at position 4 of the
# first sequence should be removed and replaced with the +f+ from position 4
# of the second sequence. The other two hunks are described similarly.
#
# === Use
# This callback object must be initialised and is used by the Diff::LCS#diff
# method.
#
# cbo = Diff::LCS::DiffCallbacks.new
# Diff::LCS.LCS(seq1, seq2, cbo)
# cbo.finish
#
# Note that the call to #finish is absolutely necessary, or the last set of
# changes will not be visible. Alternatively, can be used as:
#
# cbo = Diff::LCS::DiffCallbacks.new { |tcbo| Diff::LCS.LCS(seq1, seq2, tcbo) }
#
# The necessary #finish call will be made.
#
# === Simplified Array Format
# The simplified array format used in the example above can be obtained
# with:
#
# require 'pp'
# pp diffs.map { |e| e.map { |f| f.to_a } }
class Diff::LCS::DiffCallbacks
# Returns the difference set collected during the diff process.
attr_reader :diffs
def initialize # :yields self:
@hunk = []
@diffs = []
if block_given?
begin
yield self
ensure
self.finish
end
end
end
# Finalizes the diff process. If an unprocessed hunk still exists, then it
# is appended to the diff list.
def finish
add_nonempty_hunk
end
def match(event)
add_nonempty_hunk
end
def discard_a(event)
@hunk << Diff::LCS::Change.new('-', event.old_position, event.old_element)
end
def discard_b(event)
@hunk << Diff::LCS::Change.new('+', event.new_position, event.new_element)
end
private
def add_nonempty_hunk
@diffs << @hunk unless @hunk.empty?
@hunk = []
end
end
# This will produce a compound array of contextual diff change objects. Each
# element in the #diffs array is a "hunk" array, where each element in each
# "hunk" array is a single change. Each change is a Diff::LCS::ContextChange
# that contains both the old index and new index values for the change. The
# "hunk" provides the full context for the changes. Both old and new objects
# will be presented for changed objects. +nil+ will be substituted for a
# discarded object.
#
# seq1 = %w(a b c e h j l m n p)
# seq2 = %w(b c d e f j k l m r s t)
#
# diffs = Diff::LCS.diff(seq1, seq2, Diff::LCS::ContextDiffCallbacks)
# # This example shows a simplified array format.
# # [ [ [ '-', [ 0, 'a' ], [ 0, nil ] ] ], # 1
# # [ [ '+', [ 3, nil ], [ 2, 'd' ] ] ], # 2
# # [ [ '-', [ 4, 'h' ], [ 4, nil ] ], # 3
# # [ '+', [ 5, nil ], [ 4, 'f' ] ] ],
# # [ [ '+', [ 6, nil ], [ 6, 'k' ] ] ], # 4
# # [ [ '-', [ 8, 'n' ], [ 9, nil ] ], # 5
# # [ '+', [ 9, nil ], [ 9, 'r' ] ],
# # [ '-', [ 9, 'p' ], [ 10, nil ] ],
# # [ '+', [ 10, nil ], [ 10, 's' ] ],
# # [ '+', [ 10, nil ], [ 11, 't' ] ] ] ]
#
# The five hunks shown are comprised of individual changes; if there is a
# related set of changes, they are still shown individually.
#
# This callback can also be used with Diff::LCS#sdiff, which will produce
# results like:
#
# diffs = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextCallbacks)
# # This example shows a simplified array format.
# # [ [ [ "-", [ 0, "a" ], [ 0, nil ] ] ], # 1
# # [ [ "+", [ 3, nil ], [ 2, "d" ] ] ], # 2
# # [ [ "!", [ 4, "h" ], [ 4, "f" ] ] ], # 3
# # [ [ "+", [ 6, nil ], [ 6, "k" ] ] ], # 4
# # [ [ "!", [ 8, "n" ], [ 9, "r" ] ], # 5
# # [ "!", [ 9, "p" ], [ 10, "s" ] ],
# # [ "+", [ 10, nil ], [ 11, "t" ] ] ] ]
#
# The five hunks are still present, but are significantly shorter in total
# presentation, because changed items are shown as changes ("!") instead of
# potentially "mismatched" pairs of additions and deletions.
#
# The result of this operation is similar to that of
# Diff::LCS::SDiffCallbacks. They may be compared as:
#
# s = Diff::LCS.sdiff(seq1, seq2).reject { |e| e.action == "=" }
# c = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextDiffCallbacks).flatten
#
# s == c # -> true
#
# === Use
# This callback object must be initialised and can be used by the
# Diff::LCS#diff or Diff::LCS#sdiff methods.
#
# cbo = Diff::LCS::ContextDiffCallbacks.new
# Diff::LCS.LCS(seq1, seq2, cbo)
# cbo.finish
#
# Note that the call to #finish is absolutely necessary, or the last set of
# changes will not be visible. Alternatively, can be used as:
#
# cbo = Diff::LCS::ContextDiffCallbacks.new { |tcbo| Diff::LCS.LCS(seq1, seq2, tcbo) }
#
# The necessary #finish call will be made.
#
# === Simplified Array Format
# The simplified array format used in the example above can be obtained
# with:
#
# require 'pp'
# pp diffs.map { |e| e.map { |f| f.to_a } }
class Diff::LCS::ContextDiffCallbacks < Diff::LCS::DiffCallbacks
def discard_a(event)
@hunk << Diff::LCS::ContextChange.simplify(event)
end
def discard_b(event)
@hunk << Diff::LCS::ContextChange.simplify(event)
end
def change(event)
@hunk << Diff::LCS::ContextChange.simplify(event)
end
end
# This will produce a simple array of diff change objects. Each element in
# the #diffs array is a single ContextChange. In the set of #diffs provided
# by SDiffCallbacks, both old and new objects will be presented for both
# changed <strong>and unchanged</strong> objects. +nil+ will be substituted
# for a discarded object.
#
# The diffset produced by this callback, when provided to Diff::LCS#sdiff,
# will compute and display the necessary components to show two sequences
# and their minimized differences side by side, just like the Unix utility
# +sdiff+.
#
# same same
# before | after
# old < -
# - > new
#
# seq1 = %w(a b c e h j l m n p)
# seq2 = %w(b c d e f j k l m r s t)
#
# diffs = Diff::LCS.sdiff(seq1, seq2)
# # This example shows a simplified array format.
# # [ [ "-", [ 0, "a"], [ 0, nil ] ],
# # [ "=", [ 1, "b"], [ 0, "b" ] ],
# # [ "=", [ 2, "c"], [ 1, "c" ] ],
# # [ "+", [ 3, nil], [ 2, "d" ] ],
# # [ "=", [ 3, "e"], [ 3, "e" ] ],
# # [ "!", [ 4, "h"], [ 4, "f" ] ],
# # [ "=", [ 5, "j"], [ 5, "j" ] ],
# # [ "+", [ 6, nil], [ 6, "k" ] ],
# # [ "=", [ 6, "l"], [ 7, "l" ] ],
# # [ "=", [ 7, "m"], [ 8, "m" ] ],
# # [ "!", [ 8, "n"], [ 9, "r" ] ],
# # [ "!", [ 9, "p"], [ 10, "s" ] ],
# # [ "+", [ 10, nil], [ 11, "t" ] ] ]
#
# The result of this operation is similar to that of
# Diff::LCS::ContextDiffCallbacks. They may be compared as:
#
# s = Diff::LCS.sdiff(seq1, seq2).reject { |e| e.action == "=" }
# c = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextDiffCallbacks).flatten
#
# s == c # -> true
#
# === Use
# This callback object must be initialised and is used by the Diff::LCS#sdiff
# method.
#
# cbo = Diff::LCS::SDiffCallbacks.new
# Diff::LCS.LCS(seq1, seq2, cbo)
#
# As with the other initialisable callback objects, Diff::LCS::SDiffCallbacks
# can be initialised with a block. As there is no "fininishing" to be done,
# this has no effect on the state of the object.
#
# cbo = Diff::LCS::SDiffCallbacks.new { |tcbo| Diff::LCS.LCS(seq1, seq2, tcbo) }
#
# === Simplified Array Format
# The simplified array format used in the example above can be obtained
# with:
#
# require 'pp'
# pp diffs.map { |e| e.to_a }
class Diff::LCS::SDiffCallbacks
# Returns the difference set collected during the diff process.
attr_reader :diffs
def initialize #:yields self:
@diffs = []
yield self if block_given?
end
def match(event)
@diffs << Diff::LCS::ContextChange.simplify(event)
end
def discard_a(event)
@diffs << Diff::LCS::ContextChange.simplify(event)
end
def discard_b(event)
@diffs << Diff::LCS::ContextChange.simplify(event)
end
def change(event)
@diffs << Diff::LCS::ContextChange.simplify(event)
end
end
#! /usr/env/bin ruby
#--
# Copyright 2004 Austin Ziegler <diff-lcs@halostatue.ca>
# adapted from:
# Algorithm::Diff (Perl) by Ned Konz <perl@bike-nomad.com>
# Smalltalk by Mario I. Wolczko <mario@wolczko.com>
# implements McIlroy-Hunt diff algorithm
#
# This program is free software. It may be redistributed and/or modified under
# the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
# Ruby licence.
#
# $Id$
#++
# Provides Diff::LCS::Change and Diff::LCS::ContextChange.
# Centralises the change test code in Diff::LCS::Change and
# Diff::LCS::ContextChange, since it's the same for both classes.
module Diff::LCS::ChangeTypeTests
def deleting?
@action == '-'
end
def adding?
@action == '+'
end
def unchanged?
@action == '='
end
def changed?
@changed == '!'
end
def finished_a?
@changed == '>'
end
def finished_b?
@changed == '<'
end
end
# Represents a simplistic (non-contextual) change. Represents the removal or
# addition of an element from either the old or the new sequenced enumerable.
class Diff::LCS::Change
# Returns the action this Change represents. Can be '+' (#adding?), '-'
# (#deleting?), '=' (#unchanged?), # or '!' (#changed?). When created by
# Diff::LCS#diff or Diff::LCS#sdiff, it may also be '>' (#finished_a?) or
# '<' (#finished_b?).
attr_reader :action
attr_reader :position
attr_reader :element
include Comparable
def ==(other)
(self.action == other.action) and
(self.position == other.position) and
(self.element == other.element)
end
def <=>(other)
r = self.action <=> other.action
r = self.position <=> other.position if r.zero?
r = self.element <=> other.element if r.zero?
r
end
def initialize(action, position, element)
@action = action
@position = position
@element = element
end
# Creates a Change from an array produced by Change#to_a.
def to_a
[@action, @position, @element]
end
def self.from_a(arr)
Diff::LCS::Change.new(arr[0], arr[1], arr[2])
end
include Diff::LCS::ChangeTypeTests
end
# Represents a contextual change. Contains the position and values of the
# elements in the old and the new sequenced enumerables as well as the action
# taken.
class Diff::LCS::ContextChange
# Returns the action this Change represents. Can be '+' (#adding?), '-'
# (#deleting?), '=' (#unchanged?), # or '!' (#changed?). When
# created by Diff::LCS#diff or Diff::LCS#sdiff, it may also be '>'
# (#finished_a?) or '<' (#finished_b?).
attr_reader :action
attr_reader :old_position
attr_reader :old_element
attr_reader :new_position
attr_reader :new_element
include Comparable
def ==(other)
(@action == other.action) and
(@old_position == other.old_position) and
(@new_position == other.new_position) and
(@old_element == other.old_element) and
(@new_element == other.new_element)
end
def inspect(*args)
%Q(#<#{self.class.name}:#{__id__} @action=#{action} positions=#{old_position},#{new_position} elements=#{old_element.inspect},#{new_element.inspect}>)
end
def <=>(other)
r = @action <=> other.action
r = @old_position <=> other.old_position if r.zero?
r = @new_position <=> other.new_position if r.zero?
r = @old_element <=> other.old_element if r.zero?
r = @new_element <=> other.new_element if r.zero?
r
end
def initialize(action, old_position, old_element, new_position, new_element)
@action = action
@old_position = old_position
@old_element = old_element
@new_position = new_position
@new_element = new_element
end
def to_a
[@action, [@old_position, @old_element], [@new_position, @new_element]]
end
# Creates a ContextChange from an array produced by ContextChange#to_a.
def self.from_a(arr)
if arr.size == 5
Diff::LCS::ContextChange.new(arr[0], arr[1], arr[2], arr[3], arr[4])
else
Diff::LCS::ContextChange.new(arr[0], arr[1][0], arr[1][1], arr[2][0],
arr[2][1])
end
end
# Simplifies a context change for use in some diff callbacks. '<' actions
# are converted to '-' and '>' actions are converted to '+'.
def self.simplify(event)
ea = event.to_a
case ea[0]
when '-'
ea[2][1] = nil
when '<'
ea[0] = '-'
ea[2][1] = nil
when '+'
ea[1][1] = nil
when '>'
ea[0] = '+'
ea[1][1] = nil
end
Diff::LCS::ContextChange.from_a(ea)
end
include Diff::LCS::ChangeTypeTests
end
module RSpec
module Core
class CommandLine
def initialize(options, configuration=RSpec::configuration, world=RSpec::world)
if Array === options
options = ConfigurationOptions.new(options)
options.parse_options
end
@options = options
@configuration = configuration
@world = world
end
# Configures and runs a suite
#
# @param [IO] err
# @param [IO] out
def run(err, out)
@configuration.error_stream = err
@configuration.output_stream ||= out
@options.configure(@configuration)
@configuration.load_spec_files
@world.announce_filters
@configuration.reporter.report(@world.example_count, @configuration.randomize? ? @configuration.seed : nil) do |reporter|
begin
@configuration.run_hook(:before, :suite)
@world.example_groups.ordered.map {|g| g.run(reporter)}.all? ? 0 : @configuration.failure_exit_code
ensure
@configuration.run_hook(:after, :suite)
end
end
end
end
end
end
require "spec_helper"
require "stringio"
require 'tmpdir'
module RSpec::Core
describe CommandLine do
describe "#run" do
include_context "spec files"
let(:out) { StringIO.new }
let(:err) { StringIO.new }
def command_line(args)
RSpec::Core::CommandLine.new(config_options(args))
end
def config_options(argv=[])
options = RSpec::Core::ConfigurationOptions.new(argv)
options.parse_options
options
end
it "returns 0 if spec passes" do
result = command_line([passing_spec_filename]).run(err, out)
result.should be(0)
end
it "returns 1 if spec fails" do
result = command_line([failing_spec_filename]).run(err, out)
result.should be(1)
end
it "returns 2 if spec fails and --failure-exit-code is 2" do
result = command_line([failing_spec_filename, "--failure-exit-code", "2"]).run(err, out)
result.should be(2)
end
end
context "given an Array of options" do
it "assigns ConfigurationOptions built from Array to @options" do
config_options = ConfigurationOptions.new(%w[--color])
config_options.parse_options
array_options = %w[--color]
command_line = CommandLine.new(array_options)
command_line.instance_eval { @options.options }.should eq(config_options.options)
end
end
context "given a ConfigurationOptions object" do
it "assigns it to @options" do
config_options = ConfigurationOptions.new(%w[--color])
config_options.parse_options
command_line = CommandLine.new(config_options)
command_line.instance_eval { @options }.should be(config_options)
end
end
describe "#run" do
let(:config_options) do
config_options = ConfigurationOptions.new(%w[--color])
config_options.parse_options
config_options
end
let(:config) do
RSpec::Core::Configuration.new
end
let(:world) do
RSpec::Core::World.new
end
let(:command_line) do
CommandLine.new(config_options, config, world)
end
let(:out) { ::StringIO.new }
let(:err) { ::StringIO.new }
before do
config.stub(:run_hook)
config.should_receive(:load_spec_files)
world.should_receive(:announce_inclusion_filter)
world.should_receive(:announce_exclusion_filter)
end
it "configures streams before command line options" do
# this is necessary to ensure that color works correctly on windows
config.should_receive(:error_stream=).ordered
config.should_receive(:output_stream=).ordered
config.should_receive(:force).any_number_of_times.ordered
command_line.run(err, out) rescue nil
end
it "runs before suite hooks" do
config.should_receive(:run_hook).with(:before, :suite)
command_line.run(err, out)
end
it "runs after suite hooks" do
config.should_receive(:run_hook).with(:after, :suite)
command_line.run(err, out)
end
it "runs after suite hooks even after an error" do
after_suite_called = false
config.stub(:run_hook) do |*args|
case args.first
when :before
raise "this error"
when :after
after_suite_called = true
end
end
expect do
command_line.run(err, out)
end.to raise_error
after_suite_called.should be_true
end
end
describe "#run with custom output" do
let(:config_options) do
config_options = ConfigurationOptions.new(%w[--color])
config_options.parse_options
config_options
end
let(:command_line) do
CommandLine.new(config_options, config)
end
let(:output_file_path) do
Dir.tmpdir + "/command_line_spec_output.txt"
end
let(:output_file) do
File.new(output_file_path, 'w')
end
let(:config) do
config = RSpec::Core::Configuration.new
config.output_stream = output_file
config
end
let(:out) { ::StringIO.new }
before do
config.stub(:run_hook)
config.stub(:files_to_run) { [] }
end
it "doesn't override output_stream" do
command_line.run(out, out)
command_line.instance_eval { @configuration.output_stream }.should eql(output_file)
end
end
end
end
---
BUNDLE_PATH: json
BUNDLE_DISABLE_SHARED_GEMS: '1'
require "rbconfig"
require 'fileutils'
module RSpec
module Core
# Stores runtime configuration information.
#
# @example Standard settings
# RSpec.configure do |c|
# c.drb = true
# c.drb_port = 1234
# c.default_path = 'behavior'
# end
#
# @example Hooks
# RSpec.configure do |c|
# c.before(:suite) { establish_connection }
# c.before(:each) { log_in_as :authorized }
# c.around(:each) { |ex| Database.transaction(&ex) }
# end
#
# @see RSpec.configure
# @see Hooks
class Configuration
include RSpec::Core::Hooks
class MustBeConfiguredBeforeExampleGroupsError < StandardError; end
# @private
def self.define_reader(name)
eval <<-CODE
def #{name}
value_for(#{name.inspect}, defined?(@#{name}) ? @#{name} : nil)
end
CODE
end
# @private
def self.deprecate_alias_key
RSpec.warn_deprecation <<-MESSAGE
The :alias option to add_setting is deprecated. Use :alias_with on the original setting instead.
Called from #{caller(0)[5]}
MESSAGE
end
# @private
def self.define_aliases(name, alias_name)
alias_method alias_name, name
alias_method "#{alias_name}=", "#{name}="
define_predicate_for alias_name
end
# @private
def self.define_predicate_for(*names)
names.each {|name| alias_method "#{name}?", name}
end
# @private
#
# Invoked by the `add_setting` instance method. Use that method on a
# `Configuration` instance rather than this class method.
def self.add_setting(name, opts={})
raise "Use the instance add_setting method if you want to set a default" if opts.has_key?(:default)
if opts[:alias]
deprecate_alias_key
define_aliases(opts[:alias], name)
else
attr_writer name
define_reader name
define_predicate_for name
end
[opts[:alias_with]].flatten.compact.each do |alias_name|
define_aliases(name, alias_name)
end
end
# @macro [attach] add_setting
# @attribute $1
# Patterns to match against lines in backtraces presented in failure
# messages in order to filter them out (default:
# DEFAULT_BACKTRACE_PATTERNS). You can either replace this list using
# the setter or modify it using the getter.
#
# To override this behavior and display a full backtrace, use
# `--backtrace` on the command line, in a `.rspec` file, or in the
# `rspec_options` attribute of RSpec's rake task.
add_setting :backtrace_clean_patterns
# Path to use if no path is provided to the `rspec` command (default:
# `"spec"`). Allows you to just type `rspec` instead of `rspec spec` to
# run all the examples in the `spec` directory.
add_setting :default_path
# Run examples over DRb (default: `false`). RSpec doesn't supply the DRb
# server, but you can use tools like spork.
add_setting :drb
# The drb_port (default: `8989`).
add_setting :drb_port
# Default: `$stderr`.
add_setting :error_stream
# Clean up and exit after the first failure (default: `false`).
add_setting :fail_fast
# The exit code to return if there are any failures (default: 1).
add_setting :failure_exit_code
# Determines the order in which examples are run (default: OS standard
# load order for files, declaration order for groups and examples).
define_reader :order
# Default: `$stdout`.
# Also known as `output` and `out`
add_setting :output_stream, :alias_with => [:output, :out]
# Load files matching this pattern (default: `'**/*_spec.rb'`)
add_setting :pattern, :alias_with => :filename_pattern
# Report the times for the 10 slowest examples (default: `false`).
add_setting :profile_examples
# Run all examples if none match the configured filters (default: `false`).
add_setting :run_all_when_everything_filtered
# Seed for random ordering (default: generated randomly each run).
#
# When you run specs with `--order random`, RSpec generates a random seed
# for the randomization and prints it to the `output_stream` (assuming
# you're using RSpec's built-in formatters). If you discover an ordering
# dependency (i.e. examples fail intermittently depending on order), set
# this (on Configuration or on the command line with `--seed`) to run
# using the same seed while you debug the issue.
#
# We recommend, actually, that you use the command line approach so you
# don't accidentally leave the seed encoded.
define_reader :seed
# When a block passed to pending fails (as expected), display the failure
# without reporting it as a failure (default: false).
add_setting :show_failures_in_pending_blocks
# Convert symbols to hashes with the symbol as a key with a value of
# `true` (default: false).
#
# This allows you to tag a group or example like this:
#
# describe "something slow", :slow do
# # ...
# end
#
# ... instead of having to type:
#
# describe "something slow", :slow => true do
# # ...
# end
add_setting :treat_symbols_as_metadata_keys_with_true_values
# @private
add_setting :tty
# @private
add_setting :include_or_extend_modules
# @private
add_setting :files_to_run
# @private
add_setting :expecting_with_rspec
# @private
attr_accessor :filter_manager
DEFAULT_BACKTRACE_PATTERNS = [
/\/lib\d*\/ruby\//,
/org\/jruby\//,
/bin\//,
/gems/,
/spec\/spec_helper\.rb/,
/lib\/rspec\/(core|expectations|matchers|mocks)/
]
def initialize
@expectation_frameworks = []
@include_or_extend_modules = []
@mock_framework = nil
@files_to_run = []
@formatters = []
@color = false
@pattern = '**/*_spec.rb'
@failure_exit_code = 1
@backtrace_clean_patterns = DEFAULT_BACKTRACE_PATTERNS.dup
@default_path = 'spec'
@filter_manager = FilterManager.new
@preferred_options = {}
@seed = srand % 0xFFFF
end
# @private
#
# Used to set higher priority option values from the command line.
def force(hash)
if hash.has_key?(:seed)
hash[:order], hash[:seed] = order_and_seed_from_seed(hash[:seed])
elsif hash.has_key?(:order)
set_order_and_seed(hash)
end
@preferred_options.merge!(hash)
end
# @private
def reset
@reporter = nil
@formatters.clear
end
# @overload add_setting(name)
# @overload add_setting(name, opts)
# @option opts [Symbol] :default
#
# set a default value for the generated getter and predicate methods:
#
# add_setting(:foo, :default => "default value")
#
# @option opts [Symbol] :alias_with
#
# Use `:alias_with` to alias the setter, getter, and predicate to another
# name, or names:
#
# add_setting(:foo, :alias_with => :bar)
# add_setting(:foo, :alias_with => [:bar, :baz])
#
# Adds a custom setting to the RSpec.configuration object.
#
# RSpec.configuration.add_setting :foo
#
# Used internally and by extension frameworks like rspec-rails, so they
# can add config settings that are domain specific. For example:
#
# RSpec.configure do |c|
# c.add_setting :use_transactional_fixtures,
# :default => true,
# :alias_with => :use_transactional_examples
# end
#
# `add_setting` creates three methods on the configuration object, a
# setter, a getter, and a predicate:
#
# RSpec.configuration.foo=(value)
# RSpec.configuration.foo
# RSpec.configuration.foo? # returns true if foo returns anything but nil or false
def add_setting(name, opts={})
default = opts.delete(:default)
(class << self; self; end).class_eval do
add_setting(name, opts)
end
send("#{name}=", default) if default
end
# Used by formatters to ask whether a backtrace line should be displayed
# or not, based on the line matching any `backtrace_clean_patterns`.
def cleaned_from_backtrace?(line)
# TODO (David 2011-12-25) why are we asking the configuration to do
# stuff? Either use the patterns directly or enapsulate the filtering
# in a BacktraceCleaner object.
backtrace_clean_patterns.any? { |regex| line =~ regex }
end
# Returns the configured mock framework adapter module
def mock_framework
mock_with :rspec unless @mock_framework
@mock_framework
end
# Delegates to mock_framework=(framework)
def mock_framework=(framework)
mock_with framework
end
# Sets the mock framework adapter module.
#
# `framework` can be a Symbol or a Module.
#
# Given any of :rspec, :mocha, :flexmock, or :rr, configures the named
# framework.
#
# Given :nothing, configures no framework. Use this if you don't use any
# mocking framework to save a little bit of overhead.
#
# Given a Module, includes that module in every example group. The module
# should adhere to RSpec's mock framework adapter API:
#
# setup_mocks_for_rspec
# - called before each example
#
# verify_mocks_for_rspec
# - called after each example. Framework should raise an exception
# when expectations fail
#
# teardown_mocks_for_rspec
# - called after verify_mocks_for_rspec (even if there are errors)
def mock_with(framework)
framework_module = case framework
when Module
framework
when String, Symbol
require case framework.to_s
when /rspec/i
'rspec/core/mocking/with_rspec'
when /mocha/i
'rspec/core/mocking/with_mocha'
when /rr/i
'rspec/core/mocking/with_rr'
when /flexmock/i
'rspec/core/mocking/with_flexmock'
else
'rspec/core/mocking/with_absolutely_nothing'
end
RSpec::Core::MockFrameworkAdapter
end
new_name, old_name = [framework_module, @mock_framework].map do |mod|
mod.respond_to?(:framework_name) ? mod.framework_name : :unnamed
end
unless new_name == old_name
assert_no_example_groups_defined(:mock_framework)
end
@mock_framework = framework_module
end
# Returns the configured expectation framework adapter module(s)
def expectation_frameworks
expect_with :rspec if @expectation_frameworks.empty?
@expectation_frameworks
end
# Delegates to expect_with(framework)
def expectation_framework=(framework)
expect_with(framework)
end
# Sets the expectation framework module(s).
#
# `frameworks` can be :rspec, :stdlib, or both
#
# Given :rspec, configures rspec/expectations.
# Given :stdlib, configures test/unit/assertions
# Given both, configures both
def expect_with(*frameworks)
modules = frameworks.map do |framework|
case framework
when :rspec
require 'rspec/expectations'
self.expecting_with_rspec = true
::RSpec::Matchers
when :stdlib
require 'test/unit/assertions'
::Test::Unit::Assertions
else
raise ArgumentError, "#{framework.inspect} is not supported"
end
end
if (modules - @expectation_frameworks).any?
assert_no_example_groups_defined(:expect_with)
end
@expectation_frameworks.clear
@expectation_frameworks.push(*modules)
end
def full_backtrace=(true_or_false)
@backtrace_clean_patterns = true_or_false ? [] : DEFAULT_BACKTRACE_PATTERNS
end
def color
return false unless output_to_tty?
value_for(:color, @color)
end
def color=(bool)
return unless bool
@color = true
if bool && ::RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
unless ENV['ANSICON']
warn "You must use ANSICON 1.31 or later (http://adoxa.110mb.com/ansicon/) to use colour on Windows"
@color = false
end
end
end
# TODO - deprecate color_enabled - probably not until the last 2.x
# release before 3.0
alias_method :color_enabled, :color
alias_method :color_enabled=, :color=
define_predicate_for :color_enabled, :color
def libs=(libs)
libs.map {|lib| $LOAD_PATH.unshift lib}
end
def requires=(paths)
paths.map {|path| require path}
end
def debug=(bool)
return unless bool
begin
require 'ruby-debug'
Debugger.start
rescue LoadError => e
raise <<-EOM
#{'*'*50}
#{e.message}
If you have it installed as a ruby gem, then you need to either require
'rubygems' or configure the RUBYOPT environment variable with the value
'rubygems'.
#{e.backtrace.join("\n")}
#{'*'*50}
EOM
end
end
# Run examples defined on `line_numbers` in all files to run.
def line_numbers=(line_numbers)
filter_run :line_numbers => line_numbers.map{|l| l.to_i}
end
def full_description=(description)
filter_run :full_description => /#{description}/
end
# @overload add_formatter(formatter)
#
# Adds a formatter to the formatters collection. `formatter` can be a
# string representing any of the built-in formatters (see
# `built_in_formatter`), or a custom formatter class.
#
# ### Note
#
# For internal purposes, `add_formatter` also accepts the name of a class
# and path to a file that contains that class definition, but you should
# consider that a private api that may change at any time without notice.
def add_formatter(formatter_to_use, path=nil)
formatter_class =
built_in_formatter(formatter_to_use) ||
custom_formatter(formatter_to_use) ||
(raise ArgumentError, "Formatter '#{formatter_to_use}' unknown - maybe you meant 'documentation' or 'progress'?.")
formatters << formatter_class.new(path ? file_at(path) : output)
end
alias_method :formatter=, :add_formatter
def formatters
@formatters ||= []
end
def reporter
@reporter ||= begin
add_formatter('progress') if formatters.empty?
Reporter.new(*formatters)
end
end
# @private
def files_or_directories_to_run=(*files)
files = files.flatten
files << default_path if command == 'rspec' && default_path && files.empty?
self.files_to_run = get_files_to_run(files)
end
# Creates a method that delegates to `example` including the submitted
# `args`. Used internally to add variants of `example` like `pending`:
#
# @example
# alias_example_to :pending, :pending => true
#
# # This lets you do this:
#
# describe Thing do
# pending "does something" do
# thing = Thing.new
# end
# end
#
# # ... which is the equivalent of
#
# describe Thing do
# it "does something", :pending => true do
# thing = Thing.new
# end
# end
def alias_example_to(new_name, *args)
extra_options = build_metadata_hash_from(args)
RSpec::Core::ExampleGroup.alias_example_to(new_name, extra_options)
end
# Define an alias for it_should_behave_like that allows different
# language (like "it_has_behavior" or "it_behaves_like") to be
# employed when including shared examples.
#
# Example:
#
# alias_it_should_behave_like_to(:it_has_behavior, 'has behavior:')
#
# allows the user to include a shared example group like:
#
# describe Entity do
# it_has_behavior 'sortability' do
# let(:sortable) { Entity.new }
# end
# end
#
# which is reported in the output as:
#
# Entity
# has behavior: sortability
# # sortability examples here
def alias_it_should_behave_like_to(new_name, report_label = '')
RSpec::Core::ExampleGroup.alias_it_should_behave_like_to(new_name, report_label)
end
# Adds key/value pairs to the `inclusion_filter`. If the
# `treat_symbols_as_metadata_keys_with_true_values` config option is set
# to true and `args` includes any symbols that are not part of a hash,
# each symbol is treated as a key in the hash with the value `true`.
#
# ### Note
#
# Filters set using this method can be overridden from the command line
# or config files (e.g. `.rspec`).
#
# @example
# filter_run_including :x => 'y'
#
# # with treat_symbols_as_metadata_keys_with_true_values = true
# filter_run_including :foo # results in {:foo => true}
def filter_run_including(*args)
filter_manager.include_with_low_priority build_metadata_hash_from(args)
end
alias_method :filter_run, :filter_run_including
# Clears and reassigns the `inclusion_filter`. Set to `nil` if you don't
# want any inclusion filter at all.
#
# ### Warning
#
# This overrides any inclusion filters/tags set on the command line or in
# configuration files.
def inclusion_filter=(filter)
filter_manager.include! build_metadata_hash_from([filter])
end
alias_method :filter=, :inclusion_filter=
# Returns the `inclusion_filter`. If none has been set, returns an empty
# hash.
def inclusion_filter
filter_manager.inclusions
end
alias_method :filter, :inclusion_filter
# Adds key/value pairs to the `exclusion_filter`. If the
# `treat_symbols_as_metadata_keys_with_true_values` config option is set
# to true and `args` excludes any symbols that are not part of a hash,
# each symbol is treated as a key in the hash with the value `true`.
#
# ### Note
#
# Filters set using this method can be overridden from the command line
# or config files (e.g. `.rspec`).
#
# @example
# filter_run_excluding :x => 'y'
#
# # with treat_symbols_as_metadata_keys_with_true_values = true
# filter_run_excluding :foo # results in {:foo => true}
def filter_run_excluding(*args)
filter_manager.exclude_with_low_priority build_metadata_hash_from(args)
end
# Clears and reassigns the `exclusion_filter`. Set to `nil` if you don't
# want any exclusion filter at all.
#
# ### Warning
#
# This overrides any exclusion filters/tags set on the command line or in
# configuration files.
def exclusion_filter=(filter)
filter_manager.exclude! build_metadata_hash_from([filter])
end
# Returns the `exclusion_filter`. If none has been set, returns an empty
# hash.
def exclusion_filter
filter_manager.exclusions
end
# Tells RSpec to include `mod` in example groups. Methods defined in
# `mod` are exposed to examples (not example groups). Use `filters` to
# constrain the groups in which to include the module.
#
# @example
#
# module AuthenticationHelpers
# def login_as(user)
# # ...
# end
# end
#
# module UserHelpers
# def users(username)
# # ...
# end
# end
#
# RSpec.configure do |config|
# config.include(UserHelpers) # included in all modules
# config.include(AuthenticationHelpers, :type => :request)
# end
#
# describe "edit profile", :type => :request do
# it "can be viewed by owning user" do
# login_as users(:jdoe)
# get "/profiles/jdoe"
# assert_select ".username", :text => 'jdoe'
# end
# end
#
# @see #extend
def include(mod, *filters)
include_or_extend_modules << [:include, mod, build_metadata_hash_from(filters)]
end
# Tells RSpec to extend example groups with `mod`. Methods defined in
# `mod` are exposed to example groups (not examples). Use `filters` to
# constrain the groups to extend.
#
# Similar to `include`, but behavior is added to example groups, which
# are classes, rather than the examples, which are instances of those
# classes.
#
# @example
#
# module UiHelpers
# def run_in_browser
# # ...
# end
# end
#
# RSpec.configure do |config|
# config.extend(UiHelpers, :type => :request)
# end
#
# describe "edit profile", :type => :request do
# run_in_browser
#
# it "does stuff in the client" do
# # ...
# end
# end
#
# @see #include
def extend(mod, *filters)
include_or_extend_modules << [:extend, mod, build_metadata_hash_from(filters)]
end
# @private
#
# Used internally to extend a group with modules using `include` and/or
# `extend`.
def configure_group(group)
include_or_extend_modules.each do |include_or_extend, mod, filters|
next unless filters.empty? || group.any_apply?(filters)
group.send(include_or_extend, mod)
end
end
# @private
def configure_mock_framework
RSpec::Core::ExampleGroup.send(:include, mock_framework)
end
# @private
def configure_expectation_framework
expectation_frameworks.each do |framework|
RSpec::Core::ExampleGroup.send(:include, framework)
end
end
# @private
def load_spec_files
files_to_run.uniq.map {|f| load File.expand_path(f) }
raise_if_rspec_1_is_loaded
end
# @api
#
# Sets the seed value and sets `order='rand'`
def seed=(seed)
order_and_seed_from_seed(seed)
end
# @api
#
# Sets the order and, if order is `'rand:<seed>'`, also sets the seed.
def order=(type)
order_and_seed_from_order(type)
end
def randomize?
order.to_s.match(/rand/)
end
private
def get_files_to_run(paths)
patterns = pattern.split(",")
paths.map do |path|
File.directory?(path) ? gather_directories(path, patterns) : extract_location(path)
end.flatten
end
def gather_directories(path, patterns)
patterns.map do |pattern|
pattern =~ /^#{path}/ ? Dir[pattern.strip] : Dir["#{path}/{#{pattern.strip}}"]
end
end
def extract_location(path)
if path =~ /^(.*?)((?:\:\d+)+)$/
path, lines = $1, $2[1..-1].split(":").map{|n| n.to_i}
filter_manager.add_location path, lines
end
path
end
def command
$0.split(File::SEPARATOR).last
end
def value_for(key, default=nil)
@preferred_options.has_key?(key) ? @preferred_options[key] : default
end
def assert_no_example_groups_defined(config_option)
if RSpec.world.example_groups.any?
raise MustBeConfiguredBeforeExampleGroupsError.new(
"RSpec's #{config_option} configuration option must be configured before " +
"any example groups are defined, but you have already defined a group."
)
end
end
def raise_if_rspec_1_is_loaded
if defined?(Spec) && defined?(Spec::VERSION::MAJOR) && Spec::VERSION::MAJOR == 1
raise <<-MESSAGE
#{'*'*80}
You are running rspec-2, but it seems as though rspec-1 has been loaded as
well. This is likely due to a statement like this somewhere in the specs:
require 'spec'
Please locate that statement, remove it, and try again.
#{'*'*80}
MESSAGE
end
end
def output_to_tty?
begin
output_stream.tty? || tty?
rescue NoMethodError
false
end
end
def built_in_formatter(key)
case key.to_s
when 'd', 'doc', 'documentation', 's', 'n', 'spec', 'nested'
require 'rspec/core/formatters/documentation_formatter'
RSpec::Core::Formatters::DocumentationFormatter
when 'h', 'html'
require 'rspec/core/formatters/html_formatter'
RSpec::Core::Formatters::HtmlFormatter
when 't', 'textmate'
require 'rspec/core/formatters/text_mate_formatter'
RSpec::Core::Formatters::TextMateFormatter
when 'p', 'progress'
require 'rspec/core/formatters/progress_formatter'
RSpec::Core::Formatters::ProgressFormatter
end
end
def custom_formatter(formatter_ref)
if Class === formatter_ref
formatter_ref
elsif string_const?(formatter_ref)
begin
eval(formatter_ref)
rescue NameError
require path_for(formatter_ref)
eval(formatter_ref)
end
end
end
def string_const?(str)
str.is_a?(String) && /\A[A-Z][a-zA-Z0-9_:]*\z/ =~ str
end
def path_for(const_ref)
underscore_with_fix_for_non_standard_rspec_naming(const_ref)
end
def underscore_with_fix_for_non_standard_rspec_naming(string)
underscore(string).sub(%r{(^|/)r_spec($|/)}, '\\1rspec\\2')
end
# activesupport/lib/active_support/inflector/methods.rb, line 48
def underscore(camel_cased_word)
word = camel_cased_word.to_s.dup
word.gsub!(/::/, '/')
word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
word.tr!("-", "_")
word.downcase!
word
end
def file_at(path)
FileUtils.mkdir_p(File.dirname(path))
File.new(path, 'w')
end
def order_and_seed_from_seed(value)
@order, @seed = 'rand', value.to_i
end
def set_order_and_seed(hash)
hash[:order], seed = order_and_seed_from_order(hash[:order])
hash[:seed] = seed if seed
end
def order_and_seed_from_order(type)
order, seed = type.to_s.split(':')
@order = order
@seed = seed = seed.to_i if seed
@order, @seed = nil, nil if order == 'default'
return order, seed
end
end
end
end
require 'erb'
module RSpec
module Core
# @private
class ConfigurationOptions
attr_reader :options
def initialize(args)
@args = args
end
def configure(config)
formatters = options.delete(:formatters)
config.filter_manager = filter_manager
order(options.keys, :libs, :requires, :default_path, :pattern).each do |key|
force?(key) ? config.force(key => options[key]) : config.send("#{key}=", options[key])
end
formatters.each {|pair| config.add_formatter(*pair) } if formatters
end
def parse_options
@options ||= extract_filters_from(*all_configs).inject do |merged, pending|
merged.merge(pending)
end
end
def drb_argv
DrbOptions.new(options, filter_manager).options
end
def filter_manager
@filter_manager ||= FilterManager.new
end
private
NON_FORCED_OPTIONS = [:debug, :requires, :libs, :files_or_directories_to_run, :line_numbers, :full_description]
def force?(key)
!NON_FORCED_OPTIONS.include?(key)
end
def order(keys, *ordered)
ordered.reverse.each do |key|
keys.unshift(key) if keys.delete(key)
end
keys
end
def extract_filters_from(*configs)
configs.compact.each do |config|
filter_manager.include config.delete(:inclusion_filter) if config.has_key?(:inclusion_filter)
filter_manager.exclude config.delete(:exclusion_filter) if config.has_key?(:exclusion_filter)
end
end
def all_configs
@all_configs ||= file_options << command_line_options << env_options
end
def file_options
custom_options_file ? [custom_options] : [global_options, local_options]
end
def env_options
ENV["SPEC_OPTS"] ? Parser.parse!(ENV["SPEC_OPTS"].split) : {}
end
def command_line_options
@command_line_options ||= Parser.parse!(@args).merge :files_or_directories_to_run => @args
end
def custom_options
options_from(custom_options_file)
end
def local_options
@local_options ||= options_from(local_options_file)
end
def global_options
@global_options ||= options_from(global_options_file)
end
def options_from(path)
Parser.parse(args_from_options_file(path))
end
def args_from_options_file(path)
return [] unless path && File.exist?(path)
config_string = options_file_as_erb_string(path)
config_string.split(/\n+/).map {|l| l.split}.flatten
end
def options_file_as_erb_string(path)
ERB.new(File.read(path)).result(binding)
end
def custom_options_file
command_line_options[:custom_options_file]
end
def local_options_file
".rspec"
end
def global_options_file
begin
File.join(File.expand_path("~"), ".rspec")
rescue ArgumentError
warn "Unable to find ~/.rspec because the HOME environment variable is not set"
nil
end
end
end
end
end
require 'spec_helper'
require 'ostruct'
describe RSpec::Core::ConfigurationOptions do
include ConfigOptionsHelper
it "warns when HOME env var is not set", :unless => (RUBY_PLATFORM == 'java') do
begin
orig_home = ENV.delete("HOME")
coo = RSpec::Core::ConfigurationOptions.new([])
coo.should_receive(:warn)
coo.parse_options
ensure
ENV["HOME"] = orig_home
end
end
describe "#configure" do
it "sends libs before requires" do
opts = config_options_object(*%w[--require a/path -I a/lib])
config = double("config").as_null_object
config.should_receive(:libs=).ordered
config.should_receive(:requires=).ordered
opts.configure(config)
end
it "sends requires before formatter" do
opts = config_options_object(*%w[--require a/path -f a/formatter])
config = double("config").as_null_object
config.should_receive(:requires=).ordered
config.should_receive(:add_formatter).ordered
opts.configure(config)
end
it "sends default_path before files_or_directories_to_run" do
opts = config_options_object(*%w[--default_path spec])
config = double("config").as_null_object
config.should_receive(:force).with(:default_path => 'spec').ordered
config.should_receive(:files_or_directories_to_run=).ordered
opts.configure(config)
end
it "sends pattern before files_or_directories_to_run" do
opts = config_options_object(*%w[--pattern **/*.spec])
config = double("config").as_null_object
config.should_receive(:force).with(:pattern => '**/*.spec').ordered
config.should_receive(:files_or_directories_to_run=).ordered
opts.configure(config)
end
it "assigns inclusion_filter" do
opts = config_options_object(*%w[--tag awesome])
config = RSpec::Core::Configuration.new
opts.configure(config)
config.inclusion_filter.should have_key(:awesome)
end
it "merges the :exclusion_filter option with the default exclusion_filter" do
opts = config_options_object(*%w[--tag ~slow])
config = RSpec::Core::Configuration.new
opts.configure(config)
config.exclusion_filter.should have_key(:slow)
end
it "forces color_enabled" do
opts = config_options_object(*%w[--color])
config = RSpec::Core::Configuration.new
config.should_receive(:force).with(:color => true)
opts.configure(config)
end
[
["--failure-exit-code", "3", :failure_exit_code, 3 ],
["--pattern", "foo/bar", :pattern, "foo/bar"],
["--failure-exit-code", "37", :failure_exit_code, 37],
["--default_path", "behavior", :default_path, "behavior"],
["--drb", nil, :drb, true],
["--order", "rand", :order, "rand"],
["--seed", "37", :order, "rand:37"],
["--drb-port", "37", :drb_port, 37],
["--backtrace", nil, :full_backtrace, true],
["--profile", nil, :profile_examples, true],
["--tty", nil, :tty, true]
].each do |cli_option, cli_value, config_key, config_value|
it "forces #{config_key}" do
opts = config_options_object(*[cli_option, cli_value].compact)
config = RSpec::Core::Configuration.new
config.should_receive(:force) do |pair|
pair.keys.first.should eq(config_key)
pair.values.first.should eq(config_value)
end
opts.configure(config)
end
end
it "sets debug directly" do
opts = config_options_object("--debug")
config = RSpec::Core::Configuration.new
config.should_receive(:debug=).with(true)
opts.configure(config)
end
end
describe "-c, --color, and --colour" do
it "sets :color => true" do
parse_options('-c').should include(:color => true)
parse_options('--color').should include(:color => true)
parse_options('--colour').should include(:color => true)
end
end
describe "--no-color" do
it "sets :color => false" do
parse_options('--no-color').should include(:color => false)
end
it "overrides previous :color => true" do
parse_options('--color', '--no-color').should include(:color => false)
end
it "gets overriden by a subsequent :color => true" do
parse_options('--no-color', '--color').should include(:color => true)
end
end
describe "-I" do
example "adds to :libs" do
parse_options('-I', 'a_dir').should include(:libs => ['a_dir'])
end
example "can be used more than once" do
parse_options('-I', 'dir_1', '-I', 'dir_2').should include(:libs => ['dir_1','dir_2'])
end
end
describe '--require' do
example "requires files" do
parse_options('--require', 'a/path').should include(:requires => ['a/path'])
end
example "can be used more than once" do
parse_options('--require', 'path/1', '--require', 'path/2').should include(:requires => ['path/1','path/2'])
end
end
describe "--format, -f" do
it "sets :formatter" do
[['--format', 'd'], ['-f', 'd'], '-fd'].each do |args|
parse_options(*args).should include(:formatters => [['d']])
end
end
example "can accept a class name" do
parse_options('-fSome::Formatter::Class').should include(:formatters => [['Some::Formatter::Class']])
end
end
describe "--profile, -p" do
it "sets :profile_examples => true" do
parse_options('-p').should include(:profile_examples => true)
parse_options('--profile').should include(:profile_examples => true)
end
end
describe '--line_number' do
it "sets :line_number" do
parse_options('-l','3').should include(:line_numbers => ['3'])
parse_options('--line_number','3').should include(:line_numbers => ['3'])
end
it "can be specified multiple times" do
parse_options('-l','3', '-l', '6').should include(:line_numbers => ['3', '6'])
parse_options('--line_number','3', '--line_number', '6').should include(:line_numbers => ['3', '6'])
end
end
describe "--example" do
it "sets :full_description" do
parse_options('--example','foo').should include(:full_description => /foo/)
parse_options('-e','bar').should include(:full_description => /bar/)
end
end
describe "--backtrace, -b" do
it "sets full_backtrace on config" do
parse_options("--backtrace").should include(:full_backtrace => true)
parse_options("-b").should include(:full_backtrace => true)
end
end
describe "--debug, -d" do
it "sets :debug => true" do
parse_options("--debug").should include(:debug => true)
parse_options("-d").should include(:debug => true)
end
end
describe "--fail-fast" do
it "defaults to false" do
parse_options[:fail_fast].should be_false
end
it "sets fail_fast on config" do
parse_options("--fail-fast")[:fail_fast].should be_true
end
end
describe "--failure-exit-code" do
it "sets :failure_exit_code" do
parse_options('--failure-exit-code', '0').should include(:failure_exit_code => 0)
parse_options('--failure-exit-code', '1').should include(:failure_exit_code => 1)
parse_options('--failure-exit-code', '2').should include(:failure_exit_code => 2)
end
it "overrides previous :failure_exit_code" do
parse_options('--failure-exit-code', '2', '--failure-exit-code', '3').should include(:failure_exit_code => 3)
end
end
describe "--options" do
it "sets :custom_options_file" do
parse_options(*%w[-O my.opts]).should include(:custom_options_file => "my.opts")
parse_options(*%w[--options my.opts]).should include(:custom_options_file => "my.opts")
end
end
describe "--drb, -X" do
context "combined with --debug" do
it "turns off the debugger if --drb is specified first" do
config_options_object("--drb", "--debug").drb_argv.should_not include("--debug")
config_options_object("--drb", "-d" ).drb_argv.should_not include("--debug")
config_options_object("-X", "--debug").drb_argv.should_not include("--debug")
config_options_object("-X", "-d" ).drb_argv.should_not include("--debug")
end
it "turns off the debugger option if --drb is specified later" do
config_options_object("--debug", "--drb").drb_argv.should_not include("--debug")
config_options_object("-d", "--drb").drb_argv.should_not include("--debug")
config_options_object("--debug", "-X" ).drb_argv.should_not include("--debug")
config_options_object("-d", "-X" ).drb_argv.should_not include("--debug")
end
it "turns off the debugger option if --drb is specified in the options file" do
File.open("./.rspec", "w") {|f| f << "--drb"}
config_options_object("--debug").drb_argv.should_not include("--debug")
config_options_object("-d" ).drb_argv.should_not include("--debug")
end
it "turns off the debugger option if --debug is specified in the options file" do
File.open("./.rspec", "w") {|f| f << "--debug"}
config_options_object("--drb").drb_argv.should_not include("--debug")
config_options_object("-X" ).drb_argv.should_not include("--debug")
end
end
it "does not send --drb back to the parser after parsing options" do
config_options_object("--drb", "--color").drb_argv.should_not include("--drb")
end
end
describe "--no-drb" do
it "disables drb" do
parse_options("--no-drb").should include(:drb => false)
end
it "overrides a previous drb => true" do
parse_options("--drb", "--no-drb").should include(:drb => false)
end
it "gets overriden by a subsquent drb => true" do
parse_options("--no-drb", "--drb").should include(:drb => true)
end
end
describe "files_or_directories_to_run" do
it "parses files from '-c file.rb dir/file.rb'" do
parse_options("-c", "file.rb", "dir/file.rb").should include(
:files_or_directories_to_run => ["file.rb", "dir/file.rb"]
)
end
it "parses dir from 'dir'" do
parse_options("dir").should include(:files_or_directories_to_run => ["dir"])
end
it "parses dir and files from 'spec/file1_spec.rb, spec/file2_spec.rb'" do
parse_options("dir", "spec/file1_spec.rb", "spec/file2_spec.rb").should include(
:files_or_directories_to_run => ["dir", "spec/file1_spec.rb", "spec/file2_spec.rb"]
)
end
it "provides no files or directories if spec directory does not exist" do
FileTest.stub(:directory?).with("spec").and_return false
parse_options().should include(:files_or_directories_to_run => [])
end
end
describe "default_path" do
it "gets set before files_or_directories_to_run" do
config = double("config").as_null_object
config.should_receive(:force).with(:default_path => 'foo').ordered
config.should_receive(:files_or_directories_to_run=).ordered
opts = config_options_object("--default_path", "foo")
opts.configure(config)
end
end
describe "sources: ~/.rspec, ./.rspec, custom, CLI, and SPEC_OPTS" do
before(:each) do
FileUtils.mkpath(File.expand_path("~"))
end
it "merges global, local, SPEC_OPTS, and CLI" do
File.open("./.rspec", "w") {|f| f << "--line 37"}
File.open("~/.rspec", "w") {|f| f << "--color"}
ENV["SPEC_OPTS"] = "--debug"
options = parse_options("--drb")
options[:color].should be_true
options[:line_numbers].should eq(["37"])
options[:debug].should be_true
options[:drb].should be_true
end
it "prefers SPEC_OPTS over CLI" do
ENV["SPEC_OPTS"] = "--format spec_opts"
parse_options("--format", "cli")[:formatters].should eq([['spec_opts']])
end
it "prefers CLI over file options" do
File.open("./.rspec", "w") {|f| f << "--format local"}
File.open("~/.rspec", "w") {|f| f << "--format global"}
parse_options("--format", "cli")[:formatters].should eq([['cli']])
end
it "prefers local file options over global" do
File.open("./.rspec", "w") {|f| f << "--format local"}
File.open("~/.rspec", "w") {|f| f << "--format global"}
parse_options[:formatters].should eq([['local']])
end
context "with custom options file" do
it "ignores local and global options files" do
File.open("./.rspec", "w") {|f| f << "--format local"}
File.open("~/.rspec", "w") {|f| f << "--format global"}
File.open("./custom.opts", "w") {|f| f << "--color"}
options = parse_options("-O", "./custom.opts")
options[:format].should be_nil
options[:color].should be_true
end
end
end
end
require 'spec_helper'
require 'tmpdir'
# so the stdlib module is available...
module Test; module Unit; module Assertions; end; end; end
module RSpec::Core
describe Configuration do
let(:config) { Configuration.new }
describe "#load_spec_files" do
it "loads files using load" do
config.files_to_run = ["foo.bar", "blah_spec.rb"]
config.should_receive(:load).twice
config.load_spec_files
end
it "loads each file once, even if duplicated in list" do
config.files_to_run = ["a_spec.rb", "a_spec.rb"]
config.should_receive(:load).once
config.load_spec_files
end
context "with rspec-1 loaded" do
before do
Object.const_set(:Spec, Module.new)
::Spec::const_set(:VERSION, Module.new)
::Spec::VERSION::const_set(:MAJOR, 1)
end
after { Object.__send__(:remove_const, :Spec) }
it "raises with a helpful message" do
expect {
config.load_spec_files
}.to raise_error(/rspec-1 has been loaded/)
end
end
end
describe "#treat_symbols_as_metadata_keys_with_true_values?" do
it 'defaults to false' do
config.treat_symbols_as_metadata_keys_with_true_values?.should be_false
end
it 'can be set to true' do
config.treat_symbols_as_metadata_keys_with_true_values = true
config.treat_symbols_as_metadata_keys_with_true_values?.should be_true
end
end
describe "#mock_framework" do
it "defaults to :rspec" do
config.should_receive(:require).with('rspec/core/mocking/with_rspec')
config.mock_framework
end
end
describe "#mock_framework="do
it "delegates to mock_with" do
config.should_receive(:mock_with).with(:rspec)
config.mock_framework = :rspec
end
end
describe "#mock_with" do
[:rspec, :mocha, :rr, :flexmock].each do |framework|
context "with #{framework}" do
it "requires the adapter for #{framework.inspect}" do
config.should_receive(:require).with("rspec/core/mocking/with_#{framework}")
config.mock_with framework
end
end
end
context "with a module" do
it "sets the mock_framework_adapter to that module" do
config.stub(:require)
mod = Module.new
config.mock_with mod
config.mock_framework.should eq(mod)
end
end
it "uses the null adapter when set to any unknown key" do
config.should_receive(:require).with('rspec/core/mocking/with_absolutely_nothing')
config.mock_with :crazy_new_mocking_framework_ive_not_yet_heard_of
end
context 'when there are already some example groups defined' do
before(:each) { config.stub(:require) }
it 'raises an error since this setting must be applied before any groups are defined' do
RSpec.world.stub(:example_groups).and_return([double.as_null_object])
expect {
config.mock_with :mocha
}.to raise_error(/must be configured before any example groups are defined/)
end
it 'does not raise an error if the default `mock_with :rspec` is re-configured' do
config.mock_framework # called by RSpec when configuring the first example group
RSpec.world.stub(:example_groups).and_return([double.as_null_object])
config.mock_with :rspec
end
it 'does not raise an error if re-setting the same config' do
groups = []
RSpec.world.stub(:example_groups => groups)
config.mock_with :mocha
groups << double.as_null_object
config.mock_with :mocha
end
end
end
describe "#expectation_framework" do
it "defaults to :rspec" do
config.should_receive(:require).with('rspec/expectations')
config.expectation_frameworks
end
end
describe "#expectation_framework=" do
it "delegates to expect_with=" do
config.should_receive(:expect_with).with(:rspec)
config.expectation_framework = :rspec
end
end
describe "#expect_with" do
before(:each) do
# we need to prevent stdlib from being required because it defines a
# `pass` method that conflicts with our `pass` matcher.
config.stub(:require)
end
[
[:rspec, 'rspec/expectations'],
[:stdlib, 'test/unit/assertions']
].each do |(framework, required_file)|
context "with #{framework}" do
it "requires #{required_file}" do
config.should_receive(:require).with(required_file)
config.expect_with framework
end
end
end
it "raises ArgumentError if framework is not supported" do
expect do
config.expect_with :not_supported
end.to raise_error(ArgumentError)
end
context 'when there are already some example groups defined' do
it 'raises an error since this setting must be applied before any groups are defined' do
RSpec.world.stub(:example_groups).and_return([double.as_null_object])
expect {
config.expect_with :rspec
}.to raise_error(/must be configured before any example groups are defined/)
end
it 'does not raise an error if the default `expect_with :rspec` is re-configured' do
config.expectation_frameworks # called by RSpec when configuring the first example group
RSpec.world.stub(:example_groups).and_return([double.as_null_object])
config.expect_with :rspec
end
it 'does not raise an error if re-setting the same config' do
groups = []
RSpec.world.stub(:example_groups => groups)
config.expect_with :stdlib
groups << double.as_null_object
config.expect_with :stdlib
end
end
end
describe "#expecting_with_rspec?" do
before(:each) do
# prevent minitest assertions from being required and included,
# as that causes problems in some of our specs.
config.stub(:require)
end
it "returns false by default" do
config.should_not be_expecting_with_rspec
end
it "returns true when `expect_with :rspec` has been configured" do
config.expect_with :rspec
config.should be_expecting_with_rspec
end
it "returns true when `expect_with :rspec, :stdlib` has been configured" do
config.expect_with :rspec, :stdlib
config.should be_expecting_with_rspec
end
it "returns true when `expect_with :stdlib, :rspec` has been configured" do
config.expect_with :stdlib, :rspec
config.should be_expecting_with_rspec
end
it "returns false when `expect_with :stdlib` has been configured" do
config.expect_with :stdlib
config.should_not be_expecting_with_rspec
end
end
describe "#files_to_run" do
it "loads files not following pattern if named explicitly" do
config.files_or_directories_to_run = "spec/rspec/core/resources/a_bar.rb"
config.files_to_run.should eq([ "spec/rspec/core/resources/a_bar.rb"])
end
it "prevents repitition of dir when start of the pattern" do
config.pattern = "spec/**/a_spec.rb"
config.files_or_directories_to_run = "spec"
config.files_to_run.should eq(["spec/rspec/core/resources/a_spec.rb"])
end
it "does not prevent repitition of dir when later of the pattern" do
config.pattern = "rspec/**/a_spec.rb"
config.files_or_directories_to_run = "spec"
config.files_to_run.should eq(["spec/rspec/core/resources/a_spec.rb"])
end
context "with <path>:<line_number>" do
it "overrides inclusion filters set on config" do
config.filter_run_including :foo => :bar
config.files_or_directories_to_run = "path/to/file.rb:37"
config.inclusion_filter.size.should eq(1)
config.inclusion_filter[:locations].keys.first.should match(/path\/to\/file\.rb$/)
config.inclusion_filter[:locations].values.first.should eq([37])
end
it "overrides inclusion filters set before config" do
config.force(:inclusion_filter => {:foo => :bar})
config.files_or_directories_to_run = "path/to/file.rb:37"
config.inclusion_filter.size.should eq(1)
config.inclusion_filter[:locations].keys.first.should match(/path\/to\/file\.rb$/)
config.inclusion_filter[:locations].values.first.should eq([37])
end
it "clears exclusion filters set on config" do
config.exclusion_filter = { :foo => :bar }
config.files_or_directories_to_run = "path/to/file.rb:37"
config.exclusion_filter.should be_empty,
"expected exclusion filter to be empty:\n#{config.exclusion_filter}"
end
it "clears exclusion filters set before config" do
config.force(:exclusion_filter => { :foo => :bar })
config.files_or_directories_to_run = "path/to/file.rb:37"
config.exclusion_filter.should be_empty,
"expected exclusion filter to be empty:\n#{config.exclusion_filter}"
end
end
context "with default pattern" do
it "loads files named _spec.rb" do
config.files_or_directories_to_run = "spec/rspec/core/resources"
config.files_to_run.should eq([ "spec/rspec/core/resources/a_spec.rb"])
end
it "loads files in Windows" do
file = "C:\\path\\to\\project\\spec\\sub\\foo_spec.rb"
config.files_or_directories_to_run = file
config.files_to_run.should eq([file])
end
end
context "with default default_path" do
it "loads files in the default path when run by rspec" do
config.stub(:command) { 'rspec' }
config.files_or_directories_to_run = []
config.files_to_run.should_not be_empty
end
it "does not load files in the default path when run by ruby" do
config.stub(:command) { 'ruby' }
config.files_or_directories_to_run = []
config.files_to_run.should be_empty
end
end
end
%w[pattern= filename_pattern=].each do |setter|
describe "##{setter}" do
context "with single pattern" do
before { config.send(setter, "**/*_foo.rb") }
it "loads files following pattern" do
file = File.expand_path(File.dirname(__FILE__) + "/resources/a_foo.rb")
config.files_or_directories_to_run = file
config.files_to_run.should include(file)
end
it "loads files in directories following pattern" do
dir = File.expand_path(File.dirname(__FILE__) + "/resources")
config.files_or_directories_to_run = dir
config.files_to_run.should include("#{dir}/a_foo.rb")
end
it "does not load files in directories not following pattern" do
dir = File.expand_path(File.dirname(__FILE__) + "/resources")
config.files_or_directories_to_run = dir
config.files_to_run.should_not include("#{dir}/a_bar.rb")
end
end
context "with multiple patterns" do
it "supports comma separated values" do
config.send(setter, "**/*_foo.rb,**/*_bar.rb")
dir = File.expand_path(File.dirname(__FILE__) + "/resources")
config.files_or_directories_to_run = dir
config.files_to_run.should include("#{dir}/a_foo.rb")
config.files_to_run.should include("#{dir}/a_bar.rb")
end
it "supports comma separated values with spaces" do
config.send(setter, "**/*_foo.rb, **/*_bar.rb")
dir = File.expand_path(File.dirname(__FILE__) + "/resources")
config.files_or_directories_to_run = dir
config.files_to_run.should include("#{dir}/a_foo.rb")
config.files_to_run.should include("#{dir}/a_bar.rb")
end
end
end
end
describe "path with line number" do
it "assigns the line number as a location filter" do
config.files_or_directories_to_run = "path/to/a_spec.rb:37"
config.filter.should eq({:locations => {File.expand_path("path/to/a_spec.rb") => [37]}})
end
end
context "with full_description" do
it "overrides filters" do
config.filter_run :focused => true
config.full_description = "foo"
config.filter.should_not have_key(:focused)
end
end
context "with line number" do
it "assigns the file and line number as a location filter" do
config.files_or_directories_to_run = "path/to/a_spec.rb:37"
config.filter.should eq({:locations => {File.expand_path("path/to/a_spec.rb") => [37]}})
end
it "assigns multiple files with line numbers as location filters" do
config.files_or_directories_to_run = "path/to/a_spec.rb:37", "other_spec.rb:44"
config.filter.should eq({:locations => {File.expand_path("path/to/a_spec.rb") => [37],
File.expand_path("other_spec.rb") => [44]}})
end
it "assigns files with multiple line numbers as location filters" do
config.files_or_directories_to_run = "path/to/a_spec.rb:37", "path/to/a_spec.rb:44"
config.filter.should eq({:locations => {File.expand_path("path/to/a_spec.rb") => [37, 44]}})
end
end
context "with multiple line numbers" do
it "assigns the file and line numbers as a location filter" do
config.files_or_directories_to_run = "path/to/a_spec.rb:1:3:5:7"
config.filter.should eq({:locations => {File.expand_path("path/to/a_spec.rb") => [1,3,5,7]}})
end
end
it "assigns the example name as the filter on description" do
config.full_description = "foo"
config.filter.should eq({:full_description => /foo/})
end
describe "#default_path" do
it 'defaults to "spec"' do
config.default_path.should eq('spec')
end
end
describe "#include" do
module InstanceLevelMethods
def you_call_this_a_blt?
"egad man, where's the mayo?!?!?"
end
end
it_behaves_like "metadata hash builder" do
def metadata_hash(*args)
config.include(InstanceLevelMethods, *args)
config.include_or_extend_modules.last.last
end
end
context "with no filter" do
it "includes the given module into each example group" do
RSpec.configure do |c|
c.include(InstanceLevelMethods)
end
group = ExampleGroup.describe('does like, stuff and junk', :magic_key => :include) { }
group.should_not respond_to(:you_call_this_a_blt?)
group.new.you_call_this_a_blt?.should eq("egad man, where's the mayo?!?!?")
end
end
context "with a filter" do
it "includes the given module into each matching example group" do
RSpec.configure do |c|
c.include(InstanceLevelMethods, :magic_key => :include)
end
group = ExampleGroup.describe('does like, stuff and junk', :magic_key => :include) { }
group.should_not respond_to(:you_call_this_a_blt?)
group.new.you_call_this_a_blt?.should eq("egad man, where's the mayo?!?!?")
end
end
end
describe "#extend" do
module ThatThingISentYou
def that_thing
end
end
it_behaves_like "metadata hash builder" do
def metadata_hash(*args)
config.extend(ThatThingISentYou, *args)
config.include_or_extend_modules.last.last
end
end
it "extends the given module into each matching example group" do
RSpec.configure do |c|
c.extend(ThatThingISentYou, :magic_key => :extend)
end
group = ExampleGroup.describe(ThatThingISentYou, :magic_key => :extend) { }
group.should respond_to(:that_thing)
end
end
describe "#run_all_when_everything_filtered?" do
it "defaults to false" do
config.run_all_when_everything_filtered?.should be_false
end
it "can be queried with question method" do
config.run_all_when_everything_filtered = true
config.run_all_when_everything_filtered?.should be_true
end
end
%w[color color_enabled].each do |color_option|
describe "##{color_option}=" do
context "given true" do
context "with non-tty output and no autotest" do
it "does not set color_enabled" do
config.output_stream = StringIO.new
config.output_stream.stub(:tty?) { false }
config.tty = false
config.send "#{color_option}=", true
config.send(color_option).should be_false
end
end
context "with tty output" do
it "does not set color_enabled" do
config.output_stream = StringIO.new
config.output_stream.stub(:tty?) { true }
config.tty = false
config.send "#{color_option}=", true
config.send(color_option).should be_true
end
end
context "with tty set" do
it "does not set color_enabled" do
config.output_stream = StringIO.new
config.output_stream.stub(:tty?) { false }
config.tty = true
config.send "#{color_option}=", true
config.send(color_option).should be_true
end
end
context "on windows" do
before do
@original_host = RbConfig::CONFIG['host_os']
RbConfig::CONFIG['host_os'] = 'mingw'
config.stub(:require)
config.stub(:warn)
end
after do
RbConfig::CONFIG['host_os'] = @original_host
end
context "with ANSICON available" do
before(:all) do
@original_ansicon = ENV['ANSICON']
ENV['ANSICON'] = 'ANSICON'
end
after(:all) do
ENV['ANSICON'] = @original_ansicon
end
it "enables colors" do
config.output_stream = StringIO.new
config.output_stream.stub(:tty?) { true }
config.send "#{color_option}=", true
config.send(color_option).should be_true
end
it "leaves output stream intact" do
config.output_stream = $stdout
config.stub(:require) do |what|
config.output_stream = 'foo' if what =~ /Win32/
end
config.send "#{color_option}=", true
config.output_stream.should eq($stdout)
end
end
context "with ANSICON NOT available" do
it "warns to install ANSICON" do
config.stub(:require) { raise LoadError }
config.should_receive(:warn).
with(/You must use ANSICON/)
config.send "#{color_option}=", true
end
it "sets color_enabled to false" do
config.stub(:require) { raise LoadError }
config.send "#{color_option}=", true
config.color_enabled = true
config.send(color_option).should be_false
end
end
end
end
end
it "prefers incoming cli_args" do
config.output_stream = StringIO.new
config.output_stream.stub :tty? => true
config.force :color => true
config.color = false
config.color.should be_true
end
end
describe '#formatter=' do
it "delegates to add_formatter (better API for user-facing configuration)" do
config.should_receive(:add_formatter).with('these','options')
config.add_formatter('these','options')
end
end
describe "#add_formatter" do
it "adds to the list of formatters" do
config.add_formatter :documentation
config.formatters.first.should be_an_instance_of(Formatters::DocumentationFormatter)
end
it "finds a formatter by name (w/ Symbol)" do
config.add_formatter :documentation
config.formatters.first.should be_an_instance_of(Formatters::DocumentationFormatter)
end
it "finds a formatter by name (w/ String)" do
config.add_formatter 'documentation'
config.formatters.first.should be_an_instance_of(Formatters::DocumentationFormatter)
end
it "finds a formatter by class" do
formatter_class = Class.new(Formatters::BaseTextFormatter)
config.add_formatter formatter_class
config.formatters.first.should be_an_instance_of(formatter_class)
end
it "finds a formatter by class name" do
Object.const_set("ACustomFormatter", Class.new(Formatters::BaseFormatter))
config.add_formatter "ACustomFormatter"
config.formatters.first.should be_an_instance_of(ACustomFormatter)
end
it "finds a formatter by class fully qualified name" do
RSpec.const_set("CustomFormatter", Class.new(Formatters::BaseFormatter))
config.add_formatter "RSpec::CustomFormatter"
config.formatters.first.should be_an_instance_of(RSpec::CustomFormatter)
end
it "requires a formatter file based on its fully qualified name" do
config.should_receive(:require).with('rspec/custom_formatter2') do
RSpec.const_set("CustomFormatter2", Class.new(Formatters::BaseFormatter))
end
config.add_formatter "RSpec::CustomFormatter2"
config.formatters.first.should be_an_instance_of(RSpec::CustomFormatter2)
end
it "raises NameError if class is unresolvable" do
config.should_receive(:require).with('rspec/custom_formatter3')
lambda { config.add_formatter "RSpec::CustomFormatter3" }.should raise_error(NameError)
end
it "raises ArgumentError if formatter is unknown" do
lambda { config.add_formatter :progresss }.should raise_error(ArgumentError)
end
context "with a 2nd arg defining the output" do
it "creates a file at that path and sets it as the output" do
path = File.join(Dir.tmpdir, 'output.txt')
config.add_formatter('doc', path)
config.formatters.first.output.should be_a(File)
config.formatters.first.output.path.should eq(path)
end
end
end
describe "#filter_run_including" do
it_behaves_like "metadata hash builder" do
def metadata_hash(*args)
config.filter_run_including(*args)
config.inclusion_filter
end
end
it "sets the filter with a hash" do
config.filter_run_including :foo => true
config.inclusion_filter[:foo].should be(true)
end
it "sets the filter with a symbol" do
RSpec.configuration.stub(:treat_symbols_as_metadata_keys_with_true_values? => true)
config.filter_run_including :foo
config.inclusion_filter[:foo].should be(true)
end
it "merges with existing filters" do
config.filter_run_including :foo => true
config.filter_run_including :bar => false
config.inclusion_filter[:foo].should be(true)
config.inclusion_filter[:bar].should be(false)
end
end
describe "#filter_run_excluding" do
it_behaves_like "metadata hash builder" do
def metadata_hash(*args)
config.filter_run_excluding(*args)
config.exclusion_filter
end
end
it "sets the filter" do
config.filter_run_excluding :foo => true
config.exclusion_filter[:foo].should be(true)
end
it "sets the filter using a symbol" do
RSpec.configuration.stub(:treat_symbols_as_metadata_keys_with_true_values? => true)
config.filter_run_excluding :foo
config.exclusion_filter[:foo].should be(true)
end
it "merges with existing filters" do
config.filter_run_excluding :foo => true
config.filter_run_excluding :bar => false
config.exclusion_filter[:foo].should be(true)
config.exclusion_filter[:bar].should be(false)
end
end
describe "#inclusion_filter" do
it "returns {} even if set to nil" do
config.inclusion_filter = nil
config.inclusion_filter.should eq({})
end
end
describe "#inclusion_filter=" do
it "treats symbols as hash keys with true values when told to" do
RSpec.configuration.stub(:treat_symbols_as_metadata_keys_with_true_values? => true)
config.inclusion_filter = :foo
config.inclusion_filter.should eq({:foo => true})
end
it "overrides any inclusion filters set on the command line or in configuration files" do
config.force(:inclusion_filter => { :foo => :bar })
config.inclusion_filter = {:want => :this}
config.inclusion_filter.should eq({:want => :this})
end
end
describe "#exclusion_filter" do
it "returns {} even if set to nil" do
config.exclusion_filter = nil
config.exclusion_filter.should eq({})
end
describe "the default :if filter" do
it "does not exclude a spec with no :if metadata" do
config.exclusion_filter[:if].call(nil, {}).should be_false
end
it "does not exclude a spec with { :if => true } metadata" do
config.exclusion_filter[:if].call(true, {:if => true}).should be_false
end
it "excludes a spec with { :if => false } metadata" do
config.exclusion_filter[:if].call(false, {:if => false}).should be_true
end
it "excludes a spec with { :if => nil } metadata" do
config.exclusion_filter[:if].call(false, {:if => nil}).should be_true
end
end
describe "the default :unless filter" do
it "excludes a spec with { :unless => true } metadata" do
config.exclusion_filter[:unless].call(true).should be_true
end
it "does not exclude a spec with { :unless => false } metadata" do
config.exclusion_filter[:unless].call(false).should be_false
end
it "does not exclude a spec with { :unless => nil } metadata" do
config.exclusion_filter[:unless].call(nil).should be_false
end
end
end
describe "#exclusion_filter=" do
it "treats symbols as hash keys with true values when told to" do
RSpec.configuration.stub(:treat_symbols_as_metadata_keys_with_true_values? => true)
config.exclusion_filter = :foo
config.exclusion_filter.should eq({:foo => true})
end
it "overrides any exclusion filters set on the command line or in configuration files" do
config.force(:exclusion_filter => { :foo => :bar })
config.exclusion_filter = {:want => :this}
config.exclusion_filter.should eq({:want => :this})
end
end
describe "line_numbers=" do
before { config.filter_manager.stub(:warn) }
it "sets the line numbers" do
config.line_numbers = ['37']
config.filter.should eq({:line_numbers => [37]})
end
it "overrides filters" do
config.filter_run :focused => true
config.line_numbers = ['37']
config.filter.should eq({:line_numbers => [37]})
end
it "prevents subsequent filters" do
config.line_numbers = ['37']
config.filter_run :focused => true
config.filter.should eq({:line_numbers => [37]})
end
end
describe "#full_backtrace=" do
context "given true" do
it "clears the backtrace clean patterns" do
config.full_backtrace = true
config.backtrace_clean_patterns.should eq([])
end
end
context "given false" do
it "restores backtrace clean patterns" do
config.full_backtrace = false
config.backtrace_clean_patterns.should eq(RSpec::Core::Configuration::DEFAULT_BACKTRACE_PATTERNS)
end
end
it "doesn't impact other instances of config" do
config_1 = Configuration.new
config_2 = Configuration.new
config_1.full_backtrace = true
config_2.backtrace_clean_patterns.should_not be_empty
end
end
describe "#cleaned_from_backtrace? defaults" do
it "returns true for rspec files" do
config.cleaned_from_backtrace?("lib/rspec/core.rb").
should be_true
end
it "returns true for spec_helper" do
config.cleaned_from_backtrace?("spec/spec_helper.rb").
should be_true
end
it "returns true for java files (for JRuby)" do
config.cleaned_from_backtrace?("org/jruby/RubyArray.java:2336").
should be_true
end
end
describe "#debug=true" do
before do
if defined?(Debugger)
@orig_debugger = Debugger
Object.send(:remove_const, :Debugger)
else
@orig_debugger = nil
end
Object.const_set("Debugger", debugger)
end
after do
Object.send(:remove_const, :Debugger)
Object.const_set("Debugger", @orig_debugger) if @orig_debugger
end
let(:debugger) { double('Debugger').as_null_object }
it "requires 'ruby-debug'" do
config.should_receive(:require).with('ruby-debug')
config.debug = true
end
it "starts the debugger" do
config.stub(:require)
debugger.should_receive(:start)
config.debug = true
end
end
describe "#debug=false" do
it "does not require 'ruby-debug'" do
config.should_not_receive(:require).with('ruby-debug')
config.debug = false
end
end
describe "#output=" do
it "sets the output" do
output = mock("output")
config.output = output
config.output.should equal(output)
end
end
describe "#libs=" do
it "adds directories to the LOAD_PATH" do
$LOAD_PATH.should_receive(:unshift).with("a/dir")
config.libs = ["a/dir"]
end
end
describe "#requires=" do
it "requires paths" do
config.should_receive(:require).with("a/path")
config.requires = ["a/path"]
end
end
describe "#add_setting" do
describe "with no modifiers" do
context "with no additional options" do
before do
config.add_setting :custom_option
end
it "defaults to nil" do
config.custom_option.should be_nil
end
it "adds a predicate" do
config.custom_option?.should be_false
end
it "can be overridden" do
config.custom_option = "a value"
config.custom_option.should eq("a value")
end
end
context "with :default => 'a value'" do
before do
config.add_setting :custom_option, :default => 'a value'
end
it "defaults to 'a value'" do
config.custom_option.should eq("a value")
end
it "returns true for the predicate" do
config.custom_option?.should be_true
end
it "can be overridden with a truthy value" do
config.custom_option = "a new value"
config.custom_option.should eq("a new value")
end
it "can be overridden with nil" do
config.custom_option = nil
config.custom_option.should eq(nil)
end
it "can be overridden with false" do
config.custom_option = false
config.custom_option.should eq(false)
end
end
end
context "with :alias => " do
it "is deprecated" do
RSpec::should_receive(:warn).with(/deprecated/)
config.add_setting :custom_option
config.add_setting :another_custom_option, :alias => :custom_option
end
end
context "with :alias_with => " do
before do
config.add_setting :custom_option, :alias_with => :another_custom_option
end
it "delegates the getter to the other option" do
config.another_custom_option = "this value"
config.custom_option.should eq("this value")
end
it "delegates the setter to the other option" do
config.custom_option = "this value"
config.another_custom_option.should eq("this value")
end
it "delegates the predicate to the other option" do
config.custom_option = true
config.another_custom_option?.should be_true
end
end
end
describe "#configure_group" do
it "extends with 'extend'" do
mod = Module.new
group = ExampleGroup.describe("group", :foo => :bar)
config.extend(mod, :foo => :bar)
config.configure_group(group)
group.should be_a(mod)
end
it "extends with 'module'" do
mod = Module.new
group = ExampleGroup.describe("group", :foo => :bar)
config.include(mod, :foo => :bar)
config.configure_group(group)
group.included_modules.should include(mod)
end
it "requires only one matching filter" do
mod = Module.new
group = ExampleGroup.describe("group", :foo => :bar)
config.include(mod, :foo => :bar, :baz => :bam)
config.configure_group(group)
group.included_modules.should include(mod)
end
it "includes each one before deciding whether to include the next" do
mod1 = Module.new do
def self.included(host)
host.metadata[:foo] = :bar
end
end
mod2 = Module.new
group = ExampleGroup.describe("group")
config.include(mod1)
config.include(mod2, :foo => :bar)
config.configure_group(group)
group.included_modules.should include(mod1)
group.included_modules.should include(mod2)
end
end
describe "#alias_example_to" do
it_behaves_like "metadata hash builder" do
after do
RSpec::Core::ExampleGroup.module_eval do
class << self
undef :my_example_method if method_defined? :my_example_method
end
end
end
def metadata_hash(*args)
config.alias_example_to :my_example_method, *args
group = ExampleGroup.describe("group")
example = group.my_example_method("description")
example.metadata
end
end
end
describe "#reset" do
it "clears the reporter" do
config.reporter.should_not be_nil
config.reset
config.instance_variable_get("@reporter").should be_nil
end
it "clears the formatters" do
config.add_formatter "doc"
config.reset
config.formatters.should be_empty
end
end
describe "#force" do
it "forces order" do
config.force :order => "default"
config.order = "rand"
config.order.should eq("default")
end
it "forces order and seed with :order => 'rand:37'" do
config.force :order => "rand:37"
config.order = "default"
config.order.should eq("rand")
config.seed.should eq(37)
end
it "forces order and seed with :seed => '37'" do
config.force :seed => "37"
config.order = "default"
config.seed.should eq(37)
config.order.should eq("rand")
end
it "forces 'false' value" do
config.add_setting :custom_option
config.custom_option = true
config.custom_option?.should be_true
config.force :custom_option => false
config.custom_option?.should be_false
config.custom_option = true
config.custom_option?.should be_false
end
end
describe '#seed' do
it 'returns the seed as an int' do
config.seed = '123'
config.seed.should eq(123)
end
end
describe '#randomize?' do
context 'with order set to :random' do
before { config.order = :random }
it 'returns true' do
config.randomize?.should be_true
end
end
context 'with order set to nil' do
before { config.order = nil }
it 'returns false' do
config.randomize?.should be_false
end
end
end
describe '#order=' do
context 'given "random:123"' do
before { config.order = 'random:123' }
it 'sets order to "random"' do
config.order.should eq('random')
end
it 'sets seed to 123' do
config.seed.should eq(123)
end
end
context 'given "default"' do
before do
config.order = 'rand:123'
config.order = 'default'
end
it "sets the order to nil" do
config.order.should be_nil
end
it "sets the seed to nil" do
config.seed.should be_nil
end
end
end
end
end
Feature: configure expectation framework
By default, RSpec is configured to include rspec-expectations for expressing
desired outcomes. You can also configure RSpec to use:
* rspec/expectations (explicitly)
* stdlib assertions
* test/unit assertions in ruby 1.8
* minitest assertions in ruby 1.9
* rspec/expecations _and_ stlib assertions
Note that when you do not use rspec-expectations, you must explicitly
provide a description to every example. You cannot rely on the generated
descriptions provided by rspec-expectations.
Scenario: configure rspec-expectations (explicitly)
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.expect_with :rspec
end
describe 5 do
it "is greater than 4" do
5.should be > 4
end
end
"""
When I run `rspec example_spec.rb`
Then the examples should all pass
Scenario: configure test/unit assertions
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.expect_with :stdlib
end
describe 5 do
it "is greater than 4" do
assert 5 > 4, "expected 5 to be greater than 4"
end
specify { assert 5 < 6 }
end
"""
When I run `rspec example_spec.rb`
Then the output should contain "2 examples, 1 failure"
And the output should contain:
"""
NotImplementedError:
Generated descriptions are only supported when you use rspec-expectations.
"""
Scenario: configure rspec/expecations AND test/unit assertions
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.expect_with :rspec, :stdlib
end
describe 5 do
it "is greater than 4" do
assert 5 > 4, "expected 5 to be greater than 4"
end
it "is less than 6" do
5.should be < 6
end
end
"""
When I run `rspec example_spec.rb`
Then the examples should all pass
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
if defined?(require_relative)
# @private
def require_rspec(path)
require_relative path
end
else
# @private
def require_rspec(path)
require "rspec/#{path}"
end
end
require_rspec 'core/filter_manager'
require_rspec 'core/dsl'
require_rspec 'core/extensions'
require_rspec 'core/load_path'
require_rspec 'core/deprecation'
require_rspec 'core/backward_compatibility'
require_rspec 'core/reporter'
require_rspec 'core/metadata_hash_builder'
require_rspec 'core/hooks'
require_rspec 'core/subject'
require_rspec 'core/let'
require_rspec 'core/metadata'
require_rspec 'core/pending'
require_rspec 'core/world'
require_rspec 'core/configuration'
require_rspec 'core/project_initializer'
require_rspec 'core/option_parser'
require_rspec 'core/drb_options'
require_rspec 'core/configuration_options'
require_rspec 'core/command_line'
require_rspec 'core/drb_command_line'
require_rspec 'core/runner'
require_rspec 'core/example'
require_rspec 'core/shared_example_group'
require_rspec 'core/example_group'
require_rspec 'core/version'
module RSpec
autoload :Matchers, 'rspec/matchers'
autoload :SharedContext, 'rspec/core/shared_context'
# @private
def self.wants_to_quit
# Used internally to determine what to do when a SIGINT is received
world.wants_to_quit
end
# @private
# Used internally to determine what to do when a SIGINT is received
def self.wants_to_quit=(maybe)
world.wants_to_quit=(maybe)
end
# @private
# Internal container for global non-configuration data
def self.world
@world ||= RSpec::Core::World.new
end
# @private
# Used internally to ensure examples get reloaded between multiple runs in
# the same process.
def self.reset
world.reset
configuration.reset
end
# Returns the global [Configuration](RSpec/Core/Configuration) object. While you
# _can_ use this method to access the configuration, the more common
# convention is to use [RSpec.configure](RSpec#configure-class_method).
#
# @example
# RSpec.configuration.drb_port = 1234
# @see RSpec.configure
# @see Core::Configuration
def self.configuration
@configuration ||= RSpec::Core::Configuration.new
end
# Yields the global configuration to a block.
# @yield [Configuration] global configuration
#
# @example
# RSpec.configure do |config|
# config.add_formatter 'documentation'
# end
# @see Core::Configuration
def self.configure
yield configuration if block_given?
end
# @private
# Used internally to clear remaining groups when fail_fast is set
def self.clear_remaining_example_groups
world.example_groups.clear
end
module Core
end
end
require_rspec 'core/backward_compatibility'
require_rspec 'monkey'
Feature: current example
You can reference the example object, and access its metadata, using
the `example` method within an example.
Scenario: access the example object from within an example
Given a file named "spec/example_spec.rb" with:
"""
describe "an example" do
it "knows itself as example" do
example.description.should eq("knows itself as example")
end
end
"""
When I run `rspec spec/example_spec.rb`
Then the example should pass
Feature: custom formatters
RSpec ships with general purpose output formatters. You can tell RSpec which
one to use using the [`--format` command line
option]('../command_line/format_option').
When RSpec's built-in output formatters don't, however, give you everything
you need, you can write your own custom formatter and tell RSpec to use that
one instead. The simplest way is to subclass RSpec's `BaseTextFormatter`,
and then override just the methods that you want to modify.
Scenario: custom formatter
Given a file named "custom_formatter.rb" with:
"""
require "rspec/core/formatters/base_text_formatter"
class CustomFormatter < RSpec::Core::Formatters::BaseTextFormatter
def initialize(output)
super(output)
end
def example_started(proxy)
output << "example: " << proxy.description
end
end
"""
And a file named "example_spec.rb" with:
"""
describe "my group" do
specify "my example" do
end
end
"""
When I run `rspec example_spec.rb --require ./custom_formatter.rb --format CustomFormatter`
Then the output should contain "example: my example"
And the exit status should be 0
Feature: custom settings
Extensions like rspec-rails can add their own configuration settings.
Scenario: simple setting (with defaults)
Given a file named "additional_setting_spec.rb" with:
"""
RSpec.configure do |c|
c.add_setting :custom_setting
end
describe "custom setting" do
it "is nil by default" do
RSpec.configuration.custom_setting.should be_nil
end
it "acts false by default" do
RSpec.configuration.custom_setting.should be_false
end
it "is exposed as a predicate" do
RSpec.configuration.custom_setting?.should be_false
end
it "can be overridden" do
RSpec.configuration.custom_setting = true
RSpec.configuration.custom_setting.should be_true
RSpec.configuration.custom_setting?.should be_true
end
end
"""
When I run `rspec ./additional_setting_spec.rb`
Then the examples should all pass
Scenario: default to true
Given a file named "additional_setting_spec.rb" with:
"""
RSpec.configure do |c|
c.add_setting :custom_setting, :default => true
end
describe "custom setting" do
it "is true by default" do
RSpec.configuration.custom_setting.should be_true
end
it "is exposed as a predicate" do
RSpec.configuration.custom_setting?.should be_true
end
it "can be overridden" do
RSpec.configuration.custom_setting = false
RSpec.configuration.custom_setting.should be_false
RSpec.configuration.custom_setting?.should be_false
end
end
"""
When I run `rspec ./additional_setting_spec.rb`
Then the examples should all pass
Scenario: overridden in a subsequent RSpec.configure block
Given a file named "additional_setting_spec.rb" with:
"""
RSpec.configure do |c|
c.add_setting :custom_setting
end
RSpec.configure do |c|
c.custom_setting = true
end
describe "custom setting" do
it "returns the value set in the last cofigure block to get eval'd" do
RSpec.configuration.custom_setting.should be_true
end
it "is exposed as a predicate" do
RSpec.configuration.custom_setting?.should be_true
end
end
"""
When I run `rspec ./additional_setting_spec.rb`
Then the examples should all pass
Feature: default_path
As of rspec-2.7, you can just type `rspec` to run all specs that live
in the `spec` directory.
This is supported by a `--default_path` option, which is set to `spec` by
default. If you prefer to keep your specs in a different directory, or assign
an individual file to `--default_path`, you can do so on the command line or
in a configuration file (`.rspec`, `~/.rspec`, or a custom file).
NOTE: this option is not supported on `RSpec.configuration`, as it needs to
be set before spec files are loaded.
Scenario: run `rspec` with default default_path (`spec` directory)
Given a file named "spec/example_spec.rb" with:
"""
describe "an example" do
it "passes" do
end
end
"""
When I run `rspec`
Then the output should contain "1 example, 0 failures"
Scenario: run `rspec` with customized default_path
Given a file named ".rspec" with:
"""
--default_path behavior
"""
Given a file named "behavior/example_spec.rb" with:
"""
describe "an example" do
it "passes" do
end
end
"""
When I run `rspec`
Then the output should contain "1 example, 0 failures"
module RSpec
class << self
# @private
#
# Used internally to print deprecation warnings
def deprecate(method, alternate_method=nil, version=nil)
version_string = version ? "rspec-#{version}" : "a future version of RSpec"
message = <<-NOTICE
*****************************************************************
DEPRECATION WARNING: you are using deprecated behaviour that will
be removed from #{version_string}.
#{caller(0)[2]}
* #{method} is deprecated.
NOTICE
if alternate_method
message << <<-ADDITIONAL
* please use #{alternate_method} instead.
ADDITIONAL
end
message << "*****************************************************************"
warn_deprecation(message)
end
# @private
#
# Used internally to print deprecation warnings
def warn_deprecation(message)
send :warn, message
end
end
# @private
class HashWithDeprecationNotice < Hash
def initialize(method, alternate_method=nil)
@method, @alternate_method = method, alternate_method
end
def []=(k,v)
RSpec.deprecate(@method, @alternate_method)
super(k,v)
end
end
end
require "spec_helper"
describe "deprecations" do
describe "Spec" do
it "is deprecated" do
RSpec.should_receive(:warn_deprecation).with(/Spec .* RSpec/i)
Spec
end
it "returns RSpec" do
RSpec.stub(:warn_deprecation)
Spec.should eq(RSpec)
end
it "doesn't include backward compatibility in const_missing backtrace" do
RSpec.stub(:warn_deprecation)
exception = nil
begin
ConstantThatDoesNotExist
rescue Exception => exception
end
exception.backtrace.find { |l| l =~ /lib\/rspec\/core\/backward_compatibility/ }.should be_nil
end
end
describe RSpec::Core::ExampleGroup do
describe 'running_example' do
it 'is deprecated' do
RSpec.should_receive(:warn_deprecation)
self.running_example
end
it "delegates to example" do
RSpec.stub(:warn_deprecation)
running_example.should eq(example)
end
end
end
describe "Spec::Runner.configure" do
it "is deprecated" do
RSpec.stub(:warn_deprecation)
RSpec.should_receive(:deprecate)
Spec::Runner.configure
end
end
describe "Spec::Rake::SpecTask" do
it "is deprecated" do
RSpec.stub(:warn_deprecation)
RSpec.should_receive(:deprecate)
Spec::Rake::SpecTask
end
it "doesn't include backward compatibility in const_missing backtrace" do
RSpec.stub(:warn_deprecation)
exception = nil
begin
Spec::Rake::ConstantThatDoesNotExist
rescue Exception => exception
end
exception.backtrace.find { |l| l =~ /lib\/rspec\/core\/backward_compatibility/ }.should be_nil
end
end
end
Feature: described class
If the first argument to the outermost example group is a class, the class is
exposed to each example via the described_class() method.
Scenario: access the described class from the example
Given a file named "spec/example_spec.rb" with:
"""
describe Fixnum do
it "is available as described_class" do
described_class.should eq(Fixnum)
end
end
"""
When I run `rspec spec/example_spec.rb`
Then the example should pass
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
s.name = %q{diff-lcs}
s.version = "1.1.3"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Austin Ziegler"]
s.date = %q{2011-08-27}
s.description = %q{Diff::LCS is a port of Perl's Algorithm::Diff that uses the McIlroy-Hunt
longest common subsequence (LCS) algorithm to compute intelligent differences
between two sequenced enumerable containers. The implementation is based on
Mario I. Wolczko's {Smalltalk version 1.2}[ftp://st.cs.uiuc.edu/pub/Smalltalk/MANCHESTER/manchester/4.0/diff.st]
(1993) and Ned Konz's Perl version
{Algorithm::Diff 1.15}[http://search.cpan.org/~nedkonz/Algorithm-Diff-1.15/].
This is release 1.1.3, fixing several small bugs found over the years. Version
1.1.0 added new features, including the ability to #patch and #unpatch changes
as well as a new contextual diff callback, Diff::LCS::ContextDiffCallbacks,
that should improve the context sensitivity of patching.
This library is called Diff::LCS because of an early version of Algorithm::Diff
which was restrictively licensed. This version has seen a minor license change:
instead of being under Ruby's license as an option, the third optional license
is the MIT license.}
s.email = ["austin@rubyforge.org"]
s.executables = ["htmldiff", "ldiff"]
s.extra_rdoc_files = ["Manifest.txt", "docs/COPYING.txt", "History.rdoc", "License.rdoc", "README.rdoc"]
s.files = ["History.rdoc", "License.rdoc", "Manifest.txt", "README.rdoc", "Rakefile", "bin/htmldiff", "bin/ldiff", "diff-lcs.gemspec", "docs/COPYING.txt", "docs/artistic.html", "lib/diff-lcs.rb", "lib/diff/lcs.rb", "lib/diff/lcs/array.rb", "lib/diff/lcs/block.rb", "lib/diff/lcs/callbacks.rb", "lib/diff/lcs/change.rb", "lib/diff/lcs/htmldiff.rb", "lib/diff/lcs/hunk.rb", "lib/diff/lcs/ldiff.rb", "lib/diff/lcs/string.rb", "spec/diff_spec.rb", "spec/lcs_spec.rb", "spec/patch_spec.rb", "spec/sdiff_spec.rb", "spec/spec_helper.rb", "spec/traverse_balanced_spec.rb", "spec/traverse_sequences_spec.rb", ".gemtest"]
s.rdoc_options = ["--main", "README.rdoc"]
s.require_paths = ["lib"]
s.rubyforge_project = %q{ruwiki}
s.rubygems_version = %q{1.3.6}
s.summary = %q{Diff::LCS is a port of Perl's Algorithm::Diff that uses the McIlroy-Hunt longest common subsequence (LCS) algorithm to compute intelligent differences between two sequenced enumerable containers}
if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 3
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
s.add_development_dependency(%q<rspec>, ["~> 2.0"])
s.add_development_dependency(%q<hoe>, ["~> 2.10"])
else
s.add_dependency(%q<rspec>, ["~> 2.0"])
s.add_dependency(%q<hoe>, ["~> 2.10"])
end
else
s.add_dependency(%q<rspec>, ["~> 2.0"])
s.add_dependency(%q<hoe>, ["~> 2.10"])
end
end
# -*- ruby encoding: utf-8 -*-
require 'diff/lcs'
# vim: ft=ruby
# -*- ruby encoding: utf-8 -*-
require 'spec_helper'
describe "Diff::LCS.diff" do
include Diff::LCS::SpecHelper::Matchers
it "should correctly diff seq1 to seq2" do
diff_s1_s2 = Diff::LCS.diff(seq1, seq2)
change_diff(correct_forward_diff).should == diff_s1_s2
end
it "should correctly diff seq2 to seq1" do
diff_s2_s1 = Diff::LCS.diff(seq2, seq1)
change_diff(correct_backward_diff).should == diff_s2_s1
end
it "should correctly diff against an empty sequence" do
diff = Diff::LCS.diff(word_sequence, [])
correct_diff = [
[ [ '-', 0, 'abcd' ],
[ '-', 1, 'efgh' ],
[ '-', 2, 'ijkl' ],
[ '-', 3, 'mnopqrstuvwxyz' ] ]
]
change_diff(correct_diff).should == diff
diff = Diff::LCS.diff([], word_sequence)
correct_diff.each { |hunk| hunk.each { |change| change[0] = '+' } }
change_diff(correct_diff).should == diff
end
end
# vim: ft=ruby
Autotest.add_discovery { "rspec2" } if File.exist?("./.rspec")
require "spec_helper"
describe "autotest/discover.rb" do
context "with ./.rspec present" do
it "adds 'rspec2' to the list of discoveries" do
File.stub(:exist?).with("./.rspec") { true }
Autotest.should_receive(:add_discovery)
load File.expand_path("../../../lib/autotest/discover.rb", __FILE__)
end
end
context "with ./.rspec absent" do
it "does not add 'rspec2' to the list of discoveries" do
File.stub(:exist?) { false }
Autotest.should_not_receive(:add_discovery)
load File.expand_path("../../../lib/autotest/discover.rb", __FILE__)
end
end
end
require 'rspec/core/formatters/base_text_formatter'
module RSpec
module Core
module Formatters
class DocumentationFormatter < BaseTextFormatter
def initialize(output)
super(output)
@group_level = 0
end
def example_group_started(example_group)
super(example_group)
output.puts if @group_level == 0
output.puts "#{current_indentation}#{example_group.description}"
@group_level += 1
end
def example_group_finished(example_group)
@group_level -= 1
end
def example_passed(example)
super(example)
output.puts passed_output(example)
end
def example_pending(example)
super(example)
output.puts pending_output(example, example.execution_result[:pending_message])
end
def example_failed(example)
super(example)
output.puts failure_output(example, example.execution_result[:exception])
end
def failure_output(example, exception)
red("#{current_indentation}#{example.description} (FAILED - #{next_failure_index})")
end
def next_failure_index
@next_failure_index ||= 0
@next_failure_index += 1
end
def passed_output(example)
green("#{current_indentation}#{example.description}")
end
def pending_output(example, message)
yellow("#{current_indentation}#{example.description} (PENDING: #{message})")
end
def current_indentation
' ' * @group_level
end
def example_group_chain
example_group.ancestors.reverse
end
end
end
end
end
module RSpec
module Core
class DRbCommandLine
def initialize(options)
@options = options
end
def drb_port
@options.options[:drb_port] || ENV['RSPEC_DRB'] || 8989
end
def run(err, out)
begin
DRb.start_service("druby://localhost:0")
rescue SocketError, Errno::EADDRNOTAVAIL
DRb.start_service("druby://:0")
end
spec_server = DRbObject.new_with_uri("druby://127.0.0.1:#{drb_port}")
spec_server.run(@options.drb_argv, err, out)
end
end
end
end
require "spec_helper"
describe "::DRbCommandLine", :type => :drb, :unless => RUBY_PLATFORM == 'java' do
let(:config) { RSpec::Core::Configuration.new }
let(:out) { StringIO.new }
let(:err) { StringIO.new }
include_context "spec files"
def command_line(*args)
RSpec::Core::DRbCommandLine.new(config_options(*args))
end
def config_options(*args)
options = RSpec::Core::ConfigurationOptions.new(args)
options.parse_options
options
end
context "without server running" do
it "raises an error" do
lambda { command_line.run(err, out) }.should raise_error(DRb::DRbConnError)
end
end
describe "--drb-port" do
def with_RSPEC_DRB_set_to(val)
original = ENV['RSPEC_DRB']
ENV['RSPEC_DRB'] = val
begin
yield
ensure
ENV['RSPEC_DRB'] = original
end
end
context "without RSPEC_DRB environment variable set" do
it "defaults to 8989" do
with_RSPEC_DRB_set_to(nil) do
command_line.drb_port.should eq(8989)
end
end
it "sets the DRb port" do
with_RSPEC_DRB_set_to(nil) do
command_line("--drb-port", "1234").drb_port.should eq(1234)
command_line("--drb-port", "5678").drb_port.should eq(5678)
end
end
end
context "with RSPEC_DRB environment variable set" do
context "without config variable set" do
it "uses RSPEC_DRB value" do
with_RSPEC_DRB_set_to('9000') do
command_line.drb_port.should eq("9000")
end
end
end
context "and config variable set" do
it "uses configured value" do
with_RSPEC_DRB_set_to('9000') do
command_line(*%w[--drb-port 5678]).drb_port.should eq(5678)
end
end
end
end
end
context "with server running" do
class SimpleDRbSpecServer
def self.run(argv, err, out)
options = RSpec::Core::ConfigurationOptions.new(argv)
options.parse_options
RSpec::Core::CommandLine.new(options, RSpec::Core::Configuration.new).run(err, out)
end
end
before(:all) do
@drb_port = '8990'
@drb_example_file_counter = 0
DRb::start_service("druby://127.0.0.1:#{@drb_port}", SimpleDRbSpecServer)
end
after(:all) do
DRb::stop_service
end
it "returns 0 if spec passes" do
result = command_line("--drb-port", @drb_port, passing_spec_filename).run(err, out)
result.should be(0)
end
it "returns 1 if spec fails" do
result = command_line("--drb-port", @drb_port, failing_spec_filename).run(err, out)
result.should be(1)
end
it "outputs colorized text when running with --colour option" do
pending "figure out a way to tell the output to say it's tty"
command_line(failing_spec_filename, "--color", "--drb-port", @drb_port).run(err, out)
out.rewind
out.read.should =~ /\e\[31m/m
end
end
end
# Builds command line arguments to pass to the rspec command over DRb
module RSpec::Core
# @private
class DrbOptions
def initialize(submitted_options, filter_manager)
@submitted_options = submitted_options
@filter_manager = filter_manager
end
def options
argv = []
argv << "--color" if @submitted_options[:color]
argv << "--profile" if @submitted_options[:profile_examples]
argv << "--backtrace" if @submitted_options[:full_backtrace]
argv << "--tty" if @submitted_options[:tty]
argv << "--fail-fast" if @submitted_options[:fail_fast]
argv << "--options" << @submitted_options[:custom_options_file] if @submitted_options[:custom_options_file]
argv << "--order" << @submitted_options[:order] if @submitted_options[:order]
add_failure_exit_code(argv)
add_full_description(argv)
add_line_numbers(argv)
add_filter(argv, :inclusion, @filter_manager.inclusions)
add_filter(argv, :exclusion, @filter_manager.exclusions)
add_formatters(argv)
add_libs(argv)
add_requires(argv)
argv + @submitted_options[:files_or_directories_to_run]
end
def add_failure_exit_code(argv)
if @submitted_options[:failure_exit_code]
argv << "--failure-exit-code" << @submitted_options[:failure_exit_code].to_s
end
end
def add_full_description(argv)
if @submitted_options[:full_description]
# The argument to --example is regexp-escaped before being stuffed
# into a regexp when received for the first time (see OptionParser).
# Hence, merely grabbing the source of this regexp will retain the
# backslashes, so we must remove them.
argv << "--example" << @submitted_options[:full_description].source.delete('\\')
end
end
def add_line_numbers(argv)
if @submitted_options[:line_numbers]
argv.push(*@submitted_options[:line_numbers].inject([]){|a,l| a << "--line_number" << l})
end
end
def add_filter(argv, name, hash)
hash.each_pair do |k, v|
next if [:if,:unless].include?(k)
tag = name == :inclusion ? k.to_s : "~#{k}"
tag << ":#{v}" if v.is_a?(String)
argv << "--tag" << tag
end unless hash.empty?
end
def add_formatters(argv)
@submitted_options[:formatters].each do |pair|
argv << "--format" << pair[0]
argv << "--out" << pair[1] if pair[1]
end if @submitted_options[:formatters]
end
def add_libs(argv)
@submitted_options[:libs].each do |path|
argv << "-I" << path
end if @submitted_options[:libs]
end
def add_requires(argv)
@submitted_options[:requires].each do |path|
argv << "--require" << path
end if @submitted_options[:requires]
end
end
end
require "spec_helper"
describe RSpec::Core::DrbOptions do
include ConfigOptionsHelper
describe "#drb_argv" do
it "preserves extra arguments" do
File.stub(:exist?) { false }
config_options_object(*%w[ a --drb b --color c ]).drb_argv.should =~ %w[ --color a b c ]
end
%w(--color --fail-fast --profile --backtrace --tty).each do |option|
it "includes #{option}" do
config_options_object("#{option}").drb_argv.should include("#{option}")
end
end
it "includes --failure-exit-code" do
config_options_object(*%w[--failure-exit-code 2]).drb_argv.should include("--failure-exit-code", "2")
end
it "includes --options" do
config_options_object(*%w[--options custom.opts]).drb_argv.should include("--options", "custom.opts")
end
it "includes --order" do
config_options_object(*%w[--order random]).drb_argv.should include('--order', 'random')
end
context "with --example" do
it "includes --example" do
config_options_object(*%w[--example foo]).drb_argv.should include("--example", "foo")
end
it "unescapes characters which were escaped upon storing --example originally" do
config_options_object("--example", "foo\\ bar").drb_argv.should include("--example", "foo bar")
end
end
context "with tags" do
it "includes the inclusion tags" do
coo = config_options_object("--tag", "tag")
coo.drb_argv.should eq(["--tag", "tag"])
end
it "includes the inclusion tags with values" do
coo = config_options_object("--tag", "tag:foo")
coo.drb_argv.should eq(["--tag", "tag:foo"])
end
it "leaves inclusion tags intact" do
coo = config_options_object("--tag", "tag")
coo.drb_argv
coo.filter_manager.inclusions.should eq( {:tag=>true} )
end
it "leaves inclusion tags with values intact" do
coo = config_options_object("--tag", "tag:foo")
coo.drb_argv
coo.filter_manager.inclusions.should eq( {:tag=>'foo'} )
end
it "includes the exclusion tags" do
coo = config_options_object("--tag", "~tag")
coo.drb_argv.should eq(["--tag", "~tag"])
end
it "includes the exclusion tags with values" do
coo = config_options_object("--tag", "~tag:foo")
coo.drb_argv.should eq(["--tag", "~tag:foo"])
end
it "leaves exclusion tags intact" do
coo = config_options_object("--tag", "~tag")
coo.drb_argv
coo.filter_manager.exclusions.should include(:tag=>true)
end
it "leaves exclusion tags with values intact" do
coo = config_options_object("--tag", "~tag:foo")
coo.drb_argv
coo.filter_manager.exclusions.should include(:tag=>'foo')
end
end
context "with formatters" do
it "includes the formatters" do
coo = config_options_object("--format", "d")
coo.drb_argv.should eq(["--format", "d"])
end
it "leaves formatters intact" do
coo = config_options_object("--format", "d")
coo.drb_argv
coo.options[:formatters].should eq([["d"]])
end
it "leaves output intact" do
coo = config_options_object("--format", "p", "--out", "foo.txt", "--format", "d")
coo.drb_argv
coo.options[:formatters].should eq([["p","foo.txt"],["d"]])
end
end
context "with --out" do
it "combines with formatters" do
coo = config_options_object(*%w[--format h --out report.html])
coo.drb_argv.should eq(%w[--format h --out report.html])
end
end
context "with --line_number" do
it "includes --line_number" do
config_options_object(*%w[--line_number 35]).drb_argv.should eq(%w[--line_number 35])
end
it "includes multiple lines" do
config_options_object(*%w[-l 90 -l 4 -l 55]).drb_argv.should eq(
%w[--line_number 90 --line_number 4 --line_number 55]
)
end
end
context "with -I libs" do
it "includes -I" do
config_options_object(*%w[-I a_dir]).drb_argv.should eq(%w[-I a_dir])
end
it "includes multiple paths" do
config_options_object(*%w[-I dir_1 -I dir_2 -I dir_3]).drb_argv.should eq(
%w[-I dir_1 -I dir_2 -I dir_3]
)
end
end
context "with --require" do
it "includes --require" do
config_options_object(*%w[--require a_path]).drb_argv.should eq(%w[--require a_path])
end
it "includes multiple paths" do
config_options_object(*%w[--require dir/ --require file.rb]).drb_argv.should eq(
%w[--require dir/ --require file.rb]
)
end
end
context "--drb specified in ARGV" do
it "renders all the original arguments except --drb" do
config_options_object(*%w[ --drb --color --format s --example pattern --line_number 1 --profile --backtrace -I path/a -I path/b --require path/c --require path/d]).
drb_argv.should eq(%w[ --color --profile --backtrace --example pattern --line_number 1 --format s -I path/a -I path/b --require path/c --require path/d])
end
end
context "--drb specified in the options file" do
it "renders all the original arguments except --drb" do
File.open("./.rspec", "w") {|f| f << "--drb --color"}
config_options_object(*%w[ --tty --format s --example pattern --line_number 1 --profile --backtrace ]).
drb_argv.should eq(%w[ --color --profile --backtrace --tty --example pattern --line_number 1 --format s])
end
end
context "--drb specified in ARGV and the options file" do
it "renders all the original arguments except --drb" do
File.open("./.rspec", "w") {|f| f << "--drb --color"}
config_options_object(*%w[ --drb --format s --example pattern --line_number 1 --profile --backtrace]).
drb_argv.should eq(%w[ --color --profile --backtrace --example pattern --line_number 1 --format s])
end
end
context "--drb specified in ARGV and in as ARGV-specified --options file" do
it "renders all the original arguments except --drb and --options" do
File.open("./.rspec", "w") {|f| f << "--drb --color"}
config_options_object(*%w[ --drb --format s --example pattern --line_number 1 --profile --backtrace]).
drb_argv.should eq(%w[ --color --profile --backtrace --example pattern --line_number 1 --format s ])
end
end
end
end
module RSpec
module Core
# Adds the `describe` method to the top-level namespace.
module DSL
# Generates a subclass of [ExampleGroup](ExampleGroup)
#
# ## Examples:
#
# describe "something" do
# it "does something" do
# # example code goes here
# end
# end
#
# @see ExampleGroup
# @see ExampleGroup.describe
def describe(*args, &example_group_block)
RSpec::Core::ExampleGroup.describe(*args, &example_group_block).register
end
end
end
end
include RSpec::Core::DSL
require 'aruba/cucumber'
Before do
if RUBY_PLATFORM =~ /java/
# ideas taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html
set_env('JRUBY_OPTS', '-X-C') # disable JIT since these processes are so short lived
set_env('JAVA_OPTS', '-d32') # force jRuby to use client JVM for faster startup times
@aruba_timeout_seconds = 60
else
@aruba_timeout_seconds = 5
end
end
module RSpec
module Core
# Wrapper for an instance of a subclass of [ExampleGroup](ExampleGroup). An
# instance of `Example` is returned by the
# [example](ExampleGroup#example-instance_method) method available in
# examples, [before](Hooks#before-instance_method) and
# [after](Hooks#after-instance_method) hooks, and yielded to
# [around](Hooks#around-instance_method) hooks.
# @see ExampleGroup
class Example
# @private
#
# Used to define methods that delegate to this example's metadata
def self.delegate_to_metadata(*keys)
keys.each do |key|
define_method(key) {@metadata[key]}
end
end
delegate_to_metadata :description, :full_description, :execution_result, :file_path, :pending, :location
# @attr_reader
#
# Returns the first exception raised in the context of running this
# example (nil if no exception is raised)
attr_reader :exception
# @attr_reader
#
# Returns the metadata object associated with this example.
attr_reader :metadata
# @attr_reader
# @private
#
# Returns the example_group_instance that provides the context for
# running this example.
attr_reader :example_group_instance
# Creates a new instance of Example.
# @param example_group_class the subclass of ExampleGroup in which this Example is declared
# @param description the String passed to the `it` method (or alias)
# @param metadata additional args passed to `it` to be used as metadata
# @param example_block the block of code that represents the example
def initialize(example_group_class, description, metadata, example_block=nil)
@example_group_class, @options, @example_block = example_group_class, metadata, example_block
@metadata = @example_group_class.metadata.for_example(description, metadata)
@exception = nil
@pending_declared_in_example = false
end
# @deprecated access options via metadata instead
def options
@options
end
# Returns the example group class that provides the context for running
# this example.
def example_group
@example_group_class
end
alias_method :pending?, :pending
# @api private
# @param example_group_instance the instance of an ExampleGroup subclass
# instance_evals the block submitted to the constructor in the
# context of the instance of ExampleGroup
def run(example_group_instance, reporter)
@example_group_instance = example_group_instance
@example_group_instance.example = self
start(reporter)
begin
unless pending
with_around_hooks do
begin
run_before_each
@example_group_instance.instance_eval(&@example_block)
rescue Pending::PendingDeclaredInExample => e
@pending_declared_in_example = e.message
rescue Exception => e
set_exception(e)
ensure
run_after_each
end
end
end
rescue Exception => e
set_exception(e)
ensure
@example_group_instance.instance_variables.each do |ivar|
@example_group_instance.instance_variable_set(ivar, nil)
end
@example_group_instance = nil
begin
assign_auto_description
rescue Exception => e
set_exception(e)
end
end
finish(reporter)
end
# @private
#
# Wraps the example block in a Proc so it can invoked using `run` or
# `call` in [around](../Hooks#around-instance_method) hooks.
def self.procsy(metadata, &proc)
Proc.new(&proc).extend(Procsy).with(metadata)
end
# @private
module Procsy
attr_reader :metadata
# @private
# @param [Proc]
# Adds a `run` method to the extended Proc, allowing it to be invoked
# in an [around](../Hooks#around-instance_method) hook using either
# `run` or `call`.
def self.extended(object)
def object.run; call; end
end
# @private
def with(metadata)
@metadata = metadata
self
end
end
# @private
def any_apply?(filters)
metadata.any_apply?(filters)
end
# @private
def all_apply?(filters)
@metadata.all_apply?(filters) || @example_group_class.all_apply?(filters)
end
# @private
def around_hooks
@around_hooks ||= example_group.around_hooks_for(self)
end
# @private
#
# Used internally to set an exception in an after hook, which
# captures the exception but doesn't raise it.
def set_exception(exception)
@exception ||= exception
end
# @private
#
# Used internally to set an exception and fail without actually executing
# the example when an exception is raised in before(:all).
def fail_with_exception(reporter, exception)
start(reporter)
set_exception(exception)
finish(reporter)
end
private
def with_around_hooks(&block)
if around_hooks.empty?
yield
else
@example_group_class.run_around_each_hooks(self, Example.procsy(metadata, &block)).call
end
end
def start(reporter)
reporter.example_started(self)
record :started_at => Time.now
end
# @private
module NotPendingExampleFixed
def pending_fixed?; false; end
end
def finish(reporter)
if @exception
@exception.extend(NotPendingExampleFixed) unless @exception.respond_to?(:pending_fixed?)
record_finished 'failed', :exception => @exception
reporter.example_failed self
false
elsif @pending_declared_in_example
record_finished 'pending', :pending_message => @pending_declared_in_example
reporter.example_pending self
true
elsif pending
record_finished 'pending', :pending_message => String === pending ? pending : Pending::NO_REASON_GIVEN
reporter.example_pending self
true
else
record_finished 'passed'
reporter.example_passed self
true
end
end
def record_finished(status, results={})
finished_at = Time.now
record results.merge(:status => status, :finished_at => finished_at, :run_time => (finished_at - execution_result[:started_at]))
end
def run_before_each
@example_group_instance.setup_mocks_for_rspec if @example_group_instance.respond_to?(:setup_mocks_for_rspec)
@example_group_class.run_before_each_hooks(self)
end
def run_after_each
@example_group_class.run_after_each_hooks(self)
@example_group_instance.verify_mocks_for_rspec if @example_group_instance.respond_to?(:verify_mocks_for_rspec)
ensure
@example_group_instance.teardown_mocks_for_rspec if @example_group_instance.respond_to?(:teardown_mocks_for_rspec)
end
def assign_auto_description
if description.empty? and !pending?
if RSpec.configuration.expecting_with_rspec?
metadata[:description] = RSpec::Matchers.generated_description
RSpec::Matchers.clear_generated_description
else
raise NotImplementedError.new(
"Generated descriptions are only supported when you use rspec-expectations. " +
"You must give every example an explicit description."
)
end
end
end
def record(results={})
execution_result.update(results)
end
end
end
end
module RSpec
module Core
# ExampleGroup and Example are the main structural elements of rspec-core.
# Consider this example:
#
# describe Thing do
# it "does something" do
# end
# end
#
# The object returned by `describe Thing` is a subclass of ExampleGroup.
# The object returned by `it "does something"` is an instance of Example,
# which serves as a wrapper for an instance of the ExampleGroup in which it
# is declared.
class ExampleGroup
extend MetadataHashBuilder::WithDeprecationWarning
extend Extensions::ModuleEvalWithArgs
extend Subject::ExampleGroupMethods
extend Hooks
include Extensions::InstanceEvalWithArgs
include Subject::ExampleMethods
include Pending
include Let
# @private
def self.world
RSpec.world
end
# @private
def self.register
world.register(self)
end
class << self
# @private
def self.delegate_to_metadata(*names)
names.each do |name|
define_method name do
metadata[:example_group][name]
end
end
end
delegate_to_metadata :description, :described_class, :file_path
alias_method :display_name, :description
# @private
alias_method :describes, :described_class
end
# @private
def self.define_example_method(name, extra_options={})
module_eval(<<-END_RUBY, __FILE__, __LINE__)
def self.#{name}(desc=nil, *args, &block)
options = build_metadata_hash_from(args)
options.update(:pending => RSpec::Core::Pending::NOT_YET_IMPLEMENTED) unless block
options.update(#{extra_options.inspect})
examples << RSpec::Core::Example.new(self, desc, options, block)
examples.last
end
END_RUBY
end
define_example_method :example
define_example_method :it
define_example_method :specify
define_example_method :focused, :focused => true, :focus => true
define_example_method :focus, :focused => true, :focus => true
define_example_method :pending, :pending => true
define_example_method :xexample, :pending => 'Temporarily disabled with xexample'
define_example_method :xit, :pending => 'Temporarily disabled with xit'
define_example_method :xspecify, :pending => 'Temporarily disabled with xspecify'
class << self
alias_method :alias_example_to, :define_example_method
end
# @private
def self.define_nested_shared_group_method(new_name, report_label=nil)
module_eval(<<-END_RUBY, __FILE__, __LINE__)
def self.#{new_name}(name, *args, &customization_block)
group = describe("#{report_label || "it should behave like"} \#{name}") do
find_and_eval_shared("examples", name, *args, &customization_block)
end
group.metadata[:shared_group_name] = name
group
end
END_RUBY
end
define_nested_shared_group_method :it_should_behave_like
class << self
alias_method :alias_it_should_behave_like_to, :define_nested_shared_group_method
end
alias_it_should_behave_like_to :it_behaves_like, "behaves like"
# Includes shared content declared with `name`.
#
# @see SharedExampleGroup
def self.include_context(name, *args)
block_given? ? block_not_supported("context") : find_and_eval_shared("context", name, *args)
end
# Includes shared content declared with `name`.
#
# @see SharedExampleGroup
def self.include_examples(name, *args)
block_given? ? block_not_supported("examples") : find_and_eval_shared("examples", name, *args)
end
# @private
def self.block_not_supported(label)
warn("Customization blocks not supported for include_#{label}. Use it_behaves_like instead.")
end
# @private
def self.find_and_eval_shared(label, name, *args, &customization_block)
raise ArgumentError, "Could not find shared #{label} #{name.inspect}" unless
shared_block = world.shared_example_groups[name]
module_eval_with_args(*args, &shared_block)
module_eval(&customization_block) if customization_block
end
# @private
def self.examples
@examples ||= []
end
# @private
def self.filtered_examples
world.filtered_examples[self]
end
# @private
def self.descendant_filtered_examples
@descendant_filtered_examples ||= filtered_examples + children.inject([]){|l,c| l + c.descendant_filtered_examples}
end
# The [Metadata](Metadata) object associated with this group.
# @see Metadata
def self.metadata
@metadata if defined?(@metadata)
end
# @private
# @return [Metadata] belonging to the parent of a nested [ExampleGroup](ExampleGroup)
def self.superclass_metadata
@superclass_metadata ||= self.superclass.respond_to?(:metadata) ? self.superclass.metadata : nil
end
# Generates a subclass of this example group which inherits
# everything except the examples themselves.
#
# ## Examples
#
# describe "something" do # << This describe method is defined in
# # << RSpec::Core::DSL, included in the
# # << global namespace
# before do
# do_something_before
# end
#
# let(:thing) { Thing.new }
#
# describe "attribute (of something)" do
# # examples in the group get the before hook
# # declared above, and can access `thing`
# end
# end
#
# @see DSL#describe
def self.describe(*args, &example_group_block)
@_subclass_count ||= 0
@_subclass_count += 1
args << {} unless args.last.is_a?(Hash)
args.last.update(:example_group_block => example_group_block)
# TODO 2010-05-05: Because we don't know if const_set is thread-safe
child = const_set(
"Nested_#{@_subclass_count}",
subclass(self, args, &example_group_block)
)
children << child
child
end
class << self
alias_method :context, :describe
end
# @private
def self.subclass(parent, args, &example_group_block)
subclass = Class.new(parent)
subclass.set_it_up(*args)
subclass.module_eval(&example_group_block) if example_group_block
subclass
end
# @private
def self.children
@children ||= [].extend(Extensions::Ordered)
end
# @private
def self.descendants
@_descendants ||= [self] + children.inject([]) {|list, c| list + c.descendants}
end
# @private
def self.ancestors
@_ancestors ||= super().select {|a| a < RSpec::Core::ExampleGroup}
end
# @private
def self.top_level?
@top_level ||= superclass == ExampleGroup
end
# @private
def self.ensure_example_groups_are_configured
unless defined?(@@example_groups_configured)
RSpec.configuration.configure_mock_framework
RSpec.configuration.configure_expectation_framework
@@example_groups_configured = true
end
end
# @private
def self.set_it_up(*args)
# Ruby 1.9 has a bug that can lead to infinite recursion and a
# SystemStackError if you include a module in a superclass after
# including it in a subclass: https://gist.github.com/845896
# To prevent this, we must include any modules in RSpec::Core::ExampleGroup
# before users create example groups and have a chance to include
# the same module in a subclass of RSpec::Core::ExampleGroup.
# So we need to configure example groups here.
ensure_example_groups_are_configured
symbol_description = args.shift if args.first.is_a?(Symbol)
args << build_metadata_hash_from(args)
args.unshift(symbol_description) if symbol_description
@metadata = RSpec::Core::Metadata.new(superclass_metadata).process(*args)
world.configure_group(self)
end
# @private
def self.before_all_ivars
@before_all_ivars ||= {}
end
# @private
def self.store_before_all_ivars(example_group_instance)
return if example_group_instance.instance_variables.empty?
example_group_instance.instance_variables.each { |ivar|
before_all_ivars[ivar] = example_group_instance.instance_variable_get(ivar)
}
end
# @private
def self.assign_before_all_ivars(ivars, example_group_instance)
return if ivars.empty?
ivars.each { |ivar, val| example_group_instance.instance_variable_set(ivar, val) }
end
# @private
def self.run_before_all_hooks(example_group_instance)
return if descendant_filtered_examples.empty?
assign_before_all_ivars(superclass.before_all_ivars, example_group_instance)
world.run_hook_filtered(:before, :all, self, example_group_instance)
run_hook!(:before, :all, example_group_instance)
store_before_all_ivars(example_group_instance)
end
# @private
def self.run_around_each_hooks(example, initial_procsy)
example.around_hooks.reverse.inject(initial_procsy) do |procsy, around_hook|
Example.procsy(procsy.metadata) do
example.example_group_instance.instance_eval_with_args(procsy, &around_hook)
end
end
end
# @private
def self.run_before_each_hooks(example)
world.run_hook_filtered(:before, :each, self, example.example_group_instance, example)
ancestors.reverse.each { |ancestor| ancestor.run_hook(:before, :each, example.example_group_instance) }
end
# @private
def self.run_after_each_hooks(example)
ancestors.each { |ancestor| ancestor.run_hook(:after, :each, example.example_group_instance) }
world.run_hook_filtered(:after, :each, self, example.example_group_instance, example)
end
# @private
def self.run_after_all_hooks(example_group_instance)
return if descendant_filtered_examples.empty?
assign_before_all_ivars(before_all_ivars, example_group_instance)
begin
run_hook!(:after, :all, example_group_instance)
rescue => e
# TODO: come up with a better solution for this.
RSpec.configuration.reporter.message <<-EOS
An error occurred in an after(:all) hook.
#{e.class}: #{e.message}
occurred at #{e.backtrace.first}
EOS
end
world.run_hook_filtered(:after, :all, self, example_group_instance)
end
# @private
def self.around_hooks_for(example)
world.find_hook(:around, :each, self, example) + ancestors.reverse.inject([]){|l,a| l + a.find_hook(:around, :each, self, example)}
end
# Runs all the examples in this group
def self.run(reporter)
if RSpec.wants_to_quit
RSpec.clear_remaining_example_groups if top_level?
return
end
reporter.example_group_started(self)
begin
run_before_all_hooks(new)
result_for_this_group = run_examples(reporter)
results_for_descendants = children.ordered.map {|child| child.run(reporter)}.all?
result_for_this_group && results_for_descendants
rescue Exception => ex
fail_filtered_examples(ex, reporter)
ensure
run_after_all_hooks(new)
before_all_ivars.clear
reporter.example_group_finished(self)
end
end
# @private
def self.run_examples(reporter)
filtered_examples.ordered.map do |example|
next if RSpec.wants_to_quit
instance = new
set_ivars(instance, before_all_ivars)
succeeded = example.run(instance, reporter)
RSpec.wants_to_quit = true if fail_fast? && !succeeded
succeeded
end.all?
end
# @private
def self.fail_filtered_examples(exception, reporter)
filtered_examples.each { |example| example.fail_with_exception(reporter, exception) }
children.each do |child|
reporter.example_group_started(child)
child.fail_filtered_examples(exception, reporter)
reporter.example_group_finished(child)
end
false
end
# @private
def self.fail_fast?
RSpec.configuration.fail_fast?
end
# @private
def self.any_apply?(filters)
metadata.any_apply?(filters)
end
# @private
def self.all_apply?(filters)
metadata.all_apply?(filters)
end
# @private
def self.declaration_line_numbers
@declaration_line_numbers ||= [metadata[:example_group][:line_number]] +
examples.collect {|e| e.metadata[:line_number]} +
children.inject([]) {|l,c| l + c.declaration_line_numbers}
end
# @private
def self.top_level_description
ancestors.last.description
end
# @private
def self.set_ivars(instance, ivars)
ivars.each {|name, value| instance.instance_variable_set(name, value)}
end
# @attr_reader
# Returns the [Example](Example) object that wraps this instance of
# `ExampleGroup`
attr_accessor :example
# @deprecated use [example](ExampleGroup#example-instance_method)
def running_example
RSpec.deprecate("running_example", "example")
example
end
# Returns the class or module passed to the `describe` method (or alias).
# Returns nil if the subject is not a class or module.
# @example
# describe Thing do
# it "does something" do
# described_class == Thing
# end
# end
#
#
def described_class
self.class.described_class
end
# @private
# instance_evals the block, capturing and reporting an exception if
# raised
def instance_eval_with_rescue(&hook)
begin
instance_eval(&hook)
rescue Exception => e
raise unless example
example.set_exception(e)
end
end
end
end
end
require 'spec_helper'
class SelfObserver
def self.cache
@cache ||= []
end
def initialize
self.class.cache << self
end
end
module RSpec::Core
describe ExampleGroup do
it_behaves_like "metadata hash builder" do
def metadata_hash(*args)
group = ExampleGroup.describe('example description', *args)
group.metadata
end
end
context 'when RSpec.configuration.treat_symbols_as_metadata_keys_with_true_values is set to false' do
before(:each) do
RSpec.configure { |c| c.treat_symbols_as_metadata_keys_with_true_values = false }
end
it 'processes string args as part of the description' do
group = ExampleGroup.describe("some", "separate", "strings")
group.description.should eq("some separate strings")
end
it 'processes symbol args as part of the description' do
Kernel.stub(:warn) # to silence Symbols as args warning
group = ExampleGroup.describe(:some, :separate, :symbols)
group.description.should eq("some separate symbols")
end
end
context 'when RSpec.configuration.treat_symbols_as_metadata_keys_with_true_values is set to true' do
let(:group) { ExampleGroup.describe(:symbol) }
before(:each) do
RSpec.configure { |c| c.treat_symbols_as_metadata_keys_with_true_values = true }
end
it 'does not treat the first argument as a metadata key even if it is a symbol' do
group.metadata.should_not include(:symbol)
end
it 'treats the first argument as part of the description when it is a symbol' do
group.description.should eq("symbol")
end
end
describe "top level group" do
it "runs its children" do
examples_run = []
group = ExampleGroup.describe("parent") do
describe("child") do
it "does something" do
examples_run << example
end
end
end
group.run
examples_run.should have(1).example
end
context "with a failure in the top level group" do
it "runs its children " do
examples_run = []
group = ExampleGroup.describe("parent") do
it "fails" do
examples_run << example
raise "fail"
end
describe("child") do
it "does something" do
examples_run << example
end
end
end
group.run
examples_run.should have(2).examples
end
end
describe "descendants" do
it "returns self + all descendants" do
group = ExampleGroup.describe("parent") do
describe("child") do
describe("grandchild 1") {}
describe("grandchild 2") {}
end
end
group.descendants.size.should eq(4)
end
end
end
describe "child" do
it "is known by parent" do
parent = ExampleGroup.describe
child = parent.describe
parent.children.should eq([child])
end
it "is not registered in world" do
world = RSpec::Core::World.new
parent = ExampleGroup.describe
world.register(parent)
parent.describe
world.example_groups.should eq([parent])
end
end
describe "filtering" do
let(:world) { World.new }
shared_examples "matching filters" do
context "inclusion" do
before do
filter_manager = FilterManager.new
filter_manager.include filter_metadata
world.stub(:filter_manager => filter_manager)
end
it "includes examples in groups matching filter" do
group = ExampleGroup.describe("does something", spec_metadata)
group.stub(:world) { world }
all_examples = [ group.example("first"), group.example("second") ]
group.filtered_examples.should eq(all_examples)
end
it "includes examples directly matching filter" do
group = ExampleGroup.describe("does something")
group.stub(:world) { world }
filtered_examples = [
group.example("first", spec_metadata),
group.example("second", spec_metadata)
]
group.example("third (not-filtered)")
group.filtered_examples.should eq(filtered_examples)
end
end
context "exclusion" do
before do
filter_manager = FilterManager.new
filter_manager.exclude filter_metadata
world.stub(:filter_manager => filter_manager)
end
it "excludes examples in groups matching filter" do
group = ExampleGroup.describe("does something", spec_metadata)
group.stub(:world) { world }
[ group.example("first"), group.example("second") ]
group.filtered_examples.should be_empty
end
it "excludes examples directly matching filter" do
group = ExampleGroup.describe("does something")
group.stub(:world) { world }
[
group.example("first", spec_metadata),
group.example("second", spec_metadata)
]
unfiltered_example = group.example("third (not-filtered)")
group.filtered_examples.should eq([unfiltered_example])
end
end
end
context "matching false" do
let(:spec_metadata) { { :awesome => false }}
context "against false" do
let(:filter_metadata) { { :awesome => false }}
include_examples "matching filters"
end
context "against 'false'" do
let(:filter_metadata) { { :awesome => 'false' }}
include_examples "matching filters"
end
context "against :false" do
let(:filter_metadata) { { :awesome => :false }}
include_examples "matching filters"
end
end
context "matching true" do
let(:spec_metadata) { { :awesome => true }}
context "against true" do
let(:filter_metadata) { { :awesome => true }}
include_examples "matching filters"
end
context "against 'true'" do
let(:filter_metadata) { { :awesome => 'true' }}
include_examples "matching filters"
end
context "against :true" do
let(:filter_metadata) { { :awesome => :true }}
include_examples "matching filters"
end
end
context "matching a string" do
let(:spec_metadata) { { :type => 'special' }}
context "against a string" do
let(:filter_metadata) { { :type => 'special' }}
include_examples "matching filters"
end
context "against a symbol" do
let(:filter_metadata) { { :type => :special }}
include_examples "matching filters"
end
end
context "matching a symbol" do
let(:spec_metadata) { { :type => :special }}
context "against a string" do
let(:filter_metadata) { { :type => 'special' }}
include_examples "matching filters"
end
context "against a symbol" do
let(:filter_metadata) { { :type => :special }}
include_examples "matching filters"
end
end
context "with no filters" do
it "returns all" do
group = ExampleGroup.describe
group.stub(:world) { world }
example = group.example("does something")
group.filtered_examples.should eq([example])
end
end
context "with no examples or groups that match filters" do
it "returns none" do
filter_manager = FilterManager.new
filter_manager.include :awesome => false
world.stub(:filter_manager => filter_manager)
group = ExampleGroup.describe
group.stub(:world) { world }
group.example("does something")
group.filtered_examples.should eq([])
end
end
end
describe '#described_class' do
context "with a constant as the first parameter" do
it "is that constant" do
ExampleGroup.describe(Object) { }.described_class.should eq(Object)
end
end
context "with a string as the first parameter" do
it "is nil" do
ExampleGroup.describe("i'm a computer") { }.described_class.should be_nil
end
end
context "with a constant in an outer group" do
context "and a string in an inner group" do
it "is the top level constant" do
group = ExampleGroup.describe(String) do
describe :symbol do
example "described_class is String" do
described_class.should eq(String)
end
end
end
group.run.should be_true
end
end
end
context "in a nested group" do
it "inherits the described class/module from the outer group" do
group = ExampleGroup.describe(String) do
describe Array do
example "desribes is String" do
described_class.should eq(String)
end
end
end
group.run.should be_true, "expected examples in group to pass"
end
end
end
describe '#described_class' do
it "is the same as described_class" do
self.class.described_class.should eq(self.class.described_class)
end
end
describe '#description' do
it "grabs the description from the metadata" do
group = ExampleGroup.describe(Object, "my desc") { }
group.description.should eq(group.metadata[:example_group][:description])
end
end
describe '#metadata' do
it "adds the third parameter to the metadata" do
ExampleGroup.describe(Object, nil, 'foo' => 'bar') { }.metadata.should include({ "foo" => 'bar' })
end
it "adds the the file_path to metadata" do
ExampleGroup.describe(Object) { }.metadata[:example_group][:file_path].should eq(__FILE__)
end
it "has a reader for file_path" do
ExampleGroup.describe(Object) { }.file_path.should eq(__FILE__)
end
it "adds the line_number to metadata" do
ExampleGroup.describe(Object) { }.metadata[:example_group][:line_number].should eq(__LINE__)
end
end
[:focus, :focused].each do |example_alias|
describe "##{example_alias}" do
let(:group) { ExampleGroup.describe }
subject { group.send example_alias, "a focused example" }
it 'defines an example that can be filtered with :focused => true' do
subject.metadata.should include(:focused => true)
end
it 'defines an example that can be filtered with :focus => true' do
subject.metadata.should include(:focus => true)
end
end
end
describe "#before, after, and around hooks" do
it "runs the before alls in order" do
group = ExampleGroup.describe
order = []
group.before(:all) { order << 1 }
group.before(:all) { order << 2 }
group.before(:all) { order << 3 }
group.example("example") {}
group.run
order.should eq([1,2,3])
end
it "runs the before eachs in order" do
group = ExampleGroup.describe
order = []
group.before(:each) { order << 1 }
group.before(:each) { order << 2 }
group.before(:each) { order << 3 }
group.example("example") {}
group.run
order.should eq([1,2,3])
end
it "runs the after eachs in reverse order" do
group = ExampleGroup.describe
order = []
group.after(:each) { order << 1 }
group.after(:each) { order << 2 }
group.after(:each) { order << 3 }
group.example("example") {}
group.run
order.should eq([3,2,1])
end
it "runs the after alls in reverse order" do
group = ExampleGroup.describe
order = []
group.after(:all) { order << 1 }
group.after(:all) { order << 2 }
group.after(:all) { order << 3 }
group.example("example") {}
group.run
order.should eq([3,2,1])
end
it "only runs before/after(:all) hooks from example groups that have specs that run" do
hooks_run = []
RSpec.configure do |c|
c.filter_run :focus => true
end
unfiltered_group = ExampleGroup.describe "unfiltered" do
before(:all) { hooks_run << :unfiltered_before_all }
after(:all) { hooks_run << :unfiltered_after_all }
context "a subcontext" do
it("has an example") { }
end
end
filtered_group = ExampleGroup.describe "filtered", :focus => true do
before(:all) { hooks_run << :filtered_before_all }
after(:all) { hooks_run << :filtered_after_all }
context "a subcontext" do
it("has an example") { }
end
end
unfiltered_group.run
filtered_group.run
hooks_run.should eq([:filtered_before_all, :filtered_after_all])
end
it "runs before_all_defined_in_config, before all, before each, example, after each, after all, after_all_defined_in_config in that order" do
order = []
RSpec.configure do |c|
c.before(:all) { order << :before_all_defined_in_config }
c.after(:all) { order << :after_all_defined_in_config }
end
group = ExampleGroup.describe
group.before(:all) { order << :top_level_before_all }
group.before(:each) { order << :before_each }
group.after(:each) { order << :after_each }
group.after(:all) { order << :top_level_after_all }
group.example("top level example") { order << :top_level_example }
context1 = group.describe("context 1")
context1.before(:all) { order << :nested_before_all }
context1.example("nested example 1") { order << :nested_example_1 }
context2 = group.describe("context 2")
context2.after(:all) { order << :nested_after_all }
context2.example("nested example 2") { order << :nested_example_2 }
group.run
order.should eq([
:before_all_defined_in_config,
:top_level_before_all,
:before_each,
:top_level_example,
:after_each,
:nested_before_all,
:before_each,
:nested_example_1,
:after_each,
:before_each,
:nested_example_2,
:after_each,
:nested_after_all,
:top_level_after_all,
:after_all_defined_in_config
])
end
context "after(:all)" do
let(:outer) { ExampleGroup.describe }
let(:inner) { outer.describe }
it "has access to state defined before(:all)" do
outer.before(:all) { @outer = "outer" }
inner.before(:all) { @inner = "inner" }
outer.after(:all) do
@outer.should eq("outer")
@inner.should eq("inner")
end
inner.after(:all) do
@inner.should eq("inner")
@outer.should eq("outer")
end
outer.run
end
it "cleans up ivars in after(:all)" do
outer.before(:all) { @outer = "outer" }
inner.before(:all) { @inner = "inner" }
outer.run
inner.before_all_ivars[:@inner].should be_nil
inner.before_all_ivars[:@outer].should be_nil
outer.before_all_ivars[:@inner].should be_nil
outer.before_all_ivars[:@outer].should be_nil
end
end
it "treats an error in before(:each) as a failure" do
group = ExampleGroup.describe
group.before(:each) { raise "error in before each" }
example = group.example("equality") { 1.should eq(2) }
group.run.should be(false)
example.metadata[:execution_result][:exception].message.should eq("error in before each")
end
it "treats an error in before(:all) as a failure" do
group = ExampleGroup.describe
group.before(:all) { raise "error in before all" }
example = group.example("equality") { 1.should eq(2) }
group.run.should be_false
example.metadata.should_not be_nil
example.metadata[:execution_result].should_not be_nil
example.metadata[:execution_result][:exception].should_not be_nil
example.metadata[:execution_result][:exception].message.should eq("error in before all")
end
it "treats an error in before(:all) as a failure for a spec in a nested group" do
example = nil
group = ExampleGroup.describe do
before(:all) { raise "error in before all" }
describe "nested" do
example = it("equality") { 1.should eq(2) }
end
end
group.run
example.metadata.should_not be_nil
example.metadata[:execution_result].should_not be_nil
example.metadata[:execution_result][:exception].should_not be_nil
example.metadata[:execution_result][:exception].message.should eq("error in before all")
end
context "when an error occurs in an after(:all) hook" do
before(:each) do
RSpec.configuration.reporter.stub(:message)
end
let(:group) do
ExampleGroup.describe do
after(:all) { raise "error in after all" }
it("equality") { 1.should eq(1) }
end
end
it "allows the example to pass" do
group.run
example = group.examples.first
example.metadata.should_not be_nil
example.metadata[:execution_result].should_not be_nil
example.metadata[:execution_result][:status].should eq("passed")
end
it "rescues the error and prints it out" do
RSpec.configuration.reporter.should_receive(:message).with(/error in after all/)
group.run
end
end
it "has no 'running example' within before(:all)" do
group = ExampleGroup.describe
running_example = :none
group.before(:all) { running_example = example }
group.example("no-op") { }
group.run
running_example.should be(nil)
end
it "has access to example options within before(:each)" do
group = ExampleGroup.describe
option = nil
group.before(:each) { option = example.options[:data] }
group.example("no-op", :data => :sample) { }
group.run
option.should eq(:sample)
end
it "has access to example options within after(:each)" do
group = ExampleGroup.describe
option = nil
group.after(:each) { option = example.options[:data] }
group.example("no-op", :data => :sample) { }
group.run
option.should eq(:sample)
end
it "has no 'running example' within after(:all)" do
group = ExampleGroup.describe
running_example = :none
group.after(:all) { running_example = example }
group.example("no-op") { }
group.run
running_example.should be(nil)
end
end
%w[pending xit xspecify xexample].each do |method_name|
describe "::#{method_name}" do
before do
@group = ExampleGroup.describe
@group.send(method_name, "is pending") { }
end
it "generates a pending example" do
@group.run
@group.examples.first.should be_pending
end
it "sets the pending message", :if => method_name == 'pending' do
@group.run
@group.examples.first.metadata[:execution_result][:pending_message].should eq(RSpec::Core::Pending::NO_REASON_GIVEN)
end
it "sets the pending message", :unless => method_name == 'pending' do
@group.run
@group.examples.first.metadata[:execution_result][:pending_message].should eq("Temporarily disabled with #{method_name}")
end
end
end
describe "adding examples" do
it "allows adding an example using 'it'" do
group = ExampleGroup.describe
group.it("should do something") { }
group.examples.size.should eq(1)
end
it "exposes all examples at examples" do
group = ExampleGroup.describe
group.it("should do something 1") { }
group.it("should do something 2") { }
group.it("should do something 3") { }
group.should have(3).examples
end
it "maintains the example order" do
group = ExampleGroup.describe
group.it("should 1") { }
group.it("should 2") { }
group.it("should 3") { }
group.examples[0].description.should eq('should 1')
group.examples[1].description.should eq('should 2')
group.examples[2].description.should eq('should 3')
end
end
describe Object, "describing nested example_groups", :little_less_nested => 'yep' do
describe "A sample nested group", :nested_describe => "yep" do
it "sets the described class to the described class of the outer most group" do
example.example_group.described_class.should eq(ExampleGroup)
end
it "sets the description to 'A sample nested describe'" do
example.example_group.description.should eq('A sample nested group')
end
it "has top level metadata from the example_group and its ancestors" do
example.example_group.metadata.should include(:little_less_nested => 'yep', :nested_describe => 'yep')
end
it "exposes the parent metadata to the contained examples" do
example.metadata.should include(:little_less_nested => 'yep', :nested_describe => 'yep')
end
end
end
describe "#run_examples" do
let(:reporter) { double("reporter").as_null_object }
it "returns true if all examples pass" do
group = ExampleGroup.describe('group') do
example('ex 1') { 1.should eq(1) }
example('ex 2') { 1.should eq(1) }
end
group.stub(:filtered_examples) { group.examples.extend(Extensions::Ordered) }
group.run(reporter).should be_true
end
it "returns false if any of the examples fail" do
group = ExampleGroup.describe('group') do
example('ex 1') { 1.should eq(1) }
example('ex 2') { 1.should eq(2) }
end
group.stub(:filtered_examples) { group.examples.extend(Extensions::Ordered) }
group.run(reporter).should be_false
end
it "runs all examples, regardless of any of them failing" do
group = ExampleGroup.describe('group') do
example('ex 1') { 1.should eq(2) }
example('ex 2') { 1.should eq(1) }
end
group.stub(:filtered_examples) { group.examples.extend(Extensions::Ordered) }
group.filtered_examples.each do |example|
example.should_receive(:run)
end
group.run(reporter).should be_false
end
end
describe "how instance variables are inherited" do
before(:all) do
@before_all_top_level = 'before_all_top_level'
end
before(:each) do
@before_each_top_level = 'before_each_top_level'
end
it "can access a before each ivar at the same level" do
@before_each_top_level.should eq('before_each_top_level')
end
it "can access a before all ivar at the same level" do
@before_all_top_level.should eq('before_all_top_level')
end
it "can access the before all ivars in the before_all_ivars hash", :ruby => 1.8 do
example.example_group.before_all_ivars.should include('@before_all_top_level' => 'before_all_top_level')
end
it "can access the before all ivars in the before_all_ivars hash", :ruby => 1.9 do
example.example_group.before_all_ivars.should include(:@before_all_top_level => 'before_all_top_level')
end
describe "but now I am nested" do
it "can access a parent example groups before each ivar at a nested level" do
@before_each_top_level.should eq('before_each_top_level')
end
it "can access a parent example groups before all ivar at a nested level" do
@before_all_top_level.should eq("before_all_top_level")
end
it "changes to before all ivars from within an example do not persist outside the current describe" do
@before_all_top_level = "ive been changed"
end
describe "accessing a before_all ivar that was changed in a parent example_group" do
it "does not have access to the modified version" do
@before_all_top_level.should eq('before_all_top_level')
end
end
end
end
describe "ivars are not shared across examples" do
it "(first example)" do
@a = 1
defined?(@b).should be_false
end
it "(second example)" do
@b = 2
defined?(@a).should be_false
end
end
describe "#top_level_description" do
it "returns the description from the outermost example group" do
group = nil
ExampleGroup.describe("top") do
context "middle" do
group = describe "bottom" do
end
end
end
group.top_level_description.should eq("top")
end
end
describe "#run" do
let(:reporter) { double("reporter").as_null_object }
context "with fail_fast? => true" do
it "does not run examples after the failed example" do
group = RSpec::Core::ExampleGroup.describe
group.stub(:fail_fast?) { true }
examples_run = []
group.example('example 1') { examples_run << self }
group.example('example 2') { examples_run << self; fail; }
group.example('example 3') { examples_run << self }
group.run
examples_run.length.should eq(2)
end
end
context "with RSpec.wants_to_quit=true" do
let(:group) { RSpec::Core::ExampleGroup.describe }
before do
RSpec.stub(:wants_to_quit) { true }
RSpec.stub(:clear_remaining_example_groups)
end
it "returns without starting the group" do
reporter.should_not_receive(:example_group_started)
group.run(reporter)
end
context "at top level" do
it "purges remaining groups" do
RSpec.should_receive(:clear_remaining_example_groups)
group.run(reporter)
end
end
context "in a nested group" do
it "does not purge remaining groups" do
nested_group = group.describe
RSpec.should_not_receive(:clear_remaining_example_groups)
nested_group.run(reporter)
end
end
end
context "with all examples passing" do
it "returns true" do
group = describe("something") do
it "does something" do
# pass
end
describe "nested" do
it "does something else" do
# pass
end
end
end
group.run(reporter).should be_true
end
end
context "with top level example failing" do
it "returns false" do
group = describe("something") do
it "does something (wrong - fail)" do
raise "fail"
end
describe "nested" do
it "does something else" do
# pass
end
end
end
group.run(reporter).should be_false
end
end
context "with nested example failing" do
it "returns true" do
group = describe("something") do
it "does something" do
# pass
end
describe "nested" do
it "does something else (wrong -fail)" do
raise "fail"
end
end
end
group.run(reporter).should be_false
end
end
end
%w[include_examples include_context].each do |name|
describe "##{name}" do
before do
shared_examples "named this" do
example("does something") {}
end
end
it "includes the named examples" do
group = ExampleGroup.describe
group.send(name, "named this")
group.examples.first.description.should eq("does something")
end
it "raises a helpful error message when shared content is not found" do
group = ExampleGroup.describe
expect do
group.send(name, "shared stuff")
end.to raise_error(ArgumentError, /Could not find .* "shared stuff"/)
end
it "passes parameters to the shared content" do
passed_params = {}
shared_examples "named this with params" do |param1, param2|
it("has access to the given parameters") do
passed_params[:param1] = param1
passed_params[:param2] = param2
end
end
group = ExampleGroup.describe
group.send(name, "named this with params", :value1, :value2)
group.run
passed_params.should eq({ :param1 => :value1, :param2 => :value2 })
end
it "adds shared instance methods to the group" do
shared_examples "named this with params" do |param1|
def foo; end
end
group = ExampleGroup.describe('fake group')
group.send(name, "named this with params", :a)
group.public_instance_methods.map{|m| m.to_s}.should include("foo")
end
it "evals the shared example group only once" do
eval_count = 0
shared_examples("named this with params") { |p| eval_count += 1 }
group = ExampleGroup.describe('fake group')
group.send(name, "named this with params", :a)
eval_count.should eq(1)
end
it "warns the user that blocks are not supported when given a block" do
group = ExampleGroup.describe
group.should_receive(:warn).with(/blocks not supported for #{name}/)
group.send(name, "named this with block") {}
end
end
end
describe "#it_should_behave_like" do
it "creates a nested group" do
shared_examples_for("thing") {}
group = ExampleGroup.describe('fake group')
group.it_should_behave_like("thing")
group.should have(1).children
end
it "creates a nested group for a class" do
klass = Class.new
shared_examples_for(klass) {}
group = ExampleGroup.describe('fake group')
group.it_should_behave_like(klass)
group.should have(1).children
end
it "adds shared examples to nested group" do
shared_examples_for("thing") do
it("does something")
end
group = ExampleGroup.describe('fake group')
shared_group = group.it_should_behave_like("thing")
shared_group.should have(1).examples
end
it "adds shared instance methods to nested group" do
shared_examples_for("thing") do
def foo; end
end
group = ExampleGroup.describe('fake group')
shared_group = group.it_should_behave_like("thing")
shared_group.public_instance_methods.map{|m| m.to_s}.should include("foo")
end
it "adds shared class methods to nested group" do
shared_examples_for("thing") do
def self.foo; end
end
group = ExampleGroup.describe('fake group')
shared_group = group.it_should_behave_like("thing")
shared_group.methods.map{|m| m.to_s}.should include("foo")
end
it "passes parameters to the shared example group" do
passed_params = {}
shared_examples_for("thing") do |param1, param2|
it("has access to the given parameters") do
passed_params[:param1] = param1
passed_params[:param2] = param2
end
end
group = ExampleGroup.describe("group") do
it_should_behave_like "thing", :value1, :value2
end
group.run
passed_params.should eq({ :param1 => :value1, :param2 => :value2 })
end
it "adds shared instance methods to nested group" do
shared_examples_for("thing") do |param1|
def foo; end
end
group = ExampleGroup.describe('fake group')
shared_group = group.it_should_behave_like("thing", :a)
shared_group.public_instance_methods.map{|m| m.to_s}.should include("foo")
end
it "evals the shared example group only once" do
eval_count = 0
shared_examples_for("thing") { |p| eval_count += 1 }
group = ExampleGroup.describe('fake group')
group.it_should_behave_like("thing", :a)
eval_count.should eq(1)
end
context "given a block" do
it "evaluates the block in nested group" do
scopes = []
shared_examples_for("thing") do
it("gets run in the nested group") do
scopes << self.class
end
end
group = ExampleGroup.describe("group") do
it_should_behave_like "thing" do
it("gets run in the same nested group") do
scopes << self.class
end
end
end
group.run
scopes[0].should be(scopes[1])
end
end
it "raises a helpful error message when shared context is not found" do
expect do
ExampleGroup.describe do
it_should_behave_like "shared stuff"
end
end.to raise_error(ArgumentError,%q|Could not find shared examples "shared stuff"|)
end
end
end
end
Feature: --example option
Use the --example (or -e) option to filter examples by name.
The argument is compiled to a Ruby Regexp, and matched against the full
description of the example, which is the concatenation of descriptions of the
group (including any nested groups) and the example.
This allows you to run a single uniquely named example, all examples with
similar names, all the examples in a uniquely named group, etc, etc.
Background:
Given a file named "first_spec.rb" with:
"""
describe "first group" do
it "first example in first group" do; end
it "second example in first group" do; end
end
"""
And a file named "second_spec.rb" with:
"""
describe "second group" do
it "first example in second group" do; end
it "second example in second group" do; end
end
"""
And a file named "third_spec.rb" with:
"""
describe "third group" do
it "first example in third group" do; end
context "nested group" do
it "first example in nested group" do; end
it "second example in nested group" do; end
end
end
"""
And a file named "fourth_spec.rb" with:
"""
describe Array do
describe "#length" do
it "is the number of items" do
Array.new([1,2,3]).length.should eq 3
end
end
end
"""
Scenario: no matches
When I run `rspec . --example nothing_like_this`
Then the examples should all pass
Scenario: match on one word
When I run `rspec . --example example`
Then the examples should all pass
Scenario: one match in each context
When I run `rspec . --example 'first example'`
Then the examples should all pass
Scenario: one match in one file using just the example name
When I run `rspec . --example 'first example in first group'`
Then the examples should all pass
Scenario: one match in one file using the example name and the group name
When I run `rspec . --example 'first group first example in first group'`
Then the examples should all pass
Scenario: one match in one file using regexp
When I run `rspec . --example 'first .* first example'`
Then the examples should all pass
Scenario: all examples in one group
When I run `rspec . --example 'first group'`
Then the examples should all pass
Scenario: one match in one file with group name
When I run `rspec . --example 'second group first example'`
Then the examples should all pass
Scenario: all examples in one group including examples in nested groups
When I run `rspec . --example 'third group'`
Then the examples should all pass
Scenario: Object#method
When I run `rspec . --example 'Array#length'`
Then the examples should all pass
require 'spec_helper'
describe RSpec::Core::Example, :parent_metadata => 'sample' do
let(:example_group) do
RSpec::Core::ExampleGroup.describe('group description')
end
let(:example_instance) do
example_group.example('example description')
end
it_behaves_like "metadata hash builder" do
def metadata_hash(*args)
example = example_group.example('example description', *args)
example.metadata
end
end
describe "#exception" do
it "supplies the first exception raised, if any" do
example = example_group.example { raise "first" }
example_group.after { raise "second" }
example_group.run
example.exception.message.should eq("first")
end
it "returns nil if there is no exception" do
example = example_group.example('example') { }
example_group.run
example.exception.should be_nil
end
it "returns false for pending_fixed? if not pending fixed" do
example = example_group.example { fail }
example_group.run
example.exception.should_not be_pending_fixed
end
it "returns true for pending_fixed? if pending fixed" do
example = example_group.example do
pending("fixed") {}
end
example_group.run
example.exception.should be_pending_fixed
end
end
describe "auto-generated example descriptions" do
let(:generated_description) { "the generated description" }
let(:rspec_example) { example_group.specify { 5.should eq(5) } }
before(:each) { RSpec::Matchers.stub(:generated_description => generated_description) }
def expect_with(*frameworks)
RSpec.configuration.stub(:expecting_with_rspec?).and_return(frameworks.include?(:rspec))
if frameworks.include?(:stdlib)
example_group.class_eval do
def assert(val)
raise "Expected #{val} to be true" unless val
end
end
end
end
context "when `expect_with :rspec` is configured" do
before(:each) { expect_with :rspec }
it "generates a description for an example with no description" do
expect {
example_group.run
}.to change { rspec_example.metadata[:description] }.from("").to(generated_description)
end
end
context "when `expect_with :rspec, :stdlib` is configured" do
before(:each) { expect_with :rspec, :stdlib }
it "generates a description for an example with no description" do
expect {
example_group.run
}.to change { rspec_example.metadata[:description] }.from("").to(generated_description)
end
end
context "when `expect_with :stdlib` is configured" do
let!(:stdlib_example) { example_group.specify { assert 5 == 5 } }
before(:each) { expect_with :stdlib }
it "does not attempt to get the generated description from RSpec::Matchers" do
RSpec::Matchers.should_not_receive(:generated_description)
example_group.run
end
it "fails an example with no description" do
example_group.run
stdlib_example.should have_failed_with(NotImplementedError)
end
end
end
describe '#described_class' do
it "returns the class (if any) of the outermost example group" do
described_class.should eq(RSpec::Core::Example)
end
end
describe "accessing metadata within a running example" do
it "has a reference to itself when running" do
example.description.should eq("has a reference to itself when running")
end
it "can access the example group's top level metadata as if it were its own" do
example.example_group.metadata.should include(:parent_metadata => 'sample')
example.metadata.should include(:parent_metadata => 'sample')
end
end
describe "accessing options within a running example" do
it "can look up option values by key", :demo => :data do
example.metadata[:demo].should eq(:data)
end
end
describe "#run" do
it "sets its reference to the example group instance to nil" do
group = RSpec::Core::ExampleGroup.describe do
example('example') { 1.should eq(1) }
end
group.run
group.examples.first.instance_variable_get("@example_group_instance").should be_nil
end
it "runs after(:each) when the example passes" do
after_run = false
group = RSpec::Core::ExampleGroup.describe do
after(:each) { after_run = true }
example('example') { 1.should eq(1) }
end
group.run
after_run.should be_true, "expected after(:each) to be run"
end
it "runs after(:each) when the example fails" do
after_run = false
group = RSpec::Core::ExampleGroup.describe do
after(:each) { after_run = true }
example('example') { 1.should eq(2) }
end
group.run
after_run.should be_true, "expected after(:each) to be run"
end
it "runs after(:each) when the example raises an Exception" do
after_run = false
group = RSpec::Core::ExampleGroup.describe do
after(:each) { after_run = true }
example('example') { raise "this error" }
end
group.run
after_run.should be_true, "expected after(:each) to be run"
end
context "with an after(:each) that raises" do
it "runs subsequent after(:each)'s" do
after_run = false
group = RSpec::Core::ExampleGroup.describe do
after(:each) { after_run = true }
after(:each) { raise "FOO" }
example('example') { 1.should eq(1) }
end
group.run
after_run.should be_true, "expected after(:each) to be run"
end
it "stores the exception" do
group = RSpec::Core::ExampleGroup.describe
group.after(:each) { raise "FOO" }
example = group.example('example') { 1.should eq(1) }
group.run
example.metadata[:execution_result][:exception].message.should eq("FOO")
end
end
it "wraps before/after(:each) inside around" do
results = []
group = RSpec::Core::ExampleGroup.describe do
around(:each) do |e|
results << "around (before)"
e.run
results << "around (after)"
end
before(:each) { results << "before" }
after(:each) { results << "after" }
example { results << "example" }
end
group.run
results.should eq([
"around (before)",
"before",
"example",
"after",
"around (after)"
])
end
context "clearing ivars" do
it "sets ivars to nil to prep them for GC" do
group = RSpec::Core::ExampleGroup.describe do
before(:all) { @before_all = :before_all }
before(:each) { @before_each = :before_each }
after(:each) { @after_each = :after_each }
after(:all) { @after_all = :after_all }
end
group.example("does something") do
@before_all.should eq(:before_all)
@before_each.should eq(:before_each)
end
group.run(double.as_null_object).should be_true
group.new do |example|
%w[@before_all @before_each @after_each @after_all].each do |ivar|
example.instance_variable_get(ivar).should be_nil
end
end
end
it "does not impact the before_all_ivars which are copied to each example" do
group = RSpec::Core::ExampleGroup.describe do
before(:all) { @before_all = "abc" }
example("first") { @before_all.should_not be_nil }
example("second") { @before_all.should_not be_nil }
end
group.run.should be_true
end
end
end
describe "#pending" do
context "in the example" do
it "sets the example to pending" do
group = RSpec::Core::ExampleGroup.describe do
example { pending }
end
group.run
group.examples.first.should be_pending
end
it "allows post-example processing in around hooks (see https://github.com/rspec/rspec-core/issues/322)" do
blah = nil
group = RSpec::Core::ExampleGroup.describe do
around do |example|
example.run
blah = :success
end
example { pending }
end
group.run
blah.should be(:success)
end
end
context "in before(:each)" do
it "sets each example to pending" do
group = RSpec::Core::ExampleGroup.describe do
before(:each) { pending }
example {}
example {}
end
group.run
group.examples.first.should be_pending
group.examples.last.should be_pending
end
end
context "in before(:all)" do
it "sets each example to pending" do
group = RSpec::Core::ExampleGroup.describe do
before(:all) { pending }
example {}
example {}
end
group.run
group.examples.first.should be_pending
group.examples.last.should be_pending
end
end
context "in around(:each)" do
it "sets the example to pending" do
group = RSpec::Core::ExampleGroup.describe do
around(:each) { pending }
example {}
end
group.run
group.examples.first.should be_pending
end
end
end
end
Feature: exclusion filters
You can exclude examples from a run by declaring an exclusion filter and
then tagging examples, or entire groups, with that filter.
If you set the `treat_symbols_as_metadata_keys_with_true_values` config option
to `true`, you can specify metadata using only symbols.
Scenario: exclude an example
Given a file named "spec/sample_spec.rb" with:
"""
RSpec.configure do |c|
# declare an exclusion filter
c.filter_run_excluding :broken => true
end
describe "something" do
it "does one thing" do
end
# tag example for exclusion by adding metadata
it "does another thing", :broken => true do
end
end
"""
When I run `rspec ./spec/sample_spec.rb --format doc`
Then the output should contain "does one thing"
And the output should not contain "does another thing"
Scenario: exclude a group
Given a file named "spec/sample_spec.rb" with:
"""
RSpec.configure do |c|
c.filter_run_excluding :broken => true
end
describe "group 1", :broken => true do
it "group 1 example 1" do
end
it "group 1 example 2" do
end
end
describe "group 2" do
it "group 2 example 1" do
end
end
"""
When I run `rspec ./spec/sample_spec.rb --format doc`
Then the output should contain "group 2 example 1"
And the output should not contain "group 1 example 1"
And the output should not contain "group 1 example 2"
Scenario: exclude multiple groups
Given a file named "spec/sample_spec.rb" with:
"""
RSpec.configure do |c|
c.filter_run_excluding :broken => true
end
describe "group 1", :broken => true do
before(:all) do
raise "you should not see me"
end
it "group 1 example 1" do
end
it "group 1 example 2" do
end
end
describe "group 2", :broken => true do
before(:each) do
raise "you should not see me"
end
it "group 2 example 1" do
end
end
"""
When I run `rspec ./spec/sample_spec.rb --format doc`
Then the output should match /All examples were filtered out/
And the examples should all pass
And the output should not contain "group 1"
And the output should not contain "group 2"
Scenario: before/after(:all) hooks in excluded example group are not run
Given a file named "spec/before_after_all_exclusion_filter_spec.rb" with:
"""
RSpec.configure do |c|
c.filter_run_excluding :broken => true
end
describe "group 1" do
before(:all) { puts "before all in included group" }
after(:all) { puts "after all in included group" }
it "group 1 example" do
end
end
describe "group 2", :broken => true do
before(:all) { puts "before all in excluded group" }
after(:all) { puts "after all in excluded group" }
context "context 1" do
it "group 2 context 1 example 1" do
end
end
end
"""
When I run `rspec ./spec/before_after_all_exclusion_filter_spec.rb`
Then the output should contain "before all in included group"
And the output should contain "after all in included group"
And the output should not contain "before all in excluded group"
And the output should not contain "after all in excluded group"
Scenario: Use symbols as metadata
Given a file named "symbols_as_metadata_spec.rb" with:
"""
RSpec.configure do |c|
c.treat_symbols_as_metadata_keys_with_true_values = true
c.filter_run_excluding :broken
end
describe "something" do
it "does one thing" do
end
# tag example for exclusion by adding metadata
it "does another thing", :broken do
end
end
"""
When I run `rspec symbols_as_metadata_spec.rb --format doc`
Then the output should contain "does one thing"
And the output should not contain "does another thing"
Feature: exit status
The rspec command exits with an exit status of 0 if all examples pass,
and 1 if any examples fail. The failure exit code can be overridden
using the --failure-exit-code option.
Scenario: exit with 0 when all examples pass
Given a file named "ok_spec.rb" with:
"""
describe "ok" do
it "passes" do
end
end
"""
When I run `rspec ok_spec.rb`
Then the exit status should be 0
And the examples should all pass
Scenario: exit with 1 when one example fails
Given a file named "ko_spec.rb" with:
"""
describe "KO" do
it "fails" do
raise "KO"
end
end
"""
When I run `rspec ko_spec.rb`
Then the exit status should be 1
And the output should contain "1 example, 1 failure"
Scenario: exit with 1 when a nested examples fails
Given a file named "nested_ko_spec.rb" with:
"""
describe "KO" do
describe "nested" do
it "fails" do
raise "KO"
end
end
end
"""
When I run `rspec nested_ko_spec.rb`
Then the exit status should be 1
And the output should contain "1 example, 1 failure"
Scenario: exit with 0 when no examples are run
Given a file named "a_no_examples_spec.rb" with:
"""
"""
When I run `rspec a_no_examples_spec.rb`
Then the exit status should be 0
And the output should contain "0 examples"
Scenario: exit with 2 when one example fails and --failure-exit-code is 2
Given a file named "ko_spec.rb" with:
"""
describe "KO" do
it "fails" do
raise "KO"
end
end
"""
When I run `rspec --failure-exit-code 2 ko_spec.rb`
Then the exit status should be 2
And the output should contain "1 example, 1 failure"
@wip
Scenario: exit with rspec's exit code when an at_exit hook is added upstream
Given a file named "exit_at_spec.rb" with:
"""
require 'rspec/autorun'
describe "exit 0 at_exit" do
it "does not interfere with rspec's exit code" do
at_exit { exit 0 }
fail
end
end
"""
When I run `rspec exit_at_spec.rb`
Then the exit status should be 1
And the output should contain "1 example, 1 failure"
Feature: explicit subject
Use subject() in the group scope to explicitly define the value that is
returned by the subject() method in the example scope.
Scenario: subject in top level group
Given a file named "top_level_subject_spec.rb" with:
"""
describe Array, "with some elements" do
subject { [1,2,3] }
it "should have the prescribed elements" do
subject.should == [1,2,3]
end
end
"""
When I run `rspec top_level_subject_spec.rb`
Then the examples should all pass
Scenario: subject in a nested group
Given a file named "nested_subject_spec.rb" with:
"""
describe Array do
subject { [1,2,3] }
describe "with some elements" do
it "should have the prescribed elements" do
subject.should == [1,2,3]
end
end
end
"""
When I run `rspec nested_subject_spec.rb`
Then the examples should all pass
Scenario: access subject from before block
Given a file named "top_level_subject_spec.rb" with:
"""
describe Array, "with some elements" do
subject { [] }
before { subject.push(1,2,3) }
it "should have the prescribed elements" do
subject.should == [1,2,3]
end
end
"""
When I run `rspec top_level_subject_spec.rb`
Then the examples should all pass
Scenario: invoke helper method from subject block
Given a file named "helper_subject_spec.rb" with:
"""
describe Array do
def prepared_array; [1,2,3] end
subject { prepared_array }
describe "with some elements" do
it "should have the prescribed elements" do
subject.should == [1,2,3]
end
end
end
"""
When I run `rspec helper_subject_spec.rb`
Then the examples should all pass
Scenario: subject block is invoked at most once per example
Given a file named "nil_subject_spec.rb" with:
"""
describe Array do
describe "#[]" do
context "with index out of bounds" do
before { Array.should_receive(:one_two_three).once.and_return([1,2,3]) }
subject { Array.one_two_three[42] }
it { should be_nil }
end
end
end
"""
When I run `rspec nil_subject_spec.rb`
Then the examples should all pass
require_rspec 'core/extensions/kernel'
require_rspec 'core/extensions/instance_eval_with_args'
require_rspec 'core/extensions/module_eval_with_args'
require_rspec 'core/extensions/ordered'
Feature: fail fast
Use the fail_fast option to tell RSpec to abort the run on first failure:
RSpec.configure {|c| c.fail_fast = true}
Background:
Given a file named "spec/spec_helper.rb" with:
"""
RSpec.configure {|c| c.fail_fast = true}
"""
Scenario: fail_fast with no failures (runs all examples)
Given a file named "spec/example_spec.rb" with:
"""
describe "something" do
it "passes" do
end
it "passes too" do
end
end
"""
When I run `rspec spec/example_spec.rb`
Then the examples should all pass
Scenario: fail_fast with first example failing (only runs the one example)
Given a file named "spec/example_spec.rb" with:
"""
require "spec_helper"
describe "something" do
it "fails" do
fail
end
it "passes" do
end
end
"""
When I run `rspec spec/example_spec.rb -fd`
Then the output should contain "1 example, 1 failure"
Scenario: fail_fast with multiple files, second example failing (only runs the first two examples)
Given a file named "spec/example_1_spec.rb" with:
"""
require "spec_helper"
describe "something" do
it "passes" do
end
it "fails" do
fail
end
end
describe "something else" do
it "fails" do
fail
end
end
"""
And a file named "spec/example_2_spec.rb" with:
"""
require "spec_helper"
describe "something" do
it "passes" do
end
end
describe "something else" do
it "fails" do
fail
end
end
"""
When I run `rspec spec`
Then the output should contain "2 examples, 1 failure"
require "spec_helper"
describe "failed_results_re for autotest" do
def run_example
group = RSpec::Core::ExampleGroup.describe("group")
group.example("example") { yield }
io = StringIO.new
formatter = RSpec::Core::Formatters::BaseTextFormatter.new(io)
reporter = RSpec::Core::Reporter.new(formatter)
group.run(reporter)
reporter.report(1, nil) {}
io.string
end
shared_examples "autotest failed_results_re" do
it "matches a failure" do
output = run_example { fail }
output.should match(Autotest::Rspec2.new.failed_results_re)
output.should include(__FILE__.sub(File.expand_path('.'),'.'))
end
it "does not match when there are no failures" do
output = run_example { } # pass
output.should_not match(Autotest::Rspec2.new.failed_results_re)
output.should_not include(__FILE__.sub(File.expand_path('.'),'.'))
end
end
context "with color enabled" do
before do
RSpec.configuration.stub(:color_enabled? => true)
end
include_examples "autotest failed_results_re"
end
context "with color disabled " do
before do
RSpec.configuration.stub(:color_enabled? => false)
end
include_examples "autotest failed_results_re"
end
end
module RSpec
module Core
# Manages the filtering of examples and groups by matching tags declared on
# the command line or options files, or filters declared via
# `RSpec.configure`, with hash key/values submitted within example group
# and/or example declarations. For example, given this declaration:
#
# describe Thing, :awesome => true do
# it "does something" do
# # ...
# end
# end
#
# That group (or any other with `:awesome => true`) would be filtered in
# with any of the following commands:
#
# rspec --tag awesome:true
# rspec --tag awesome
# rspec -t awesome:true
# rspec -t awesome
#
# Prefixing the tag names with `~` negates the tags, thus excluding this group with
# any of:
#
# rspec --tag ~awesome:true
# rspec --tag ~awesome
# rspec -t ~awesome:true
# rspec -t ~awesome
#
# ## Options files and command line overrides
#
# Tag declarations can be stored in `.rspec`, `~/.rspec`, or a custom
# options file. This is useful for storing defaults. For example, let's
# say you've got some slow specs that you want to suppress most of the
# time. You can tag them like this:
#
# describe Something, :slow => true do
#
# And then store this in `.rspec`:
#
# --tag ~slow:true
#
# Now when you run `rspec`, that group will be excluded.
#
# ## Overriding
#
# Of course, you probably want to run them sometimes, so you can override
# this tag on the command line like this:
#
# rspec --tag slow:true
#
# ## RSpec.configure
#
# You can also store default tags with `RSpec.configure`. We use `tag` on
# the command line (and in options files like `.rspec`), but for historical
# reasons we use the term `filter` in `RSpec.configure:
#
# RSpec.configure do |c|
# c.filter_run_including :foo => :bar
# c.filter_run_excluding :foo => :bar
# end
#
# These declarations can also be overridden from the command line.
#
# @see RSpec.configure
# @see Configuration#filter_run_including
# @see Configuration#filter_run_excluding
class FilterManager
DEFAULT_EXCLUSIONS = {
:if => lambda { |value, metadata| metadata.has_key?(:if) && !value },
:unless => lambda { |value| value }
}
STANDALONE_FILTERS = [:locations, :line_numbers, :full_description]
module Describable
PROC_HEX_NUMBER = /0x[0-9a-f]+@/
PROJECT_DIR = File.expand_path('.')
def description
reject { |k, v| RSpec::Core::FilterManager::DEFAULT_EXCLUSIONS[k] == v }.inspect.gsub(PROC_HEX_NUMBER, '').gsub(PROJECT_DIR, '.').gsub(' (lambda)','')
end
def empty_without_conditional_filters?
reject { |k, v| RSpec::Core::FilterManager::DEFAULT_EXCLUSIONS[k] == v }.empty?
end
end
module BackwardCompatibility
def merge(orig, opposite, *updates)
_warn_deprecated_keys(updates.last)
super
end
def reverse_merge(orig, opposite, *updates)
_warn_deprecated_keys(updates.last)
super
end
# Supports a use case that probably doesn't exist: overriding the
# if/unless procs.
def _warn_deprecated_keys(updates)
_warn_deprecated_key(:unless, updates) if updates.has_key?(:unless)
_warn_deprecated_key(:if, updates) if updates.has_key?(:if)
end
# Emits a deprecation warning for keys that will not be supported in
# the future.
def _warn_deprecated_key(key, updates)
RSpec.warn_deprecation("\nDEPRECATION NOTICE: FilterManager#exclude(#{key.inspect} => #{updates[key].inspect}) is deprecated with no replacement, and will be removed from rspec-3.0.")
@exclusions[key] = updates.delete(key)
end
end
attr_reader :exclusions, :inclusions
def initialize
@exclusions = DEFAULT_EXCLUSIONS.dup.extend(Describable)
@inclusions = {}.extend(Describable)
extend(BackwardCompatibility)
end
def add_location(file_path, line_numbers)
# locations is a hash of expanded paths to arrays of line
# numbers to match against. e.g.
# { "path/to/file.rb" => [37, 42] }
locations = @inclusions.delete(:locations) || Hash.new {|h,k| h[k] = []}
locations[File.expand_path(file_path)].push(*line_numbers)
@inclusions.replace(:locations => locations)
@exclusions.clear
end
def empty?
inclusions.empty? && exclusions.empty_without_conditional_filters?
end
def prune(examples)
examples.select {|e| !exclude?(e) && include?(e)}
end
def exclude(*args)
merge(@exclusions, @inclusions, *args)
end
def exclude!(*args)
replace(@exclusions, @inclusions, *args)
end
def exclude_with_low_priority(*args)
reverse_merge(@exclusions, @inclusions, *args)
end
def exclude?(example)
@exclusions.empty? ? false : example.any_apply?(@exclusions)
end
def include(*args)
unless_standalone(*args) { merge(@inclusions, @exclusions, *args) }
end
def include!(*args)
unless_standalone(*args) { replace(@inclusions, @exclusions, *args) }
end
def include_with_low_priority(*args)
unless_standalone(*args) { reverse_merge(@inclusions, @exclusions, *args) }
end
def include?(example)
@inclusions.empty? ? true : example.any_apply?(@inclusions)
end
private
def unless_standalone(*args)
is_standalone_filter?(args.last) ? @inclusions.replace(args.last) : yield unless already_set_standalone_filter?
end
def merge(orig, opposite, *updates)
orig.merge!(updates.last).each_key {|k| opposite.delete(k)}
end
def replace(orig, opposite, *updates)
updates.last.each_key {|k| opposite.delete(k)}
orig.replace(updates.last)
end
def reverse_merge(orig, opposite, *updates)
updated = updates.last.merge(orig)
opposite.each_pair {|k,v| updated.delete(k) if updated[k] == v}
orig.replace(updated)
end
def already_set_standalone_filter?
is_standalone_filter?(inclusions)
end
def is_standalone_filter?(filter)
STANDALONE_FILTERS.any? {|key| filter.has_key?(key)}
end
end
end
end
require 'spec_helper'
module RSpec::Core
describe FilterManager do
def opposite(name)
name =~ /^in/ ? name.sub(/^(in)/,'ex') : name.sub(/^(ex)/,'in')
end
%w[include inclusions exclude exclusions].each_slice(2) do |name, type|
describe "##{name}" do
it "merges #{type}" do
filter_manager = FilterManager.new
filter_manager.exclusions.clear # defaults
filter_manager.send name, :foo => :bar
filter_manager.send name, :baz => :bam
filter_manager.send(type).should eq(:foo => :bar, :baz => :bam)
end
it "overrides previous #{type} with (via merge)" do
filter_manager = FilterManager.new
filter_manager.exclusions.clear # defaults
filter_manager.send name, :foo => 1
filter_manager.send name, :foo => 2
filter_manager.send(type).should eq(:foo => 2)
end
it "deletes matching opposites" do
filter_manager = FilterManager.new
filter_manager.exclusions.clear # defaults
filter_manager.send opposite(name), :foo => 1
filter_manager.send name, :foo => 2
filter_manager.send(type).should eq(:foo => 2)
filter_manager.send(opposite(type)).should be_empty
end
end
describe "##{name}!" do
it "replaces existing #{type}" do
filter_manager = FilterManager.new
filter_manager.exclusions.clear # defaults
filter_manager.send name, :foo => 1, :bar => 2
filter_manager.send "#{name}!", :foo => 3
filter_manager.send(type).should eq(:foo => 3)
end
it "deletes matching opposites" do
filter_manager = FilterManager.new
filter_manager.exclusions.clear # defaults
filter_manager.send opposite(name), :foo => 1
filter_manager.send "#{name}!", :foo => 2
filter_manager.send(type).should eq(:foo => 2)
filter_manager.send(opposite(type)).should be_empty
end
end
describe "##{name}_with_low_priority" do
it "ignores new #{type} if same key exists" do
filter_manager = FilterManager.new
filter_manager.exclusions.clear # defaults
filter_manager.send name, :foo => 1
filter_manager.send "#{name}_with_low_priority", :foo => 2
filter_manager.send(type).should eq(:foo => 1)
end
it "ignores new #{type} if same key exists in opposite" do
filter_manager = FilterManager.new
filter_manager.exclusions.clear # defaults
filter_manager.send opposite(name), :foo => 1
filter_manager.send "#{name}_with_low_priority", :foo => 1
filter_manager.send(type).should be_empty
filter_manager.send(opposite(type)).should eq(:foo => 1)
end
it "keeps new #{type} if same key exists in opposite but values are different" do
filter_manager = FilterManager.new
filter_manager.exclusions.clear # defaults
filter_manager.send opposite(name), :foo => 1
filter_manager.send "#{name}_with_low_priority", :foo => 2
filter_manager.send(type).should eq(:foo => 2)
filter_manager.send(opposite(type)).should eq(:foo => 1)
end
end
end
describe "#prune" do
def filterable_object_with(args = {})
object = double('a filterable object')
object.stub(:any_apply?) { |f| Metadata.new(args).any_apply?(f) }
object
end
it "includes objects with tags matching inclusions" do
included = filterable_object_with({:foo => :bar})
excluded = filterable_object_with
filter_manager = FilterManager.new
filter_manager.include :foo => :bar
filter_manager.prune([included, excluded]).should eq([included])
end
it "excludes objects with tags matching exclusions" do
included = filterable_object_with
excluded = filterable_object_with({:foo => :bar})
filter_manager = FilterManager.new
filter_manager.exclude :foo => :bar
filter_manager.prune([included, excluded]).should eq([included])
end
it "prefers exclusion when matches previously set inclusion" do
included = filterable_object_with
excluded = filterable_object_with({:foo => :bar})
filter_manager = FilterManager.new
filter_manager.include :foo => :bar
filter_manager.exclude :foo => :bar
filter_manager.prune([included, excluded]).should eq([included])
end
it "prefers inclusion when matches previously set exclusion" do
included = filterable_object_with({:foo => :bar})
excluded = filterable_object_with
filter_manager = FilterManager.new
filter_manager.exclude :foo => :bar
filter_manager.include :foo => :bar
filter_manager.prune([included, excluded]).should eq([included])
end
it "prefers previously set inclusion when exclusion matches but has lower priority" do
included = filterable_object_with({:foo => :bar})
excluded = filterable_object_with
filter_manager = FilterManager.new
filter_manager.include :foo => :bar
filter_manager.exclude_with_low_priority :foo => :bar
filter_manager.prune([included, excluded]).should eq([included])
end
it "prefers previously set exclusion when inclusion matches but has lower priority" do
included = filterable_object_with
excluded = filterable_object_with({:foo => :bar})
filter_manager = FilterManager.new
filter_manager.exclude :foo => :bar
filter_manager.include_with_low_priority :foo => :bar
filter_manager.prune([included, excluded]).should eq([included])
end
end
describe "#inclusions#description" do
it 'cleans up the description' do
project_dir = File.expand_path('.')
lambda { }.inspect.should include(project_dir)
lambda { }.inspect.should include(' (lambda)') if RUBY_VERSION > '1.9'
lambda { }.inspect.should include('0x')
filter_manager = FilterManager.new
filter_manager.include :foo => lambda { }
filter_manager.inclusions.description.should_not include(project_dir)
filter_manager.inclusions.description.should_not include(' (lambda)')
filter_manager.inclusions.description.should_not include('0x')
end
end
describe "#exclusions#description" do
it 'cleans up the description' do
project_dir = File.expand_path('.')
lambda { }.inspect.should include(project_dir)
lambda { }.inspect.should include(' (lambda)') if RUBY_VERSION > '1.9'
lambda { }.inspect.should include('0x')
filter_manager = FilterManager.new
filter_manager.exclude :foo => lambda { }
filter_manager.exclusions.description.should_not include(project_dir)
filter_manager.exclusions.description.should_not include(' (lambda)')
filter_manager.exclusions.description.should_not include('0x')
end
it 'returns `{}` when it only contains the default filters' do
filter_manager = FilterManager.new
filter_manager.exclusions.description.should eq({}.inspect)
end
it 'includes other filters' do
filter_manager = FilterManager.new
filter_manager.exclude :foo => :bar
filter_manager.exclusions.description.should eq({ :foo => :bar }.inspect)
end
it 'deprecates an overridden :if filter' do
RSpec.should_receive(:warn_deprecation).with(/exclude\(:if.*is deprecated/)
filter_manager = FilterManager.new
filter_manager.exclude :if => :custom_filter
end
it 'deprecates an :if filter overridden with low priority' do
RSpec.should_receive(:warn_deprecation).with(/exclude\(:if.*is deprecated/)
filter_manager = FilterManager.new
filter_manager.exclude_with_low_priority :if => :custom_filter
end
it 'deprecates an overridden :unless filter' do
RSpec.should_receive(:warn_deprecation).with(/exclude\(:unless.*is deprecated/)
filter_manager = FilterManager.new
filter_manager.exclude :unless => :custom_filter
end
it 'deprecates an :unless filter overridden with low priority' do
RSpec.should_receive(:warn_deprecation).with(/exclude\(:unless.*is deprecated/)
filter_manager = FilterManager.new
filter_manager.exclude_with_low_priority :unless => :custom_filter
end
it 'includes an overriden :if filter' do
RSpec.stub(:warn_deprecation)
filter_manager = FilterManager.new
filter_manager.exclude :if => :custom_filter
filter_manager.exclusions.description.should eq({ :if => :custom_filter }.inspect)
end
it 'includes an overriden :unless filter' do
RSpec.stub(:warn_deprecation)
filter_manager = FilterManager.new
filter_manager.exclude :unless => :custom_filter
filter_manager.exclusions.description.should eq({ :unless => :custom_filter }.inspect)
end
end
it "clears the inclusion filter on include :line_numbers" do
filter_manager = FilterManager.new
filter_manager.include :foo => :bar
filter_manager.include :line_numbers => [100]
filter_manager.inclusions.should eq(:line_numbers => [100])
end
it "clears the inclusion filter on include :locations" do
filter_manager = FilterManager.new
filter_manager.include :foo => :bar
filter_manager.include :locations => { "path/to/file.rb" => [37] }
filter_manager.inclusions.should eq(:locations => { "path/to/file.rb" => [37] })
end
it "clears the inclusion filter on include :full_description" do
filter_manager = FilterManager.new
filter_manager.include :foo => :bar
filter_manager.include :full_description => "this and that"
filter_manager.inclusions.should eq(:full_description => "this and that")
end
[:locations, :line_numbers, :full_description].each do |filter|
it "does nothing on include if already set standalone filter #{filter}" do
filter_manager = FilterManager.new
filter_manager.include filter => "a_value"
filter_manager.include :foo => :bar
filter_manager.inclusions.should eq(filter => "a_value")
end
end
end
end
Feature: filters
`before`, `after`, and `around` hooks defined in the block passed to
`RSpec.configure` can be constrained to specific examples and/or groups using
metadata as a filter.
RSpec.configure do |c|
c.before(:each, :type => :model) do
end
end
describe "something", :type => :model do
end
You can specify metadata using only symbols if you set the
`treat_symbols_as_metadata_keys_with_true_values` config option to `true`.
Scenario: filter `before(:each)` hooks using arbitrary metadata
Given a file named "filter_before_each_hooks_spec.rb" with:
"""
RSpec.configure do |config|
config.before(:each, :foo => :bar) do
invoked_hooks << :before_each_foo_bar
end
end
describe "a filtered before :each hook" do
let(:invoked_hooks) { [] }
describe "group without matching metadata" do
it "does not run the hook" do
invoked_hooks.should be_empty
end
it "runs the hook for an example with matching metadata", :foo => :bar do
invoked_hooks.should == [:before_each_foo_bar]
end
end
describe "group with matching metadata", :foo => :bar do
it "runs the hook" do
invoked_hooks.should == [:before_each_foo_bar]
end
end
end
"""
When I run `rspec filter_before_each_hooks_spec.rb`
Then the examples should all pass
Scenario: filter `after(:each)` hooks using arbitrary metadata
Given a file named "filter_after_each_hooks_spec.rb" with:
"""
RSpec.configure do |config|
config.after(:each, :foo => :bar) do
raise "boom!"
end
end
describe "a filtered after :each hook" do
describe "group without matching metadata" do
it "does not run the hook" do
# should pass
end
it "runs the hook for an example with matching metadata", :foo => :bar do
# should fail
end
end
describe "group with matching metadata", :foo => :bar do
it "runs the hook" do
# should fail
end
end
end
"""
When I run `rspec filter_after_each_hooks_spec.rb`
Then the output should contain "3 examples, 2 failures"
Scenario: filter around(:each) hooks using arbitrary metadata
Given a file named "filter_around_each_hooks_spec.rb" with:
"""
RSpec.configure do |config|
config.around(:each, :foo => :bar) do |example|
order << :before_around_each_foo_bar
example.run
order.should == [:before_around_each_foo_bar, :example]
end
end
describe "a filtered around(:each) hook" do
let(:order) { [] }
describe "a group without matching metadata" do
it "does not run the hook" do
order.should be_empty
end
it "runs the hook for an example with matching metadata", :foo => :bar do
order.should == [:before_around_each_foo_bar]
order << :example
end
end
describe "a group with matching metadata", :foo => :bar do
it "runs the hook for an example with matching metadata", :foo => :bar do
order.should == [:before_around_each_foo_bar]
order << :example
end
end
end
"""
When I run `rspec filter_around_each_hooks_spec.rb`
Then the examples should all pass
Scenario: filter before(:all) hooks using arbitrary metadata
Given a file named "filter_before_all_hooks_spec.rb" with:
"""
RSpec.configure do |config|
config.before(:all, :foo => :bar) { @hook = :before_all_foo_bar }
end
describe "a filtered before(:all) hook" do
describe "a group without matching metadata" do
it "does not run the hook" do
@hook.should be_nil
end
describe "a nested subgroup with matching metadata", :foo => :bar do
it "runs the hook" do
@hook.should == :before_all_foo_bar
end
end
end
describe "a group with matching metadata", :foo => :bar do
it "runs the hook" do
@hook.should == :before_all_foo_bar
end
describe "a nested subgroup" do
it "runs the hook" do
@hook.should == :before_all_foo_bar
end
end
end
end
"""
When I run `rspec filter_before_all_hooks_spec.rb`
Then the examples should all pass
Scenario: filter after(:all) hooks using arbitrary metadata
Given a file named "filter_after_all_hooks_spec.rb" with:
"""
example_msgs = []
RSpec.configure do |config|
config.after(:all, :foo => :bar) do
puts "after :all"
end
end
describe "a filtered after(:all) hook" do
describe "a group without matching metadata" do
it "does not run the hook" do
puts "unfiltered"
end
end
describe "a group with matching metadata", :foo => :bar do
it "runs the hook" do
puts "filtered 1"
end
end
describe "another group without matching metadata" do
describe "a nested subgroup with matching metadata", :foo => :bar do
it "runs the hook" do
puts "filtered 2"
end
end
end
end
"""
When I run `rspec filter_after_all_hooks_spec.rb`
Then the examples should all pass
And the output should contain:
"""
unfiltered
.filtered 1
.after :all
filtered 2
.after :all
"""
Scenario: Use symbols as metadata
Given a file named "less_verbose_metadata_filter.rb" with:
"""
RSpec.configure do |c|
c.treat_symbols_as_metadata_keys_with_true_values = true
c.before(:each, :before_each) { puts "before each" }
c.after(:each, :after_each) { puts "after each" }
c.around(:each, :around_each) do |example|
puts "around each (before)"
example.run
puts "around each (after)"
end
c.before(:all, :before_all) { puts "before all" }
c.after(:all, :after_all) { puts "after all" }
end
describe "group 1", :before_all, :after_all do
it("") { puts "example 1" }
it("", :before_each) { puts "example 2" }
it("", :after_each) { puts "example 3" }
it("", :around_each) { puts "example 4" }
end
"""
When I run `rspec less_verbose_metadata_filter.rb`
Then the examples should all pass
And the output should contain:
"""
before all
example 1
.before each
example 2
.example 3
after each
.around each (before)
example 4
around each (after)
.after all
"""
Feature: --format option
Use the --format option to tell RSpec how to format the output.
RSpec ships with a few formatters built in. By default, it uses the progress
formatter, which generates output like this:
....F.....*.....
A '.' represents a passing example, 'F' is failing, and '*' is pending.
To see the documentation strings passed to each describe(), context(), and it()
method, use the documentation formatter:
$ rspec spec --format documentation
You can also specify an output target (STDOUT by default) by appending a
filename to the argument:
$ rspec spec --format documentation:rspec.output.txt
`rspec --help` lists available formatters:
[p]rogress (default - dots)
[d]ocumentation (group and example names)
[h]tml
[t]extmate
custom formatter class name
Background:
Given a file named "example_spec.rb" with:
"""
describe "something" do
it "does something that passes" do
5.should eq(5)
end
it "does something that fails" do
5.should eq(4)
end
it "does something that is pending", :pending => true do
5.should be > 3
end
end
"""
Scenario: progress bar format (default)
When I run `rspec example_spec.rb`
Then the output should contain ".F*"
Scenario: documentation format
When I run `rspec example_spec.rb --format documentation`
Then the output should contain:
"""
something
does something that passes
does something that fails (FAILED - 1)
does something that is pending (PENDING: No reason given)
"""
Scenario: documentation format saved to a file
When I run `rspec example_spec.rb --format documentation --out rspec.txt`
Then the file "rspec.txt" should contain:
"""
something
does something that passes
does something that fails (FAILED - 1)
does something that is pending (PENDING: No reason given)
"""
Scenario: multiple formats
When I run `rspec example_spec.rb --format progress --format documentation --out rspec.txt`
Then the output should contain ".F*"
And the file "rspec.txt" should contain:
"""
something
does something that passes
does something that fails (FAILED - 1)
does something that is pending (PENDING: No reason given)
"""
# A sample Gemfile
source "http://rubygems.org"
gem 'rspec'
GEM
remote: http://rubygems.org/
specs:
diff-lcs (1.1.3)
rspec (2.8.0)
rspec-core (~> 2.8.0)
rspec-expectations (~> 2.8.0)
rspec-mocks (~> 2.8.0)
rspec-core (2.8.0)
rspec-expectations (2.8.0)
diff-lcs (~> 1.1.2)
rspec-mocks (2.8.0)
PLATFORMS
ruby
DEPENDENCIES
rspec
module RSpec
module Core
module Formatters
module Helpers
SUB_SECOND_PRECISION = 5
DEFAULT_PRECISION = 2
def format_seconds(float)
precision ||= (float < 1) ? SUB_SECOND_PRECISION : DEFAULT_PRECISION
formatted = sprintf("%.#{precision}f", float)
strip_trailing_zeroes(formatted)
end
def strip_trailing_zeroes(string)
stripped = string.sub(/[^1-9]+$/, '')
stripped.empty? ? "0" : stripped
end
end
end
end
end

1.1.3 / 2011-08-27

  • Converted to ‘hoe’ for release.

  • Converted tests to RSpec 2.

  • Extracted the body of htmldiff into a class available from diff/lcs/htmldiff.

  • Migrated development and issue tracking to GitHub.

  • Bugs fixed:

1.1.2 / 2004-10-20

  • Fixed a problem reported by Mauricio Fernandez in htmldiff.

1.1.1 / 2004-09-25

  • Fixed bug #891: rubyforge.org/tracker/?func=detail&atid=407&aid=891&group_id=84

  • Fixed a problem with callback initialisation code (it assumed that all callbacks passed as classes can be initialised; now, it rescues NoMethodError in the event of private :new being called).

  • Modified the non-initialisable callbacks to have a private #new method.

  • Moved ldiff core code to Diff::LCS::Ldiff (diff/lcs/ldiff.rb).

1.1.0 / -

  • Eliminated the need for Diff::LCS::Event and removed it.

  • Added a contextual diff callback, Diff::LCS::ContextDiffCallback.

  • Implemented patching/unpatching for standard Diff callback output formats with both #diff and #sdiff.

  • Extensive documentation changes.

1.0.4 / -

  • Fixed a problem with bin/ldiff output, especially for unified format. Newlines that should have been present weren’t.

  • Changed the .tar.gz installer to generate Windows batch files if ones do not exist already. Removed the existing batch files as they didn’t work.

1.0.3 / -

  • Fixed a problem with #traverse_sequences where the first difference from the left sequence might not be appropriately captured.

1.0.2 / -

  • Fixed an issue with ldiff not working because actions were changed from symbols to strings.

1.0.1 / -

  • Minor modifications to the gemspec, the README.

  • Renamed the diff program to ldiff (as well as the companion batch file) so as to not collide with the standard diff program.

  • Fixed issues with RubyGems. Requires RubyGems > 0.6.1 or >= 0.6.1 with the latest CVS version.

1.0 / -

  • Initial release based mostly on Perl’s Algorithm::Diff.

module RSpec
module Core
module Hooks
include MetadataHashBuilder::WithConfigWarning
class Hook
attr_reader :options
def initialize(options, &block)
@options = options
raise "no block given for #{display_name}" unless block
@block = block
end
def options_apply?(example_or_group)
example_or_group.all_apply?(options)
end
def to_proc
@block
end
def call
@block.call
end
def display_name
self.class.name.split('::').last.gsub('Hook','').downcase << " hook"
end
end
class BeforeHook < Hook
def run_in(example_group_instance)
if example_group_instance
example_group_instance.instance_eval(&self)
else
call
end
end
end
class AfterHook < Hook
def run_in(example_group_instance)
if example_group_instance
example_group_instance.instance_eval_with_rescue(&self)
else
call
end
end
end
class AroundHook < Hook
def call(wrapped_example)
@block.call(wrapped_example)
end
end
class HookCollection < Array
def find_hooks_for(example_or_group)
self.class.new(select {|hook| hook.options_apply?(example_or_group)})
end
def without_hooks_for(example_or_group)
self.class.new(reject {|hook| hook.options_apply?(example_or_group)})
end
end
class BeforeHooks < HookCollection
def run_all(example_group_instance)
each {|h| h.run_in(example_group_instance) } unless empty?
end
def run_all!(example_group_instance)
shift.run_in(example_group_instance) until empty?
end
end
class AfterHooks < HookCollection
def run_all(example_group_instance)
reverse.each {|h| h.run_in(example_group_instance) }
end
def run_all!(example_group_instance)
pop.run_in(example_group_instance) until empty?
end
end
class AroundHooks < HookCollection; end
# @private
def hooks
@hooks ||= {
:around => { :each => AroundHooks.new },
:before => { :each => BeforeHooks.new, :all => BeforeHooks.new, :suite => BeforeHooks.new },
:after => { :each => AfterHooks.new, :all => AfterHooks.new, :suite => AfterHooks.new }
}
end
# @api public
# @overload before(&block)
# @overload before(scope, &block)
# @overload before(scope, conditions, &block)
# @overload before(conditions, &block)
#
# @param [Symbol] scope `:each`, `:all`, or `:suite` (defaults to `:each`)
# @param [Hash] conditions
# constrains this hook to examples matching these conditions e.g.
# `before(:each, :ui => true) { ... }` will only run with examples or
# groups declared with `:ui => true`.
#
# @see #after
# @see #around
# @see ExampleGroup
# @see SharedContext
# @see SharedExampleGroup
# @see Configuration
#
# Declare a block of code to be run before each example (using `:each`)
# or once before any example (using `:all`). These are usually declared
# directly in the [ExampleGroup](ExampleGroup) to which they apply, but
# they can also be shared across multiple groups.
#
# You can also use `before(:suite)` to run a block of code before any
# example groups are run. This should be declared in
# [RSpec.configure](../../RSpec#configure-class_method)
#
# Instance variables declared in `before(:each)` or `before(:all)` are
# accessible within each example.
#
# ### Order
#
# `before` hooks are stored in three scopes, which are run in order:
# `:suite`, `:all`, and `:each`. They can also be declared in several
# different places: `RSpec.configure`, a parent group, the current group.
# They are run in the following order:
#
# before(:suite) # declared in RSpec.configure
# before(:all) # declared in RSpec.configure
# before(:all) # declared in a parent group
# before(:all) # declared in the current group
# before(:each) # declared in RSpec.configure
# before(:each) # declared in a parent group
# before(:each) # declared in the current group
#
# If more than one `before` is declared within any one scope, they are run
# in the order in which they are declared.
#
# ### Conditions
#
# When you add a conditions hash to `before(:each)` or `before(:all)`,
# RSpec will only apply that hook to groups or examples that match the
# conditions. e.g.
#
# RSpec.configure do |config|
# config.before(:each, :authorized => true) do
# log_in_as :authorized_user
# end
# end
#
# describe Something, :authorized => true do
# # the before hook will run in before each example in this group
# end
#
# describe SomethingElse do
# it "does something", :authorized => true do
# # the before hook will run before this example
# end
#
# it "does something else" do
# # the hook will not run before this example
# end
# end
#
# ### Warning: `before(:suite, :with => :conditions)`
#
# The conditions hash is used to match against specific examples. Since
# `before(:suite)` is not run in relation to any specific example or
# group, conditions passed along with `:suite` are effectively ignored.
#
# ### Exceptions
#
# When an exception is raised in a `before` block, RSpec skips any
# subsequent `before` blocks and the example, but runs all of the
# `after(:each)` and `after(:all)` hooks.
#
# ### Warning: implicit before blocks
#
# `before` hooks can also be declared in shared contexts which get
# included implicitly either by you or by extension libraries. Since
# RSpec runs these in the order in which they are declared within each
# scope, load order matters, and can lead to confusing results when one
# before block depends on state that is prepared in another before block
# that gets run later.
#
# ### Warning: `before(:all)`
#
# It is very tempting to use `before(:all)` to speed things up, but we
# recommend that you avoid this as there are a number of gotchas, as well
# as things that simply don't work.
#
# #### context
#
# `before(:all)` is run in an example that is generated to provide group
# context for the block.
#
# #### instance variables
#
# Instance variables declared in `before(:all)` are shared across all the
# examples in the group. This means that each example can change the
# state of a shared object, resulting in an ordering dependency that can
# make it difficult to reason about failures.
#
# ### other frameworks
#
# Mock object frameworks and database transaction managers (like
# ActiveRecord) are typically designed around the idea of setting up
# before an example, running that one example, and then tearing down.
# This means that mocks and stubs can (sometimes) be declared in
# `before(:all)`, but get torn down before the first real example is ever
# run.
#
# You _can_ create database-backed model objects in a `before(:all)` in
# rspec-rails, but it will not be wrapped in a transaction for you, so
# you are on your own to clean up in an `after(:all)` block.
#
# @example before(:each) declared in an [ExampleGroup](ExampleGroup)
#
# describe Thing do
# before(:each) do
# @thing = Thing.new
# end
#
# it "does something" do
# # here you can access @thing
# end
# end
#
# @example before(:all) declared in an [ExampleGroup](ExampleGroup)
#
# describe Parser do
# before(:all) do
# File.open(file_to_parse, 'w') do |f|
# f.write <<-CONTENT
# stuff in the file
# CONTENT
# end
# end
#
# it "parses the file" do
# Parser.parse(file_to_parse)
# end
#
# after(:all) do
# File.delete(file_to_parse)
# end
# end
def before(*args, &block)
scope, options = scope_and_options_from(*args)
hooks[:before][scope] << BeforeHook.new(options, &block)
end
# @api public
# @overload after(&block)
# @overload after(scope, &block)
# @overload after(scope, conditions, &block)
# @overload after(conditions, &block)
#
# @param [Symbol] scope `:each`, `:all`, or `:suite` (defaults to `:each`)
# @param [Hash] conditions
# constrains this hook to examples matching these conditions e.g.
# `after(:each, :ui => true) { ... }` will only run with examples or
# groups declared with `:ui => true`.
#
# @see #before
# @see #around
# @see ExampleGroup
# @see SharedContext
# @see SharedExampleGroup
# @see Configuration
#
# Declare a block of code to be run after each example (using `:each`) or
# once after all examples (using `:all`). See
# [#before](Hooks#before-instance_method) for more information about
# ordering.
#
# ### Exceptions
#
# `after` hooks are guaranteed to run even when there are exceptions in
# `before` hooks or examples. When an exception is raised in an after
# block, the exception is captured for later reporting, and subsequent
# `after` blocks are run.
#
# ### Order
#
# `after` hooks are stored in three scopes, which are run in order:
# `:each`, `:all`, and `:suite`. They can also be declared in several
# different places: `RSpec.configure`, a parent group, the current group.
# They are run in the following order:
#
# after(:each) # declared in the current group
# after(:each) # declared in a parent group
# after(:each) # declared in RSpec.configure
# after(:all) # declared in the current group
# after(:all) # declared in a parent group
# after(:all) # declared in RSpec.configure
#
# This is the reverse of the order in which `before` hooks are run.
# Similarly, if more than one `after` is declared within any one scope,
# they are run in reverse order of that in which they are declared.
def after(*args, &block)
scope, options = scope_and_options_from(*args)
hooks[:after][scope] << AfterHook.new(options, &block)
end
# @api public
# @overload around(&block)
# @overload around(scope, &block)
# @overload around(scope, conditions, &block)
# @overload around(conditions, &block)
#
# @param [Symbol] scope `:each` (defaults to `:each`)
# present for syntax parity with `before` and `after`, but `:each` is
# the only supported value.
#
# @param [Hash] conditions
# constrains this hook to examples matching these conditions e.g.
# `around(:each, :ui => true) { ... }` will only run with examples or
# groups declared with `:ui => true`.
#
# @yield [Example] the example to run
#
# @note the syntax of `around` is similar to that of `before` and `after`
# but the semantics are quite different. `before` and `after` hooks are
# run in the context of of the examples with which they are associated,
# whereas `around` hooks are actually responsible for running the
# examples. Consequently, `around` hooks do not have direct access to
# resources that are made available within the examples and their
# associated `before` and `after` hooks.
#
# @note `:each` is the only supported scope.
#
# Declare a block of code, parts of which will be run before and parts
# after the example. It is your responsibility to run the example:
#
# around(:each) do |ex|
# # do some stuff before
# ex.run
# # do some stuff after
# end
#
# The yielded example aliases `run` with `call`, which lets you treat it
# like a `Proc`. This is especially handy when working with libaries
# that manage their own setup and teardown using a block or proc syntax,
# e.g.
#
# around(:each) {|ex| Database.transaction(&ex)}
# around(:each) {|ex| FakeFS(&ex)}
#
def around(*args, &block)
scope, options = scope_and_options_from(*args)
hooks[:around][scope] << AroundHook.new(options, &block)
end
# @private
# Runs all of the blocks stored with the hook in the context of the
# example. If no example is provided, just calls the hook directly.
def run_hook(hook, scope, example_group_instance=nil)
hooks[hook][scope].run_all(example_group_instance)
end
# @private
# Just like run_hook, except it removes the blocks as it evalutes them,
# ensuring that they will only be run once.
def run_hook!(hook, scope, example_group_instance)
hooks[hook][scope].run_all!(example_group_instance)
end
# @private
def run_hook_filtered(hook, scope, group, example_group_instance, example = nil)
find_hook(hook, scope, group, example).run_all(example_group_instance)
end
# @private
def find_hook(hook, scope, example_group_class, example = nil)
found_hooks = hooks[hook][scope].find_hooks_for(example || example_group_class)
# ensure we don't re-run :all hooks that were applied to any of the parent groups
if scope == :all
super_klass = example_group_class.superclass
while super_klass != RSpec::Core::ExampleGroup
found_hooks = found_hooks.without_hooks_for(super_klass)
super_klass = super_klass.superclass
end
end
found_hooks
end
private
def scope_and_options_from(*args)
scope = if [:each, :all, :suite].include?(args.first)
args.shift
elsif args.any? { |a| a.is_a?(Symbol) }
raise ArgumentError.new("You must explicitly give a scope (:each, :all, or :suite) when using symbols as metadata for a hook.")
else
:each
end
options = build_metadata_hash_from(args)
return scope, options
end
end
end
end
require 'erb'
require 'rspec/core/formatters/base_text_formatter'
module RSpec
module Core
module Formatters
class HtmlFormatter < BaseTextFormatter
include ERB::Util # for the #h method
def initialize(output)
super(output)
@example_group_number = 0
@example_number = 0
@header_red = nil
end
private
def method_missing(m, *a, &b)
# no-op
end
public
def message(message)
end
# The number of the currently running example_group
def example_group_number
@example_group_number
end
# The number of the currently running example (a global counter)
def example_number
@example_number
end
def start(example_count)
super(example_count)
@output.puts html_header
@output.puts report_header
@output.flush
end
def example_group_started(example_group)
super(example_group)
@example_group_red = false
@example_group_number += 1
unless example_group_number == 1
@output.puts " </dl>"
@output.puts "</div>"
end
@output.puts "<div id=\"div_group_#{example_group_number}\" class=\"example_group passed\">"
@output.puts " <dl #{current_indentation}>"
@output.puts " <dt id=\"example_group_#{example_group_number}\" class=\"passed\">#{h(example_group.description)}</dt>"
@output.flush
end
def start_dump
@output.puts " </dl>"
@output.puts "</div>"
@output.flush
end
def example_started(example)
super(example)
@example_number += 1
end
def example_passed(example)
move_progress
@output.puts " <dd class=\"example passed\"><span class=\"passed_spec_name\">#{h(example.description)}</span><span class='duration'>#{sprintf("%.5f", example.execution_result[:run_time])}s</span></dd>"
@output.flush
end
def example_failed(example)
super(example)
exception = example.metadata[:execution_result][:exception]
extra = extra_failure_content(exception)
@output.puts " <script type=\"text/javascript\">makeRed('rspec-header');</script>" unless @header_red
@header_red = true
@output.puts " <script type=\"text/javascript\">makeRed('div_group_#{example_group_number}');</script>" unless @example_group_red
@output.puts " <script type=\"text/javascript\">makeRed('example_group_#{example_group_number}');</script>" unless @example_group_red
@example_group_red = true
move_progress
@output.puts " <dd class=\"example #{exception.pending_fixed? ? 'pending_fixed' : 'failed'}\">"
@output.puts " <span class=\"failed_spec_name\">#{h(example.description)}</span>"
@output.puts " <span class=\"duration\">#{sprintf('%.5f', example.execution_result[:run_time])}s</span>"
@output.puts " <div class=\"failure\" id=\"failure_#{@failed_examples.size}\">"
@output.puts " <div class=\"message\"><pre>#{h(exception.message)}</pre></div>" unless exception.nil?
@output.puts " <div class=\"backtrace\"><pre>#{format_backtrace(exception.backtrace, example).join("\n")}</pre></div>" if exception
@output.puts extra unless extra == ""
@output.puts " </div>"
@output.puts " </dd>"
@output.flush
end
def example_pending(example)
message = example.metadata[:execution_result][:pending_message]
@output.puts " <script type=\"text/javascript\">makeYellow('rspec-header');</script>" unless @header_red
@output.puts " <script type=\"text/javascript\">makeYellow('div_group_#{example_group_number}');</script>" unless @example_group_red
@output.puts " <script type=\"text/javascript\">makeYellow('example_group_#{example_group_number}');</script>" unless @example_group_red
move_progress
@output.puts " <dd class=\"example not_implemented\"><span class=\"not_implemented_spec_name\">#{h(example.description)} (PENDING: #{h(message)})</span></dd>"
@output.flush
end
# Override this method if you wish to output extra HTML for a failed spec. For example, you
# could output links to images or other files produced during the specs.
#
def extra_failure_content(exception)
require 'rspec/core/formatters/snippet_extractor'
backtrace = exception.backtrace.map {|line| backtrace_line(line)}
backtrace.compact!
@snippet_extractor ||= SnippetExtractor.new
" <pre class=\"ruby\"><code>#{@snippet_extractor.snippet(backtrace)}</code></pre>"
end
def move_progress
@output.puts " <script type=\"text/javascript\">moveProgressBar('#{percent_done}');</script>"
@output.flush
end
def percent_done
result = 100.0
if @example_count > 0
result = ((example_number).to_f / @example_count.to_f * 1000).to_i / 10.0
end
result
end
def dump_failures
end
def dump_pending
end
def dump_summary(duration, example_count, failure_count, pending_count)
# TODO - kill dry_run?
if dry_run?
totals = "This was a dry-run"
else
totals = "#{example_count} example#{'s' unless example_count == 1}, "
totals << "#{failure_count} failure#{'s' unless failure_count == 1}"
totals << ", #{pending_count} pending" if pending_count > 0
end
@output.puts "<script type=\"text/javascript\">document.getElementById('duration').innerHTML = \"Finished in <strong>#{sprintf("%.5f", duration)} seconds</strong>\";</script>"
@output.puts "<script type=\"text/javascript\">document.getElementById('totals').innerHTML = \"#{totals}\";</script>"
@output.puts "</div>"
@output.puts "</div>"
@output.puts "</body>"
@output.puts "</html>"
@output.flush
end
def current_indentation
"style=\"margin-left: #{(example_group.ancestors.size - 1) * 15}px;\""
end
def html_header
<<-EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>RSpec results</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Expires" content="-1" />
<meta http-equiv="Pragma" content="no-cache" />
<style type="text/css">
body {
margin: 0;
padding: 0;
background: #fff;
font-size: 80%;
}
</style>
<script type="text/javascript">
// <![CDATA[
#{global_scripts}
// ]]>
</script>
<style type="text/css">
#{global_styles}
</style>
</head>
<body>
EOF
end
def report_header
<<-EOF
<div class="rspec-report">
<div id="rspec-header">
<div id="label">
<h1>RSpec Code Examples</h1>
</div>
<div id="display-filters">
<input id="passed_checkbox" name="passed_checkbox" type="checkbox" checked onchange="apply_filters()" value="1"> <label for="passed_checkbox">Passed</label>
<input id="failed_checkbox" name="failed_checkbox" type="checkbox" checked onchange="apply_filters()" value="2"> <label for="failed_checkbox">Failed</label>
<input id="pending_checkbox" name="pending_checkbox" type="checkbox" checked onchange="apply_filters()" value="3"> <label for="pending_checkbox">Pending</label>
</div>
<div id="summary">
<p id="totals">&nbsp;</p>
<p id="duration">&nbsp;</p>
</div>
</div>
<div class="results">
EOF
end
def global_scripts
<<-EOF
function addClass(element_id, classname) {
document.getElementById(element_id).className += (" " + classname);
}
function removeClass(element_id, classname) {
var elem = document.getElementById(element_id);
var classlist = elem.className.replace(classname,'');
elem.className = classlist;
}
function moveProgressBar(percentDone) {
document.getElementById("rspec-header").style.width = percentDone +"%";
}
function makeRed(element_id) {
removeClass(element_id, 'passed');
removeClass(element_id, 'not_implemented');
addClass(element_id,'failed');
}
function makeYellow(element_id) {
var elem = document.getElementById(element_id);
if (elem.className.indexOf("failed") == -1) { // class doesn't includes failed
if (elem.className.indexOf("not_implemented") == -1) { // class doesn't include not_implemented
removeClass(element_id, 'passed');
addClass(element_id,'not_implemented');
}
}
}
function apply_filters() {
var passed_filter = document.getElementById('passed_checkbox').checked;
var failed_filter = document.getElementById('failed_checkbox').checked;
var pending_filter = document.getElementById('pending_checkbox').checked;
assign_display_style("example passed", passed_filter);
assign_display_style("example failed", failed_filter);
assign_display_style("example not_implemented", pending_filter);
assign_display_style_for_group("example_group passed", passed_filter);
assign_display_style_for_group("example_group not_implemented", pending_filter, pending_filter || passed_filter);
assign_display_style_for_group("example_group failed", failed_filter, failed_filter || pending_filter || passed_filter);
}
function get_display_style(display_flag) {
var style_mode = 'none';
if (display_flag == true) {
style_mode = 'block';
}
return style_mode;
}
function assign_display_style(classname, display_flag) {
var style_mode = get_display_style(display_flag);
var elems = document.getElementsByClassName(classname)
for (var i=0; i<elems.length;i++) {
elems[i].style.display = style_mode;
}
}
function assign_display_style_for_group(classname, display_flag, subgroup_flag) {
var display_style_mode = get_display_style(display_flag);
var subgroup_style_mode = get_display_style(subgroup_flag);
var elems = document.getElementsByClassName(classname)
for (var i=0; i<elems.length;i++) {
var style_mode = display_style_mode;
if ((display_flag != subgroup_flag) && (elems[i].getElementsByTagName('dt')[0].innerHTML.indexOf(", ") != -1)) {
elems[i].style.display = subgroup_style_mode;
} else {
elems[i].style.display = display_style_mode;
}
}
}
EOF
end
def global_styles
<<-EOF
#rspec-header {
background: #65C400; color: #fff; height: 4em;
}
.rspec-report h1 {
margin: 0px 10px 0px 10px;
padding: 10px;
font-family: "Lucida Grande", Helvetica, sans-serif;
font-size: 1.8em;
position: absolute;
}
#label {
float:left;
}
#display-filters {
float:left;
padding: 28px 0 0 40%;
font-family: "Lucida Grande", Helvetica, sans-serif;
}
#summary {
float:right;
padding: 5px 10px;
font-family: "Lucida Grande", Helvetica, sans-serif;
text-align: right;
}
#summary p {
margin: 0 0 0 2px;
}
#summary #totals {
font-size: 1.2em;
}
.example_group {
margin: 0 10px 5px;
background: #fff;
}
dl {
margin: 0; padding: 0 0 5px;
font: normal 11px "Lucida Grande", Helvetica, sans-serif;
}
dt {
padding: 3px;
background: #65C400;
color: #fff;
font-weight: bold;
}
dd {
margin: 5px 0 5px 5px;
padding: 3px 3px 3px 18px;
}
dd .duration {
padding-left: 5px;
text-align: right;
right: 0px;
float:right;
}
dd.example.passed {
border-left: 5px solid #65C400;
border-bottom: 1px solid #65C400;
background: #DBFFB4; color: #3D7700;
}
dd.example.not_implemented {
border-left: 5px solid #FAF834;
border-bottom: 1px solid #FAF834;
background: #FCFB98; color: #131313;
}
dd.example.pending_fixed {
border-left: 5px solid #0000C2;
border-bottom: 1px solid #0000C2;
color: #0000C2; background: #D3FBFF;
}
dd.example.failed {
border-left: 5px solid #C20000;
border-bottom: 1px solid #C20000;
color: #C20000; background: #FFFBD3;
}
dt.not_implemented {
color: #000000; background: #FAF834;
}
dt.pending_fixed {
color: #FFFFFF; background: #C40D0D;
}
dt.failed {
color: #FFFFFF; background: #C40D0D;
}
#rspec-header.not_implemented {
color: #000000; background: #FAF834;
}
#rspec-header.pending_fixed {
color: #FFFFFF; background: #C40D0D;
}
#rspec-header.failed {
color: #FFFFFF; background: #C40D0D;
}
.backtrace {
color: #000;
font-size: 12px;
}
a {
color: #BE5C00;
}
/* Ruby code, style similar to vibrant ink */
.ruby {
font-size: 12px;
font-family: monospace;
color: white;
background-color: black;
padding: 0.1em 0 0.2em 0;
}
.ruby .keyword { color: #FF6600; }
.ruby .constant { color: #339999; }
.ruby .attribute { color: white; }
.ruby .global { color: white; }
.ruby .module { color: white; }
.ruby .class { color: white; }
.ruby .string { color: #66FF00; }
.ruby .ident { color: white; }
.ruby .method { color: #FFCC00; }
.ruby .number { color: white; }
.ruby .char { color: white; }
.ruby .comment { color: #9933CC; }
.ruby .symbol { color: white; }
.ruby .regex { color: #44B4CC; }
.ruby .punct { color: white; }
.ruby .escape { color: white; }
.ruby .interp { color: white; }
.ruby .expr { color: white; }
.ruby .offending { background-color: gray; }
.ruby .linenum {
width: 75px;
padding: 0.1em 1em 0.2em 0;
color: #000000;
background-color: #FFFBD3;
}
EOF
end
end
end
end
end
#!/bin/sh
'exec' "ruby" '-x' "$0" "$@"
#!/usr/local/rvm/rubies/ruby-1.9.2-p290/bin/ruby -w
#
# This file was generated by RubyGems.
#
# The application 'diff-lcs' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'rubygems'
version = ">= 0"
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
version = $1
ARGV.shift
end
gem 'diff-lcs', version
load Gem.bin_path('diff-lcs', 'htmldiff', version)
#!ruby -w
require 'diff/lcs'
require 'diff/lcs/htmldiff'
begin
require 'text/format'
rescue LoadError
Diff::LCS::HTMLDiff.can_expand_tabs = false
end
if ARGV.size < 2 or ARGV.size > 3
$stderr.puts "usage: #{File.basename($0)} old new [output.html]"
$stderr.puts " #{File.basename($0)} old new > output.html"
exit 127
end
left = IO.read(ARGV[0]).split($/)
right = IO.read(ARGV[1]).split($/)
options = { :title => "diff #{ARGV[0]} #{ARGV[1]}" }
htmldiff = Diff::LCS::HTMLDiff.new(left, right, options)
if ARGV[2]
File.open(ARGV[2], "w") do |f|
htmldiff.options[:output] = f
htmldiff.run
end
else
htmldiff.run
end
# -*- ruby encoding: utf-8 -*-
require 'cgi'
class Diff::LCS::HTMLDiff
class << self
attr_accessor :can_expand_tabs #:nodoc:
end
self.can_expand_tabs = true
class Callbacks
attr_accessor :output
attr_accessor :match_class
attr_accessor :only_a_class
attr_accessor :only_b_class
def initialize(output, options = {})
@output = output
options ||= {}
@match_class = options[:match_class] || "match"
@only_a_class = options[:only_a_class] || "only_a"
@only_b_class = options[:only_b_class] || "only_b"
end
def htmlize(element, css_class)
element = "&nbsp;" if element.empty?
%Q|<pre class="#{__send__(css_class)}">#{element}</pre>\n|
end
private :htmlize
# This will be called with both lines are the same
def match(event)
@output << htmlize(event.old_element, :match_class)
end
# This will be called when there is a line in A that isn't in B
def discard_a(event)
@output << htmlize(event.old_element, :only_a_class)
end
# This will be called when there is a line in B that isn't in A
def discard_b(event)
@output << htmlize(event.new_element, :only_b_class)
end
end
DEFAULT_OPTIONS = {
:expand_tabs => nil,
:output => nil,
:css => nil,
:title => nil,
}
DEFAULT_CSS = <<-CSS
body { margin: 0; }
.diff
{
border: 1px solid black;
margin: 1em 2em;
}
p
{
margin-left: 2em;
}
pre
{
padding-left: 1em;
margin: 0;
font-family: Inconsolata, Consolas, Lucida, Courier, monospaced;
white-space: pre;
}
.match { }
.only_a
{
background-color: #fdd;
color: red;
text-decoration: line-through;
}
.only_b
{
background-color: #ddf;
color: blue;
border-left: 3px solid blue
}
h1 { margin-left: 2em; }
CSS
def initialize(left, right, options = nil)
@left = left
@right = right
@options = options
@options = DEFAULT_OPTIONS.dup if @options.nil?
end
def verify_options
@options[:expand_tabs] ||= 4
@options[:expand_tabs] = 4 if @options[:expand_tabs] < 0
@options[:output] ||= $stdout
@options[:css] ||= DEFAULT_CSS.dup
@options[:title] ||= "diff"
end
private :verify_options
attr_reader :options
def run
verify_options
if @options[:expand_tabs] > 0 && self.class.can_expand_tabs
formatter = Text::Format.new
formatter.tabstop = @options[:expand_tabs]
@left = left.map { |line| formatter.expand(line.chomp) }
@right = right.map { |line| formatter.expand(line.chomp) }
end
@left.map! { |line| CGI.escapeHTML(line.chomp) }
@right.map! { |line| CGI.escapeHTML(line.chomp) }
@options[:output] << <<-OUTPUT
<html>
<head>
<title>#{@options[:title]}</title>
<style type="text/css">
#{@options[:css]}
</style>
</head>
<body>
<h1>#{@options[:title]}</h1>
<p>Legend: <span class="only_a">Only in Old</span>&nbsp;
<span class="only_b">Only in New</span></p>
<div class="diff">
OUTPUT
callbacks = Callbacks.new(@options[:output])
Diff::LCS.traverse_sequences(@left, @right, callbacks)
@options[:output] << <<-OUTPUT
</div>
</body>
</html>
OUTPUT
end
end
# vim: ft=ruby
require 'diff/lcs/block'
# A Hunk is a group of Blocks which overlap because of the context
# surrounding each block. (So if we're not using context, every hunk will
# contain one block.) Used in the diff program (bin/diff).
class Diff::LCS::Hunk
# Create a hunk using references to both the old and new data, as well as
# the piece of data
def initialize(data_old, data_new, piece, context, file_length_difference)
# At first, a hunk will have just one Block in it
@blocks = [ Diff::LCS::Block.new(piece) ]
@data_old = data_old
@data_new = data_new
before = after = file_length_difference
after += @blocks[0].diff_size
@file_length_difference = after # The caller must get this manually
# Save the start & end of each array. If the array doesn't exist
# (e.g., we're only adding items in this block), then figure out the
# line number based on the line number of the other file and the
# current difference in file lengths.
if @blocks[0].remove.empty?
a1 = a2 = nil
else
a1 = @blocks[0].remove[0].position
a2 = @blocks[0].remove[-1].position
end
if @blocks[0].insert.empty?
b1 = b2 = nil
else
b1 = @blocks[0].insert[0].position
b2 = @blocks[0].insert[-1].position
end
@start_old = a1 || (b1 - before)
@start_new = b1 || (a1 + before)
@end_old = a2 || (b2 - after)
@end_new = b2 || (a2 + after)
self.flag_context = context
end
attr_reader :blocks
attr_reader :start_old, :start_new
attr_reader :end_old, :end_new
attr_reader :file_length_difference
# Change the "start" and "end" fields to note that context should be added
# to this hunk
attr_accessor :flag_context
undef :flag_context=
def flag_context=(context) #:nodoc:
return if context.nil? or context.zero?
add_start = (context > @start_old) ? @start_old : context
@start_old -= add_start
@start_new -= add_start
if (@end_old + context) > @data_old.size
add_end = @data_old.size - @end_old
else
add_end = context
end
@end_old += add_end
@end_new += add_end
end
def unshift(hunk)
@start_old = hunk.start_old
@start_new = hunk.start_new
blocks.unshift(*hunk.blocks)
end
# Is there an overlap between hunk arg0 and old hunk arg1? Note: if end
# of old hunk is one less than beginning of second, they overlap
def overlaps?(hunk = nil)
return nil if hunk.nil?
a = (@start_old - hunk.end_old) <= 1
b = (@start_new - hunk.end_new) <= 1
return (a or b)
end
def diff(format)
case format
when :old
old_diff
when :unified
unified_diff
when :context
context_diff
when :ed
self
when :reverse_ed, :ed_finish
ed_diff(format)
else
raise "Unknown diff format #{format}."
end
end
def each_old(block)
@data_old[@start_old .. @end_old].each { |e| yield e }
end
private
# Note that an old diff can't have any context. Therefore, we know that
# there's only one block in the hunk.
def old_diff
warn "Expecting only one block in an old diff hunk!" if @blocks.size > 1
op_act = { "+" => 'a', "-" => 'd', "!" => "c" }
block = @blocks[0]
# Calculate item number range. Old diff range is just like a context
# diff range, except the ranges are on one line with the action between
# them.
s = "#{context_range(:old)}#{op_act[block.op]}#{context_range(:new)}\n"
# If removing anything, just print out all the remove lines in the hunk
# which is just all the remove lines in the block.
@data_old[@start_old .. @end_old].each { |e| s << "< #{e}\n" } unless block.remove.empty?
s << "---\n" if block.op == "!"
@data_new[@start_new .. @end_new].each { |e| s << "> #{e}\n" } unless block.insert.empty?
s
end
def unified_diff
# Calculate item number range.
s = "@@ -#{unified_range(:old)} +#{unified_range(:new)} @@\n"
# Outlist starts containing the hunk of the old file. Removing an item
# just means putting a '-' in front of it. Inserting an item requires
# getting it from the new file and splicing it in. We splice in
# +num_added+ items. Remove blocks use +num_added+ because splicing
# changed the length of outlist.
#
# We remove +num_removed+ items. Insert blocks use +num_removed+
# because their item numbers -- corresponding to positions in the NEW
# file -- don't take removed items into account.
lo, hi, num_added, num_removed = @start_old, @end_old, 0, 0
outlist = @data_old[lo .. hi].collect { |e| e.gsub(/^/, ' ') }
@blocks.each do |block|
block.remove.each do |item|
op = item.action.to_s # -
offset = item.position - lo + num_added
outlist[offset].gsub!(/^ /, op.to_s)
num_removed += 1
end
block.insert.each do |item|
op = item.action.to_s # +
offset = item.position - @start_new + num_removed
outlist[offset, 0] = "#{op}#{@data_new[item.position]}"
num_added += 1
end
end
s << outlist.join("\n")
end
def context_diff
s = "***************\n"
s << "*** #{context_range(:old)} ****\n"
r = context_range(:new)
# Print out file 1 part for each block in context diff format if there
# are any blocks that remove items
lo, hi = @start_old, @end_old
removes = @blocks.select { |e| not e.remove.empty? }
if removes
outlist = @data_old[lo .. hi].collect { |e| e.gsub(/^/, ' ') }
removes.each do |block|
block.remove.each do |item|
outlist[item.position - lo].gsub!(/^ /) { block.op } # - or !
end
end
s << outlist.join("\n")
end
s << "\n--- #{r} ----\n"
lo, hi = @start_new, @end_new
inserts = @blocks.select { |e| not e.insert.empty? }
if inserts
outlist = @data_new[lo .. hi].collect { |e| e.gsub(/^/, ' ') }
inserts.each do |block|
block.insert.each do |item|
outlist[item.position - lo].gsub!(/^ /) { block.op } # + or !
end
end
s << outlist.join("\n")
end
s
end
def ed_diff(format)
op_act = { "+" => 'a', "-" => 'd', "!" => "c" }
warn "Expecting only one block in an old diff hunk!" if @blocks.size > 1
if format == :reverse_ed
s = "#{op_act[@blocks[0].op]}#{context_range(:old)}\n"
else
s = "#{context_range(:old).gsub(/,/, ' ')}#{op_act[@blocks[0].op]}\n"
end
unless @blocks[0].insert.empty?
@data_new[@start_new .. @end_new].each { |e| s << "#{e}\n" }
s << ".\n"
end
s
end
# Generate a range of item numbers to print. Only print 1 number if the
# range has only one item in it. Otherwise, it's 'start,end'
def context_range(mode)
case mode
when :old
s, e = (@start_old + 1), (@end_old + 1)
when :new
s, e = (@start_new + 1), (@end_new + 1)
end
(s < e) ? "#{s},#{e}" : "#{e}"
end
# Generate a range of item numbers to print for unified diff. Print
# number where block starts, followed by number of lines in the block
# (don't print number of lines if it's 1)
def unified_range(mode)
case mode
when :old
s, e = (@start_old + 1), (@end_old + 1)
when :new
s, e = (@start_new + 1), (@end_new + 1)
end
length = e - s + 1
first = (length < 2) ? e : s # "strange, but correct"
(length == 1) ? "#{first}" : "#{first},#{length}"
end
end
Feature: :if and :unless
The `:if` and `:unless` metadata keys can be used to filter examples without
needing to configure an exclusion filter.
Scenario: implicit :if filter
Given a file named "implicit_if_filter_spec.rb" with:
"""
describe ":if => true group", :if => true do
it(":if => true group :if => true example", :if => true) { }
it(":if => true group :if => false example", :if => false) { }
it(":if => true group no :if example") { }
end
describe ":if => false group", :if => false do
it(":if => false group :if => true example", :if => true) { }
it(":if => false group :if => false example", :if => false) { }
it(":if => false group no :if example") { }
end
describe "no :if group" do
it("no :if group :if => true example", :if => true) { }
it("no :if group :if => false example", :if => false) { }
it("no :if group no :if example") { }
end
"""
When I run `rspec implicit_if_filter_spec.rb --format doc`
Then the output should contain all of these:
| :if => true group :if => true example |
| :if => true group no :if example |
| :if => false group :if => true example |
| no :if group :if => true example |
| no :if group no :if example |
And the output should not contain any of these:
| :if => true group :if => false example |
| :if => false group :if => false example |
| :if => false group no :if example |
| no :if group :if => false example |
Scenario: implicit :unless filter
Given a file named "implicit_unless_filter_spec.rb" with:
"""
describe ":unless => true group", :unless => true do
it(":unless => true group :unless => true example", :unless => true) { }
it(":unless => true group :unless => false example", :unless => false) { }
it(":unless => true group no :unless example") { }
end
describe ":unless => false group", :unless => false do
it(":unless => false group :unless => true example", :unless => true) { }
it(":unless => false group :unless => false example", :unless => false) { }
it(":unless => false group no :unless example") { }
end
describe "no :unless group" do
it("no :unless group :unless => true example", :unless => true) { }
it("no :unless group :unless => false example", :unless => false) { }
it("no :unless group no :unless example") { }
end
"""
When I run `rspec implicit_unless_filter_spec.rb --format doc`
Then the output should contain all of these:
| :unless => true group :unless => false example |
| :unless => false group :unless => false example |
| :unless => false group no :unless example |
| no :unless group :unless => false example |
| no :unless group no :unless example |
And the output should not contain any of these:
| :unless => true group :unless => true example |
| :unless => true group no :unless example |
| :unless => false group :unless => true example |
| no :unless group :unless => true example |
Scenario: combining implicit filter with explicit inclusion filter
Given a file named "explicit_inclusion_filter_spec.rb" with:
"""
RSpec.configure do |c|
c.filter_run :focus => true
end
describe "group with :focus", :focus => true do
it("focused example") { }
it("focused :if => true example", :if => true) { }
it("focused :if => false example", :if => false) { }
it("focused :unless => true example", :unless => true) { }
it("focused :unless => false example", :unless => false) { }
end
describe "group without :focus" do
it("unfocused example") { }
it("unfocused :if => true example", :if => true) { }
it("unfocused :if => false example", :if => false) { }
it("unfocused :unless => true example", :unless => true) { }
it("unfocused :unless => false example", :unless => false) { }
end
"""
When I run `rspec explicit_inclusion_filter_spec.rb --format doc`
Then the output should contain all of these:
| focused example |
| focused :if => true example |
| focused :unless => false example |
And the output should not contain any of these:
| focused :if => false example |
| focused :unless => true example |
| unfocused |
Scenario: combining implicit filter with explicit exclusion filter
Given a file named "explicit_exclusion_filter_spec.rb" with:
"""
RSpec.configure do |c|
c.filter_run_excluding :broken => true
end
describe "unbroken group" do
it("included example") { }
it("included :if => true example", :if => true) { }
it("included :if => false example", :if => false) { }
it("included :unless => true example", :unless => true) { }
it("included :unless => false example", :unless => false) { }
end
describe "broken group", :broken => true do
it("excluded example") { }
it("excluded :if => true example", :if => true) { }
it("excluded :if => false example", :if => false) { }
it("excluded :unless => true example", :unless => true) { }
it("excluded :unless => false example", :unless => false) { }
end
"""
When I run `rspec explicit_exclusion_filter_spec.rb --format doc`
Then the output should contain all of these:
| included example |
| included :if => true example |
| included :unless => false example |
And the output should not contain any of these:
| included :if => false example |
| included :unless => true example |
| excluded |
Scenario: override implicit :if and :unless exclusion filters
Given a file named "override_implicit_filters_spec.rb" with:
"""
RSpec.configure do |c|
c.filter_run_excluding :if => :exclude_me, :unless => :exclude_me_for_unless
end
describe ":if filtering" do
it(":if => true example", :if => true) { }
it(":if => false example", :if => false) { }
it(":if => :exclude_me example", :if => :exclude_me) { }
end
describe ":unless filtering" do
it(":unless => true example", :unless => true) { }
it(":unless => false example", :unless => false) { }
it(":unless => :exclude_me_for_unless example", :unless => :exclude_me_for_unless) { }
end
"""
When I run `rspec override_implicit_filters_spec.rb --format doc`
Then the output should contain all of these:
| :if => true example |
| :if => false example |
| :unless => true example |
| :unless => false example |
And the output should not contain any of these:
| :if => :exclude_me example |
| :unless => :exclude_me_for_unless example |
Feature: implicit receiver
When should() is called in an example without an explicit receiver, it is
invoked against the subject (explicit or implicit).
Scenario: implicit subject
Given a file named "example_spec.rb" with:
"""
describe Array do
describe "when first created" do
it { should be_empty }
end
end
"""
When I run `rspec example_spec.rb`
Then the examples should all pass
Scenario: explicit subject
Given a file named "example_spec.rb" with:
"""
describe Array do
describe "with 3 items" do
subject { [1,2,3] }
it { should_not be_empty }
end
end
"""
When I run `rspec example_spec.rb`
Then the examples should all pass
Feature: implicit subject
If the first argument to the outermost example group is a class, an instance
of that class is exposed to each example via the subject() method.
Scenario: subject in top level group
Given a file named "top_level_subject_spec.rb" with:
"""
describe Array, "when first created" do
it "should be empty" do
subject.should eq([])
end
end
"""
When I run `rspec ./top_level_subject_spec.rb`
Then the examples should all pass
Scenario: subject in a nested group
Given a file named "nested_subject_spec.rb" with:
"""
describe Array do
describe "when first created" do
it "should be empty" do
subject.should eq([])
end
end
end
"""
When I run `rspec nested_subject_spec.rb`
Then the examples should all pass
Feature: inclusion filters
You can constrain which examples are run by declaring an inclusion filter. The
most common use case is to focus on a subset of examples as you're focused on
a particular problem.
You can specify metadata using only symbols if you set the
`treat_symbols_as_metadata_keys_with_true_values` config option to `true`.
Background:
Given a file named "spec/spec_helper.rb" with:
"""
RSpec.configure do |c|
c.filter_run_including :focus => true
end
"""
Scenario: focus on an example
Given a file named "spec/sample_spec.rb" with:
"""
require "spec_helper"
describe "something" do
it "does one thing" do
end
it "does another thing", :focus => true do
end
end
"""
When I run `rspec spec/sample_spec.rb --format doc`
Then the output should contain "does another thing"
And the output should not contain "does one thing"
Scenario: focus on a group
Given a file named "spec/sample_spec.rb" with:
"""
require "spec_helper"
describe "group 1", :focus => true do
it "group 1 example 1" do
end
it "group 1 example 2" do
end
end
describe "group 2" do
it "group 2 example 1" do
end
end
"""
When I run `rspec spec/sample_spec.rb --format doc`
Then the output should contain "group 1 example 1"
And the output should contain "group 1 example 2"
And the output should not contain "group 2 example 1"
Scenario: before/after(:all) hooks in unmatched example group are not run
Given a file named "spec/before_after_all_inclusion_filter_spec.rb" with:
"""
require "spec_helper"
describe "group 1", :focus => true do
before(:all) { puts "before all in focused group" }
after(:all) { puts "after all in focused group" }
it "group 1 example" do
end
end
describe "group 2" do
before(:all) { puts "before all in unfocused group" }
after(:all) { puts "after all in unfocused group" }
context "context 1" do
it "group 2 context 1 example 1" do
end
end
end
"""
When I run `rspec ./spec/before_after_all_inclusion_filter_spec.rb`
Then the output should contain "before all in focused group"
And the output should contain "after all in focused group"
And the output should not contain "before all in unfocused group"
And the output should not contain "after all in unfocused group"
Scenario: Use symbols as metadata
Given a file named "symbols_as_metadata_spec.rb" with:
"""
RSpec.configure do |c|
c.treat_symbols_as_metadata_keys_with_true_values = true
c.filter_run :current_example
end
describe "something" do
it "does one thing" do
end
it "does another thing", :current_example do
end
end
"""
When I run `rspec symbols_as_metadata_spec.rb --format doc`
Then the output should contain "does another thing"
And the output should not contain "does one thing"
Feature: --init option
Use the --init option on the command line to generate conventional
files for an rspec project.
Scenario: generate .rspec
When I run `rspec --init`
Then the following files should exist:
| .rspec |
And the output should contain "create .rspec"
Scenario: .rspec file already exists
Given a file named ".rspec" with:
"""
--color
"""
When I run `rspec --init`
Then the output should contain "exist .rspec"
module RSpec
module Core
module Extensions
# @private
module InstanceEvalWithArgs
# @private
#
# Used internally to support `instance_exec` in Ruby 1.8.6.
#
# based on Bounded Spec InstanceExec (Mauricio Fernandez)
# http://eigenclass.org/hiki/bounded+space+instance_exec
# - uses singleton_class instead of global InstanceExecHelper module
# - this keeps it scoped to classes/modules that include this module
# - only necessary for ruby 1.8.6
def instance_eval_with_args(*args, &block)
return instance_exec(*args, &block) if respond_to?(:instance_exec)
# If there are no args and the block doesn't expect any, there's no
# need to fake instance_exec with our hack below.
# Notes:
# * lambda { }.arity # => -1
# * lambda { || }.arity # => 0
# * lambda { |*a| }.arity # -1
return instance_eval(&block) if block.arity < 1 && args.size.zero?
singleton_class = (class << self; self; end)
begin
orig_critical, Thread.critical = Thread.critical, true
n = 0
n += 1 while respond_to?(method_name="__instance_exec#{n}")
singleton_class.module_eval{ define_method(method_name, &block) }
ensure
Thread.critical = orig_critical
end
begin
return send(method_name, *args)
ensure
singleton_class.module_eval{ remove_method(method_name) } rescue nil
end
end
end
end
end
end
module Kernel
unless defined?(debugger)
# If not already defined by ruby-debug, this implementation prints helpful
# message to STDERR when ruby-debug is not loaded.
def debugger(*args)
(RSpec.configuration.error_stream || $stderr).puts "\n***** debugger statement ignored, use -d or --debug option to enable debugging\n#{caller(0)[1]}"
end
end
end
# -*- ruby encoding: utf-8 -*-
module Diff
# = Diff::LCS 1.1.3
# Computes "intelligent" differences between two sequenced Enumerables.
# This is an implementation of the McIlroy-Hunt "diff" algorithm for
# Enumerable objects that include Diffable.
#
# Based on Mario I. Wolczko's Smalltalk version (1.2, 1993) and Ned Konz's
# Perl version (Algorithm::Diff 1.15).
#
# == Synopsis
# require 'diff/lcs'
#
# seq1 = %w(a b c e h j l m n p)
# seq2 = %w(b c d e f j k l m r s t)
#
# lcs = Diff::LCS.LCS(seq1, seq2)
# diffs = Diff::LCS.diff(seq1, seq2)
# sdiff = Diff::LCS.sdiff(seq1, seq2)
# seq = Diff::LCS.traverse_sequences(seq1, seq2, callback_obj)
# bal = Diff::LCS.traverse_balanced(seq1, seq2, callback_obj)
# seq2 == Diff::LCS.patch(seq1, diffs)
# seq2 == Diff::LCS.patch!(seq1, diffs)
# seq1 == Diff::LCS.unpatch(seq2, diffs)
# seq1 == Diff::LCS.unpatch!(seq2, diffs)
# seq2 == Diff::LCS.patch(seq1, sdiff)
# seq2 == Diff::LCS.patch!(seq1, sdiff)
# seq1 == Diff::LCS.unpatch(seq2, sdiff)
# seq1 == Diff::LCS.unpatch!(seq2, sdiff)
#
# Alternatively, objects can be extended with Diff::LCS:
#
# seq1.extend(Diff::LCS)
# lcs = seq1.lcs(seq2)
# diffs = seq1.diff(seq2)
# sdiff = seq1.sdiff(seq2)
# seq = seq1.traverse_sequences(seq2, callback_obj)
# bal = seq1.traverse_balanced(seq2, callback_obj)
# seq2 == seq1.patch(diffs)
# seq2 == seq1.patch!(diffs)
# seq1 == seq2.unpatch(diffs)
# seq1 == seq2.unpatch!(diffs)
# seq2 == seq1.patch(sdiff)
# seq2 == seq1.patch!(sdiff)
# seq1 == seq2.unpatch(sdiff)
# seq1 == seq2.unpatch!(sdiff)
#
# Default extensions are provided for Array and String objects through the
# use of 'diff/lcs/array' and 'diff/lcs/string'.
#
# == Introduction (by Mark-Jason Dominus)
#
# <em>The following text is from the Perl documentation. The only changes
# have been to make the text appear better in Rdoc</em>.
#
# I once read an article written by the authors of +diff+; they said that
# they hard worked very hard on the algorithm until they found the right
# one.
#
# I think what they ended up using (and I hope someone will correct me,
# because I am not very confident about this) was the `longest common
# subsequence' method. In the LCS problem, you have two sequences of
# items:
#
# a b c d f g h j q z
# a b c d e f g i j k r x y z
#
# and you want to find the longest sequence of items that is present in
# both original sequences in the same order. That is, you want to find a
# new sequence *S* which can be obtained from the first sequence by
# deleting some items, and from the second sequence by deleting other
# items. You also want *S* to be as long as possible. In this case *S* is:
#
# a b c d f g j z
#
# From there it's only a small step to get diff-like output:
#
# e h i k q r x y
# + - + + - + + +
#
# This module solves the LCS problem. It also includes a canned function
# to generate +diff+-like output.
#
# It might seem from the example above that the LCS of two sequences is
# always pretty obvious, but that's not always the case, especially when
# the two sequences have many repeated elements. For example, consider
#
# a x b y c z p d q
# a b c a x b y c z
#
# A naive approach might start by matching up the +a+ and +b+ that appear
# at the beginning of each sequence, like this:
#
# a x b y c z p d q
# a b c a b y c z
#
# This finds the common subsequence +a b c z+. But actually, the LCS is
# +a x b y c z+:
#
# a x b y c z p d q
# a b c a x b y c z
#
# == Author
# This version is by Austin Ziegler <austin@rubyforge.org>.
#
# It is based on the Perl Algorithm::Diff (1.15) by Ned Konz , copyright
# &copy; 2000&ndash;2002 and the Smalltalk diff version by Mario I.
# Wolczko, copyright &copy; 1993. Documentation includes work by
# Mark-Jason Dominus.
#
# == Licence
# Copyright &copy; 2004 Austin Ziegler
# This program is free software; you can redistribute it and/or modify it
# under the same terms as Ruby, or alternatively under the Perl Artistic
# licence.
#
# == Credits
# Much of the documentation is taken directly from the Perl
# Algorithm::Diff implementation and was written originally by Mark-Jason
# Dominus and later by Ned Konz. The basic Ruby implementation was
# re-ported from the Smalltalk implementation, available at
# ftp://st.cs.uiuc.edu/pub/Smalltalk/MANCHESTER/manchester/4.0/diff.st
#
# #sdiff and #traverse_balanced were written for the Perl version by Mike
# Schilli <m@perlmeister.com>.
#
# "The algorithm is described in <em>A Fast Algorithm for Computing
# Longest Common Subsequences</em>, CACM, vol.20, no.5, pp.350-353, May
# 1977, with a few minor improvements to improve the speed."
module LCS
VERSION = '1.1.3'
end
end
require 'diff/lcs/callbacks'
module Diff::LCS
# Returns an Array containing the longest common subsequence(s) between
# +self+ and +other+. See Diff::LCS#LCS.
#
# lcs = seq1.lcs(seq2)
def lcs(other, &block) #:yields self[ii] if there are matched subsequences:
Diff::LCS.LCS(self, other, &block)
end
# Returns the difference set between +self+ and +other+. See
# Diff::LCS#diff.
def diff(other, callbacks = nil, &block)
Diff::LCS::diff(self, other, callbacks, &block)
end
# Returns the balanced ("side-by-side") difference set between +self+ and
# +other+. See Diff::LCS#sdiff.
def sdiff(other, callbacks = nil, &block)
Diff::LCS::sdiff(self, other, callbacks, &block)
end
# Traverses the discovered longest common subsequences between +self+ and
# +other+. See Diff::LCS#traverse_sequences.
def traverse_sequences(other, callbacks = nil, &block)
traverse_sequences(self, other, callbacks ||
Diff::LCS::YieldingCallbacks, &block)
end
# Traverses the discovered longest common subsequences between +self+ and
# +other+ using the alternate, balanced algorithm. See
# Diff::LCS#traverse_balanced.
def traverse_balanced(other, callbacks = nil, &block)
traverse_balanced(self, other, callbacks ||
Diff::LCS::YieldingCallbacks, &block)
end
# Attempts to patch a copy of +self+ with the provided +patchset+. See
# Diff::LCS#patch.
def patch(patchset)
Diff::LCS::patch(self.dup, patchset)
end
# Attempts to unpatch a copy of +self+ with the provided +patchset+. See
# Diff::LCS#patch.
def unpatch(patchset)
Diff::LCS::unpatch(self.dup, patchset)
end
# Attempts to patch +self+ with the provided +patchset+. See
# Diff::LCS#patch!. Does no autodiscovery.
def patch!(patchset)
Diff::LCS::patch!(self, patchset)
end
# Attempts to unpatch +self+ with the provided +patchset+. See
# Diff::LCS#unpatch. Does no autodiscovery.
def unpatch!(patchset)
Diff::LCS::unpatch!(self, patchset)
end
end
module Diff::LCS
class << self
# Given two sequenced Enumerables, LCS returns an Array containing their
# longest common subsequences.
#
# lcs = Diff::LCS.LCS(seq1, seq2)
#
# This array whose contents is such that:
#
# lcs.each_with_index do |ee, ii|
# assert(ee.nil? || (seq1[ii] == seq2[ee]))
# end
#
# If a block is provided, the matching subsequences will be yielded from
# +seq1+ in turn and may be modified before they are placed into the
# returned Array of subsequences.
def LCS(seq1, seq2, &block) #:yields seq1[ii] for each matched:
matches = Diff::LCS.__lcs(seq1, seq2)
ret = []
matches.each_with_index do |ee, ii|
unless matches[ii].nil?
if block_given?
ret << (yield seq1[ii])
else
ret << seq1[ii]
end
end
end
ret
end
# Diff::LCS.diff computes the smallest set of additions and deletions
# necessary to turn the first sequence into the second, and returns a
# description of these changes.
#
# See Diff::LCS::DiffCallbacks for the default behaviour. An alternate
# behaviour may be implemented with Diff::LCS::ContextDiffCallbacks. If
# a Class argument is provided for +callbacks+, #diff will attempt to
# initialise it. If the +callbacks+ object (possibly initialised)
# responds to #finish, it will be called.
def diff(seq1, seq2, callbacks = nil, &block) # :yields diff changes:
callbacks ||= Diff::LCS::DiffCallbacks
if callbacks.kind_of?(Class)
cb = callbacks.new rescue callbacks
callbacks = cb
end
traverse_sequences(seq1, seq2, callbacks)
callbacks.finish if callbacks.respond_to?(:finish)
if block_given?
res = callbacks.diffs.map do |hunk|
if hunk.kind_of?(Array)
hunk = hunk.map { |hunk_block| yield hunk_block }
else
yield hunk
end
end
res
else
callbacks.diffs
end
end
# Diff::LCS.sdiff computes all necessary components to show two sequences
# and their minimized differences side by side, just like the Unix
# utility <em>sdiff</em> does:
#
# old < -
# same same
# before | after
# - > new
#
# See Diff::LCS::SDiffCallbacks for the default behaviour. An alternate
# behaviour may be implemented with Diff::LCS::ContextDiffCallbacks. If
# a Class argument is provided for +callbacks+, #diff will attempt to
# initialise it. If the +callbacks+ object (possibly initialised)
# responds to #finish, it will be called.
def sdiff(seq1, seq2, callbacks = nil, &block) #:yields diff changes:
callbacks ||= Diff::LCS::SDiffCallbacks
if callbacks.kind_of?(Class)
cb = callbacks.new rescue callbacks
callbacks = cb
end
traverse_balanced(seq1, seq2, callbacks)
callbacks.finish if callbacks.respond_to?(:finish)
if block_given?
res = callbacks.diffs.map do |hunk|
if hunk.kind_of?(Array)
hunk = hunk.map { |hunk_block| yield hunk_block }
else
yield hunk
end
end
res
else
callbacks.diffs
end
end
# Diff::LCS.traverse_sequences is the most general facility provided by this
# module; +diff+ and +LCS+ are implemented as calls to it.
#
# The arguments to #traverse_sequences are the two sequences to
# traverse, and a callback object, like this:
#
# traverse_sequences(seq1, seq2, Diff::LCS::ContextDiffCallbacks.new)
#
# #diff is implemented with #traverse_sequences.
#
# == Callback Methods
# Optional callback methods are <em>emphasized</em>.
#
# callbacks#match:: Called when +a+ and +b+ are pointing
# to common elements in +A+ and +B+.
# callbacks#discard_a:: Called when +a+ is pointing to an
# element not in +B+.
# callbacks#discard_b:: Called when +b+ is pointing to an
# element not in +A+.
# <em>callbacks#finished_a</em>:: Called when +a+ has reached the end of
# sequence +A+.
# <em>callbacks#finished_b</em>:: Called when +b+ has reached the end of
# sequence +B+.
#
# == Algorithm
# a---+
# v
# A = a b c e h j l m n p
# B = b c d e f j k l m r s t
# ^
# b---+
#
# If there are two arrows (+a+ and +b+) pointing to elements of
# sequences +A+ and +B+, the arrows will initially point to the first
# elements of their respective sequences. #traverse_sequences will
# advance the arrows through the sequences one element at a time,
# calling a method on the user-specified callback object before each
# advance. It will advance the arrows in such a way that if there are
# elements <tt>A[ii]</tt> and <tt>B[jj]</tt> which are both equal and
# part of the longest common subsequence, there will be some moment
# during the execution of #traverse_sequences when arrow +a+ is pointing
# to <tt>A[ii]</tt> and arrow +b+ is pointing to <tt>B[jj]</tt>. When
# this happens, #traverse_sequences will call <tt>callbacks#match</tt>
# and then it will advance both arrows.
#
# Otherwise, one of the arrows is pointing to an element of its sequence
# that is not part of the longest common subsequence.
# #traverse_sequences will advance that arrow and will call
# <tt>callbacks#discard_a</tt> or <tt>callbacks#discard_b</tt>, depending
# on which arrow it advanced. If both arrows point to elements that are
# not part of the longest common subsequence, then #traverse_sequences
# will advance one of them and call the appropriate callback, but it is
# not specified which it will call.
#
# The methods for <tt>callbacks#match</tt>, <tt>callbacks#discard_a</tt>,
# and <tt>callbacks#discard_b</tt> are invoked with an event comprising
# the action ("=", "+", or "-", respectively), the indicies +ii+ and
# +jj+, and the elements <tt>A[ii]</tt> and <tt>B[jj]</tt>. Return
# values are discarded by #traverse_sequences.
#
# === End of Sequences
# If arrow +a+ reaches the end of its sequence before arrow +b+ does,
# #traverse_sequence will try to call <tt>callbacks#finished_a</tt> with
# the last index and element of +A+ (<tt>A[-1]</tt>) and the current
# index and element of +B+ (<tt>B[jj]</tt>). If
# <tt>callbacks#finished_a</tt> does not exist, then
# <tt>callbacks#discard_b</tt> will be called on each element of +B+
# until the end of the sequence is reached (the call
# will be done with <tt>A[-1]</tt> and <tt>B[jj]</tt> for each element).
#
# If +b+ reaches the end of +B+ before +a+ reaches the end of +A+,
# <tt>callbacks#finished_b</tt> will be called with the current index
# and element of +A+ (<tt>A[ii]</tt>) and the last index and element of
# +B+ (<tt>A[-1]</tt>). Again, if <tt>callbacks#finished_b</tt> does not
# exist on the callback object, then <tt>callbacks#discard_a</tt> will
# be called on each element of +A+ until the end of the sequence is
# reached (<tt>A[ii]</tt> and <tt>B[-1]</tt>).
#
# There is a chance that one additional <tt>callbacks#discard_a</tt> or
# <tt>callbacks#discard_b</tt> will be called after the end of the
# sequence is reached, if +a+ has not yet reached the end of +A+ or +b+
# has not yet reached the end of +B+.
def traverse_sequences(seq1, seq2, callbacks = Diff::LCS::SequenceCallbacks, &block) #:yields change events:
matches = Diff::LCS.__lcs(seq1, seq2)
run_finished_a = run_finished_b = false
string = seq1.kind_of?(String)
a_size = seq1.size
b_size = seq2.size
ai = bj = 0
(0 .. matches.size).each do |ii|
b_line = matches[ii]
ax = string ? seq1[ii, 1] : seq1[ii]
bx = string ? seq2[bj, 1] : seq2[bj]
if b_line.nil?
unless ax.nil?
event = Diff::LCS::ContextChange.new('-', ii, ax, bj, bx)
event = yield event if block_given?
callbacks.discard_a(event)
end
else
loop do
break unless bj < b_line
bx = string ? seq2[bj, 1] : seq2[bj]
event = Diff::LCS::ContextChange.new('+', ii, ax, bj, bx)
event = yield event if block_given?
callbacks.discard_b(event)
bj += 1
end
bx = string ? seq2[bj, 1] : seq2[bj]
event = Diff::LCS::ContextChange.new('=', ii, ax, bj, bx)
event = yield event if block_given?
callbacks.match(event)
bj += 1
end
ai = ii
end
ai += 1
# The last entry (if any) processed was a match. +ai+ and +bj+ point
# just past the last matching lines in their sequences.
while (ai < a_size) or (bj < b_size)
# last A?
if ai == a_size and bj < b_size
if callbacks.respond_to?(:finished_a) and not run_finished_a
ax = string ? seq1[-1, 1] : seq1[-1]
bx = string ? seq2[bj, 1] : seq2[bj]
event = Diff::LCS::ContextChange.new('>', (a_size - 1), ax, bj, bx)
event = yield event if block_given?
callbacks.finished_a(event)
run_finished_a = true
else
ax = string ? seq1[ai, 1] : seq1[ai]
loop do
bx = string ? seq2[bj, 1] : seq2[bj]
event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx)
event = yield event if block_given?
callbacks.discard_b(event)
bj += 1
break unless bj < b_size
end
end
end
# last B?
if bj == b_size and ai < a_size
if callbacks.respond_to?(:finished_b) and not run_finished_b
ax = string ? seq1[ai, 1] : seq1[ai]
bx = string ? seq2[-1, 1] : seq2[-1]
event = Diff::LCS::ContextChange.new('<', ai, ax, (b_size - 1), bx)
event = yield event if block_given?
callbacks.finished_b(event)
run_finished_b = true
else
bx = string ? seq2[bj, 1] : seq2[bj]
loop do
ax = string ? seq1[ai, 1] : seq1[ai]
event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
event = yield event if block_given?
callbacks.discard_a(event)
ai += 1
break unless bj < b_size
end
end
end
if ai < a_size
ax = string ? seq1[ai, 1] : seq1[ai]
bx = string ? seq2[bj, 1] : seq2[bj]
event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
event = yield event if block_given?
callbacks.discard_a(event)
ai += 1
end
if bj < b_size
ax = string ? seq1[ai, 1] : seq1[ai]
bx = string ? seq2[bj, 1] : seq2[bj]
event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx)
event = yield event if block_given?
callbacks.discard_b(event)
bj += 1
end
end
end
# #traverse_balanced is an alternative to #traverse_sequences. It
# uses a different algorithm to iterate through the entries in the
# computed longest common subsequence. Instead of viewing the changes as
# insertions or deletions from one of the sequences, #traverse_balanced
# will report <em>changes</em> between the sequences. To represent a
#
# The arguments to #traverse_balanced are the two sequences to traverse
# and a callback object, like this:
#
# traverse_balanced(seq1, seq2, Diff::LCS::ContextDiffCallbacks.new)
#
# #sdiff is implemented with #traverse_balanced.
#
# == Callback Methods
# Optional callback methods are <em>emphasized</em>.
#
# callbacks#match:: Called when +a+ and +b+ are pointing
# to common elements in +A+ and +B+.
# callbacks#discard_a:: Called when +a+ is pointing to an
# element not in +B+.
# callbacks#discard_b:: Called when +b+ is pointing to an
# element not in +A+.
# <em>callbacks#change</em>:: Called when +a+ and +b+ are pointing
# to the same relative position, but
# <tt>A[a]</tt> and <tt>B[b]</tt> are
# not the same; a <em>change</em> has
# occurred.
#
# #traverse_balanced might be a bit slower than #traverse_sequences,
# noticable only while processing huge amounts of data.
#
# The +sdiff+ function of this module is implemented as call to
# #traverse_balanced.
#
# == Algorithm
# a---+
# v
# A = a b c e h j l m n p
# B = b c d e f j k l m r s t
# ^
# b---+
#
# === Matches
# If there are two arrows (+a+ and +b+) pointing to elements of
# sequences +A+ and +B+, the arrows will initially point to the first
# elements of their respective sequences. #traverse_sequences will
# advance the arrows through the sequences one element at a time,
# calling a method on the user-specified callback object before each
# advance. It will advance the arrows in such a way that if there are
# elements <tt>A[ii]</tt> and <tt>B[jj]</tt> which are both equal and
# part of the longest common subsequence, there will be some moment
# during the execution of #traverse_sequences when arrow +a+ is pointing
# to <tt>A[ii]</tt> and arrow +b+ is pointing to <tt>B[jj]</tt>. When
# this happens, #traverse_sequences will call <tt>callbacks#match</tt>
# and then it will advance both arrows.
#
# === Discards
# Otherwise, one of the arrows is pointing to an element of its sequence
# that is not part of the longest common subsequence.
# #traverse_sequences will advance that arrow and will call
# <tt>callbacks#discard_a</tt> or <tt>callbacks#discard_b</tt>,
# depending on which arrow it advanced.
#
# === Changes
# If both +a+ and +b+ point to elements that are not part of the longest
# common subsequence, then #traverse_sequences will try to call
# <tt>callbacks#change</tt> and advance both arrows. If
# <tt>callbacks#change</tt> is not implemented, then
# <tt>callbacks#discard_a</tt> and <tt>callbacks#discard_b</tt> will be
# called in turn.
#
# The methods for <tt>callbacks#match</tt>, <tt>callbacks#discard_a</tt>,
# <tt>callbacks#discard_b</tt>, and <tt>callbacks#change</tt> are
# invoked with an event comprising the action ("=", "+", "-", or "!",
# respectively), the indicies +ii+ and +jj+, and the elements
# <tt>A[ii]</tt> and <tt>B[jj]</tt>. Return values are discarded by
# #traverse_balanced.
#
# === Context
# Note that +ii+ and +jj+ may not be the same index position, even if
# +a+ and +b+ are considered to be pointing to matching or changed
# elements.
def traverse_balanced(seq1, seq2, callbacks = Diff::LCS::BalancedCallbacks)
matches = Diff::LCS.__lcs(seq1, seq2)
a_size = seq1.size
b_size = seq2.size
ai = bj = mb = 0
ma = -1
string = seq1.kind_of?(String)
# Process all the lines in the match vector.
loop do
# Find next match indices +ma+ and +mb+
loop do
ma += 1
break unless ma < matches.size and matches[ma].nil?
end
break if ma >= matches.size # end of matches?
mb = matches[ma]
# Change(seq2)
while (ai < ma) or (bj < mb)
ax = string ? seq1[ai, 1] : seq1[ai]
bx = string ? seq2[bj, 1] : seq2[bj]
case [(ai < ma), (bj < mb)]
when [true, true]
if callbacks.respond_to?(:change)
event = Diff::LCS::ContextChange.new('!', ai, ax, bj, bx)
event = yield event if block_given?
callbacks.change(event)
ai += 1
bj += 1
else
event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
event = yield event if block_given?
callbacks.discard_a(event)
ai += 1
ax = string ? seq1[ai, 1] : seq1[ai]
event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx)
event = yield event if block_given?
callbacks.discard_b(event)
bj += 1
end
when [true, false]
event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
event = yield event if block_given?
callbacks.discard_a(event)
ai += 1
when [false, true]
event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx)
event = yield event if block_given?
callbacks.discard_b(event)
bj += 1
end
end
# Match
ax = string ? seq1[ai, 1] : seq1[ai]
bx = string ? seq2[bj, 1] : seq2[bj]
event = Diff::LCS::ContextChange.new('=', ai, ax, bj, bx)
event = yield event if block_given?
callbacks.match(event)
ai += 1
bj += 1
end
while (ai < a_size) or (bj < b_size)
ax = string ? seq1[ai, 1] : seq1[ai]
bx = string ? seq2[bj, 1] : seq2[bj]
case [(ai < a_size), (bj < b_size)]
when [true, true]
if callbacks.respond_to?(:change)
event = Diff::LCS::ContextChange.new('!', ai, ax, bj, bx)
event = yield event if block_given?
callbacks.change(event)
ai += 1
bj += 1
else
event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
event = yield event if block_given?
callbacks.discard_a(event)
ai += 1
ax = string ? seq1[ai, 1] : seq1[ai]
event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx)
event = yield event if block_given?
callbacks.discard_b(event)
bj += 1
end
when [true, false]
event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
event = yield event if block_given?
callbacks.discard_a(event)
ai += 1
when [false, true]
event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx)
event = yield event if block_given?
callbacks.discard_b(event)
bj += 1
end
end
end
PATCH_MAP = { #:nodoc:
:patch => { '+' => '+', '-' => '-', '!' => '!', '=' => '=' },
:unpatch => { '+' => '-', '-' => '+', '!' => '!', '=' => '=' }
}
# Given a patchset, convert the current version to the new
# version. If +direction+ is not specified (must be
# <tt>:patch</tt> or <tt>:unpatch</tt>), then discovery of the
# direction of the patch will be attempted.
def patch(src, patchset, direction = nil)
string = src.kind_of?(String)
# Start with a new empty type of the source's class
res = src.class.new
# Normalize the patchset.
patchset = __normalize_patchset(patchset)
direction ||= Diff::LCS.__diff_direction(src, patchset)
direction ||= :patch
ai = bj = 0
patchset.each do |change|
# Both Change and ContextChange support #action
action = PATCH_MAP[direction][change.action]
case change
when Diff::LCS::ContextChange
case direction
when :patch
el = change.new_element
op = change.old_position
np = change.new_position
when :unpatch
el = change.old_element
op = change.new_position
np = change.old_position
end
case action
when '-' # Remove details from the old string
while ai < op
res << (string ? src[ai, 1] : src[ai])
ai += 1
bj += 1
end
ai += 1
when '+'
while bj < np
res << (string ? src[ai, 1] : src[ai])
ai += 1
bj += 1
end
res << el
bj += 1
when '='
# This only appears in sdiff output with the SDiff callback.
# Therefore, we only need to worry about dealing with a single
# element.
res << el
ai += 1
bj += 1
when '!'
while ai < op
res << (string ? src[ai, 1] : src[ai])
ai += 1
bj += 1
end
bj += 1
ai += 1
res << el
end
when Diff::LCS::Change
case action
when '-'
while ai < change.position
res << (string ? src[ai, 1] : src[ai])
ai += 1
bj += 1
end
ai += 1
when '+'
while bj < change.position
res << (string ? src[ai, 1] : src[ai])
ai += 1
bj += 1
end
bj += 1
res << change.element
end
end
end
while ai < src.size
res << (string ? src[ai, 1] : src[ai])
ai += 1
bj += 1
end
res
end
# Given a set of patchset, convert the current version to the prior
# version. Does no auto-discovery.
def unpatch!(src, patchset)
Diff::LCS.patch(src, patchset, :unpatch)
end
# Given a set of patchset, convert the current version to the next
# version. Does no auto-discovery.
def patch!(src, patchset)
Diff::LCS.patch(src, patchset, :patch)
end
# private
# Compute the longest common subsequence between the sequenced
# Enumerables +a+ and +b+. The result is an array whose contents is such
# that
#
# result = Diff::LCS.__lcs(a, b)
# result.each_with_index do |e, ii|
# assert_equal(a[ii], b[e]) unless e.nil?
# end
#
# Note: This will be deprecated as a public function in a future release.
def __lcs(a, b)
a_start = b_start = 0
a_finish = a.size - 1
b_finish = b.size - 1
vector = []
# Prune off any common elements at the beginning...
while (a_start <= a_finish) and
(b_start <= b_finish) and
(a[a_start] == b[b_start])
vector[a_start] = b_start
a_start += 1
b_start += 1
end
# Now the end...
while (a_start <= a_finish) and
(b_start <= b_finish) and
(a[a_finish] == b[b_finish])
vector[a_finish] = b_finish
a_finish -= 1
b_finish -= 1
end
# Now, compute the equivalence classes of positions of elements.
b_matches = Diff::LCS.__position_hash(b, b_start .. b_finish)
thresh = []
links = []
(a_start .. a_finish).each do |ii|
ai = a.kind_of?(String) ? a[ii, 1] : a[ii]
bm = b_matches[ai]
kk = nil
bm.reverse_each do |jj|
if kk and (thresh[kk] > jj) and (thresh[kk - 1] < jj)
thresh[kk] = jj
else
kk = Diff::LCS.__replace_next_larger(thresh, jj, kk)
end
links[kk] = [ (kk > 0) ? links[kk - 1] : nil, ii, jj ] unless kk.nil?
end
end
unless thresh.empty?
link = links[thresh.size - 1]
while not link.nil?
vector[link[1]] = link[2]
link = link[0]
end
end
vector
end
# Find the place at which +value+ would normally be inserted into the
# Enumerable. If that place is already occupied by +value+, do nothing
# and return +nil+. If the place does not exist (i.e., it is off the end
# of the Enumerable), add it to the end. Otherwise, replace the element
# at that point with +value+. It is assumed that the Enumerable's values
# are numeric.
#
# This operation preserves the sort order.
#
# Note: This will be deprecated as a public function in a future release.
def __replace_next_larger(enum, value, last_index = nil)
# Off the end?
if enum.empty? or (value > enum[-1])
enum << value
return enum.size - 1
end
# Binary search for the insertion point
last_index ||= enum.size
first_index = 0
while (first_index <= last_index)
ii = (first_index + last_index) >> 1
found = enum[ii]
if value == found
return nil
elsif value > found
first_index = ii + 1
else
last_index = ii - 1
end
end
# The insertion point is in first_index; overwrite the next larger
# value.
enum[first_index] = value
return first_index
end
# If +vector+ maps the matching elements of another collection onto this
# Enumerable, compute the inverse +vector+ that maps this Enumerable
# onto the collection. (Currently unused.)
#
# Note: This will be deprecated as a public function in a future release.
def __inverse_vector(a, vector)
inverse = a.dup
(0 ... vector.size).each do |ii|
inverse[vector[ii]] = ii unless vector[ii].nil?
end
inverse
end
# Returns a hash mapping each element of an Enumerable to the set of
# positions it occupies in the Enumerable, optionally restricted to the
# elements specified in the range of indexes specified by +interval+.
#
# Note: This will be deprecated as a public function in a future release.
def __position_hash(enum, interval = 0 .. -1)
hash = Hash.new { |hh, kk| hh[kk] = [] }
interval.each do |ii|
kk = enum.kind_of?(String) ? enum[ii, 1] : enum[ii]
hash[kk] << ii
end
hash
end
# Examine the patchset and the source to see in which direction the
# patch should be applied.
#
# WARNING: By default, this examines the whole patch, so this could take
# some time. This also works better with Diff::LCS::ContextChange or
# Diff::LCS::Change as its source, as an array will cause the creation
# of one of the above.
#
# Note: This will be deprecated as a public function in a future release.
def __diff_direction(src, patchset, limit = nil)
count = left = left_miss = right = right_miss = 0
string = src.kind_of?(String)
patchset.each do |change|
count += 1
case change
when Diff::LCS::Change
# With a simplistic change, we can't tell the difference between
# the left and right on '!' actions, so we ignore those. On '='
# actions, if there's a miss, we miss both left and right.
element = string ? src[change.position, 1] : src[change.position]
case change.action
when '-'
if element == change.element
left += 1
else
left_miss += 1
end
when '+'
if element == change.element
right += 1
else
right_miss += 1
end
when '='
if element != change.element
left_miss += 1
right_miss += 1
end
end
when Diff::LCS::ContextChange
case change.action
when '-' # Remove details from the old string
element = string ? src[change.old_position, 1] : src[change.old_position]
if element == change.old_element
left += 1
else
left_miss += 1
end
when '+'
element = string ? src[change.new_position, 1] : src[change.new_position]
if element == change.new_element
right += 1
else
right_miss += 1
end
when '='
le = string ? src[change.old_position, 1] : src[change.old_position]
re = string ? src[change.new_position, 1] : src[change.new_position]
left_miss += 1 if le != change.old_element
right_miss += 1 if re != change.new_element
when '!'
element = string ? src[change.old_position, 1] : src[change.old_position]
if element == change.old_element
left += 1
else
element = string ? src[change.new_position, 1] : src[change.new_position]
if element == change.new_element
right += 1
else
left_miss += 1
right_miss += 1
end
end
end
end
break if (not limit.nil?) && (count > limit)
end
no_left = (left == 0) and (left_miss >= 0)
no_right = (right == 0) and (right_miss >= 0)
case [no_left, no_right]
when [false, true]
return :patch
when [true, false]
return :unpatch
else
raise "The provided patchset does not appear to apply to the provided value as either source or destination value."
end
end
# Normalize the patchset. A patchset is always a sequence of changes, but
# how those changes are represented may vary, depending on how they were
# generated. In all cases we support, we also support the array
# representation of the changes. The formats are:
#
# [ # patchset <- Diff::LCS.diff(a, b)
# [ # one or more hunks
# Diff::LCS::Change # one or more changes
# ] ]
#
# [ # patchset, equivalent to the above
# [ # one or more hunks
# [ action, line, value ] # one or more changes
# ] ]
#
# [ # patchset <- Diff::LCS.diff(a, b, Diff::LCS::ContextDiffCallbacks)
# # OR <- Diff::LCS.sdiff(a, b, Diff::LCS::ContextDiffCallbacks)
# [ # one or more hunks
# Diff::LCS::ContextChange # one or more changes
# ] ]
#
# [ # patchset, equivalent to the above
# [ # one or more hunks
# [ action, [ old line, old value ], [ new line, new value ] ]
# # one or more changes
# ] ]
#
# [ # patchset <- Diff::LCS.sdiff(a, b)
# # OR <- Diff::LCS.diff(a, b, Diff::LCS::SDiffCallbacks)
# Diff::LCS::ContextChange # one or more changes
# ]
#
# [ # patchset, equivalent to the above
# [ action, [ old line, old value ], [ new line, new value ] ]
# # one or more changes
# ]
#
# The result of this will be either of the following.
#
# [ # patchset
# Diff::LCS::ContextChange # one or more changes
# ]
#
# [ # patchset
# Diff::LCS::Change # one or more changes
# ]
#
# If either of the above is provided, it will be returned as such.
#
# Note: This will be deprecated as a public function in a future release.
def __normalize_patchset(patchset)
patchset.map do |hunk|
case hunk
when Diff::LCS::ContextChange, Diff::LCS::Change
hunk
when Array
if (not hunk[0].kind_of?(Array)) and hunk[1].kind_of?(Array) and hunk[2].kind_of?(Array)
Diff::LCS::ContextChange.from_a(hunk)
else
hunk.map do |change|
case change
when Diff::LCS::ContextChange, Diff::LCS::Change
change
when Array
# change[1] will ONLY be an array in a ContextChange#to_a call.
# In Change#to_a, it represents the line (singular).
if change[1].kind_of?(Array)
Diff::LCS::ContextChange.from_a(change)
else
Diff::LCS::Change.from_a(change)
end
end
end
end
else
raise ArgumentError, "Cannot normalise a hunk of class #{hunk.class}."
end
end.flatten
end
end
end
# vim: ft=ruby
# -*- ruby encoding: utf-8 -*-
require 'spec_helper'
describe "Diff::LCS.LCS and Diff::LCS.__lcs" do
include Diff::LCS::SpecHelper::Matchers
it "should return the correct raw values from Diff::LCS.__lcs" do
res = Diff::LCS.__lcs(seq1, seq2)
# The result of the LCS (less the +nil+ values) must be as long as the
# correct result.
res.compact.size.should == correct_lcs.size
res.should correctly_map_sequence(seq1).to_other_sequence(seq2)
# Compact these transformations and they should be the correct LCS.
x_seq1 = (0...res.size).map { |ix| res[ix] ? seq1[ix] : nil }.compact
x_seq2 = (0...res.size).map { |ix| res[ix] ? seq2[res[ix]] : nil }.compact
x_seq1.should == correct_lcs
x_seq2.should == correct_lcs
end
it "should return the correct compacted values from Diff::LCS.LCS" do
res = Diff::LCS.LCS(seq1, seq2)
res.should == correct_lcs
res.compact.should == res
end
it "should be transitive" do
res = Diff::LCS.LCS(seq2, seq1)
res.should == correct_lcs
res.compact.should == res
end
end
# vim: ft=ruby
#!/bin/sh
'exec' "ruby" '-x' "$0" "$@"
#!/usr/local/rvm/rubies/ruby-1.9.2-p290/bin/ruby -w
#
# This file was generated by RubyGems.
#
# The application 'diff-lcs' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'rubygems'
version = ">= 0"
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
version = $1
ARGV.shift
end
gem 'diff-lcs', version
load Gem.bin_path('diff-lcs', 'ldiff', version)
#!ruby -w
require 'diff/lcs'
require 'diff/lcs/ldiff'
exit Diff::LCS::Ldiff.run(ARGV)
#!/usr/bin/env ruby
require 'optparse'
require 'ostruct'
require 'diff/lcs/hunk'
# == ldiff Usage
# ldiff [options] oldfile newfile
#
# -c:: Displays a context diff with 3 lines of context.
# -C [LINES], --context [LINES]:: Displays a context diff with LINES lines of context. Default 3 lines.
# -u:: Displays a unified diff with 3 lines of context.
# -U [LINES], --unified [LINES]:: Displays a unified diff with LINES lines of context. Default 3 lines.
# -e:: Creates an 'ed' script to change oldfile to newfile.
# -f:: Creates an 'ed' script to change oldfile to newfile in reverse order.
# -a, --text:: Treats the files as text and compares them line-by-line, even if they do not seem to be text.
# --binary:: Treats the files as binary.
# -q, --brief:: Reports only whether or not the files differ, not the details.
# --help:: Shows the command-line help.
# --version:: Shows the version of Diff::LCS.
#
# By default, runs produces an "old-style" diff, with output like UNIX diff.
#
# == Copyright
# Copyright &copy; 2004 Austin Ziegler
#
# Part of Diff::LCS <http://rubyforge.org/projects/ruwiki/>
# Austin Ziegler <diff-lcs@halostatue.ca>
#
# This program is free software. It may be redistributed and/or modified under
# the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
# Ruby licence.
module Diff::LCS::Ldiff
BANNER = <<-COPYRIGHT
ldiff #{Diff::LCS::VERSION}
Copyright 2004-2011 Austin Ziegler
Part of Diff::LCS.
http://rubyforge.org/projects/ruwiki/
Austin Ziegler <diff-lcs@halostatue.ca>
This program is free software. It may be redistributed and/or modified under
the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
MIT licence.
$Id$
COPYRIGHT
class << self
attr_reader :format, :lines #:nodoc:
attr_reader :file_old, :file_new #:nodoc:
attr_reader :data_old, :data_new #:nodoc:
def run(args, input = $stdin, output = $stdout, error = $stderr) #:nodoc:
@binary = nil
args.options do |o|
o.banner = "Usage: #{File.basename($0)} [options] oldfile newfile"
o.separator ""
o.on('-c', 'Displays a context diff with 3 lines of', 'context.') do |ctx|
@format = :context
@lines = 3
end
o.on('-C', '--context [LINES]', Numeric, 'Displays a context diff with LINES lines', 'of context. Default 3 lines.') do |ctx|
@format = :context
@lines = ctx || 3
end
o.on('-u', 'Displays a unified diff with 3 lines of', 'context.') do |ctx|
@format = :unified
@lines = 3
end
o.on('-U', '--unified [LINES]', Numeric, 'Displays a unified diff with LINES lines', 'of context. Default 3 lines.') do |ctx|
@format = :unified
@lines = ctx || 3
end
o.on('-e', 'Creates an \'ed\' script to change', 'oldfile to newfile.') do |ctx|
@format = :ed
end
o.on('-f', 'Creates an \'ed\' script to change', 'oldfile to newfile in reverse order.') do |ctx|
@format = :reverse_ed
end
o.on('-a', '--text', 'Treat the files as text and compare them', 'line-by-line, even if they do not seem', 'to be text.') do |txt|
@binary = false
end
o.on('--binary', 'Treats the files as binary.') do |bin|
@binary = true
end
o.on('-q', '--brief', 'Report only whether or not the files', 'differ, not the details.') do |ctx|
@format = :report
end
o.on_tail('--help', 'Shows this text.') do
error << o
return 0
end
o.on_tail('--version', 'Shows the version of Diff::LCS.') do
error << BANNER
return 0
end
o.on_tail ""
o.on_tail 'By default, runs produces an "old-style" diff, with output like UNIX diff.'
o.parse!
end
unless args.size == 2
error << args.options
return 127
end
# Defaults are for old-style diff
@format ||= :old
@lines ||= 0
file_old, file_new = *ARGV
case @format
when :context
char_old = '*' * 3
char_new = '-' * 3
when :unified
char_old = '-' * 3
char_new = '+' * 3
end
# After we've read up to a certain point in each file, the number of
# items we've read from each file will differ by FLD (could be 0).
file_length_difference = 0
if @binary.nil? or @binary
data_old = IO::read(file_old)
data_new = IO::read(file_new)
# Test binary status
if @binary.nil?
old_txt = data_old[0...4096].grep(/\0/).empty?
new_txt = data_new[0...4096].grep(/\0/).empty?
@binary = (not old_txt) or (not new_txt)
old_txt = new_txt = nil
end
unless @binary
data_old = data_old.split(/\n/).map! { |e| e.chomp }
data_new = data_new.split(/\n/).map! { |e| e.chomp }
end
else
data_old = IO::readlines(file_old).map! { |e| e.chomp }
data_new = IO::readlines(file_new).map! { |e| e.chomp }
end
# diff yields lots of pieces, each of which is basically a Block object
if @binary
diffs = (data_old == data_new)
else
diffs = Diff::LCS.diff(data_old, data_new)
diffs = nil if diffs.empty?
end
return 0 unless diffs
if (@format == :report) and diffs
output << "Files #{file_old} and #{file_new} differ\n"
return 1
end
if (@format == :unified) or (@format == :context)
ft = File.stat(file_old).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S %z')
puts "#{char_old} #{file_old}\t#{ft}"
ft = File.stat(file_new).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S %z')
puts "#{char_new} #{file_new}\t#{ft}"
end
# Loop over hunks. If a hunk overlaps with the last hunk, join them.
# Otherwise, print out the old one.
oldhunk = hunk = nil
if @format == :ed
real_output = output
output = []
end
diffs.each do |piece|
begin
hunk = Diff::LCS::Hunk.new(data_old, data_new, piece, @lines,
file_length_difference)
file_length_difference = hunk.file_length_difference
next unless oldhunk
if (@lines > 0) and hunk.overlaps?(oldhunk)
hunk.unshift(oldhunk)
else
output << oldhunk.diff(@format)
end
ensure
oldhunk = hunk
output << "\n"
end
end
output << oldhunk.diff(@format)
output << "\n"
if @format == :ed
output.reverse_each { |e| real_output << e.diff(:ed_finish) }
end
return 1
end
end
end
Feature: let and let!
Use `let` to define a memoized helper method. The value will be cached
across multiple calls in the same example but not across examples.
Note that `let` is lazy-evaluated: it is not evaluated until the first time
the method it defines is invoked. You can use `let!` to force the method's
invocation before each example.
Scenario: use let to define memoized helper method
Given a file named "let_spec.rb" with:
"""
$count = 0
describe "let" do
let(:count) { $count += 1 }
it "memoizes the value" do
count.should eq(1)
count.should eq(1)
end
it "is not cached across examples" do
count.should eq(2)
end
end
"""
When I run `rspec let_spec.rb`
Then the examples should all pass
Scenario: use let! to define a memoized helper method that is called in a before hook
Given a file named "let_bang_spec.rb" with:
"""
$count = 0
describe "let!" do
invocation_order = []
let!(:count) do
invocation_order << :let!
$count += 1
end
it "calls the helper method in a before hook" do
invocation_order << :example
invocation_order.should == [:let!, :example]
count.should eq(1)
end
end
"""
When I run `rspec let_bang_spec.rb`
Then the examples should all pass
module RSpec
module Core
module Let
module ExampleGroupMethods
# Generates a method whose return value is memoized
# after the first call.
#
# @example
#
# describe Thing do
# let(:thing) { Thing.new }
#
# it "does something" do
# # first invocation, executes block, memoizes and returns result
# thing.do_something
#
# # second invocation, returns the memoized value
# thing.should be_something
# end
# end
def let(name, &block)
define_method(name) do
__memoized.fetch(name) {|k| __memoized[k] = instance_eval(&block) }
end
end
# Just like <tt>let()</tt>, except the block is invoked
# by an implicit <tt>before</tt> hook. This serves a dual
# purpose of setting up state and providing a memoized
# reference to that state.
#
# @example
#
# class Thing
# def self.count
# @count ||= 0
# end
#
# def self.count=(val)
# @count += val
# end
#
# def self.reset_count
# @count = 0
# end
#
# def initialize
# self.class.count += 1
# end
# end
#
# describe Thing do
# after(:each) { Thing.reset_count }
#
# context "using let" do
# let(:thing) { Thing.new }
#
# it "is not invoked implicitly" do
# Thing.count.should eq(0)
# end
#
# it "can be invoked explicitly" do
# thing
# Thing.count.should eq(1)
# end
# end
#
# context "using let!" do
# let!(:thing) { Thing.new }
#
# it "is invoked implicitly" do
# Thing.count.should eq(1)
# end
#
# it "returns memoized version on first invocation" do
# thing
# Thing.count.should eq(1)
# end
# end
# end
def let!(name, &block)
let(name, &block)
before { __send__(name) }
end
end
# @private
module ExampleMethods
# @private
def __memoized
@__memoized ||= {}
end
end
def self.included(mod)
mod.extend ExampleGroupMethods
mod.__send__ :include, ExampleMethods
end
end
end
end

License

This software is available under three licenses: the GNU GPL version 2 (or at your option, a later version), the Perl Artistic license, or the MIT license. Note that my preference for licensing is the MIT license, but Algorithm::Diff was dually originally licensed with the Perl Artistic and the GNU GPL (“the same terms as Perl itself”) and that the Ruby implementation hews pretty closely to the Perl version, so I must maintain the additional licensing terms.

  • Copyright 2004–2011 Austin Ziegler.

  • Adapted from Algorithm::Diff (Perl) by Ned Konz and a Smalltalk versionby Mario I. Wolczko <mario@wolczko.com>

MIT License

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Perl Artistic License (version 2)

See the file docs/artistic.txt in the main distribution.

GNU GPL version 2

See the file docs/COPYING.txt in the main distribution.

(The MIT License)
Copyright (c) 2009 Chad Humphries, David Chelimsky
Copyright (c) 2006 David Chelimsky, The RSpec Development Team
Copyright (c) 2005 Steven Baker
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
(The MIT License)
Copyright (c) 2009 Chad Humphries, David Chelimsky
Copyright (c) 2006 David Chelimsky, The RSpec Development Team
Copyright (c) 2005 Steven Baker
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Feature: line number appended to file path
To run one or more examples or groups, you can append the line number to the path, e.g.
rspec path/to/example_spec.rb:37
Background:
Given a file named "example_spec.rb" with:
"""
describe "outer group" do
it "first example in outer group" do
end
it "second example in outer group" do
end
describe "nested group" do
it "example in nested group" do
end
end
end
"""
And a file named "example2_spec.rb" with:
"""
describe "yet another group" do
it "first example in second file" do
end
it "second example in second file" do
end
end
"""
Scenario: nested groups - outer group on declaration line
When I run `rspec example_spec.rb:1 --format doc`
Then the examples should all pass
And the output should contain "second example in outer group"
And the output should contain "first example in outer group"
And the output should contain "example in nested group"
Scenario: nested groups - outer group inside block before example
When I run `rspec example_spec.rb:2 --format doc`
Then the examples should all pass
And the output should contain "second example in outer group"
And the output should contain "first example in outer group"
And the output should contain "example in nested group"
Scenario: nested groups - inner group on declaration line
When I run `rspec example_spec.rb:11 --format doc`
Then the examples should all pass
And the output should contain "example in nested group"
And the output should not contain "second example in outer group"
And the output should not contain "first example in outer group"
Scenario: nested groups - inner group inside block before example
When I run `rspec example_spec.rb:12 --format doc`
Then the examples should all pass
And the output should contain "example in nested group"
And the output should not contain "second example in outer group"
And the output should not contain "first example in outer group"
Scenario: two examples - first example on declaration line
When I run `rspec example_spec.rb:3 --format doc`
Then the examples should all pass
And the output should contain "first example in outer group"
But the output should not contain "second example in outer group"
And the output should not contain "example in nested group"
Scenario: two examples - first example inside block
When I run `rspec example_spec.rb:4 --format doc`
Then the examples should all pass
And the output should contain "first example in outer group"
But the output should not contain "second example in outer group"
And the output should not contain "example in nested group"
Scenario: two examples - first example on end
When I run `rspec example_spec.rb:5 --format doc`
Then the examples should all pass
And the output should contain "first example in outer group"
But the output should not contain "second example in outer group"
And the output should not contain "example in nested group"
Scenario: two examples - first example after end but before next example
When I run `rspec example_spec.rb:6 --format doc`
Then the examples should all pass
And the output should contain "first example in outer group"
But the output should not contain "second example in outer group"
And the output should not contain "example in nested group"
Scenario: two examples - second example on declaration line
When I run `rspec example_spec.rb:7 --format doc`
Then the examples should all pass
And the output should contain "second example in outer group"
But the output should not contain "first example in outer group"
And the output should not contain "example in nested group"
Scenario: two examples - second example inside block
When I run `rspec example_spec.rb:7 --format doc`
Then the examples should all pass
And the output should contain "second example in outer group"
But the output should not contain "first example in outer group"
And the output should not contain "example in nested group"
Scenario: two examples - second example on end
When I run `rspec example_spec.rb:7 --format doc`
Then the examples should all pass
And the output should contain "second example in outer group"
But the output should not contain "first example in outer group"
And the output should not contain "example in nested group"
Scenario: specified multiple times for different files
When I run `rspec example_spec.rb:7 example2_spec.rb:4 --format doc`
Then the examples should all pass
And the output should contain "second example in outer group"
And the output should contain "second example in second file"
But the output should not contain "first example in outer group"
And the output should not contain "nested group"
And the output should not contain "first example in second file"
Scenario: specified multiple times for the same file with multiple arguments
When I run `rspec example_spec.rb:7 example_spec.rb:11 --format doc`
Then the examples should all pass
And the output should contain "second example in outer group"
And the output should contain "nested group"
But the output should not contain "first example in outer group"
And the output should not contain "second file"
Scenario: specified multiple times for the same file with a single argument
When I run `rspec example_spec.rb:7:11 --format doc`
Then the examples should all pass
And the output should contain "second example in outer group"
And the output should contain "nested group"
But the output should not contain "first example in outer group"
And the output should not contain "second file"
Feature: --line_number option
To run a examples or groups by line numbers, one can use the --line_number option:
rspec path/to/example_spec.rb --line_number 37
This option can be specified multiple times.
Scenario: standard examples
Given a file named "example_spec.rb" with:
"""
require "rspec/expectations"
describe 9 do
it "should be > 8" do
9.should be > 8
end
it "should be < 10" do
9.should be < 10
end
it "should be 3 squared" do
9.should be 3*3
end
end
"""
When I run `rspec example_spec.rb --line_number 5 --format doc`
Then the examples should all pass
And the output should contain "should be > 8"
But the output should not contain "should be < 10"
And the output should not contain "should be 3*3"
When I run `rspec example_spec.rb --line_number 5 --line_number 9 --format doc`
Then the examples should all pass
And the output should contain "should be > 8"
And the output should contain "should be < 10"
But the output should not contain "should be 3*3"
Scenario: one liner
Given a file named "example_spec.rb" with:
"""
require "rspec/expectations"
describe 9 do
it { should be > 8 }
it { should be < 10 }
end
"""
When I run `rspec example_spec.rb --line_number 5 --format doc`
Then the examples should all pass
Then the output should contain "should be > 8"
But the output should not contain "should be < 10"
require 'rspec/core/ruby_project'
RSpec::Core::RubyProject.add_to_load_path('lib', 'spec')
History.rdoc
License.rdoc
Manifest.txt
README.rdoc
Rakefile
bin/htmldiff
bin/ldiff
diff-lcs.gemspec
docs/COPYING.txt
docs/artistic.html
lib/diff-lcs.rb
lib/diff/lcs.rb
lib/diff/lcs/array.rb
lib/diff/lcs/block.rb
lib/diff/lcs/callbacks.rb
lib/diff/lcs/change.rb
lib/diff/lcs/htmldiff.rb
lib/diff/lcs/hunk.rb
lib/diff/lcs/ldiff.rb
lib/diff/lcs/string.rb
spec/diff_spec.rb
spec/lcs_spec.rb
spec/patch_spec.rb
spec/sdiff_spec.rb
spec/spec_helper.rb
spec/traverse_balanced_spec.rb
spec/traverse_sequences_spec.rb
module RSpec
module Core
# Each ExampleGroup class and Example instance owns an instance of
# Metadata, which is Hash extended to support lazy evaluation of values
# associated with keys that may or may not be used by any example or group.
#
# In addition to metadata that is used internally, this also stores
# user-supplied metadata, e.g.
#
# describe Something, :type => :ui do
# it "does something", :slow => true do
# # ...
# end
# end
#
# `:type => :ui` is stored in the Metadata owned by the example group, and
# `:slow => true` is stored in the Metadata owned by the example. These can
# then be used to select which examples are run using the `--tag` option on
# the command line, or several methods on `Configuration` used to filter a
# run (e.g. `filter_run_including`, `filter_run_excluding`, etc).
#
# @see Example#metadata
# @see ExampleGroup.metadata
# @see FilterManager
# @see Configuration#filter_run_including
# @see Configuration#filter_run_excluding
class Metadata < Hash
# @private
module MetadataHash
# @private
# Supports lazy evaluation of some values. Extended by
# ExampleMetadataHash and GroupMetadataHash, which get mixed in to
# Metadata for ExampleGroups and Examples (respectively).
def [](key)
return super if has_key?(key)
case key
when :location
store(:location, location)
when :file_path, :line_number
file_path, line_number = file_and_line_number
store(:file_path, file_path)
store(:line_number, line_number)
super
when :execution_result
store(:execution_result, {})
when :describes, :described_class
klass = described_class
store(:described_class, klass)
# TODO (2011-11-07 DC) deprecate :describes as a key
store(:describes, klass)
when :full_description
store(:full_description, full_description)
when :description
store(:description, build_description_from(*self[:description_args]))
else
super
end
end
private
def location
"#{self[:file_path]}:#{self[:line_number]}"
end
def file_and_line_number
first_caller_from_outside_rspec =~ /(.+?):(\d+)(|:\d+)/
return [$1, $2.to_i]
end
def first_caller_from_outside_rspec
self[:caller].detect {|l| l !~ /\/lib\/rspec\/core/}
end
def build_description_from(*parts)
parts.map {|p| p.to_s}.inject do |desc, p|
p =~ /^(#|::|\.)/ ? "#{desc}#{p}" : "#{desc} #{p}"
end || ""
end
end
# Mixed in to Metadata for an Example (extends MetadataHash) to support
# lazy evaluation of some values.
module ExampleMetadataHash
include MetadataHash
def described_class
self[:example_group].described_class
end
def full_description
build_description_from(self[:example_group][:full_description], *self[:description_args])
end
end
# Mixed in to Metadata for an ExampleGroup (extends MetadataHash) to
# support lazy evaluation of some values.
module GroupMetadataHash
include MetadataHash
def described_class
container_stack.each do |g|
return g[:describes] if g.has_key?(:describes)
return g[:described_class] if g.has_key?(:described_class)
end
container_stack.reverse.each do |g|
candidate = g[:description_args].first
return candidate unless String === candidate || Symbol === candidate
end
nil
end
def full_description
build_description_from(*container_stack.reverse.map {|a| a[:description_args]}.flatten)
end
def container_stack
@container_stack ||= begin
groups = [group = self]
while group.has_key?(:example_group)
groups << group[:example_group]
group = group[:example_group]
end
groups
end
end
end
def initialize(parent_group_metadata=nil)
if parent_group_metadata
update(parent_group_metadata)
store(:example_group, {:example_group => parent_group_metadata[:example_group].extend(GroupMetadataHash)}.extend(GroupMetadataHash))
else
store(:example_group, {}.extend(GroupMetadataHash))
end
yield self if block_given?
end
# @private
def process(*args)
user_metadata = args.last.is_a?(Hash) ? args.pop : {}
ensure_valid_keys(user_metadata)
self[:example_group].store(:description_args, args)
self[:example_group].store(:caller, user_metadata.delete(:caller) || caller)
update(user_metadata)
end
# @private
def for_example(description, user_metadata)
dup.extend(ExampleMetadataHash).configure_for_example(description, user_metadata)
end
# @private
def any_apply?(filters)
filters.any? {|k,v| filter_applies?(k,v)}
end
# @private
def all_apply?(filters)
filters.all? {|k,v| filter_applies?(k,v)}
end
# @private
def filter_applies?(key, value, metadata=self)
return metadata.filter_applies_to_any_value?(key, value) if Array === metadata[key] && !(Proc === value)
return metadata.line_number_filter_applies?(value) if key == :line_numbers
return metadata.location_filter_applies?(value) if key == :locations
return metadata.filters_apply?(key, value) if Hash === value
case value
when Regexp
metadata[key] =~ value
when Proc
if value.arity == 2
# Pass the metadata hash to allow the proc to check if it even has the key.
# This is necessary for the implicit :if exclusion filter:
# { } # => run the example
# { :if => nil } # => exclude the example
# The value of metadata[:if] is the same in these two cases but
# they need to be treated differently.
value.call(metadata[key], metadata) rescue false
else
value.call(metadata[key]) rescue false
end
else
metadata[key].to_s == value.to_s
end
end
# @private
def filters_apply?(key, value)
value.all? {|k, v| filter_applies?(k, v, self[key])}
end
# @private
def filter_applies_to_any_value?(key, value)
self[key].any? {|v| filter_applies?(key, v, {key => value})}
end
# @private
def location_filter_applies?(locations)
# it ignores location filters for other files
line_number = example_group_declaration_line(locations)
line_number ? line_number_filter_applies?(line_number) : true
end
# @private
def line_number_filter_applies?(line_numbers)
preceding_declaration_lines = line_numbers.map {|n| RSpec.world.preceding_declaration_line(n)}
!(relevant_line_numbers & preceding_declaration_lines).empty?
end
protected
def configure_for_example(description, user_metadata)
store(:description_args, [description])
store(:caller, user_metadata.delete(:caller) || caller)
update(user_metadata)
end
private
RESERVED_KEYS = [
:description,
:example_group,
:execution_result,
:file_path,
:full_description,
:line_number,
:location
]
def ensure_valid_keys(user_metadata)
RESERVED_KEYS.each do |key|
if user_metadata.has_key?(key)
raise <<-EOM
#{"*"*50}
:#{key} is not allowed
RSpec reserves some hash keys for its own internal use,
including :#{key}, which is used on:
#{caller(0)[4]}.
Here are all of RSpec's reserved hash keys:
#{RESERVED_KEYS.join("\n ")}
#{"*"*50}
EOM
end
end
end
def example_group_declaration_line(locations)
locations[File.expand_path(self[:example_group][:file_path])] if self[:example_group]
end
# TODO - make this a method on metadata - the problem is
# metadata[:example_group] is not always a kind of GroupMetadataHash.
def relevant_line_numbers(metadata=self)
[metadata[:line_number]] + (metadata[:example_group] ? relevant_line_numbers(metadata[:example_group]) : [])
end
end
end
end
module RSpec
module Core
# @private
module MetadataHashBuilder
# @private
module Common
def build_metadata_hash_from(args)
metadata = args.last.is_a?(Hash) ? args.pop : {}
if RSpec.configuration.treat_symbols_as_metadata_keys_with_true_values?
add_symbols_to_hash(metadata, args)
else
warn_about_symbol_usage(args)
end
metadata
end
private
def add_symbols_to_hash(hash, args)
while args.last.is_a?(Symbol)
hash[args.pop] = true
end
end
def warn_about_symbol_usage(args)
symbols = args.select { |a| a.is_a?(Symbol) }
return if symbols.empty?
Kernel.warn symbol_metadata_warning(symbols)
end
end
# @private
module WithConfigWarning
include Common
private
def symbol_metadata_warning(symbols)
<<-NOTICE
*****************************************************************
WARNING: You have passed symbols (#{symbols.inspect}) as metadata
arguments to a configuration option.
In RSpec 3, these symbols will be treated as metadata keys with
a value of `true`. To get this behavior now (and prevent this
warning), you can set a configuration option:
RSpec.configure do |c|
c.treat_symbols_as_metadata_keys_with_true_values = true
end
Note that this config setting should go before your other config
settings so that they can use symbols as metadata.
*****************************************************************
NOTICE
end
end
# @private
module WithDeprecationWarning
include Common
private
def symbol_metadata_warning(symbols)
<<-NOTICE
*****************************************************************
DEPRECATION WARNING: you are using deprecated behaviour that will
be removed from RSpec 3.
You have passed symbols (#{symbols.inspect}) as additional
arguments for a doc string.
In RSpec 3, these symbols will be treated as metadata keys with
a value of `true`. To get this behavior now (and prevent this
warning), you can set a configuration option:
RSpec.configure do |c|
c.treat_symbols_as_metadata_keys_with_true_values = true
end
Alternately, if your intention is to use the symbol as part of the
doc string (i.e. to specify a method name), you can change it to
a string such as "#method_name" to remove this warning.
*****************************************************************
NOTICE
end
end
end
end
end
module RSpec
module Core
module Extensions
# @private
module ModuleEvalWithArgs
include InstanceEvalWithArgs
# @private
#
# Used internally to support `module_exec` in Ruby 1.8.6.
def module_eval_with_args(*args, &block)
# ruby > 1.8.6
return module_exec(*args, &block) if respond_to?(:module_exec)
# If there are no args and the block doesn't expect any, there's no
# need to fake module_exec with our hack below.
# Notes:
# * lambda { }.arity # => -1
# * lambda { || }.arity # => 0
# * lambda { |*a| }.arity # => -1
return module_eval(&block) if block.arity < 1 && args.size.zero?
orig_singleton_methods = singleton_methods
instance_eval_with_args(*args, &block)
# The only difference between instance_eval and module_eval is static method defs.
# * `def foo` in instance_eval defines a singleton method on the instance
# * `def foo` in class/module_eval defines an instance method for the class/module
# Here we deal with this difference by defining an instance method for
# each new singleton method.
# This has the side effect of duplicating methods (all new class methods will
# become instance methods and vice versa), but I don't see a way around it...
(singleton_methods - orig_singleton_methods).each { |m| define_method(m, &method(m)) }
end
end
end
end
end
Feature: Define helper methods in a module
You can define helper methods in a module and include it in
your example groups using the `config.include` configuration
option. `config.extend` can be used to extend the module onto
your example groups so that the methods in the module are available
in the example groups themselves (but not in the actual examples).
You can also include or extend the module onto only certain example
groups by passing a metadata hash as the last argument. Only groups
that match the given metadata will include or extend the module.
If you set the `treat_symbols_as_metadata_keys_with_true_values` config option
to `true`, you can specify metadata using only symbols.
Background:
Given a file named "helpers.rb" with:
"""
module Helpers
def help
:available
end
end
"""
Scenario: include a module in all example groups
Given a file named "include_module_spec.rb" with:
"""
require './helpers'
RSpec.configure do |c|
c.include Helpers
end
describe "an example group" do
it "has access the helper methods defined in the module" do
help.should be(:available)
end
end
"""
When I run `rspec include_module_spec.rb`
Then the examples should all pass
Scenario: extend a module in all example groups
Given a file named "extend_module_spec.rb" with:
"""
require './helpers'
RSpec.configure do |c|
c.extend Helpers
end
describe "an example group" do
puts "Help is #{help}"
it "does not have access to the helper methods defined in the module" do
expect { help }.to raise_error(NameError)
end
end
"""
When I run `rspec extend_module_spec.rb`
Then the examples should all pass
And the output should contain "Help is available"
Scenario: include a module in only some example groups
Given a file named "include_module_in_some_groups_spec.rb" with:
"""
require './helpers'
RSpec.configure do |c|
c.include Helpers, :foo => :bar
end
describe "an example group with matching metadata", :foo => :bar do
it "has access the helper methods defined in the module" do
help.should be(:available)
end
end
describe "an example group without matching metadata" do
it "does not have access to the helper methods defined in the module" do
expect { help }.to raise_error(NameError)
end
end
"""
When I run `rspec include_module_in_some_groups_spec.rb`
Then the examples should all pass
Scenario: extend a module in only some example groups
Given a file named "extend_module_in_only_some_groups_spec.rb" with:
"""
require './helpers'
RSpec.configure do |c|
c.extend Helpers, :foo => :bar
end
describe "an example group with matching metadata", :foo => :bar do
puts "In a matching group, help is #{help}"
it "does not have access to the helper methods defined in the module" do
expect { help }.to raise_error(NameError)
end
end
describe "an example group without matching metadata" do
puts "In a non-matching group, help is #{help rescue 'not available'}"
it "does not have access to the helper methods defined in the module" do
expect { help }.to raise_error(NameError)
end
end
"""
When I run `rspec extend_module_in_only_some_groups_spec.rb`
Then the examples should all pass
And the output should contain "In a matching group, help is available"
And the output should contain "In a non-matching group, help is not available"
Scenario: use symbols as metadata
Given a file named "symbols_as_metadata_spec.rb" with:
"""
require './helpers'
RSpec.configure do |c|
c.treat_symbols_as_metadata_keys_with_true_values = true
c.include Helpers, :include_helpers
c.extend Helpers, :extend_helpers
end
describe "an example group with matching include metadata", :include_helpers do
puts "In a group not matching the extend filter, help is #{help rescue 'not available'}"
it "has access the helper methods defined in the module" do
help.should be(:available)
end
end
describe "an example group with matching extend metadata", :extend_helpers do
puts "In a group matching the extend filter, help is #{help}"
it "does not have access to the helper methods defined in the module" do
expect { help }.to raise_error(NameError)
end
end
"""
When I run `rspec symbols_as_metadata_spec.rb`
Then the examples should all pass
And the output should contain "In a group not matching the extend filter, help is not available"
And the output should contain "In a group matching the extend filter, help is available"
require_rspec 'monkey/spork/test_framework/rspec.rb'
# http://www.ruby-doc.org/stdlib/libdoc/optparse/rdoc/classes/OptionParser.html
require 'optparse'
module RSpec::Core
class Parser
def self.parse!(args)
new.parse!(args)
end
class << self
alias_method :parse, :parse!
end
def parse!(args)
return {} if args.empty?
if args.include?("--formatter")
RSpec.deprecate("the --formatter option", "-f or --format")
args[args.index("--formatter")] = "--format"
end
options = args.delete('--tty') ? {:tty => true} : {}
parser(options).parse!(args)
options
end
alias_method :parse, :parse!
def parser(options)
OptionParser.new do |parser|
parser.banner = "Usage: rspec [options] [files or directories]\n\n"
parser.on('-I PATH', 'Specify PATH to add to $LOAD_PATH (may be used more than once).') do |dir|
options[:libs] ||= []
options[:libs] << dir
end
parser.on('-r', '--require PATH', 'Require a file.') do |path|
options[:requires] ||= []
options[:requires] << path
end
parser.on('-O', '--options PATH', 'Specify the path to a custom options file.') do |path|
options[:custom_options_file] = path
end
parser.on('--order TYPE[:SEED]', 'Run examples by the specified order type.',
' [default] files are ordered based on the underlying file',
' system\'s order',
' [rand] randomize the order of files, groups and examples',
' [random] alias for rand',
' [random:SEED] e.g. --order random:123') do |o|
options[:order] = o
end
parser.on('--seed SEED', Integer, 'Equivalent of --order rand:SEED.') do |seed|
options[:order] = "rand:#{seed}"
end
parser.on('-d', '--debugger', 'Enable debugging.') do |o|
options[:debug] = true
end
parser.on('--fail-fast', 'Abort the run on first failure.') do |o|
options[:fail_fast] = true
end
parser.on('--failure-exit-code CODE', Integer, 'Override the exit code used when there are failing specs.') do |code|
options[:failure_exit_code] = code
end
parser.on('-X', '--[no-]drb', 'Run examples via DRb.') do |o|
options[:drb] = o
end
parser.on('--drb-port PORT', 'Port to connect to the DRb server.') do |o|
options[:drb_port] = o.to_i
end
parser.on('--init', 'Initialize your project with RSpec.') do |cmd|
ProjectInitializer.new(cmd).run
exit
end
parser.on('--configure', 'Deprecated. Use --init instead.') do |cmd|
warn "--configure is deprecated with no effect. Use --init instead."
exit
end
parser.separator("\n **** Output ****\n\n")
parser.on('-f', '--format FORMATTER', 'Choose a formatter.',
' [p]rogress (default - dots)',
' [d]ocumentation (group and example names)',
' [h]tml',
' [t]extmate',
' custom formatter class name') do |o|
options[:formatters] ||= []
options[:formatters] << [o]
end
parser.on('-o', '--out FILE',
'Write output to a file instead of STDOUT. This option applies',
' to the previously specified --format, or the default format',
' if no format is specified.'
) do |o|
options[:formatters] ||= [['progress']]
options[:formatters].last << o
end
parser.on('-b', '--backtrace', 'Enable full backtrace.') do |o|
options[:full_backtrace] = true
end
parser.on('-c', '--[no-]color', '--[no-]colour', 'Enable color in the output.') do |o|
options[:color] = o
end
parser.on('-p', '--profile', 'Enable profiling of examples and list 10 slowest examples.') do |o|
options[:profile_examples] = o
end
parser.separator <<-FILTERING
**** Filtering/tags ****
In addition to the following options for selecting specific files, groups,
or examples, you can select a single example by appending the line number to
the filename:
rspec path/to/a_spec.rb:37
FILTERING
parser.on('-P', '--pattern PATTERN', 'Load files matching pattern (default: "spec/**/*_spec.rb").') do |o|
options[:pattern] = o
end
parser.on('-e', '--example STRING', "Run examples whose full nested names include STRING") do |o|
options[:full_description] = Regexp.compile(Regexp.escape(o))
end
parser.on('-l', '--line_number LINE', 'Specify line number of an example or group (may be',
' used more than once).') do |o|
(options[:line_numbers] ||= []) << o
end
parser.on('-t', '--tag TAG[:VALUE]',
'Run examples with the specified tag, or exclude examples',
'by adding ~ before the tag.',
' - e.g. ~slow',
' - TAG is always converted to a symbol') do |tag|
filter_type = tag =~ /^~/ ? :exclusion_filter : :inclusion_filter
name,value = tag.gsub(/^(~@|~|@)/, '').split(':')
name = name.to_sym
options[filter_type] ||= {}
options[filter_type][name] = value.nil? ? true : eval(value) rescue value
end
parser.on('--default_path PATH', 'Set the default path where RSpec looks for examples (can',
' be a path to a file or a directory).') do |path|
options[:default_path] = path
end
parser.separator("\n **** Utility ****\n\n")
parser.on('-v', '--version', 'Display the version.') do
puts RSpec::Core::Version::STRING
exit
end
parser.on_tail('-h', '--help', "You're looking at it.") do
puts parser
exit
end
end
end
end
end
Feature: --order (new in rspec-core-2.8)
Use the `--order` option to tell RSpec how to order the files, groups, and
examples. Options are `default` and `rand`:
Default is:
* files are ordered based on the underlying file system's order (typically
case-sensitive alpha on *nix OS's and case-insenstive alpha in Windows)
* groups/examples are loaded in the order in which they are declared
Use `rand` to randomize the order of files, groups within files, and
examples within groups.*
* Nested groups are always run from top-level to bottom-level in order to avoid
executing `before(:all)` and `after(:all)` hooks more than once, but the order
of groups at each level is randomized.
You can also specify a seed
<h3>Examples</h3>
--order default
--order rand
--order rand:123
--seed 123 # same as --order rand:123
The `default` option is only necessary when you have `--order rand` stored in a
config file (e.g. `.rspec`) and you want to override it from the command line.
require 'spec_helper'
describe 'command line', :ui do
let(:stderr) { StringIO.new }
let(:stdout) { StringIO.new }
before :all do
write_file 'spec/order_spec.rb', """
describe 'group 1' do
specify('group 1 example 1') {}
specify('group 1 example 2') {}
specify('group 1 example 3') {}
specify('group 1 example 4') {}
specify('group 1 example 5') {}
specify('group 1 example 6') {}
specify('group 1 example 5') {}
specify('group 1 example 7') {}
specify('group 1 example 8') {}
specify('group 1 example 9') {}
specify('group 1 example 10') {}
describe 'group 1-1' do
specify('group 1-1 example 1') {}
specify('group 1-1 example 2') {}
specify('group 1-1 example 3') {}
specify('group 1-1 example 4') {}
specify('group 1-1 example 5') {}
specify('group 1-1 example 6') {}
specify('group 1-1 example 7') {}
specify('group 1-1 example 8') {}
specify('group 1-1 example 9') {}
specify('group 1-1 example 10') {}
end
describe('group 1-2') { specify('example') {} }
describe('group 1-3') { specify('example') {} }
describe('group 1-4') { specify('example') {} }
describe('group 1-5') { specify('example') {} }
describe('group 1-6') { specify('example') {} }
describe('group 1-7') { specify('example') {} }
describe('group 1-8') { specify('example') {} }
describe('group 1-9') { specify('example') {} }
describe('group 1-10') { specify('example') {} }
end
describe('group 2') { specify('example') {} }
describe('group 3') { specify('example') {} }
describe('group 4') { specify('example') {} }
describe('group 5') { specify('example') {} }
describe('group 6') { specify('example') {} }
describe('group 7') { specify('example') {} }
describe('group 8') { specify('example') {} }
describe('group 9') { specify('example') {} }
describe('group 10') { specify('example') {} }
"""
end
describe '--order rand' do
it 'runs the examples and groups in a different order each time' do
run_command 'tmp/aruba/spec/order_spec.rb --order rand -f doc'
RSpec.configuration.seed = srand && srand # reset seed in same process
run_command 'tmp/aruba/spec/order_spec.rb --order rand -f doc'
stdout.string.should match(/Randomized with seed \d+/)
top_level_groups {|first_run, second_run| first_run.should_not eq(second_run)}
nested_groups {|first_run, second_run| first_run.should_not eq(second_run)}
examples('group 1') {|first_run, second_run| first_run.should_not eq(second_run)}
examples('group 1-1') {|first_run, second_run| first_run.should_not eq(second_run)}
end
end
describe '--order rand:SEED' do
it 'runs the examples and groups in the same order each time' do
2.times { run_command 'tmp/aruba/spec/order_spec.rb --order rand:123 -f doc' }
stdout.string.should match(/Randomized with seed 123/)
top_level_groups {|first_run, second_run| first_run.should eq(second_run)}
nested_groups {|first_run, second_run| first_run.should eq(second_run)}
examples('group 1') {|first_run, second_run| first_run.should eq(second_run)}
examples('group 1-1') {|first_run, second_run| first_run.should eq(second_run)}
end
end
describe '--seed SEED' do
it "forces '--order rand' and runs the examples and groups in the same order each time" do
2.times { run_command 'tmp/aruba/spec/order_spec.rb --seed 123 -f doc' }
stdout.string.should match(/Randomized with seed \d+/)
top_level_groups {|first_run, second_run| first_run.should eq(second_run)}
nested_groups {|first_run, second_run| first_run.should eq(second_run)}
examples('group 1') {|first_run, second_run| first_run.should eq(second_run)}
examples('group 1-1') {|first_run, second_run| first_run.should eq(second_run)}
end
end
describe '--order default on CLI with --order rand in .rspec' do
it "overrides --order rand with --order default" do
write_file '.rspec', '--order rand'
run_command 'tmp/aruba/spec/order_spec.rb --order default -f doc'
stdout.string.should_not match(/Randomized/)
stdout.string.should match(
/group 1.*group 1 example 1.*group 1 example 2.*group 1-1.*group 1-2.*group 2.*/m
)
end
end
def examples(group)
yield split_in_half(stdout.string.scan(/^\s+#{group} example.*$/))
end
def top_level_groups
yield example_groups_at_level(0)
end
def nested_groups
yield example_groups_at_level(2)
end
def example_groups_at_level(level)
split_in_half(stdout.string.scan(/^\s{#{level*2}}group.*$/))
end
def split_in_half(array)
length, midpoint = array.length, array.length / 2
return array.slice(0, midpoint), array.slice(midpoint, length)
end
def run_command(cmd)
RSpec::Core::Runner.run(cmd.split, stderr, stdout)
end
end
module RSpec
module Core
# @private
module Extensions
# @private
# Used to extend lists of examples and groups to support ordering
# strategies like randomization.
module Ordered
# @private
def ordered
if RSpec.configuration.randomize?
Kernel.srand RSpec.configuration.seed
sort_by { Kernel.rand size }
else
self
end
end
end
end
end
end
# -*- ruby encoding: utf-8 -*-
require 'spec_helper'
describe "Diff::LCS.patch" do
include Diff::LCS::SpecHelper::Matchers
shared_examples "patch sequences correctly" do
it "should correctly patch left-to-right (patch autodiscovery)" do
Diff::LCS.patch(s1, patch_set).should == s2
end
it "should correctly patch left-to-right (explicit patch)" do
Diff::LCS.patch(s1, patch_set, :patch).should == s2
Diff::LCS.patch!(s1, patch_set).should == s2
end
it "should correctly patch right-to-left (unpatch autodiscovery)" do
Diff::LCS.patch(s2, patch_set).should == s1
end
it "should correctly patch right-to-left (explicit unpatch)" do
Diff::LCS.patch(s2, patch_set, :unpatch).should == s1
Diff::LCS.unpatch!(s2, patch_set).should == s1
end
end
describe "using a Diff::LCS.diff patchset" do
describe "with default diff callbacks (DiffCallbacks)" do
describe "forward (s1 -> s2)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq1 }
let(:s2) { seq2 }
let(:patch_set) { Diff::LCS.diff(seq1, seq2) }
end
end
describe "reverse (s2 -> s1)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq2 }
let(:s2) { seq1 }
let(:patch_set) { Diff::LCS.diff(seq2, seq1) }
end
end
end
describe "with context diff callbacks (ContextDiffCallbacks)" do
describe "forward (s1 -> s2)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq1 }
let(:s2) { seq2 }
let(:patch_set) {
Diff::LCS.diff(seq1, seq2, Diff::LCS::ContextDiffCallbacks)
}
end
end
describe "reverse (s2 -> s1)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq2 }
let(:s2) { seq1 }
let(:patch_set) {
Diff::LCS.diff(seq2, seq1, Diff::LCS::ContextDiffCallbacks)
}
end
end
end
describe "with sdiff callbacks (SDiffCallbacks)" do
describe "forward (s1 -> s2)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq1 }
let(:s2) { seq2 }
let(:patch_set) {
Diff::LCS.diff(seq1, seq2, Diff::LCS::SDiffCallbacks)
}
end
end
describe "reverse (s2 -> s1)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq2 }
let(:s2) { seq1 }
let(:patch_set) {
Diff::LCS.diff(seq2, seq1, Diff::LCS::SDiffCallbacks)
}
end
end
end
end
describe "using a Diff::LCS.sdiff patchset" do
describe "with default diff callbacks (DiffCallbacks)" do
describe "forward (s1 -> s2)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq1 }
let(:s2) { seq2 }
let(:patch_set) {
Diff::LCS.sdiff(seq1, seq2, Diff::LCS::DiffCallbacks)
}
end
end
describe "reverse (s2 -> s1)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq2 }
let(:s2) { seq1 }
let(:patch_set) {
Diff::LCS.sdiff(seq2, seq1, Diff::LCS::DiffCallbacks)
}
end
end
end
describe "with context diff callbacks (DiffCallbacks)" do
describe "forward (s1 -> s2)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq1 }
let(:s2) { seq2 }
let(:patch_set) {
Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextDiffCallbacks)
}
end
end
describe "reverse (s2 -> s1)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq2 }
let(:s2) { seq1 }
let(:patch_set) {
Diff::LCS.sdiff(seq2, seq1, Diff::LCS::ContextDiffCallbacks)
}
end
end
end
describe "with sdiff callbacks (SDiffCallbacks)" do
describe "forward (s1 -> s2)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq1 }
let(:s2) { seq2 }
let(:patch_set) { Diff::LCS.sdiff(seq1, seq2) }
end
end
describe "reverse (s2 -> s1)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq2 }
let(:s2) { seq1 }
let(:patch_set) { Diff::LCS.sdiff(seq2, seq1) }
end
end
end
end
# Note: because of the error in autodiscovery ("does not autodiscover s1
# to s2 patches"), this cannot use the "patch sequences correctly" shared
# set. Once the bug in autodiscovery is fixed, this can be converted as
# above.
describe "fix bug 891: patchsets do not contain the last equal part" do
before(:each) do
@s1 = %w(a b c d e f g h i j k)
@s2 = %w(a b c d D e f g h i j k)
end
describe "using Diff::LCS.diff with default diff callbacks" do
before(:each) do
@patch_set_s1_s2 = Diff::LCS.diff(@s1, @s2)
@patch_set_s2_s1 = Diff::LCS.diff(@s2, @s1)
end
it "does not autodiscover s1 to s2 patches" do
# It should, but it doesn't.
expect do
Diff::LCS.patch(@s1, @patch_set_s1_s2).should == @s2
end.to raise_error(RuntimeError, /provided patchset/)
expect do
Diff::LCS.patch(@s1, @patch_set_s2_s1).should == @s2
end.to raise_error(RuntimeError, /provided patchset/)
end
it "should autodiscover s2 to s1 the left-to-right patches" do
Diff::LCS.patch(@s2, @patch_set_s2_s1).should == @s1
Diff::LCS.patch(@s2, @patch_set_s1_s2).should == @s1
end
it "should correctly patch left-to-right (explicit patch)" do
Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch).should == @s2
Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch).should == @s1
Diff::LCS.patch!(@s1, @patch_set_s1_s2).should == @s2
Diff::LCS.patch!(@s2, @patch_set_s2_s1).should == @s1
end
it "should correctly patch right-to-left (explicit unpatch)" do
Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch).should == @s1
Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch).should == @s2
Diff::LCS.unpatch!(@s2, @patch_set_s1_s2).should == @s1
Diff::LCS.unpatch!(@s1, @patch_set_s2_s1).should == @s2
end
end
describe "using Diff::LCS.diff with context diff callbacks" do
before(:each) do
@patch_set_s1_s2 = Diff::LCS.diff(@s1, @s2, Diff::LCS::ContextDiffCallbacks)
@patch_set_s2_s1 = Diff::LCS.diff(@s2, @s1, Diff::LCS::ContextDiffCallbacks)
end
it "does not autodiscover s1 to s2 patches" do
# It should, but it doesn't.
expect do
Diff::LCS.patch(@s1, @patch_set_s1_s2).should == @s2
end.to raise_error(RuntimeError, /provided patchset/)
expect do
Diff::LCS.patch(@s1, @patch_set_s2_s1).should == @s2
end.to raise_error(RuntimeError, /provided patchset/)
end
it "should autodiscover s2 to s1 the left-to-right patches" do
Diff::LCS.patch(@s2, @patch_set_s2_s1).should == @s1
Diff::LCS.patch(@s2, @patch_set_s1_s2).should == @s1
end
it "should correctly patch left-to-right (explicit patch)" do
Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch).should == @s2
Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch).should == @s1
Diff::LCS.patch!(@s1, @patch_set_s1_s2).should == @s2
Diff::LCS.patch!(@s2, @patch_set_s2_s1).should == @s1
end
it "should correctly patch right-to-left (explicit unpatch)" do
Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch).should == @s1
Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch).should == @s2
Diff::LCS.unpatch!(@s2, @patch_set_s1_s2).should == @s1
Diff::LCS.unpatch!(@s1, @patch_set_s2_s1).should == @s2
end
end
describe "using Diff::LCS.diff with sdiff callbacks" do
before(:each) do
@patch_set_s1_s2 = Diff::LCS.diff(@s1, @s2, Diff::LCS::SDiffCallbacks)
@patch_set_s2_s1 = Diff::LCS.diff(@s2, @s1, Diff::LCS::SDiffCallbacks)
end
it "does not autodiscover s1 to s2 patches" do
# It should, but it doesn't.
expect do
Diff::LCS.patch(@s1, @patch_set_s1_s2).should == @s2
end.to raise_error(RuntimeError, /provided patchset/)
expect do
Diff::LCS.patch(@s1, @patch_set_s2_s1).should == @s2
end.to raise_error(RuntimeError, /provided patchset/)
end
it "should autodiscover s2 to s1 the left-to-right patches" do
Diff::LCS.patch(@s2, @patch_set_s2_s1).should == @s1
Diff::LCS.patch(@s2, @patch_set_s1_s2).should == @s1
end
it "should correctly patch left-to-right (explicit patch)" do
Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch).should == @s2
Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch).should == @s1
Diff::LCS.patch!(@s1, @patch_set_s1_s2).should == @s2
Diff::LCS.patch!(@s2, @patch_set_s2_s1).should == @s1
end
it "should correctly patch right-to-left (explicit unpatch)" do
Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch).should == @s1
Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch).should == @s2
Diff::LCS.unpatch!(@s2, @patch_set_s1_s2).should == @s1
Diff::LCS.unpatch!(@s1, @patch_set_s2_s1).should == @s2
end
end
describe "using Diff::LCS.sdiff with default sdiff callbacks" do
before(:each) do
@patch_set_s1_s2 = Diff::LCS.sdiff(@s1, @s2)
@patch_set_s2_s1 = Diff::LCS.sdiff(@s2, @s1)
end
it "does not autodiscover s1 to s2 patches" do
# It should, but it doesn't.
expect do
Diff::LCS.patch(@s1, @patch_set_s1_s2).should == @s2
end.to raise_error(RuntimeError, /provided patchset/)
expect do
Diff::LCS.patch(@s1, @patch_set_s2_s1).should == @s2
end.to raise_error(RuntimeError, /provided patchset/)
end
it "should autodiscover s2 to s1 the left-to-right patches" do
Diff::LCS.patch(@s2, @patch_set_s2_s1).should == @s1
Diff::LCS.patch(@s2, @patch_set_s1_s2).should == @s1
end
it "should correctly patch left-to-right (explicit patch)" do
Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch).should == @s2
Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch).should == @s1
Diff::LCS.patch!(@s1, @patch_set_s1_s2).should == @s2
Diff::LCS.patch!(@s2, @patch_set_s2_s1).should == @s1
end
it "should correctly patch right-to-left (explicit unpatch)" do
Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch).should == @s1
Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch).should == @s2
Diff::LCS.unpatch!(@s2, @patch_set_s1_s2).should == @s1
Diff::LCS.unpatch!(@s1, @patch_set_s2_s1).should == @s2
end
end
describe "using Diff::LCS.sdiff with context diff callbacks" do
before(:each) do
@patch_set_s1_s2 = Diff::LCS.sdiff(@s1, @s2, Diff::LCS::ContextDiffCallbacks)
@patch_set_s2_s1 = Diff::LCS.sdiff(@s2, @s1, Diff::LCS::ContextDiffCallbacks)
end
it "does not autodiscover s1 to s2 patches" do
# It should, but it doesn't.
expect do
Diff::LCS.patch(@s1, @patch_set_s1_s2).should == @s2
end.to raise_error(RuntimeError, /provided patchset/)
expect do
Diff::LCS.patch(@s1, @patch_set_s2_s1).should == @s2
end.to raise_error(RuntimeError, /provided patchset/)
end
it "should autodiscover s2 to s1 the left-to-right patches" do
Diff::LCS.patch(@s2, @patch_set_s2_s1).should == @s1
Diff::LCS.patch(@s2, @patch_set_s1_s2).should == @s1
end
it "should correctly patch left-to-right (explicit patch)" do
Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch).should == @s2
Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch).should == @s1
Diff::LCS.patch!(@s1, @patch_set_s1_s2).should == @s2
Diff::LCS.patch!(@s2, @patch_set_s2_s1).should == @s1
end
it "should correctly patch right-to-left (explicit unpatch)" do
Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch).should == @s1
Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch).should == @s2
Diff::LCS.unpatch!(@s2, @patch_set_s1_s2).should == @s1
Diff::LCS.unpatch!(@s1, @patch_set_s2_s1).should == @s2
end
end
describe "using Diff::LCS.sdiff with default diff callbacks" do
before(:each) do
@patch_set_s1_s2 = Diff::LCS.sdiff(@s1, @s2, Diff::LCS::DiffCallbacks)
@patch_set_s2_s1 = Diff::LCS.sdiff(@s2, @s1, Diff::LCS::DiffCallbacks)
end
it "does not autodiscover s1 to s2 patches" do
# It should, but it doesn't.
expect do
Diff::LCS.patch(@s1, @patch_set_s1_s2).should == @s2
end.to raise_error(RuntimeError, /provided patchset/)
expect do
Diff::LCS.patch(@s1, @patch_set_s2_s1).should == @s2
end.to raise_error(RuntimeError, /provided patchset/)
end
it "should autodiscover s2 to s1 the left-to-right patches" do
Diff::LCS.patch(@s2, @patch_set_s2_s1).should == @s1
Diff::LCS.patch(@s2, @patch_set_s1_s2).should == @s1
end
it "should correctly patch left-to-right (explicit patch)" do
Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch).should == @s2
Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch).should == @s1
Diff::LCS.patch!(@s1, @patch_set_s1_s2).should == @s2
Diff::LCS.patch!(@s2, @patch_set_s2_s1).should == @s1
end
it "should correctly patch right-to-left (explicit unpatch)" do
Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch).should == @s1
Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch).should == @s2
Diff::LCS.unpatch!(@s2, @patch_set_s1_s2).should == @s1
Diff::LCS.unpatch!(@s1, @patch_set_s2_s1).should == @s2
end
end
end
end
# vim: ft=ruby
Feature: pattern option
By default, RSpec loads files matching the pattern:
"spec/**/*_spec.rb"
Use the `--pattern` option to declare a different pattern.
Scenario: default pattern
Given a file named "spec/example_spec.rb" with:
"""
describe "addition" do
it "adds things" do
(1 + 2).should eq(3)
end
end
"""
When I run `rspec`
Then the output should contain "1 example, 0 failures"
Scenario: override the default pattern on the command line
Given a file named "spec/example.spec" with:
"""
describe "addition" do
it "adds things" do
(1 + 2).should eq(3)
end
end
"""
When I run `rspec --pattern "spec/**/*.spec"`
Then the output should contain "1 example, 0 failures"
module RSpec
module Core
module Pending
class PendingDeclaredInExample < StandardError; end
# If Test::Unit is loaed, we'll use its error as baseclass, so that Test::Unit
# will report unmet RSpec expectations as failures rather than errors.
begin
class PendingExampleFixedError < Test::Unit::AssertionFailedError; end
rescue
class PendingExampleFixedError < StandardError; end
end
class PendingExampleFixedError
def pending_fixed?; true; end
end
NO_REASON_GIVEN = 'No reason given'
NOT_YET_IMPLEMENTED = 'Not yet implemented'
# @overload pending()
# @overload pending(message)
# @overload pending(message, &block)
#
# Stops execution of an example, and reports it as pending. Takes an
# optional message and block.
#
# @param [String] message optional message to add to the summary report.
# @param [Block] block optional block. If it fails, the example is
# reported as pending. If it executes cleanly the example fails.
#
# @example
#
# describe "an example" do
# # reported as "Pending: no reason given"
# it "is pending with no message" do
# pending
# this_does_not_get_executed
# end
#
# # reported as "Pending: something else getting finished"
# it "is pending with a custom message" do
# pending("something else getting finished")
# this_does_not_get_executed
# end
#
# # reported as "Pending: something else getting finished"
# it "is pending with a failing block" do
# pending("something else getting finished") do
# raise "this is the failure"
# end
# end
#
# # reported as failure, saying we expected the block to fail but
# # it passed.
# it "is pending with a passing block" do
# pending("something else getting finished") do
# true.should be(true)
# end
# end
# end
#
# @note `before(:each)` hooks are eval'd when you use the `pending`
# method within an example. If you want to declare an example `pending`
# and bypass the `before` hooks as well, you can pass `:pending => true`
# to the `it` method:
#
# it "does something", :pending => true do
# # ...
# end
def pending(*args)
return self.class.before(:each) { pending(*args) } unless example
options = args.last.is_a?(Hash) ? args.pop : {}
message = args.first || NO_REASON_GIVEN
if options[:unless] || (options.has_key?(:if) && !options[:if])
return block_given? ? yield : nil
end
example.metadata[:pending] = true
example.metadata[:execution_result][:pending_message] = message
if block_given?
begin
result = begin
yield
example.example_group_instance.instance_eval { verify_mocks_for_rspec }
end
example.metadata[:pending] = false
rescue Exception => e
example.execution_result[:exception] = e
ensure
teardown_mocks_for_rspec
end
raise PendingExampleFixedError.new if result
end
raise PendingDeclaredInExample.new(message)
end
end
end
end
Feature: pending examples
RSpec offers four ways to indicate that an example is disabled pending
some action.
Scenario: pending implementation
Given a file named "example_without_block_spec.rb" with:
"""
describe "an example" do
it "is a pending example"
end
"""
When I run `rspec example_without_block_spec.rb`
Then the exit status should be 0
And the output should contain "1 example, 0 failures, 1 pending"
And the output should contain "Not yet implemented"
And the output should contain "example_without_block_spec.rb:2"
Scenario: pending any arbitary reason, with no block
Given a file named "pending_without_block_spec.rb" with:
"""
describe "an example" do
it "is implemented but waiting" do
pending("something else getting finished")
this_should_not_get_executed
end
end
"""
When I run `rspec pending_without_block_spec.rb`
Then the exit status should be 0
And the output should contain "1 example, 0 failures, 1 pending"
And the output should contain:
"""
Pending:
an example is implemented but waiting
# something else getting finished
# ./pending_without_block_spec.rb:2
"""
Scenario: pending any arbitary reason, with a block that fails
Given a file named "pending_with_failing_block_spec.rb" with:
"""
describe "an example" do
it "is implemented but waiting" do
pending("something else getting finished") do
raise "this is the failure"
end
end
end
"""
When I run `rspec pending_with_failing_block_spec.rb`
Then the exit status should be 0
And the output should contain "1 example, 0 failures, 1 pending"
And the output should contain:
"""
Pending:
an example is implemented but waiting
# something else getting finished
# ./pending_with_failing_block_spec.rb:2
"""
Scenario: pending any arbitary reason, with a block that passes
Given a file named "pending_with_passing_block_spec.rb" with:
"""
describe "an example" do
it "is implemented but waiting" do
pending("something else getting finished") do
true.should be(true)
end
end
end
"""
When I run `rspec pending_with_passing_block_spec.rb`
Then the exit status should not be 0
And the output should contain "1 example, 1 failure"
And the output should contain "FIXED"
And the output should contain "Expected pending 'something else getting finished' to fail. No Error was raised."
And the output should contain "pending_with_passing_block_spec.rb:3"
Scenario: temporarily pending by prefixing `it`, `specify`, or `example` with an x
Given a file named "temporarily_pending_spec.rb" with:
"""
describe "an example" do
xit "is pending using xit" do
end
xspecify "is pending using xspecify" do
end
xexample "is pending using xexample" do
end
end
"""
When I run `rspec temporarily_pending_spec.rb`
Then the exit status should be 0
And the output should contain "3 examples, 0 failures, 3 pending"
And the output should contain:
"""
Pending:
an example is pending using xit
# Temporarily disabled with xit
# ./temporarily_pending_spec.rb:2
an example is pending using xspecify
# Temporarily disabled with xspecify
# ./temporarily_pending_spec.rb:5
an example is pending using xexample
# Temporarily disabled with xexample
# ./temporarily_pending_spec.rb:8
"""
Scenario: example with no docstring and pending method using documentation formatter
Given a file named "pending_with_no_docstring_spec.rb" with:
"""
describe "an example" do
it "checks something" do
(3+4).should eq(7)
end
specify do
pending
end
end
"""
When I run `rspec pending_with_no_docstring_spec.rb --format documentation`
Then the exit status should be 0
And the output should contain "2 examples, 0 failures, 1 pending"
And the output should contain:
"""
an example
checks something
(PENDING: No reason given)
"""
Scenario: pending with no docstring using documentation formatter
Given a file named "pending_with_no_docstring_spec.rb" with:
"""
describe "an example" do
it "checks something" do
(3+4).should eq(7)
end
pending do
"string".reverse.should eq("gnirts")
end
end
"""
When I run `rspec pending_with_no_docstring_spec.rb --format documentation`
Then the exit status should be 0
And the output should contain "2 examples, 0 failures, 1 pending"
And the output should contain:
"""
an example
checks something
(PENDING: No reason given)
"""
Scenario: conditionally pending examples
Given a file named "conditionally_pending_spec.rb" with:
"""
describe "a failing spec" do
def run_test; raise "failure"; end
it "is pending when pending with a true :if condition" do
pending("true :if", :if => true) { run_test }
end
it "fails when pending with a false :if condition" do
pending("false :if", :if => false) { run_test }
end
it "is pending when pending with a false :unless condition" do
pending("false :unless", :unless => false) { run_test }
end
it "fails when pending with a true :unless condition" do
pending("true :unless", :unless => true) { run_test }
end
end
describe "a passing spec" do
def run_test; true.should be(true); end
it "fails when pending with a true :if condition" do
pending("true :if", :if => true) { run_test }
end
it "passes when pending with a false :if condition" do
pending("false :if", :if => false) { run_test }
end
it "fails when pending with a false :unless condition" do
pending("false :unless", :unless => false) { run_test }
end
it "passes when pending with a true :unless condition" do
pending("true :unless", :unless => true) { run_test }
end
end
"""
When I run `rspec ./conditionally_pending_spec.rb`
Then the output should contain "8 examples, 4 failures, 2 pending"
And the output should contain:
"""
Pending:
a failing spec is pending when pending with a true :if condition
# true :if
# ./conditionally_pending_spec.rb:4
a failing spec is pending when pending with a false :unless condition
# false :unless
# ./conditionally_pending_spec.rb:12
"""
And the output should contain:
"""
1) a failing spec fails when pending with a false :if condition
Failure/Error: def run_test; raise "failure"; end
"""
And the output should contain:
"""
2) a failing spec fails when pending with a true :unless condition
Failure/Error: def run_test; raise "failure"; end
"""
And the output should contain:
"""
3) a passing spec fails when pending with a true :if condition FIXED
Expected pending 'true :if' to fail. No Error was raised.
"""
And the output should contain:
"""
4) a passing spec fails when pending with a false :unless condition FIXED
Expected pending 'false :unless' to fail. No Error was raised.
"""
require 'rspec/core/formatters/base_text_formatter'
module RSpec
module Core
module Formatters
class ProgressFormatter < BaseTextFormatter
def example_passed(example)
super(example)
output.print green('.')
end
def example_pending(example)
super(example)
output.print yellow('*')
end
def example_failed(example)
super(example)
output.print red('F')
end
def start_dump
super()
output.puts
end
end
end
end
end
module RSpec
module Core
class ProjectInitializer
def initialize(arg=nil)
@arg = arg
end
def run
warn "The --configure option no longer needs any arguments, so #{@arg} was ignored." if @arg
create_spec_helper_file
create_dot_rspec_file
delete_if_confirmed("autotest/discover.rb", <<-MESSAGE)
RSpec registers its own discover.rb with Autotest, so autotest/discover.rb is
no longer needed.
MESSAGE
delete_if_confirmed("lib/tasks/rspec.rake", <<-MESSAGE)
If the file in lib/tasks/rspec.rake is the one generated by rspec-rails-1x,
you can get rid of it, as it is no longer needed with rspec-2.
MESSAGE
end
def create_dot_rspec_file
if File.exist?('.rspec')
report_exists('.rspec')
else
report_creating('.rspec')
File.open('.rspec','w') do |f|
f.write <<-CONTENT
--color
--format progress
CONTENT
end
end
end
def create_spec_helper_file
if File.exist?('spec/spec_helper.rb')
report_exists("spec/spec_helper.rb")
else
report_creating("spec/spec_helper.rb")
FileUtils.mkdir_p('spec')
File.open('spec/spec_helper.rb','w') do |f|
f.write <<-CONTENT
# This file was generated by the `rspec --init` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
# Require this file using `require "spec_helper.rb"` to ensure that it is only
# loaded once.
#
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
RSpec.configure do |config|
config.treat_symbols_as_metadata_keys_with_true_values = true
config.run_all_when_everything_filtered = true
config.filter_run :focus
end
CONTENT
end
end
end
def delete_if_confirmed(path, message)
if File.exist?(path)
puts
puts message
puts
puts " delete #{path}? [y/n]"
FileUtils.rm_rf(path) if gets =~ /y/i
end
end
def report_exists(file)
puts " exist #{file}"
end
def report_creating(file)
puts " create #{file}"
end
end
end
end
Feature: rake task
RSpec ships with a rake task with a number of useful options
Scenario: default options with passing spec (prints command and exit status is 0)
Given a file named "Rakefile" with:
"""
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec)
task :default => :spec
"""
And a file named "spec/thing_spec.rb" with:
"""
describe "something" do
it "does something" do
# pass
end
end
"""
When I run `rake`
Then the output should contain "ruby -S rspec"
Then the exit status should be 0
Scenario: default options with failing spec (exit status is 1)
Given a file named "Rakefile" with:
"""
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec)
task :default => :spec
"""
And a file named "spec/thing_spec.rb" with:
"""
describe "something" do
it "does something" do
fail
end
end
"""
When I run `rake`
Then the exit status should be 1
Scenario: fail_on_error = false with failing spec (exit status is 0)
Given a file named "Rakefile" with:
"""
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec) do |t|
t.fail_on_error = false
end
task :default => :spec
"""
And a file named "spec/thing_spec.rb" with:
"""
describe "something" do
it "does something" do
fail
end
end
"""
When I run `rake`
Then the exit status should be 0
require 'rspec/core'
require 'rspec/core/deprecation'
require 'rake'
require 'rake/tasklib'
module RSpec
module Core
class RakeTask < ::Rake::TaskLib
include ::Rake::DSL if defined?(::Rake::DSL)
# Name of task.
#
# default:
# :spec
attr_accessor :name
# Glob pattern to match files.
#
# default:
# 'spec/**/*_spec.rb'
attr_accessor :pattern
# @deprecated
# Has no effect. The rake task now checks ENV['BUNDLE_GEMFILE'] instead.
def skip_bundler=(*)
RSpec.deprecate("RSpec::Core::RakeTask#skip_bundler=")
end
# @deprecated
# Has no effect. The rake task now checks ENV['BUNDLE_GEMFILE'] instead.
def gemfile=(*)
RSpec.deprecate("RSpec::Core::RakeTask#gemfile=", 'ENV["BUNDLE_GEMFILE"]')
end
# @deprecated
# Use ruby_opts="-w" instead.
#
# When true, requests that the specs be run with the warning flag set.
# e.g. "ruby -w"
#
# default:
# false
def warning=(true_or_false)
RSpec.deprecate("RSpec::Core::RakeTask#warning=", 'ruby_opts="-w"')
@warning = true_or_false
end
# Whether or not to fail Rake when an error occurs (typically when examples fail).
#
# default:
# true
attr_accessor :fail_on_error
# A message to print to stderr when there are failures.
attr_accessor :failure_message
# Use verbose output. If this is set to true, the task will print the
# executed spec command to stdout.
#
# default:
# true
attr_accessor :verbose
# Use rcov for code coverage?
#
# default:
# false
attr_accessor :rcov
# Path to rcov.
#
# default:
# 'rcov'
attr_accessor :rcov_path
# Command line options to pass to rcov.
#
# default:
# nil
attr_accessor :rcov_opts
# Command line options to pass to ruby.
#
# default:
# nil
attr_accessor :ruby_opts
# Path to rspec
#
# default:
# 'rspec'
attr_accessor :rspec_path
# Command line options to pass to rspec.
#
# default:
# nil
attr_accessor :rspec_opts
# @deprecated
# Use rspec_opts instead.
#
# Command line options to pass to rspec.
#
# default:
# nil
def spec_opts=(opts)
RSpec.deprecate('RSpec::Core::RakeTask#spec_opts=', 'rspec_opts=')
@rspec_opts = opts
end
def initialize(*args)
@name = args.shift || :spec
@pattern, @rcov_path, @rcov_opts, @ruby_opts, @rspec_opts = nil, nil, nil, nil, nil
@warning, @rcov = false, false
@verbose, @fail_on_error = true, true
yield self if block_given?
@rcov_path ||= 'rcov'
@rspec_path ||= 'rspec'
@pattern ||= './spec{,/*/**}/*_spec.rb'
desc("Run RSpec code examples") unless ::Rake.application.last_comment
task name do
RakeFileUtils.send(:verbose, verbose) do
if files_to_run.empty?
puts "No examples matching #{pattern} could be found"
else
begin
puts spec_command if verbose
success = system(spec_command)
rescue
puts failure_message if failure_message
end
raise("#{spec_command} failed") if fail_on_error unless success
end
end
end
end
private
def files_to_run
if ENV['SPEC']
FileList[ ENV['SPEC'] ]
else
FileList[ pattern ].map { |f| f.gsub(/"/, '\"').gsub(/'/, "\\\\'") }
end
end
def spec_command
@spec_command ||= begin
cmd_parts = []
cmd_parts << RUBY
cmd_parts << ruby_opts
cmd_parts << "-w" if @warning
cmd_parts << "-S" << runner
cmd_parts << "-Ispec:lib" << rcov_opts if rcov
cmd_parts << files_to_run
cmd_parts << "--" if rcov && rspec_opts
cmd_parts << rspec_opts
cmd_parts.flatten.reject(&blank).join(" ")
end
end
private
def runner
rcov ? rcov_path : rspec_path
end
def blank
lambda {|s| s.nil? || s == ""}
end
end
end
end
# -*- ruby encoding: utf-8 -*-
require 'rubygems'
require 'rspec'
require 'hoe'
Hoe.plugin :doofus
Hoe.plugin :gemspec
Hoe.plugin :git
Hoe.spec 'diff-lcs' do
self.rubyforge_name = 'ruwiki'
developer('Austin Ziegler', 'austin@rubyforge.org')
self.remote_rdoc_dir = 'diff-lcs/rdoc'
self.rsync_args << ' --exclude=statsvn/'
self.history_file = 'History.rdoc'
self.readme_file = 'README.rdoc'
self.extra_rdoc_files = FileList["*.rdoc"].to_a
self.extra_dev_deps << ['rspec', '~> 2.0']
end
# vim: syntax=ruby
Feature: read command line configuration options from files
RSpec reads command line configuration options from files in two different
locations:
Local: "./.rspec" (i.e. in the project's root directory)
Global: "~/.rspec" (i.e. in the user's home directory)
Options declared in the local file override those in the global file, while
those declared in RSpec.configure will override any ".rspec" file.
Scenario: color set in .rspec
Given a file named ".rspec" with:
"""
--color
"""
And a file named "spec/example_spec.rb" with:
"""
describe "color_enabled" do
context "when set with RSpec.configure" do
before do
# color is disabled for non-tty output, so stub the output stream
# to say it is tty, even though we're running this with cucumber
RSpec.configuration.output_stream.stub(:tty?) { true }
end
it "is true" do
RSpec.configuration.should be_color_enabled
end
end
end
"""
When I run `rspec ./spec/example_spec.rb`
Then the examples should all pass
Scenario: custom options file
Given a file named "my.options" with:
"""
--format documentation
"""
And a file named "spec/example_spec.rb" with:
"""
describe "formatter set in custom options file" do
it "sets formatter" do
RSpec.configuration.formatters.first.
should be_a(RSpec::Core::Formatters::DocumentationFormatter)
end
end
"""
When I run `rspec spec/example_spec.rb --options my.options`
Then the examples should all pass
Scenario: RSpec ignores ./.rspec when custom options file is used
Given a file named "my.options" with:
"""
--format documentation
"""
And a file named ".rspec" with:
"""
--color
"""
And a file named "spec/example_spec.rb" with:
"""
describe "custom options file" do
it "causes .rspec to be ignored" do
RSpec.configuration.color_enabled.should be_false
end
end
"""
When I run `rspec spec/example_spec.rb --options my.options`
Then the examples should all pass
Scenario: using ERB in .rspec
Given a file named ".rspec" with:
"""
--format <%= true ? 'documentation' : 'progress' %>
"""
And a file named "spec/example_spec.rb" with:
"""
describe "formatter" do
it "is set to documentation" do
RSpec.configuration.formatters.first.should be_an(RSpec::Core::Formatters::DocumentationFormatter)
end
end
"""
When I run `rspec ./spec/example_spec.rb`
Then the examples should all pass
module RSpec::Core
class Reporter
def initialize(*formatters)
@formatters = formatters
@example_count = @failure_count = @pending_count = 0
@duration = @start = nil
end
# @api
# @overload report(count, &block)
# @overload report(count, seed, &block)
# @param [Integer] count the number of examples being run
# @param [Integer] seed the seed used to randomize the spec run
# @param [Block] block yields itself for further reporting.
#
# Initializes the report run and yields itself for further reporting. The
# block is required, so that the reporter can manage cleaning up after the
# run.
#
# ### Warning:
#
# The `seed` argument is an internal API and is not guaranteed to be
# supported in the future.
#
# @example
#
# reporter.report(group.examples.size) do |r|
# example_groups.map {|g| g.run(r) }
# end
#
def report(expected_example_count, seed=nil)
start(expected_example_count)
begin
yield self
ensure
finish(seed)
end
end
def start(expected_example_count)
@start = Time.now
notify :start, expected_example_count
end
def message(message)
notify :message, message
end
def example_group_started(group)
notify :example_group_started, group unless group.descendant_filtered_examples.empty?
end
def example_group_finished(group)
notify :example_group_finished, group unless group.descendant_filtered_examples.empty?
end
def example_started(example)
@example_count += 1
notify :example_started, example
end
def example_passed(example)
notify :example_passed, example
end
def example_failed(example)
@failure_count += 1
notify :example_failed, example
end
def example_pending(example)
@pending_count += 1
notify :example_pending, example
end
def finish(seed)
begin
stop
notify :start_dump
notify :dump_pending
notify :dump_failures
notify :dump_summary, @duration, @example_count, @failure_count, @pending_count
notify :seed, seed if seed
ensure
notify :close
end
end
alias_method :abort, :finish
def stop
@duration = Time.now - @start if @start
notify :stop
end
def notify(method, *args, &block)
@formatters.each do |formatter|
formatter.send method, *args, &block
end
end
end
end
#!/usr/bin/env ruby
#
# This file was generated by RubyGems.
#
# The application 'rspec-core' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'rubygems'
version = ">= 0"
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
version = $1
ARGV.shift
end
gem 'rspec-core', version
load Gem.bin_path('rspec-core', 'rspec', version)
#!/usr/bin/env ruby
begin
require 'rspec/autorun'
rescue LoadError
$stderr.puts <<-EOS
#{'*'*50}
Could not find 'rspec/autorun'
This may happen if you're using rubygems as your package manager, but it is not
being required through some mechanism before executing the rspec command.
You may need to do one of the following in your shell:
# for bash/zsh
export RUBYOPT=rubygems
# for csh, etc.
set RUBYOPT=rubygems
For background, please see http://gist.github.com/54177.
#{'*'*50}
EOS
exit(1)
end
require 'rspec/core'
require 'rspec/expectations'
require 'rspec/mocks'
require 'rspec/version'
# TODO (2011-05-08) - remove this as soon as spork 0.9.0 is released
if defined?(Spork::TestFramework::RSpec)
# @private
class Spork::TestFramework::RSpec < Spork::TestFramework
# @private
def run_tests(argv, err, out)
::RSpec::Core::CommandLine.new(argv).run(err, out)
end
end
end
require 'autotest'
require 'rspec/core/deprecation'
# Derived from the `Autotest` class, extends the `autotest` command to work
# with RSpec.
#
# @note this will be extracted to a separate gem when we release rspec-3.
class Autotest::Rspec2 < Autotest
RSPEC_EXECUTABLE = File.expand_path('../../../exe/rspec', __FILE__)
def initialize
super()
clear_mappings
setup_rspec_project_mappings
# Example for Ruby 1.8: http://rubular.com/r/AOXNVDrZpx
# Example for Ruby 1.9: http://rubular.com/r/85ag5AZ2jP
self.failed_results_re = /^\s*\d+\).*\n\s+(?:\e\[\d*m)?Failure.*(\n(?:\e\[\d*m)?\s+#\s(.*)?:\d+(?::.*)?(?:\e\[\d*m)?)+$/m
self.completed_re = /\n(?:\e\[\d*m)?\d* examples?/m
end
# Adds conventional spec-to-file mappings to Autotest configuation.
def setup_rspec_project_mappings
add_mapping(%r%^spec/.*_spec\.rb$%) { |filename, _|
filename
}
add_mapping(%r%^lib/(.*)\.rb$%) { |_, m|
["spec/#{m[1]}_spec.rb"]
}
add_mapping(%r%^spec/(spec_helper|shared/.*)\.rb$%) {
files_matching %r%^spec/.*_spec\.rb$%
}
end
# Overrides Autotest's implementation to read rspec output
def consolidate_failures(failed)
filters = new_hash_of_arrays
failed.each do |spec, trace|
if trace =~ /(.*spec\.rb)/
filters[$1] << spec
end
end
return filters
end
# Overrides Autotest's implementation to generate the rspec command to run
def make_test_cmd(files_to_test)
files_to_test.empty? ? '' :
"#{prefix}#{ruby}#{suffix} -S #{RSPEC_EXECUTABLE} --tty #{normalize(files_to_test).keys.flatten.map { |f| "'#{f}'"}.join(' ')}"
end
# Generates a map of filenames to Arrays for Autotest
def normalize(files_to_test)
files_to_test.keys.inject({}) do |result, filename|
result.merge!(File.expand_path(filename) => [])
end
end
private
def suffix
using_bundler? ? "" : defined?(:Gem) ? " -rrubygems" : ""
end
def using_bundler?
prefix =~ /bundle exec/
end
def gemfile?
File.exist?('./Gemfile')
end
end
require "spec_helper"
describe Autotest::Rspec2 do
let(:rspec_autotest) { Autotest::Rspec2.new }
let(:spec_cmd) { File.expand_path("../../../exe/rspec", __FILE__) }
let(:ruby_cmd) { "ruby" }
before do
File.stub(:exist?) { false }
end
it "uses autotest's prefix" do
rspec_autotest.prefix = "this is the prefix "
rspec_autotest.
make_test_cmd({'a' => 'b'}).should match(/this is the prefix/)
end
describe "commands" do
before do
rspec_autotest.stub(:ruby => ruby_cmd)
files = %w[file_one file_two]
@files_to_test = {
files[0] => [],
files[1] => []
}
# this is not the inner representation of Autotest!
rspec_autotest.files_to_test = @files_to_test
@to_test = files.map { |f| File.expand_path(f) }.join ' '
end
it "makes the appropriate test command" do
actual_command = rspec_autotest.make_test_cmd(@files_to_test)
expected_command = /#{ruby_cmd}.*#{spec_cmd} (.*)/
actual_command.should match(expected_command)
actual_command =~ expected_command
$1.should =~ /#{File.expand_path('file_one')}/
$1.should =~ /#{File.expand_path('file_two')}/
end
it "returns a blank command for no files" do
rspec_autotest.make_test_cmd({}).should eq('')
end
it "quotes the paths of files to test" do
cmd = rspec_autotest.make_test_cmd(@files_to_test)
@files_to_test.keys.each do |file_to_test|
cmd.should match(/'#{File.expand_path(file_to_test)}'/)
end
end
it "gives '--tty' to #{Autotest::Rspec2::RSPEC_EXECUTABLE}, not '--autotest'" do
cmd = rspec_autotest.make_test_cmd(@files_to_test)
cmd.should match(' --tty ')
cmd.should_not match(' --autotest ')
end
end
describe "mappings" do
before do
@lib_file = "lib/something.rb"
@spec_file = "spec/something_spec.rb"
rspec_autotest.hook :initialize
end
it "finds the spec file for a given lib file" do
rspec_autotest.should map_specs([@spec_file]).to(@lib_file)
end
it "finds the spec file if given a spec file" do
rspec_autotest.should map_specs([@spec_file]).to(@spec_file)
end
it "ignores files in spec dir that aren't specs" do
rspec_autotest.should map_specs([]).to("spec/spec_helper.rb")
end
it "ignores untracked files (in @file)" do
rspec_autotest.should map_specs([]).to("lib/untracked_file")
end
end
describe "consolidating failures" do
let(:subject_file) { "lib/autotest/some.rb" }
let(:spec_file) { "spec/autotest/some_spec.rb" }
it "returns no failures if no failures were given in the output" do
rspec_autotest.consolidate_failures([[]]).should eq({})
end
it "returns a hash with the spec filename => spec name for each failure or error" do
failures = [ [ "false should be false", spec_file ] ]
rspec_autotest.consolidate_failures(failures).should eq({
spec_file => ["false should be false"]
})
end
context "when subject file appears before the spec file in the backtrace" do
let(:failures) do
[ [ "false should be false", "#{subject_file}:143:\n#{spec_file}:203:" ] ]
end
it "excludes the subject file" do
rspec_autotest.consolidate_failures(failures).keys.should_not include(subject_file)
end
it "includes the spec file" do
rspec_autotest.consolidate_failures(failures).keys.should include(spec_file)
end
end
end
describe "normalizing file names" do
it "ensures that a single file appears in files_to_test only once" do
@files_to_test = {}
['filename.rb', './filename.rb', File.expand_path('filename.rb')].each do |file|
@files_to_test[file] = []
end
rspec_autotest.normalize(@files_to_test).should have(1).file
end
end
end
Feature: run with ruby command
You can use the `ruby` command to run specs. You just need to require
`rspec/autorun`.
Generally speaking, you're better off using the `rspec` command, which
requires `rspec/autorun` for you, but some tools only work with the `ruby`
command.
Scenario:
Given a file named "example_spec.rb" with:
"""
require 'rspec/autorun'
describe 1 do
it "is < 2" do
1.should be < 2
end
end
"""
When I run `ruby example_spec.rb`
Then the output should contain "1 example, 0 failures"
# This is borrowed (slightly modified) from Scott Taylor's
# project_path project:
# http://github.com/smtlaissezfaire/project_path
require 'pathname'
module RSpec
module Core
module RubyProject
def add_to_load_path(*dirs)
dirs.map {|dir| add_dir_to_load_path(File.join(root, dir))}
end
def add_dir_to_load_path(dir)
$LOAD_PATH.unshift(dir) unless $LOAD_PATH.include?(dir)
end
def root
@project_root ||= determine_root
end
def determine_root
find_first_parent_containing('spec') || '.'
end
def find_first_parent_containing(dir)
ascend_until {|path| File.exists?(File.join(path, dir))}
end
def ascend_until
Pathname(File.expand_path('.')).ascend do |path|
return path if yield(path)
end
end
module_function :add_to_load_path
module_function :add_dir_to_load_path
module_function :root
module_function :determine_root
module_function :find_first_parent_containing
module_function :ascend_until
end
end
end
Feature: run all when everything filtered
Use the run_all_when_everything_filtered configuration option to do just
that. This works well when paired with an inclusion filter like ":focus =>
true", as it will run all the examples when none match the inclusion filter.
Background:
Given a file named "spec/spec_helper.rb" with:
"""
RSpec.configure do |c|
c.filter_run :focus => true
c.run_all_when_everything_filtered = true
end
"""
Scenario: no examples match filter (runs all examples)
Given a file named "spec/sample_spec.rb" with:
"""
require "spec_helper"
describe "group 1" do
it "group 1 example 1" do
end
it "group 1 example 2" do
end
end
describe "group 2" do
it "group 2 example 1" do
end
end
"""
When I run `rspec spec/sample_spec.rb --format doc`
Then the output should contain "All examples were filtered out; ignoring {:focus=>true}"
And the examples should all pass
And the output should contain:
"""
group 1
group 1 example 1
group 1 example 2
group 2
group 2 example 1
"""
require 'drb/drb'
module RSpec
module Core
class Runner
# Register an at_exit hook that runs the suite.
def self.autorun
return if autorun_disabled? || installed_at_exit? || running_in_drb?
at_exit { exit run(ARGV, $stderr, $stdout).to_i }
@installed_at_exit = true
end
AT_EXIT_HOOK_BACKTRACE_LINE = "#{__FILE__}:#{__LINE__ - 2}:in `autorun'"
def self.disable_autorun!
@autorun_disabled = true
end
def self.autorun_disabled?
@autorun_disabled ||= false
end
def self.installed_at_exit?
@installed_at_exit ||= false
end
def self.running_in_drb?
(DRb.current_server rescue false) &&
DRb.current_server.uri =~ /druby\:\/\/127.0.0.1\:/
end
def self.trap_interrupt
trap('INT') do
exit!(1) if RSpec.wants_to_quit
RSpec.wants_to_quit = true
STDERR.puts "\nExiting... Interrupt again to exit immediately."
end
end
# Run a suite of RSpec examples.
#
# This is used internally by RSpec to run a suite, but is available
# for use by any other automation tool.
#
# If you want to run this multiple times in the same process, and you
# want files like spec_helper.rb to be reloaded, be sure to load `load`
# instead of `require`.
#
# #### Parameters
# * +args+ - an array of command-line-supported arguments
# * +err+ - error stream (Default: $stderr)
# * +out+ - output stream (Default: $stdout)
#
# #### Returns
# * +Fixnum+ - exit status code (0/1)
def self.run(args, err=$stderr, out=$stdout)
trap_interrupt
options = ConfigurationOptions.new(args)
options.parse_options
if options.options[:drb]
begin
run_over_drb(options, err, out)
rescue DRb::DRbConnError
err.puts "No DRb server is running. Running in local process instead ..."
run_in_process(options, err, out)
end
else
run_in_process(options, err, out)
end
ensure
RSpec.reset
end
def self.run_over_drb(options, err, out)
DRbCommandLine.new(options).run(err, out)
end
def self.run_in_process(options, err, out)
CommandLine.new(options, RSpec::configuration, RSpec::world).run(err, out)
end
end
end
end
# -*- ruby encoding: utf-8 -*-
require 'spec_helper'
describe "Diff::LCS.sdiff" do
include Diff::LCS::SpecHelper::Matchers
shared_examples "compare sequences correctly" do
it "should compare s1 -> s2 correctly" do
Diff::LCS.sdiff(s1, s2).should == context_diff(result)
end
it "should compare s2 -> s1 correctly" do
Diff::LCS.sdiff(s2, s1).should == context_diff(reverse_sdiff(result))
end
end
describe "using seq1 & seq2" do
let(:s1) { seq1 }
let(:s2) { seq2 }
let(:result) { correct_forward_sdiff }
it_has_behavior "compare sequences correctly"
end
describe "using %w(abc def yyy xxx ghi jkl) & %w(abc dxf xxx ghi jkl)" do
let(:s1) { %w(abc def yyy xxx ghi jkl) }
let(:s2) { %w(abc dxf xxx ghi jkl) }
let(:result) {
[
[ '=', [ 0, 'abc' ], [ 0, 'abc' ] ],
[ '!', [ 1, 'def' ], [ 1, 'dxf' ] ],
[ '-', [ 2, 'yyy' ], [ 2, nil ] ],
[ '=', [ 3, 'xxx' ], [ 2, 'xxx' ] ],
[ '=', [ 4, 'ghi' ], [ 3, 'ghi' ] ],
[ '=', [ 5, 'jkl' ], [ 4, 'jkl' ] ]
]
}
it_has_behavior "compare sequences correctly"
end
describe "using %w(a b c d e) & %w(a e)" do
let(:s1) { %w(a b c d e) }
let(:s2) { %w(a e) }
let(:result) {
[
[ '=', [ 0, 'a' ], [ 0, 'a' ] ],
[ '-', [ 1, 'b' ], [ 1, nil ] ],
[ '-', [ 2, 'c' ], [ 1, nil ] ],
[ '-', [ 3, 'd' ], [ 1, nil ] ],
[ '=', [ 4, 'e' ], [ 1, 'e' ] ]
]
}
it_has_behavior "compare sequences correctly"
end
describe "using %w(a e) & %w(a b c d e)" do
let(:s1) { %w(a e) }
let(:s2) { %w(a b c d e) }
let(:result) {
[
[ '=', [ 0, 'a' ], [ 0, 'a' ] ],
[ '+', [ 1, nil ], [ 1, 'b' ] ],
[ '+', [ 1, nil ], [ 2, 'c' ] ],
[ '+', [ 1, nil ], [ 3, 'd' ] ],
[ '=', [ 1, 'e' ], [ 4, 'e' ] ]
]
}
it_has_behavior "compare sequences correctly"
end
describe "using %w(v x a e) & %w(w y a b c d e)" do
let(:s1) { %w(v x a e) }
let(:s2) { %w(w y a b c d e) }
let(:result) {
[
[ '!', [ 0, 'v' ], [ 0, 'w' ] ],
[ '!', [ 1, 'x' ], [ 1, 'y' ] ],
[ '=', [ 2, 'a' ], [ 2, 'a' ] ],
[ '+', [ 3, nil ], [ 3, 'b' ] ],
[ '+', [ 3, nil ], [ 4, 'c' ] ],
[ '+', [ 3, nil ], [ 5, 'd' ] ],
[ '=', [ 3, 'e' ], [ 6, 'e' ] ]
]
}
it_has_behavior "compare sequences correctly"
end
describe "using %w(x a e) & %w(a b c d e)" do
let(:s1) { %w(x a e) }
let(:s2) { %w(a b c d e) }
let(:result) {
[
[ '-', [ 0, 'x' ], [ 0, nil ] ],
[ '=', [ 1, 'a' ], [ 0, 'a' ] ],
[ '+', [ 2, nil ], [ 1, 'b' ] ],
[ '+', [ 2, nil ], [ 2, 'c' ] ],
[ '+', [ 2, nil ], [ 3, 'd' ] ],
[ '=', [ 2, 'e' ], [ 4, 'e' ] ]
]
}
it_has_behavior "compare sequences correctly"
end
describe "using %w(a e) & %w(x a b c d e)" do
let(:s1) { %w(a e) }
let(:s2) { %w(x a b c d e) }
let(:result) {
[
[ '+', [ 0, nil ], [ 0, 'x' ] ],
[ '=', [ 0, 'a' ], [ 1, 'a' ] ],
[ '+', [ 1, nil ], [ 2, 'b' ] ],
[ '+', [ 1, nil ], [ 3, 'c' ] ],
[ '+', [ 1, nil ], [ 4, 'd' ] ],
[ '=', [ 1, 'e' ], [ 5, 'e' ] ]
]
}
it_has_behavior "compare sequences correctly"
end
describe "using %w(a e v) & %w(x a b c d e w x)" do
let(:s1) { %w(a e v) }
let(:s2) { %w(x a b c d e w x) }
let(:result) {
[
[ '+', [ 0, nil ], [ 0, 'x' ] ],
[ '=', [ 0, 'a' ], [ 1, 'a' ] ],
[ '+', [ 1, nil ], [ 2, 'b' ] ],
[ '+', [ 1, nil ], [ 3, 'c' ] ],
[ '+', [ 1, nil ], [ 4, 'd' ] ],
[ '=', [ 1, 'e' ], [ 5, 'e' ] ],
[ '!', [ 2, 'v' ], [ 6, 'w' ] ],
[ '+', [ 3, nil ], [ 7, 'x' ] ]
]
}
it_has_behavior "compare sequences correctly"
end
describe "using %w() & %w(a b c)" do
let(:s1) { %w() }
let(:s2) { %w(a b c) }
let(:result) {
[
[ '+', [ 0, nil ], [ 0, 'a' ] ],
[ '+', [ 0, nil ], [ 1, 'b' ] ],
[ '+', [ 0, nil ], [ 2, 'c' ] ]
]
}
it_has_behavior "compare sequences correctly"
end
describe "using %w(a b c) & %w(1)" do
let(:s1) { %w(a b c) }
let(:s2) { %w(1) }
let(:result) {
[
[ '!', [ 0, 'a' ], [ 0, '1' ] ],
[ '-', [ 1, 'b' ], [ 1, nil ] ],
[ '-', [ 2, 'c' ], [ 1, nil ] ]
]
}
it_has_behavior "compare sequences correctly"
end
describe "using %w(a b c) & %w(c)" do
let(:s1) { %w(a b c) }
let(:s2) { %w(c) }
let(:result) {
[
[ '-', [ 0, 'a' ], [ 0, nil ] ],
[ '-', [ 1, 'b' ], [ 0, nil ] ],
[ '=', [ 2, 'c' ], [ 0, 'c' ] ]
]
}
it_has_behavior "compare sequences correctly"
end
describe "using %w(abcd efgh ijkl mnop) & []" do
let(:s1) { %w(abcd efgh ijkl mnop) }
let(:s2) { [] }
let(:result) {
[
[ '-', [ 0, 'abcd' ], [ 0, nil ] ],
[ '-', [ 1, 'efgh' ], [ 0, nil ] ],
[ '-', [ 2, 'ijkl' ], [ 0, nil ] ],
[ '-', [ 3, 'mnop' ], [ 0, nil ] ]
]
}
it_has_behavior "compare sequences correctly"
end
end
# vim: ft=ruby
Feature: shared context
Use `shared_context` to define a block that will be evaluated in the context
of example groups either explicitly, using `include_context`, or implicitly by
matching metadata.
Background:
Given a file named "shared_stuff.rb" with:
"""
shared_context "shared stuff", :a => :b do
before { @some_var = :some_value }
def shared_method
"it works"
end
let(:shared_let) { {'arbitrary' => 'object'} }
subject do
'this is the subject'
end
end
"""
Scenario: declare shared context and include it with include_context
Given a file named "shared_context_example.rb" with:
"""
require "./shared_stuff.rb"
describe "group that includes a shared context using 'include_context'" do
include_context "shared stuff"
it "has access to methods defined in shared context" do
shared_method.should eq("it works")
end
it "has access to methods defined with let in shared context" do
shared_let['arbitrary'].should eq('object')
end
it "runs the before hooks defined in the shared context" do
@some_var.should be(:some_value)
end
it "accesses the subject defined in the shared context" do
subject.should eq('this is the subject')
end
end
"""
When I run `rspec shared_context_example.rb`
Then the examples should all pass
Scenario: declare shared context and include it with metadata
Given a file named "shared_context_example.rb" with:
"""
require "./shared_stuff.rb"
describe "group that includes a shared context using metadata", :a => :b do
it "has access to methods defined in shared context" do
shared_method.should eq("it works")
end
it "has access to methods defined with let in shared context" do
shared_let['arbitrary'].should eq('object')
end
it "runs the before hooks defined in the shared context" do
@some_var.should be(:some_value)
end
it "accesses the subject defined in the shared context" do
subject.should eq('this is the subject')
end
end
"""
When I run `rspec shared_context_example.rb`
Then the examples should all pass
module RSpec
module Core
# Exposes [ExampleGroup](ExampleGroup)-level methods to a module, so you
# can include that module in an [ExampleGroup](ExampleGroup).
#
# @example
#
# module LoggedInAsAdmin
# extend RSpec::Core::SharedContext
# before(:each) do
# log_in_as :admin
# end
# end
#
# describe "admin section" do
# include LoggedInAsAdmin
# # ...
# end
module SharedContext
include Hooks
include Let::ExampleGroupMethods
def included(group)
[:before, :after].each do |type|
[:all, :each].each do |scope|
group.hooks[type][scope].concat hooks[type][scope]
end
end
_nested_group_declarations.each do |name, block, *args|
group.describe name, *args, &block
end
end
def describe(name, *args, &block)
_nested_group_declarations << [name, block, *args]
end
alias_method :context, :describe
private
def _nested_group_declarations
@_nested_group_declarations ||= []
end
end
end
SharedContext = Core::SharedContext
end
module RSpec
module Core
module SharedExampleGroup
# @overload shared_examples(name, &block)
# @overload shared_examples(name, tags, &block)
#
# Creates and stores (but does not evaluate) the block.
#
# @see ExampleGroup.include_examples
# @see ExampleGroup.include_context
def shared_examples(*args, &block)
if [String, Symbol, Module].any? {|cls| cls === args.first }
object = args.shift
ensure_shared_example_group_name_not_taken(object)
RSpec.world.shared_example_groups[object] = block
end
unless args.empty?
mod = Module.new
(class << mod; self; end).send(:define_method, :extended) do |host|
host.class_eval(&block)
end
RSpec.configuration.extend(mod, *args)
end
end
alias_method :shared_context, :shared_examples
alias_method :share_examples_for, :shared_examples
alias_method :shared_examples_for, :shared_examples
def share_as(name, &block)
if Object.const_defined?(name)
mod = Object.const_get(name)
raise_name_error unless mod.created_from_caller(caller)
end
mod = Module.new do
@shared_block = block
@caller_line = caller.last
def self.created_from_caller(other_caller)
@caller_line == other_caller.last
end
def self.included(kls)
kls.describe(&@shared_block)
kls.children.first.metadata[:shared_group_name] = name
end
end
shared_const = Object.const_set(name, mod)
RSpec.world.shared_example_groups[shared_const] = block
end
private
def raise_name_error
raise NameError, "The first argument (#{name}) to share_as must be a legal name for a constant not already in use."
end
def ensure_shared_example_group_name_not_taken(name)
if RSpec.world.shared_example_groups.has_key?(name)
raise ArgumentError.new("Shared example group '#{name}' already exists")
end
end
end
end
end
include RSpec::Core::SharedExampleGroup
Feature: shared examples
Shared examples let you describe behaviour of types or modules. When
declared, a shared group's content is stored. It is only realized in the
context of another example group, which provides any context the shared group
needs to run.
A shared group is included in another group using any of:
include_examples "name" # include the examples in the current context
it_behaves_like "name" # include the examples in a nested context
it_should_behave_like "name" # include the examples in a nested context
WARNING: Files containing shared groups must be loaded before the files that
use them. While there are conventions to handle this, RSpec does _not_ do
anything special (like autoload). Doing so would require a strict naming
convention for files that would break existing suites.
CONVENTIONS:
1. The simplest approach is to require files with shared examples explicitly
from the files that use them. Keep in mind that RSpec adds the `spec`
directory to the `LOAD_PATH`, so you can say `require
'shared_examples_for_widgets'` to require a file at
`#{PROJECT_ROOT}/spec/shared_examples_for_widgets.rb`.
2. Put files containing shared examples in `spec/support/` and require files
in that directory from `spec/spec_helper.rb`:
Dir["./spec/support/**/*.rb"].each {|f| require f}
This is included in the generated `spec/spec_helper.rb` file in
`rspec-rails`
3. When all of the groups that include the shared group, just declare
the shared group in the same file.
Scenario: shared examples group included in two groups in one file
Given a file named "collection_spec.rb" with:
"""
require "set"
shared_examples "a collection" do
let(:collection) { described_class.new([7, 2, 4]) }
context "initialized with 3 items" do
it "says it has three items" do
collection.size.should eq(3)
end
end
describe "#include?" do
context "with an an item that is in the collection" do
it "returns true" do
collection.include?(7).should be_true
end
end
context "with an an item that is not in the collection" do
it "returns false" do
collection.include?(9).should be_false
end
end
end
end
describe Array do
it_behaves_like "a collection"
end
describe Set do
it_behaves_like "a collection"
end
"""
When I run `rspec collection_spec.rb --format documentation`
Then the examples should all pass
And the output should contain:
"""
Array
behaves like a collection
initialized with 3 items
says it has three items
#include?
with an an item that is in the collection
returns true
with an an item that is not in the collection
returns false
Set
behaves like a collection
initialized with 3 items
says it has three items
#include?
with an an item that is in the collection
returns true
with an an item that is not in the collection
returns false
"""
Scenario: Providing context to a shared group using a block
Given a file named "shared_example_group_spec.rb" with:
"""
require "set"
shared_examples "a collection object" do
describe "<<" do
it "adds objects to the end of the collection" do
collection << 1
collection << 2
collection.to_a.should eq([1,2])
end
end
end
describe Array do
it_behaves_like "a collection object" do
let(:collection) { Array.new }
end
end
describe Set do
it_behaves_like "a collection object" do
let(:collection) { Set.new }
end
end
"""
When I run `rspec shared_example_group_spec.rb --format documentation`
Then the examples should all pass
And the output should contain:
"""
Array
behaves like a collection object
<<
adds objects to the end of the collection
Set
behaves like a collection object
<<
adds objects to the end of the collection
"""
Scenario: Passing parameters to a shared example group
Given a file named "shared_example_group_params_spec.rb" with:
"""
shared_examples "a measurable object" do |measurement, measurement_methods|
measurement_methods.each do |measurement_method|
it "should return #{measurement} from ##{measurement_method}" do
subject.send(measurement_method).should == measurement
end
end
end
describe Array, "with 3 items" do
subject { [1, 2, 3] }
it_should_behave_like "a measurable object", 3, [:size, :length]
end
describe String, "of 6 characters" do
subject { "FooBar" }
it_should_behave_like "a measurable object", 6, [:size, :length]
end
"""
When I run `rspec shared_example_group_params_spec.rb --format documentation`
Then the examples should all pass
And the output should contain:
"""
Array with 3 items
it should behave like a measurable object
should return 3 from #size
should return 3 from #length
String of 6 characters
it should behave like a measurable object
should return 6 from #size
should return 6 from #length
"""
Scenario: Aliasing "it_should_behave_like" to "it_has_behavior"
Given a file named "shared_example_group_spec.rb" with:
"""
RSpec.configure do |c|
c.alias_it_should_behave_like_to :it_has_behavior, 'has behavior:'
end
shared_examples 'sortability' do
it 'responds to <=>' do
sortable.should respond_to(:<=>)
end
end
describe String do
it_has_behavior 'sortability' do
let(:sortable) { 'sample string' }
end
end
"""
When I run `rspec shared_example_group_spec.rb --format documentation`
Then the examples should all pass
And the output should contain:
"""
String
has behavior: sortability
responds to <=>
"""
module RSpec
module Core
module Formatters
# This class extracts code snippets by looking at the backtrace of the passed error
class SnippetExtractor
class NullConverter; def convert(code, pre); code; end; end
begin
require 'syntax/convertors/html'
@@converter = Syntax::Convertors::HTML.for_syntax "ruby"
rescue LoadError
@@converter = NullConverter.new
end
def snippet(backtrace)
raw_code, line = snippet_for(backtrace[0])
highlighted = @@converter.convert(raw_code, false)
highlighted << "\n<span class=\"comment\"># gem install syntax to get syntax highlighting</span>" if @@converter.is_a?(NullConverter)
post_process(highlighted, line)
end
def snippet_for(error_line)
if error_line =~ /(.*):(\d+)/
file = $1
line = $2.to_i
[lines_around(file, line), line]
else
["# Couldn't get snippet for #{error_line}", 1]
end
end
def lines_around(file, line)
if File.file?(file)
lines = File.read(file).split("\n")
min = [0, line-3].max
max = [line+1, lines.length-1].min
selected_lines = []
selected_lines.join("\n")
lines[min..max].join("\n")
else
"# Couldn't get snippet for #{file}"
end
end
def post_process(highlighted, offending_line)
new_lines = []
highlighted.split("\n").each_with_index do |line, i|
new_line = "<span class=\"linenum\">#{offending_line+i-2}</span>#{line}"
new_line = "<span class=\"offending\">#{new_line}</span>" if i == 2
new_lines << new_line
end
new_lines.join("\n")
end
end
end
end
end
# -*- ruby encoding: utf-8 -*-
require 'rubygems'
require 'pathname'
file = Pathname.new(__FILE__).expand_path
path = file.parent
parent = path.parent
$:.unshift parent.join('lib')
require 'diff-lcs'
module Diff::LCS::SpecHelper
def seq1
%w(a b c e h j l m n p)
end
def skipped_seq1
%w(a h n p)
end
def seq2
%w(b c d e f j k l m r s t)
end
def skipped_seq2
%w(d f k r s t)
end
def word_sequence
%w(abcd efgh ijkl mnopqrstuvwxyz)
end
def correct_lcs
%w(b c e j l m)
end
def correct_forward_diff
[
[ [ '-', 0, 'a' ] ],
[ [ '+', 2, 'd' ] ],
[ [ '-', 4, 'h' ],
[ '+', 4, 'f' ] ],
[ [ '+', 6, 'k' ] ],
[ [ '-', 8, 'n' ],
[ '-', 9, 'p' ],
[ '+', 9, 'r' ],
[ '+', 10, 's' ],
[ '+', 11, 't' ] ]
]
end
def correct_backward_diff
[
[ [ '+', 0, 'a' ] ],
[ [ '-', 2, 'd' ] ],
[ [ '-', 4, 'f' ],
[ '+', 4, 'h' ] ],
[ [ '-', 6, 'k' ] ],
[
[ '-', 9, 'r' ],
[ '-', 10, 's' ],
[ '+', 8, 'n' ],
[ '-', 11, 't' ],
[ '+', 9, 'p' ] ]
]
end
def correct_forward_sdiff
[
[ '-', [ 0, 'a' ], [ 0, nil ] ],
[ '=', [ 1, 'b' ], [ 0, 'b' ] ],
[ '=', [ 2, 'c' ], [ 1, 'c' ] ],
[ '+', [ 3, nil ], [ 2, 'd' ] ],
[ '=', [ 3, 'e' ], [ 3, 'e' ] ],
[ '!', [ 4, 'h' ], [ 4, 'f' ] ],
[ '=', [ 5, 'j' ], [ 5, 'j' ] ],
[ '+', [ 6, nil ], [ 6, 'k' ] ],
[ '=', [ 6, 'l' ], [ 7, 'l' ] ],
[ '=', [ 7, 'm' ], [ 8, 'm' ] ],
[ '!', [ 8, 'n' ], [ 9, 'r' ] ],
[ '!', [ 9, 'p' ], [ 10, 's' ] ],
[ '+', [ 10, nil ], [ 11, 't' ] ]
]
end
def reverse_sdiff(forward_sdiff)
forward_sdiff.map { |line|
line[1], line[2] = line[2], line[1]
case line[0]
when '-' then line[0] = '+'
when '+' then line[0] = '-'
end
line
}
end
def change_diff(diff)
map_diffs(diff, Diff::LCS::Change)
end
def context_diff(diff)
map_diffs(diff, Diff::LCS::ContextChange)
end
def format_diffs(diffs)
diffs.map do |e|
if e.kind_of?(Array)
e.map { |f| f.to_a.join }.join(", ")
else
e.to_a.join
end
end.join("\n")
end
def map_diffs(diffs, klass = Diff::LCS::ContextChange)
diffs.map do |chunks|
if klass == Diff::LCS::ContextChange
klass.from_a(chunks)
else
chunks.map { |changes| klass.from_a(changes) }
end
end
end
def balanced_traversal(s1, s2, callback_type)
callback = __send__(callback_type)
Diff::LCS.traverse_balanced(s1, s2, callback)
callback
end
def balanced_reverse(change_result)
new_result = []
change_result.each { |line|
line = [ line[0], line[2], line[1] ]
case line[0]
when '<'
line[0] = '>'
when '>'
line[0] = '<'
end
new_result << line
}
new_result.sort_by { |line| [ line[1], line[2] ] }
end
def map_to_no_change(change_result)
new_result = []
change_result.each { |line|
case line[0]
when '!'
new_result << [ '<', line[1], line[2] ]
new_result << [ '>', line[1] + 1, line[2] ]
else
new_result << line
end
}
new_result
end
def simple_callback
callbacks = Object.new
class << callbacks
attr_reader :matched_a
attr_reader :matched_b
attr_reader :discards_a
attr_reader :discards_b
attr_reader :done_a
attr_reader :done_b
def reset
@matched_a = []
@matched_b = []
@discards_a = []
@discards_b = []
@done_a = []
@done_b = []
end
def match(event)
@matched_a << event.old_element
@matched_b << event.new_element
end
def discard_b(event)
@discards_b << event.new_element
end
def discard_a(event)
@discards_a << event.old_element
end
def finished_a(event)
@done_a << [event.old_element, event.old_position,
event.new_element, event.new_position]
end
def finished_b(event)
p "called #finished_b"
@done_b << [event.old_element, event.old_position,
event.new_element, event.new_position]
end
end
callbacks.reset
callbacks
end
def simple_callback_no_finishers
simple = simple_callback
class << simple
undef :finished_a
undef :finished_b
end
simple
end
def balanced_callback
cb = Object.new
class << cb
attr_reader :result
def reset
@result = []
end
def match(event)
@result << [ "=", event.old_position, event.new_position ]
end
def discard_a(event)
@result << [ "<", event.old_position, event.new_position ]
end
def discard_b(event)
@result << [ ">", event.old_position, event.new_position ]
end
def change(event)
@result << [ "!", event.old_position, event.new_position ]
end
end
cb.reset
cb
end
def balanced_callback_no_change
balanced = balanced_callback
class << balanced
undef :change
end
balanced
end
module Matchers
extend RSpec::Matchers::DSL
matcher :be_nil_or_match_values do |ii, s1, s2|
match do |ee|
ee.should satisfy { |vee| vee.nil? || s1[ii] == s2[ee] }
end
end
matcher :correctly_map_sequence do |s1|
match do |actual|
actual.each_with_index { |ee, ii|
ee.should be_nil_or_match_values(ii, s1, @s2)
}
end
chain :to_other_sequence do |s2|
@s2 = s2
end
end
end
end
RSpec.configure do |conf|
conf.include Diff::LCS::SpecHelper
conf.alias_it_should_behave_like_to :it_has_behavior, 'has behavior:'
end
# vim: ft=ruby
#! /usr/env/bin ruby
#--
# Copyright 2004 Austin Ziegler <diff-lcs@halostatue.ca>
# adapted from:
# Algorithm::Diff (Perl) by Ned Konz <perl@bike-nomad.com>
# Smalltalk by Mario I. Wolczko <mario@wolczko.com>
# implements McIlroy-Hunt diff algorithm
#
# This program is free software. It may be redistributed and/or modified under
# the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
# Ruby licence.
#
# $Id$
#++
# Includes Diff::LCS into String.
class String
include Diff::LCS
end
module RSpec
module Core
module Subject
module ExampleMethods
# Returns the subject defined by the example group. The subject block is
# only executed once per example, the result of which is cached and
# returned by any subsequent calls to +subject+.
#
# If a class is passed to +describe+ and no subject is explicitly
# declared in the example group, then +subject+ will return a new
# instance of that class.
#
# @example
#
# # explicit subject defined by the subject method
# describe Person do
# subject { Person.new(:birthdate => 19.years.ago) }
# it "should be eligible to vote" do
# subject.should be_eligible_to_vote
# end
# end
#
# # implicit subject => { Person.new }
# describe Person do
# it "should be eligible to vote" do
# subject.should be_eligible_to_vote
# end
# end
def subject
if defined?(@original_subject)
@original_subject
else
@original_subject = instance_eval(&self.class.subject)
end
end
begin
require 'rspec/expectations/extensions/kernel'
alias_method :__should_for_example_group__, :should
alias_method :__should_not_for_example_group__, :should_not
# When +should+ is called with no explicit receiver, the call is
# delegated to the object returned by +subject+. Combined with
# an implicit subject (see +subject+), this supports very concise
# expressions.
#
# @example
#
# describe Person do
# it { should be_eligible_to_vote }
# end
def should(matcher=nil, message=nil)
self == subject ? self.__should_for_example_group__(matcher) : subject.should(matcher,message)
end
# Just like +should+, +should_not+ delegates to the subject (implicit or
# explicit) of the example group.
#
# @example
#
# describe Person do
# it { should_not be_eligible_to_vote }
# end
def should_not(matcher=nil, message=nil)
self == subject ? self.__should_not_for_example_group__(matcher) : subject.should_not(matcher,message)
end
rescue LoadError
end
end
module ExampleGroupMethods
# Creates a nested example group named by the submitted +attribute+,
# and then generates an example using the submitted block.
#
# # This ...
# describe Array do
# its(:size) { should eq(0) }
# end
#
# # ... generates the same runtime structure as this:
# describe Array do
# describe "size" do
# it "should eq(0)" do
# subject.size.should eq(0)
# end
# end
# end
#
# The attribute can be a +Symbol+ or a +String+. Given a +String+
# with dots, the result is as though you concatenated that +String+
# onto the subject in an expression.
#
# describe Person do
# subject do
# Person.new.tap do |person|
# person.phone_numbers << "555-1212"
# end
# end
#
# its("phone_numbers.first") { should eq("555-1212") }
# end
#
# When the subject is a +Hash+, you can refer to the Hash keys by
# specifying a +Symbol+ or +String+ in an array.
#
# describe "a configuration Hash" do
# subject do
# { :max_users => 3,
# 'admin' => :all_permissions }
# end
#
# its([:max_users]) { should eq(3) }
# its(['admin']) { should eq(:all_permissions) }
#
# # You can still access to its regular methods this way:
# its(:keys) { should include(:max_users) }
# its(:count) { should eq(2) }
# end
def its(attribute, &block)
describe(attribute) do
example do
self.class.class_eval do
define_method(:subject) do
@_subject ||= if attribute.is_a?(Array)
super()[*attribute]
else
attribute.to_s.split('.').inject(super()) do |target, method|
target.send(method)
end
end
end
end
instance_eval(&block)
end
end
end
# Defines an explicit subject for an example group which can then be the
# implicit receiver (through delegation) of calls to +should+.
#
# @example
#
# describe CheckingAccount, "with $50" do
# subject { CheckingAccount.new(:amount => 50, :currency => :USD) }
# it { should have_a_balance_of(50, :USD) }
# it { should_not be_overdrawn }
# end
#
# See +ExampleMethods#should+ for more information about this approach.
def subject(&block)
block ? @explicit_subject_block = block : explicit_subject || implicit_subject
end
attr_reader :explicit_subject_block
private
def explicit_subject
group = self
while group.respond_to?(:explicit_subject_block)
return group.explicit_subject_block if group.explicit_subject_block
group = group.superclass
end
end
def implicit_subject
described = described_class || description
Class === described ? proc { described.new } : proc { described }
end
end
end
end
end
Feature: --tag option
Use the --tag (or -t) option to filter the examples by tags.
The tag can be a simple name or a name:value pair. In the first case,
examples with :name => true will be filtered. In the second case, examples
with :name => value will be filtered, where value is always a string. In
both cases, name is converted to a symbol.
Tags can also be used to exclude examples by adding a ~ before the tag. For
example ~tag will exclude all examples marked with :tag => true and
~tag:value will exclude all examples marked with :tag => value.
To be compatible with the Cucumber syntax, tags can optionally start with
an @ symbol, which will be ignored.
Background:
Given a file named "tagged_spec.rb" with:
"""
describe "group with tagged specs" do
it "example I'm working now", :focus => true do; end
it "special example with string", :type => 'special' do; end
it "special example with symbol", :type => :special do; end
it "slow example", :skip => true do; end
it "ordinary example", :speed => 'slow' do; end
it "untagged example" do; end
end
"""
Scenario: filter examples with non-existent tag
When I run `rspec . --tag mytag`
And the examples should all pass
Scenario: filter examples with a simple tag
When I run `rspec . --tag focus`
Then the output should contain "include {:focus=>true}"
And the examples should all pass
Scenario: filter examples with a simple tag and @
When I run `rspec . --tag @focus`
Then the output should contain "include {:focus=>true}"
Then the examples should all pass
Scenario: filter examples with a name:value tag
When I run `rspec . --tag type:special`
Then the output should contain:
"""
include {:type=>"special"}
"""
And the output should contain "2 examples"
And the examples should all pass
Scenario: filter examples with a name:value tag and @
When I run `rspec . --tag @type:special`
Then the output should contain:
"""
include {:type=>"special"}
"""
And the examples should all pass
Scenario: exclude examples with a simple tag
When I run `rspec . --tag ~skip`
Then the output should contain "exclude {:skip=>true}"
Then the examples should all pass
Scenario: exclude examples with a simple tag and @
When I run `rspec . --tag ~@skip`
Then the output should contain "exclude {:skip=>true}"
Then the examples should all pass
Scenario: exclude examples with a name:value tag
When I run `rspec . --tag ~speed:slow`
Then the output should contain:
"""
exclude {:speed=>"slow"}
"""
Then the examples should all pass
Scenario: exclude examples with a name:value tag and @
When I run `rspec . --tag ~@speed:slow`
Then the output should contain:
"""
exclude {:speed=>"slow"}
"""
Then the examples should all pass
Scenario: filter examples with a simple tag, exclude examples with another tag
When I run `rspec . --tag focus --tag ~skip`
Then the output should contain "include {:focus=>true}"
And the output should contain "exclude {:skip=>true}"
And the examples should all pass
Feature: text formatter
In order to easily see the result of running my specs
As an RSpec user
I want clear, concise, well-formatted output
Scenario: Backtrace formatting for failing specs in multiple files
Given a file named "string_spec.rb" with:
"""
describe String do
it "has a failing example" do
"foo".reverse.should eq("ofo")
end
end
"""
And a file named "integer_spec.rb" with:
"""
describe Integer do
it "has a failing example" do
(7 + 5).should eq(11)
end
end
"""
When I run `rspec integer_spec.rb string_spec.rb`
Then the backtrace-normalized output should contain:
"""
Failures:
1) Integer has a failing example
Failure/Error: (7 + 5).should eq(11)
expected: 11
got: 12
(compared using ==)
# ./integer_spec.rb:3
2) String has a failing example
Failure/Error: "foo".reverse.should eq("ofo")
expected: "ofo"
got: "oof"
(compared using ==)
# ./string_spec.rb:3
"""
require 'cgi'
require 'rspec/core/formatters/html_formatter'
module RSpec
module Core
module Formatters
# Formats backtraces so they're clickable by TextMate
class TextMateFormatter < HtmlFormatter
def backtrace_line(line, skip_textmate_conversion=false)
if skip_textmate_conversion
super(line)
else
format_backtrace_line_for_textmate(super(line))
end
end
def format_backtrace_line_for_textmate(line)
return nil unless line
CGI.escapeHTML(line).sub(/([^:]*\.e?rb):(\d*)/) do
"<a href=\"txmt://open?url=file://#{File.expand_path($1)}&line=#{$2}\">#{$1}:#{$2}</a> "
end
end
def extra_failure_content(exception)
require 'rspec/core/formatters/snippet_extractor'
backtrace = exception.backtrace.map {|line| backtrace_line(line, :skip_textmate_conversion)}
backtrace.compact!
@snippet_extractor ||= SnippetExtractor.new
" <pre class=\"ruby\"><code>#{@snippet_extractor.snippet(backtrace)}</code></pre>"
end
end
end
end
end
# -*- ruby encoding: utf-8 -*-
require 'spec_helper'
describe "Diff::LCS.traverse_balanced" do
include Diff::LCS::SpecHelper::Matchers
shared_examples "with a #change callback" do |s1, s2, result|
it "should traverse s1 -> s2 correctly" do
traversal = balanced_traversal(s1, s2, :balanced_callback)
traversal.result.should == result
end
it "should traverse s2 -> s1 correctly" do
traversal = balanced_traversal(s2, s1, :balanced_callback)
traversal.result.should == balanced_reverse(result)
end
end
shared_examples "without a #change callback" do |s1, s2, result|
it "should traverse s1 -> s2 correctly" do
traversal = balanced_traversal(s1, s2, :balanced_callback_no_change)
traversal.result.should == map_to_no_change(result)
end
it "should traverse s2 -> s1 correctly" do
traversal = balanced_traversal(s2, s1, :balanced_callback_no_change)
traversal.result.should == map_to_no_change(balanced_reverse(result))
end
end
describe "sequences %w(a b c) & %w(a x c)" do
s1 = %w(a b c)
s2 = %w(a x c)
result = [
[ '=', 0, 0 ],
[ '!', 1, 1 ],
[ '=', 2, 2 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "sequences %w(a x y c) & %w(a v w c)" do
s1 = %w(a x y c)
s2 = %w(a v w c)
result = [
[ '=', 0, 0 ],
[ '!', 1, 1 ],
[ '!', 2, 2 ],
[ '=', 3, 3 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "sequences %w(x y c) & %w(v w c)" do
s1 = %w(x y c)
s2 = %w(v w c)
result = [
[ '!', 0, 0 ],
[ '!', 1, 1 ],
[ '=', 2, 2 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "sequences %w(a x y z) & %w(b v w)" do
s1 = %w(a x y z)
s2 = %w(b v w)
result = [
[ '!', 0, 0 ],
[ '!', 1, 1 ],
[ '!', 2, 2 ],
[ '<', 3, 3 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "sequences %w(a z) & %w(a)" do
s1 = %w(a z)
s2 = %w(a)
result = [
[ '=', 0, 0 ],
[ '<', 1, 1 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "sequences %w(z a) & %w(a)" do
s1 = %w(z a)
s2 = %w(a)
result = [
[ '<', 0, 0 ],
[ '=', 1, 0 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "sequences %w(a b c) & %w(x y z)" do
s1 = %w(a b c)
s2 = %w(x y z)
result = [
[ '!', 0, 0 ],
[ '!', 1, 1 ],
[ '!', 2, 2 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "sequences %w(abcd efgh ijkl mnoopqrstuvwxyz) & []" do
s1 = %w(abcd efgh ijkl mnopqrstuvwxyz)
s2 = []
result = [
[ '<', 0, 0 ],
[ '<', 1, 0 ],
[ '<', 2, 0 ],
[ '<', 3, 0 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "strings %Q(a b c) & %Q(a x c)" do
s1 = %Q(a b c)
s2 = %Q(a x c)
result = [
[ '=', 0, 0 ],
[ '=', 1, 1 ],
[ '!', 2, 2 ],
[ '=', 3, 3 ],
[ '=', 4, 4 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "strings %Q(a x y c) & %Q(a v w c)" do
s1 = %Q(a x y c)
s2 = %Q(a v w c)
result = [
[ '=', 0, 0 ],
[ '=', 1, 1 ],
[ '!', 2, 2 ],
[ '=', 3, 3 ],
[ '!', 4, 4 ],
[ '=', 5, 5 ],
[ '=', 6, 6 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "strings %Q(x y c) & %Q(v w c)" do
s1 = %Q(x y c)
s2 = %Q(v w c)
result = [
[ '!', 0, 0 ],
[ '=', 1, 1 ],
[ '!', 2, 2 ],
[ '=', 3, 3 ],
[ '=', 4, 4 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "strings %Q(a x y z) & %Q(b v w)" do
s1 = %Q(a x y z)
s2 = %Q(b v w)
result = [
[ '!', 0, 0 ],
[ '=', 1, 1 ],
[ '!', 2, 2 ],
[ '=', 3, 3 ],
[ '!', 4, 4 ],
[ '<', 5, 5 ],
[ '<', 6, 5 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "strings %Q(a z) & %Q(a)" do
s1 = %Q(a z)
s2 = %Q(a)
result = [
[ '=', 0, 0 ],
[ '<', 1, 1 ],
[ '<', 2, 1 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "strings %Q(z a) & %Q(a)" do
s1 = %Q(z a)
s2 = %Q(a)
result = [
[ '<', 0, 0 ],
[ '<', 1, 0 ],
[ '=', 2, 0 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "strings %Q(a b c) & %Q(x y z)" do
s1 = %Q(a b c)
s2 = %Q(x y z)
result = [
[ '!', 0, 0 ],
[ '=', 1, 1 ],
[ '!', 2, 2 ],
[ '=', 3, 3 ],
[ '!', 4, 4 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "strings %Q(abcd efgh ijkl mnopqrstuvwxyz) & %Q()" do
s1 = %Q(abcd efgh ijkl mnopqrstuvwxyz)
s2 = ""
result = [
[ '<', 0, 0 ],
[ '<', 1, 0 ],
[ '<', 2, 0 ],
[ '<', 3, 0 ],
[ '<', 4, 0 ],
[ '<', 5, 0 ],
[ '<', 6, 0 ],
[ '<', 7, 0 ],
[ '<', 8, 0 ],
[ '<', 9, 0 ],
[ '<', 10, 0 ],
[ '<', 11, 0 ],
[ '<', 12, 0 ],
[ '<', 13, 0 ],
[ '<', 14, 0 ],
[ '<', 15, 0 ],
[ '<', 16, 0 ],
[ '<', 17, 0 ],
[ '<', 18, 0 ],
[ '<', 19, 0 ],
[ '<', 20, 0 ],
[ '<', 21, 0 ],
[ '<', 22, 0 ],
[ '<', 23, 0 ],
[ '<', 24, 0 ],
[ '<', 25, 0 ],
[ '<', 26, 0 ],
[ '<', 27, 0 ],
[ '<', 28, 0 ],
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
end
# vim: ft=ruby
# -*- ruby encoding: utf-8 -*-
require 'spec_helper'
describe "Diff::LCS.traverse_sequences" do
describe "callback with no finishers" do
before(:each) do
@callback_s1_s2 = simple_callback_no_finishers
Diff::LCS.traverse_sequences(seq1, seq2, @callback_s1_s2)
@callback_s2_s1 = simple_callback_no_finishers
Diff::LCS.traverse_sequences(seq2, seq1, @callback_s2_s1)
end
it "should have the correct LCS result on left-matches" do
@callback_s1_s2.matched_a.should == correct_lcs
@callback_s2_s1.matched_a.should == correct_lcs
end
it "should have the correct LCS result on right-matches" do
@callback_s1_s2.matched_b.should == correct_lcs
@callback_s2_s1.matched_b.should == correct_lcs
end
it "should have the correct skipped sequences for the left sequence" do
@callback_s1_s2.discards_a.should == skipped_seq1
@callback_s2_s1.discards_a.should == skipped_seq2
end
it "should have the correct skipped sequences for the right sequence" do
@callback_s1_s2.discards_b.should == skipped_seq2
@callback_s2_s1.discards_b.should == skipped_seq1
end
it "should not have anything done markers from the left or right sequences" do
@callback_s1_s2.done_a.should be_empty
@callback_s1_s2.done_b.should be_empty
@callback_s2_s1.done_a.should be_empty
@callback_s2_s1.done_b.should be_empty
end
end
describe "callback with finisher" do
before(:each) do
@callback_s1_s2 = simple_callback
Diff::LCS.traverse_sequences(seq1, seq2, @callback_s1_s2)
@callback_s2_s1 = simple_callback
Diff::LCS.traverse_sequences(seq2, seq1, @callback_s2_s1)
end
it "should have the correct LCS result on left-matches" do
@callback_s1_s2.matched_a.should == correct_lcs
@callback_s2_s1.matched_a.should == correct_lcs
end
it "should have the correct LCS result on right-matches" do
@callback_s1_s2.matched_b.should == correct_lcs
@callback_s2_s1.matched_b.should == correct_lcs
end
it "should have the correct skipped sequences for the left sequence" do
@callback_s1_s2.discards_a.should == skipped_seq1
@callback_s2_s1.discards_a.should == skipped_seq2
end
it "should have the correct skipped sequences for the right sequence" do
@callback_s1_s2.discards_b.should == skipped_seq2
@callback_s2_s1.discards_b.should == skipped_seq1
end
it "should have done markers differently-sized sequences" do
@callback_s1_s2.done_a.should == [[ "p", 9, "s", 10 ]]
@callback_s1_s2.done_b.should be_empty
# 20110731 I don't yet understand why this particular behaviour
# isn't transitive.
@callback_s2_s1.done_a.should be_empty
@callback_s2_s1.done_b.should be_empty
end
end
end
# vim: ft=ruby

The Changelog has a complete list of everything that changed, but here are more detailed explanations for those items that warrant them.

rspec-core-2.7.0.rc1

what's new

rspec command with no arguments

Now you can just type

rspec

to run all the specs in the spec directory. If you keep your specs in a different directory, you can override the default with the --default_path argument in a config file:

# in .rspec
--default_path specs

rspec command supports multiple line numbers

Use either of the following to run the examples declared on lines 37 and 42 of a_spec.rb:

rspec path/to/a_spec.rb --line_number 37 --line_number 42
rspec path/to/a_spec.rb:37:42

what's changed

skip_bundler and gemfile rake task options are deprecated and have no effect.

RSpec's rake task invokes the rspec command in a subshell. If you invoke bundle exec rake or include Bundler.setup in your Rakefile, then Bundler will be activated in the subshell as well.

Previously, the rake task managed this for you based on the presence of a Gemfile. In 2.7.0.rc1, this is done based on the presence of the BUNDLE_GEMFILE environment variable, which is set in the parent shell by Bundler.

In 2.7.0.rc2 (not yet released), the rake task doesn't do anything at all. Turns out Bundler just does the right thing, so rspec doesn't need to do anything.

rspec-core-2.6

new APIs for sharing content

Use shared_context together with include_context to share before/after hooks, let declarations, and method definitions across example groups.

Use shared_examples together with include_examples to share examples across different contexts.

All of the old APIs are still supported, but these 4 are easy to remember, and serve most use cases.

See shared_context and shared_examples under "Example Groups" for more information.

treat_symbols_as_metadata_keys_with_true_values

Yes it's a long name, but it's a great feature, and it's going to be the default behavior in rspec-3. This lets you add metadata to a group or example like this:

describe "something", :awesome do
  ...

And then you can run that group (or example) using the tags feature:

rspec spec --tag awesome

We're making this an opt-in for rspec-2.6 because describe "string", :symbol is a perfectly legal construct in pre-2.6 releases and we want to maintain compatibility in minor releases as much as is possible.

rspec-core-2.3

config.expect_with

Use this to configure RSpec to use rspec/expectations (default), stdlib assertions (Test::Unit with Ruby 1.8, MiniTest with Ruby 1.9), or both:

RSpec.configure do |config|
  config.expect_with :rspec          # => rspec/expectations
  config.expect_with :stdlib         # => Test::Unit or MinitTest
  config.expect_with :rspec, :stdlib # => both
end

rspec-core-2.1

Command line

--tags

Now you can tag groups and examples using metadata and access those tags from the command line. So if you have a group with :foo => true:

describe "something", :foo => true do
  it "does something" do
    # ...
  end
end

... now you can run just that group like this:

rspec spec --tags foo

--fail-fast

Add this flag to the command line to tell rspec to clean up and exit after the first failure:

rspec spec --fail-fast

Metata/filtering

:if and :unless keys

Use :if and :unless keys to conditionally run examples with simple boolean expressions:

describe "something" do
  it "does something", :if => RUBY_VERSION == 1.8.6 do
    # ...
  end
  it "does something", :unless => RUBY_VERSION == 1.8.6 do
    # ...
  end
end

Conditionally 'pending' examples

Make examples pending based on a condition. This is most useful when you have an example that runs in multiple contexts and fails in one of those due to a bug in a third-party dependency that you expect to be fixed in the future.

describe "something" do
  it "does something that doesn't yet work right on JRuby" do
    pending("waiting for the JRuby team to fix issue XYZ", :if => RUBY_PLATFORM == 'java') do
      # the content of your spec
    end
  end
end

This example would run normally on all ruby interpretters except JRuby. On JRuby, it uses the block form of pending, which causes the example to still be run and will remain pending as long as it fails. In the future, if you upgraded your JRuby installation to a newer release that allows the example to pass, RSpec will report it as a failure (Expected pending '...' to fail. No Error was raised.), so that know that you can remove the call to pending.

New features in rspec-core-2.0

Runner

The new runner for rspec-2 comes from Micronaut.

Metadata!

In rspec-2, every example and example group comes with metadata information like the file and line number on which it was declared, the arguments passed to describe and it, etc. This metadata can be appended to through a hash argument passed to describe or it, allowing us to pre and post-process each example in a variety of ways.

Filtering

The most obvious use is for filtering the run. For example:

# in spec/spec_helper.rb
RSpec.configure do |c|
  c.filter_run :focus => true
end

# in any spec file
describe "something" do
  it "does something", :focus => true do
    # ....
  end
end

When you run the rspec command, rspec will run only the examples that have :focus => true in the hash.

You can also add run_all_when_everything_filtered to the config:

RSpec.configure do |c|
  c.filter_run :focus => true
  c.run_all_when_everything_filtered = true
end

Now if there are no examples tagged with :focus => true, all examples will be run. This makes it really easy to focus on one example for a while, but then go back to running all of the examples by removing that argument from it. Works with describe too, in which case it runs all of the examples in that group.

The configuration will accept a lambda, which provides a lot of flexibility in filtering examples. Say, for example, you have a spec for functionality that behaves slightly differently in Ruby 1.8 and Ruby 1.9. We have that in rspec-core, and here's how we're getting the right stuff to run under the right version:

# in spec/spec_helper.rb
RSpec.configure do |c|
  c.exclusion_filter = { :ruby => lambda {|version|
    !(RUBY_VERSION.to_s =~ /^#{version.to_s}/)
  }}
end

# in any spec file
describe "something" do
  it "does something", :ruby => 1.8 do
    # ....
  end

  it "does something", :ruby => 1.9 do
    # ....
  end
end

In this case, we're using exclusion_filter instead of filter_run or filter, which indicate inclusion filters. So each of those examples is excluded if we're not running the version of Ruby they work with.

Shared example groups

Shared example groups are now run in a nested group within the including group (they used to be run in the same group). Nested groups inherit before, after, around, and let hooks, as well as any methods that are defined in the parent group.

This new approach provides better encapsulation, better output, and an opportunity to add contextual information to the shared group via a block passed to it_should_behave_like.

See features/example_groups/shared_example_group.feature for more information.

NOTICE: The including example groups no longer have access to any of the methods, hooks, or state defined inside a shared group. This will break rspec-1 specs that were using shared example groups to extend the behavior of including groups.

Upgrading from rspec-1.x

rspec command

The command to run specs is now rspec instead of spec.

rspec ./spec

Co-habitation of rspec-1 and rspec-2

Early beta versions of RSpec-2 included a spec command, which conflicted with the RSpec-1 spec command because RSpec-1's was installed by the rspec gem, while RSpec-2's is installed by the rspec-core gem.

If you installed one of these early versions, the safest bet is to uninstall rspec-1 and rspec-core-2, and then reinstall both. After you do this, you will be able to run rspec-2 like this:

rspec ./spec

... and rspec-1 like this:

spec _1.3.1_ ./spec

Rubygems inspects the first argument to any gem executable to see if it's formatted like a version number surrounded by underscores. If so, it uses that version (e.g. 1.3.1). If not, it uses the most recent version (e.g. 2.0.0).

rake task

A few things changed in the Rake task used to run specs:

  1. The file in which it is defined changed from spec/rake/spectask to rspec/core/rake_task

  2. The spec_opts accessor has been deprecated in favor of rspec_opts. Also, the rspec command no longer supports the --options command line option so the options must be embedded directly in the Rakefile, or stored in the .rspec files mentioned above.

  3. In RSpec-1, the rake task would read in rcov options from an rcov.opts file. This is ignored by RSpec-2. RCov options are now set directly on the Rake task:

    RSpec::Core::RakeTask.new(:rcov) do |t|
      t.rcov_opts =  %q[--exclude "spec"]
    end
    
  4. The spec_files accessor has been replaced by pattern.

    # rspec-1
    require 'spec/rake/spectask'
    
    Spec::Rake::SpecTask.new do |t|
      t.spec_opts = ['--options', "\"spec/spec.opts\""]
      t.spec_files = FileList['spec/**/*.rb']
    end
    
    # rspec-2
    require 'rspec/core/rake_task'
    
    RSpec::Core::RakeTask.new do |t|
      t.rspec_opts = ["-c", "-f progress", "-r ./spec/spec_helper.rb"]
      t.pattern = 'spec/**/*_spec.rb'
    end
    

autotest

autospec is dead. Long live autotest.

RSpec is the new Spec

The root namespace (top level module) is now RSpec instead of Spec, and the root directory under lib within all of the rspec gems is rspec instead of spec.

Configuration

Typically in spec/spec_helper.rb, configuration is now done like this:

RSpec.configure do |c|
  # ....
end

.rspec

Command line options can be persisted in a .rspec file in a project. You can also store a .rspec file in your home directory (~/.rspec) with global options. Precedence is:

command line
./.rspec
~/.rspec

context is no longer a top-level method

We removed context from the main object because it was creating conflicts with IRB and some users who had Context domain objects. describe is still there, so if you want to use context at the top level, just alias it:

alias :context :describe

Of course, you can still use context to declare a nested group:

describe "something" do
  context "in some context" do
    it "does something" do
      # ...
    end
  end
end

$KCODE no longer set implicitly to 'u'

In RSpec-1, the runner set $KCODE to 'u', which impacts, among other things, the behaviour of Regular Expressions when applied to non-ascii characters. This is no longer the case in RSpec-2.

Feature: mock with an alternative framework
In addition to rspec, mocha, flexmock, and RR, you can choose an alternate
framework as the mocking framework. You (or the framework authors) just needs
to provide an adapter that hooks RSpec's events into those of the framework.
A mock framework adapter must expose three methods:
* setup_mocks_for_rspec
* called before each example is run
* verify_mocks_for_rspec
* called after each example is run
* this is where message expectation failures should result in an error with
the appropriate failure message
* teardown_mocks_for_rspec
* called after verify_mocks_for_rspec
* use this to clean up resources, restore objects to earlier state, etc
* guaranteed to run even if there are failures
Scenario: Mock with alternate framework
Given a file named "expector.rb" with:
"""
class Expector
class << self
def expectors
@expectors ||= []
end
def clear_expectors
expectors.clear
end
def verify_expectors
expectors.each {|d| d.verify}
end
end
def initialize
self.class.expectors << self
end
def expectations
@expectations ||= []
end
def expect(message)
expectations << message.to_s
end
def verify
unless expectations.empty?
raise expectations.map {|e|
"expected #{e}, but it was never received"
}.join("\n")
end
end
private
def method_missing(name, *args, &block)
expectations.delete(name.to_s)
end
public
module RSpecAdapter
def setup_mocks_for_rspec
# no setup necessary
end
def verify_mocks_for_rspec
Expector.verify_expectors.each {|d| d.verify}
end
def teardown_mocks_for_rspec
Expector.clear_expectors
end
end
end
"""
Given a file named "example_spec.rb" with:
"""
require File.expand_path("../expector", __FILE__)
RSpec.configure do |config|
config.mock_framework = Expector::RSpecAdapter
end
describe Expector do
it "passes when message is received" do
expector = Expector.new
expector.expect(:foo)
expector.foo
end
it "fails when message is received" do
expector = Expector.new
expector.expect(:foo)
end
end
"""
When I run `rspec example_spec.rb --format doc`
Then the exit status should be 1
And the output should contain "2 examples, 1 failure"
And the output should contain "fails when message is received (FAILED - 1)"
Feature: mock with flexmock
Configure RSpec to use flexmock as shown in the scenarios below.
Scenario: passing message expectation
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.mock_framework = :flexmock
end
describe "mocking with RSpec" do
it "passes when it should" do
receiver = flexmock('receiver')
receiver.should_receive(:message).once
receiver.message
end
end
"""
When I run `rspec example_spec.rb`
Then the examples should all pass
Scenario: failing message expecation
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.mock_framework = :flexmock
end
describe "mocking with RSpec" do
it "fails when it should" do
receiver = flexmock('receiver')
receiver.should_receive(:message).once
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain "1 example, 1 failure"
Scenario: failing message expectation in pending block (remains pending)
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.mock_framework = :flexmock
end
describe "failed message expectation in a pending block" do
it "is listed as pending" do
pending do
receiver = flexmock('receiver')
receiver.should_receive(:message).once
end
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain "1 example, 0 failures, 1 pending"
And the exit status should be 0
Scenario: passing message expectation in pending block (fails)
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.mock_framework = :flexmock
end
describe "passing message expectation in a pending block" do
it "fails with FIXED" do
pending do
receiver = flexmock('receiver')
receiver.should_receive(:message).once
receiver.message
end
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain "FIXED"
Then the output should contain "1 example, 1 failure"
And the exit status should be 1
Scenario: accessing RSpec.configuration.mock_framework.framework_name
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.mock_framework = :flexmock
end
describe "RSpec.configuration.mock_framework.framework_name" do
it "returns :flexmock" do
RSpec.configuration.mock_framework.framework_name.should eq(:flexmock)
end
end
"""
When I run `rspec example_spec.rb`
Then the examples should all pass
Feature: mock with mocha
Configure RSpec to use mocha as shown in the scenarios below.
Scenario: passing message expectation
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.mock_framework = :mocha
end
describe "mocking with RSpec" do
it "passes when it should" do
receiver = mock('receiver')
receiver.expects(:message).once
receiver.message
end
end
"""
When I run `rspec example_spec.rb`
Then the examples should all pass
Scenario: failing message expecation
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.mock_framework = :mocha
end
describe "mocking with RSpec" do
it "fails when it should" do
receiver = mock('receiver')
receiver.expects(:message).once
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain "1 example, 1 failure"
Scenario: failing message expectation in pending block (remains pending)
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.mock_framework = :mocha
end
describe "failed message expectation in a pending block" do
it "is listed as pending" do
pending do
receiver = mock('receiver')
receiver.expects(:message).once
end
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain "1 example, 0 failures, 1 pending"
And the exit status should be 0
Scenario: passing message expectation in pending block (fails)
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.mock_framework = :mocha
end
describe "passing message expectation in a pending block" do
it "fails with FIXED" do
pending do
receiver = mock('receiver')
receiver.expects(:message).once
receiver.message
end
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain "FIXED"
Then the output should contain "1 example, 1 failure"
And the exit status should be 1
Scenario: accessing RSpec.configuration.mock_framework.framework_name
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.mock_framework = :mocha
end
describe "RSpec.configuration.mock_framework.framework_name" do
it "returns :mocha" do
RSpec.configuration.mock_framework.framework_name.should eq(:mocha)
end
end
"""
When I run `rspec example_spec.rb`
Then the examples should all pass
Feature: mock with rr
Configure RSpec to use rr as shown in the scenarios below.
Scenario: passing message expectation
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.mock_framework = :rr
end
describe "mocking with RSpec" do
it "passes when it should" do
receiver = Object.new
mock(receiver).message
receiver.message
end
end
"""
When I run `rspec example_spec.rb`
Then the examples should all pass
Scenario: failing message expecation
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.mock_framework = :rr
end
describe "mocking with RSpec" do
it "fails when it should" do
receiver = Object.new
mock(receiver).message
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain "1 example, 1 failure"
Scenario: failing message expectation in pending block (remains pending)
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.mock_framework = :rr
end
describe "failed message expectation in a pending block" do
it "is listed as pending" do
pending do
receiver = Object.new
mock(receiver).message
end
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain "1 example, 0 failures, 1 pending"
And the exit status should be 0
Scenario: passing message expectation in pending block (fails)
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.mock_framework = :rr
end
describe "passing message expectation in a pending block" do
it "fails with FIXED" do
pending do
receiver = Object.new
mock(receiver).message
receiver.message
end
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain "FIXED"
Then the output should contain "1 example, 1 failure"
And the exit status should be 1
Scenario: accessing RSpec.configuration.mock_framework.framework_name
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.mock_framework = :rr
end
describe "RSpec.configuration.mock_framework.framework_name" do
it "returns :rr" do
RSpec.configuration.mock_framework.framework_name.should eq(:rr)
end
end
"""
When I run `rspec example_spec.rb`
Then the examples should all pass
Feature: mock with rspec
RSpec uses its own mocking framework by default, or you can configure it
explicitly.
Scenario: passing message expectation
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.mock_framework = :rspec
end
describe "mocking with RSpec" do
it "passes when it should" do
receiver = double('receiver')
receiver.should_receive(:message)
receiver.message
end
end
"""
When I run `rspec example_spec.rb`
Then the examples should all pass
Scenario: failing message expecation
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.mock_framework = :rspec
end
describe "mocking with RSpec" do
it "fails when it should" do
receiver = double('receiver')
receiver.should_receive(:message)
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain "1 example, 1 failure"
Scenario: failing message expectation in pending block (remains pending)
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.mock_framework = :rspec
end
describe "failed message expectation in a pending block" do
it "is listed as pending" do
pending do
receiver = double('receiver')
receiver.should_receive(:message)
end
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain "1 example, 0 failures, 1 pending"
And the exit status should be 0
Scenario: passing message expectation in pending block (fails)
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.mock_framework = :rspec
end
describe "passing message expectation in a pending block" do
it "fails with FIXED" do
pending do
receiver = double('receiver')
receiver.should_receive(:message)
receiver.message
end
end
end
"""
When I run `rspec example_spec.rb`
Then the output should contain "FIXED"
Then the output should contain "1 example, 1 failure"
And the exit status should be 1
Scenario: accessing RSpec.configuration.mock_framework.framework_name
Given a file named "example_spec.rb" with:
"""
RSpec.configure do |config|
config.mock_framework = :rspec
end
describe "RSpec.configuration.mock_framework.framework_name" do
it "returns :rspec" do
RSpec.configuration.mock_framework.framework_name.should eq(:rspec)
end
end
"""
When I run `rspec example_spec.rb`
Then the examples should all pass
Feature: User-defined metadata
You can attach user-defined metadata to any example group or example.
Pass a hash as the last argument (before the block) to `describe`,
`context` or `it`. RSpec supports many configuration options that apply
only to certain examples or groups based on the metadata.
Metadata defined on an example group is available (and can be overridden)
by any sub-group or from any example in that group or a sub-group.
In addition, there is a configuration option (which will be the default
behavior in RSpec 3) that allows you to specify metadata using just
symbols:
```ruby
RSpec.configure do |c|
c.treat_symbols_as_metadata_keys_with_true_values = true
end
```
Each symbol passed as an argument to `describe`, `context` or `it` will
be a key in the metadata hash, with a corresponding value of `true`.
Scenario: define group metadata using a hash
Given a file named "define_group_metadata_with_hash_spec.rb" with:
"""
describe "a group with user-defined metadata", :foo => 17 do
it 'has access to the metadata in the example' do
example.metadata[:foo].should eq(17)
end
it 'does not have access to metadata defined on sub-groups' do
example.metadata.should_not include(:bar)
end
describe 'a sub-group with user-defined metadata', :bar => 12 do
it 'has access to the sub-group metadata' do
example.metadata[:foo].should eq(17)
end
it 'also has access to metadata defined on parent groups' do
example.metadata[:bar].should eq(12)
end
end
end
"""
When I run `rspec define_group_metadata_with_hash_spec.rb`
Then the examples should all pass
Scenario: define example metadata using a hash
Given a file named "define_example_metadata_with_hash_spec.rb" with:
"""
describe "a group with no user-defined metadata" do
it 'has an example with metadata', :foo => 17 do
example.metadata[:foo].should eq(17)
example.metadata.should_not include(:bar)
end
it 'has another example with metadata', :bar => 12, :bazz => 33 do
example.metadata[:bar].should eq(12)
example.metadata[:bazz].should eq(33)
example.metadata.should_not include(:foo)
end
end
"""
When I run `rspec define_example_metadata_with_hash_spec.rb`
Then the examples should all pass
Scenario: override user-defined metadata
Given a file named "override_metadata_spec.rb" with:
"""
describe "a group with user-defined metadata", :foo => 'bar' do
it 'can be overridden by an example', :foo => 'bazz' do
example.metadata[:foo].should == 'bazz'
end
describe "a sub-group with an override", :foo => 'goo' do
it 'can be overridden by a sub-group' do
example.metadata[:foo].should == 'goo'
end
end
end
"""
When I run `rspec override_metadata_spec.rb`
Then the examples should all pass
Scenario: less verbose metadata
Given a file named "less_verbose_metadata_spec.rb" with:
"""
RSpec.configure do |c|
c.treat_symbols_as_metadata_keys_with_true_values = true
end
describe "a group with simple metadata", :fast, :simple, :bug => 73 do
it 'has `:fast => true` metadata' do
example.metadata[:fast].should == true
end
it 'has `:simple => true` metadata' do
example.metadata[:simple].should == true
end
it 'can still use a hash for metadata' do
example.metadata[:bug].should eq(73)
end
it 'can define simple metadata on an example', :special do
example.metadata[:special].should == true
end
end
"""
When I run `rspec less_verbose_metadata_spec.rb`
Then the examples should all pass
module RSpec # :nodoc:
module Version # :nodoc:
STRING = '2.8.0'
end
end
module RSpec
module Core
module Version
STRING = '2.8.0'
end
end
end
module RSpec
module Core
module MockFrameworkAdapter
def setup_mocks_for_rspec; end
def verify_mocks_for_rspec; end
def teardown_mocks_for_rspec; end
end
end
end
#!/usr/bin/env ruby
#
# Created by Jim Weirich on 2007-04-10.
# Copyright (c) 2007. All rights reserved.
require 'flexmock/rspec'
module RSpec
module Core
module MockFrameworkAdapter
def self.framework_name; :flexmock end
include FlexMock::MockContainer
def setup_mocks_for_rspec
# No setup required
end
def verify_mocks_for_rspec
flexmock_verify
end
def teardown_mocks_for_rspec
flexmock_close
end
end
end
end
require 'mocha/standalone'
require 'mocha/object'
module RSpec
module Core
module MockFrameworkAdapter
def self.framework_name; :mocha end
# Mocha::Standalone was deprecated as of Mocha 0.9.7.
begin
include Mocha::API
rescue NameError
include Mocha::Standalone
end
alias :setup_mocks_for_rspec :mocha_setup
alias :verify_mocks_for_rspec :mocha_verify
alias :teardown_mocks_for_rspec :mocha_teardown
end
end
end
require 'rr'
RSpec.configuration.backtrace_clean_patterns.push(RR::Errors::BACKTRACE_IDENTIFIER)
module RSpec
module Core
module MockFrameworkAdapter
def self.framework_name; :rr end
include RR::Extensions::InstanceMethods
def setup_mocks_for_rspec
RR::Space.instance.reset
end
def verify_mocks_for_rspec
RR::Space.instance.verify_doubles
end
def teardown_mocks_for_rspec
RR::Space.instance.reset
end
end
end
end
require 'rspec/mocks'
module RSpec
module Core
module MockFrameworkAdapter
def self.framework_name; :rspec end
def setup_mocks_for_rspec
RSpec::Mocks::setup(self)
end
def verify_mocks_for_rspec
RSpec::Mocks::verify
end
def teardown_mocks_for_rspec
RSpec::Mocks::teardown
end
end
end
end
module RSpec
module Core
class World
include RSpec::Core::Hooks
attr_reader :example_groups, :filtered_examples, :wants_to_quit
attr_writer :wants_to_quit
def initialize(configuration=RSpec.configuration)
@configuration = configuration
@example_groups = [].extend(Extensions::Ordered)
@filtered_examples = Hash.new { |hash,group|
hash[group] = begin
examples = group.examples.dup
examples = filter_manager.prune(examples)
examples.uniq
examples.extend(Extensions::Ordered)
end
}
end
def reset
example_groups.clear
end
def filter_manager
@configuration.filter_manager
end
def register(example_group)
example_groups << example_group
example_group
end
def inclusion_filter
@configuration.inclusion_filter
end
def exclusion_filter
@configuration.exclusion_filter
end
def configure_group(group)
@configuration.configure_group(group)
end
def shared_example_groups
@shared_example_groups ||= {}
end
def example_count
example_groups.collect {|g| g.descendants}.flatten.inject(0) { |sum, g| sum += g.filtered_examples.size }
end
def preceding_declaration_line(filter_line)
declaration_line_numbers.sort.inject(nil) do |highest_prior_declaration_line, line|
line <= filter_line ? line : highest_prior_declaration_line
end
end
def reporter
@configuration.reporter
end
def announce_filters
filter_announcements = []
announce_inclusion_filter filter_announcements
announce_exclusion_filter filter_announcements
unless filter_manager.empty?
if filter_announcements.length == 1
reporter.message("Run options: #{filter_announcements[0]}")
else
reporter.message("Run options:\n #{filter_announcements.join("\n ")}")
end
end
if @configuration.run_all_when_everything_filtered? && example_count.zero?
reporter.message("#{everything_filtered_message}; ignoring #{inclusion_filter.description}")
filtered_examples.clear
inclusion_filter.clear
end
if example_count.zero?
example_groups.clear
if filter_manager.empty?
reporter.message("No examples found.")
elsif exclusion_filter.empty_without_conditional_filters?
message = everything_filtered_message
if @configuration.run_all_when_everything_filtered?
message << "; ignoring #{inclusion_filter.description}"
end
reporter.message(message)
elsif inclusion_filter.empty?
reporter.message(everything_filtered_message)
end
end
end
def everything_filtered_message
"\nAll examples were filtered out"
end
def announce_inclusion_filter(announcements)
unless inclusion_filter.empty?
announcements << "include #{inclusion_filter.description}"
end
end
def announce_exclusion_filter(announcements)
unless exclusion_filter.empty_without_conditional_filters?
announcements << "exclude #{exclusion_filter.description}"
end
end
def find_hook(hook, scope, group, example = nil)
@configuration.find_hook(hook, scope, group, example)
end
private
def declaration_line_numbers
@line_numbers ||= example_groups.inject([]) do |lines, g|
lines + g.declaration_line_numbers
end
end
end
end
end
View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

This file has been truncated, but you can view the full file.
require 'spec_helper'
require 'rspec/core/formatters/base_text_formatter'
describe RSpec::Core::Formatters::BaseTextFormatter do
let(:output) { StringIO.new }
let(:formatter) { RSpec::Core::Formatters::BaseTextFormatter.new(output) }
describe "#summary_line" do
it "with 0s outputs pluralized (excluding pending)" do
formatter.summary_line(0,0,0).should eq("0 examples, 0 failures")
end
it "with 1s outputs singular (including pending)" do
formatter.summary_line(1,1,1).should eq("1 example, 1 failure, 1 pending")
end
it "with 2s outputs pluralized (including pending)" do
formatter.summary_line(2,2,2).should eq("2 examples, 2 failures, 2 pending")
end
end
describe "#dump_commands_to_rerun_failed_examples" do
it "includes command to re-run each failed example" do
group = RSpec::Core::ExampleGroup.describe("example group") do
it("fails") { fail }
end
line = __LINE__ - 2
group.run(formatter)
formatter.dump_commands_to_rerun_failed_examples
output.string.should include("rspec #{RSpec::Core::Formatters::BaseFormatter::relative_path("#{__FILE__}:#{line}")} # example group fails")
end
end
describe "#dump_failures" do
let(:group) { RSpec::Core::ExampleGroup.describe("group name") }
before { RSpec.configuration.stub(:color_enabled?) { false } }
def run_all_and_dump_failures
group.run(formatter)
formatter.dump_failures
end
it "preserves formatting" do
group.example("example name") { "this".should eq("that") }
run_all_and_dump_failures
output.string.should =~ /group name example name/m
output.string.should =~ /(\s+)expected: \"that\"\n\1 got: \"this\"/m
end
context "with an exception without a message" do
it "does not throw NoMethodError" do
exception_without_message = Exception.new()
exception_without_message.stub(:message) { nil }
group.example("example name") { raise exception_without_message }
expect { run_all_and_dump_failures }.not_to raise_error(NoMethodError)
end
end
context "with an exception class other than RSpec" do
it "does not show the error class" do
group.example("example name") { raise NameError.new('foo') }
run_all_and_dump_failures
output.string.should =~ /NameError/m
end
end
context "with a failed expectation (rspec-expectations)" do
it "does not show the error class" do
group.example("example name") { "this".should eq("that") }
run_all_and_dump_failures
output.string.should_not =~ /RSpec/m
end
end
context "with a failed message expectation (rspec-mocks)" do
it "does not show the error class" do
group.example("example name") { "this".should_receive("that") }
run_all_and_dump_failures
output.string.should_not =~ /RSpec/m
end
end
context 'for #share_examples_for' do
it 'outputs the name and location' do
share_examples_for 'foo bar' do
it("example name") { "this".should eq("that") }
end
line = __LINE__.next
group.it_should_behave_like('foo bar')
run_all_and_dump_failures
output.string.should include(
'Shared Example Group: "foo bar" called from ' +
"./spec/rspec/core/formatters/base_text_formatter_spec.rb:#{line}"
)
end
context 'that contains nested example groups' do
it 'outputs the name and location' do
share_examples_for 'foo bar' do
describe 'nested group' do
it("example name") { "this".should eq("that") }
end
end
line = __LINE__.next
group.it_should_behave_like('foo bar')
run_all_and_dump_failures
output.string.should include(
'Shared Example Group: "foo bar" called from ' +
"./spec/rspec/core/formatters/base_text_formatter_spec.rb:#{line}"
)
end
end
end
context 'for #share_as' do
it 'outputs the name and location' do
share_as :FooBar do
it("example name") { "this".should eq("that") }
end
line = __LINE__.next
group.send(:include, FooBar)
run_all_and_dump_failures
output.string.should include(
'Shared Example Group: "FooBar" called from ' +
"./spec/rspec/core/formatters/base_text_formatter_spec.rb:#{line}"
)
end
context 'that contains nested example groups' do
it 'outputs the name and location' do
share_as :NestedFoo do
describe 'nested group' do
describe 'hell' do
it("example name") { "this".should eq("that") }
end
end
end
line = __LINE__.next
group.send(:include, NestedFoo)
run_all_and_dump_failures
output.string.should include(
'Shared Example Group: "NestedFoo" called from ' +
"./spec/rspec/core/formatters/base_text_formatter_spec.rb:#{line}"
)
end
end
end
end
describe "#dump_pending" do
let(:group) { RSpec::Core::ExampleGroup.describe("group name") }
before { RSpec.configuration.stub(:color_enabled?) { false } }
def run_all_and_dump_pending
group.run(formatter)
formatter.dump_pending
end
context "with show_failures_in_pending_blocks setting enabled" do
before { RSpec.configuration.stub(:show_failures_in_pending_blocks?) { true } }
it "preserves formatting" do
group.example("example name") { pending { "this".should eq("that") } }
run_all_and_dump_pending
output.string.should =~ /group name example name/m
output.string.should =~ /(\s+)expected: \"that\"\n\1 got: \"this\"/m
end
context "with an exception without a message" do
it "does not throw NoMethodError" do
exception_without_message = Exception.new()
exception_without_message.stub(:message) { nil }
group.example("example name") { pending { raise exception_without_message } }
expect { run_all_and_dump_pending }.not_to raise_error(NoMethodError)
end
end
context "with an exception class other than RSpec" do
it "does not show the error class" do
group.example("example name") { pending { raise NameError.new('foo') } }
run_all_and_dump_pending
output.string.should =~ /NameError/m
end
end
context "with a failed expectation (rspec-expectations)" do
it "does not show the error class" do
group.example("example name") { pending { "this".should eq("that") } }
run_all_and_dump_pending
output.string.should_not =~ /RSpec/m
end
end
context "with a failed message expectation (rspec-mocks)" do
it "does not show the error class" do
group.example("example name") { pending { "this".should_receive("that") } }
run_all_and_dump_pending
output.string.should_not =~ /RSpec/m
end
end
context 'for #share_examples_for' do
it 'outputs the name and location' do
share_examples_for 'foo bar' do
it("example name") { pending { "this".should eq("that") } }
end
line = __LINE__.next
group.it_should_behave_like('foo bar')
run_all_and_dump_pending
output.string.should include(
'Shared Example Group: "foo bar" called from ' +
"./spec/rspec/core/formatters/base_text_formatter_spec.rb:#{line}"
)
end
context 'that contains nested example groups' do
it 'outputs the name and location' do
share_examples_for 'foo bar' do
describe 'nested group' do
it("example name") { pending { "this".sh
View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment