Skip to content

Commit

Permalink
Updated MSpec source to f615b16a.
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Ford committed Aug 20, 2008
1 parent d6f1b5f commit bfa449f
Show file tree
Hide file tree
Showing 178 changed files with 12,247 additions and 1,901 deletions.
22 changes: 22 additions & 0 deletions mspec/LICENSE
@@ -0,0 +1,22 @@
Copyright (c) 2008 Engine Yard, Inc. All rights reserved.

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.
208 changes: 188 additions & 20 deletions mspec/README
@@ -1,26 +1,194 @@
mSpec and mMock are simplistic apes of RSpec.
= Overview

The primary design rationale for mSpec is simplicity to allow nascent Ruby
implementations to run the Ruby specs. So, for example, there is not great
concern given to constant clashes. Namespacing (or module scoping) is not used
because implementing this correctly took a significant amount of work in
Rubinius and it is likely that other implementations would also face
MSpec is a specialized framework that is syntax-compatible with RSpec for
basic things like +describe+, +it+ blocks and +before+, +after+ actions. MSpec
contains additional features that assist in writing the RubySpecs used by
multiple Ruby implementations.

MSpec attempts to use the simplest Ruby language features so that beginning
Ruby implementations can run the Ruby specs. So, for example, there is not
great concern given to constant clashes. Namespacing (or module scoping) is
not used because implementing this correctly took a significant amount of work
in Rubinius and it is likely that other implementations would also face
difficulties.

mSpec is not intended as a replacement for RSpec. mSpec attempts to provide a
subset of RSpec syntax. It does not provide all the matchers, for instance.
MSpec is not intended as a replacement for RSpec. MSpec attempts to provide a
subset of RSpec's features in some cases and a superset in others. It does not
provide all the matchers, for instance. However, MSpec provides several
extensions to facilitate writing the Ruby specs in a manner compatible with
multiple Ruby implementations.

mSpec also provides several extensions to facilitate writing the Ruby specs in
a manner compatible with multiple Ruby implementations. First, mSpec offers a
set of guards to control execution of the specs. These guards not only enable
or disable execution but also annotate the specs with additional information
about why they are run or not run. Second, mSpec provides a different shared
spec implementation specifically designed to ease writing specs for the
numerous aliased methods in Ruby. The mSpec shared spec implementation should
not conflict with RSpec's own shared behavior facility.
First, MSpec offers a set of guards to control execution of the specs. These
guards not only enable or disable execution but also annotate the specs with
additional information about why they are run or not run. Second, MSpec
provides a different shared spec implementation specifically designed to ease
writing specs for the numerous aliased methods in Ruby. The MSpec shared spec
implementation should not conflict with RSpec's own shared behavior facility.
Third, MSpec provides various helper methods to simplify some specs, for
example, creating temporary file names. Finally, MSpec has several specialized
runner scripts that includes a configuration facility with a default project
file and user-specific overrides.

Caveats:
* Use RSpec to run the mSpec specs. There are no plans currently to make
the mSpec specs runnable by mSpec.
* Don't mock the #hash method as mMock uses Hash internally. This can be
replaced if necessary, but at this point it's not worth it.

* Use RSpec to run the MSpec specs. There are no plans currently to make
the MSpec specs runnable by MSpec.
* Don't mock the #hash method as MSpec's Mock implementation uses Hash
internally. This can be replaced if necessary, but at this point there is no
compelling need to do so.


== Architecture


== Matchers

Matchers are additional aids for the verification process. The default
is of course to #should or #should_not using the #== operator and its
friends but the matchers add a new set of 'operators' to help in the
task. They reside in `mspec/matchers/`. There are two broad categories,
those that apply to an individual object and those that apply to a
block:

=== Object

- `base` implements the standard #==, #< #<= #>= #> and #=~ with their
normal semantics for the objects that you invoke them on.

- `be_ancestor_of` is equivalent to checking `obj.ancestors.include?`.

- `be_close` is a "delta" for floating-point math. Due to the very
nature of it, floating-point comparisons should never be treated as
exact. By default the tolerance is 0.00003 but it can be altered if
so desired. So `0.23154.should be_close(0.23157)` would succeed
(which is usually close enough for floating point unless you are
doing some scientific computing.)

- `be_empty` checks `obj.empty?`

- `be_kind_of` is equivalent to `obj.kind_of?`

- `include` is `obj.include?`

=== Block

All of these should be applied to a block created with `lambda` or `proc`:

- `complain` is probably clearer stated as `lambda {...}.should complain`;
it checks that the block issues a warning. The message can be checked
against either a String or a Regexp.

- `output` checks that the block produces the given output (stdout as well
as stderr, in that order) matched either to a String or a Regexp. This one
uses overrides so if that is a problem (for e.g. speccing Readline or
something) see below.

- `output_to_fd` is a lower-level version and actually verifies that output
to a certain file descriptor is correct whether from an in-/output stream
or an actual file. Also can check with either a String or a Regexp.

- `raise_error` verifies the exception type (if any) raised by the block it
is associated with. The exception class can be given for finer-grained
control (inheritance works normally so Exception would catch everything.)

== Nested 'describe' blocks

MSpec supports nesting one 'describe' block inside another. The examples in
the nested block are evaluated with all the before/after blocks of all the
containing 'describe' blocks. The following example illustrates this:

describe "Some#method" do
before :each do
@obj = 1
end

describe "when passed String" do
before :each do
@meth = :to_s
end

it "returns false" do
# when this example is evaluated, @obj = 1 and @meth = :to_s
end
end
end

The output when using the SpecdocFormatter (selected with -fs to the runners)
will be as follows:

Some#method when passed String
- returns false


== Shared 'describe' blocks

MSpec supports RSpec-style shared 'describe' blocks. MSpec also provides a
convenience method to assist in writing specs for the numerous aliased methods
that Ruby provides. The following example illustrates shared blocks:

describe :someclass_some_method, :shared => true do
it "does something" do
end
end

describe "SomeClass#some_method" do
it_should_behave_like "someclass_some_method"
end

The first argument to 'describe' for a shared block is an object that
duck-types as a String. The representation of the object must be unique. This
example uses a symbol. This was the convention for the previous facility that
MSpec provided for aliased method (#it_behaves_like). However, this convention
is not set in stone (but the uniqueness requirement is). Note that the
argument to the #it_should_behave_like is a String because at this time RSpec
will not find the shared block by the symbol.

MSpec continues to support the #it_behaves_like convenience method for
specifying aliased methods. The syntax is as follows:

it_behaves_like :symbol_matching_shared_describe, :method [, :object]

describe :someclass_some_method, :shared => true do
it "returns true" do
obj.send(@method).should be_true
end

it "returns something else" do
@object.send(@method).should be_something_else
end
end

# example #1
describe "SomeClass#some_method" do
it_behaves_like :someclass_some_method, :other_method
end

# example #2
describe "SomeOtherClass#some_method" do
it_behaves_like :someclass_some_method, :some_method, OtherClass
end

The first form above (#1) is used for typical aliases. That is, methods with
different names on the same class that behave identically. The
#it_behaves_like helper creates a before(:all) block that sets @method to
:other_method. The form of the first example block in the shared block
illustrates the typical form of a spec for an aliased method.

The second form above (#2) is used for methods on different classes that are
essentially aliases, even though Ruby does not provide a syntax for specifying
such methods as aliases. Examples are the methods on File, FileTest, and
File::Stat. In this case, the #it_behaves_like helper sets both @method and
@object in the before(:all) block (@method = :some_method, @object =
OtherClass in this example).

For shared specs that fall outside of either of these two narrow categories,
use nested or shared 'describe' blocks as appropriate and use the
#it_should_behave_like method directly.

== Guards


== Helpers


== Runners
45 changes: 45 additions & 0 deletions mspec/Rakefile
@@ -0,0 +1,45 @@
require 'rubygems'
require 'spec/rake/spectask'
require 'rake/gempackagetask'
require 'lib/mspec/version'

Spec::Rake::SpecTask.new

task :default => :spec


spec = Gem::Specification.new do |s|
s.name = %q{mspec}
s.version = MSpec::VERSION

s.specification_version = 2 if s.respond_to? :specification_version=

s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Brian Ford"]
s.date = %q{2008-05-21}
s.email = %q{bford@engineyard.com}
s.has_rdoc = true
s.extra_rdoc_files = %w[ README LICENSE ]
s.executables = ["mkspec", "mspec", "mspec-ci", "mspec-run", "mspec-tag"]
s.files = FileList[ '{bin,lib,spec}/**/*.{yaml,txt,rb}', 'Rakefile', *s.extra_rdoc_files ]
s.homepage = %q{http://rubyspec.org}
s.rubyforge_project = 'http://rubyforge.org/projects/mspec'
s.require_paths = ["lib"]
s.rubygems_version = %q{1.1.1}
s.summary = <<EOS
MSpec is a specialized framework that is syntax-compatible
with RSpec for basic things like describe, it blocks and
before, after actions.
MSpec contains additional features that assist in writing
the RubySpecs used by multiple Ruby implementations. Also,
MSpec attempts to use the simplest Ruby language features
so that beginning Ruby implementations can run it.
EOS

s.rdoc_options << '--title' << 'MSpec Gem' <<
'--main' << 'README' <<
'--line-numbers'
end

Rake::GemPackageTask.new(spec){ |pkg| pkg.gem_spec = spec }
7 changes: 7 additions & 0 deletions mspec/bin/mkspec
@@ -0,0 +1,7 @@
#!/usr/bin/env ruby

$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')

require 'mspec/commands/mkspec'

MkSpec.main
99 changes: 3 additions & 96 deletions mspec/bin/mspec
@@ -1,100 +1,7 @@
#!/usr/bin/env ruby

require 'optparse'
require 'mspec/version'
require 'mspec/bin/options'
$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')

includes = []
requires = []
name = nil
target = 'shotgun/rubinius'
flags = []
command = nil
options = []
require 'mspec/commands/mspec'

if ["ci", "run", "tag"].include? ARGV[0]
command = ARGV.shift
options << "-h" if ARGV.delete("-h") || ARGV.delete("--help")
end

opts = OptionParser.new("", 24, ' ') do |opts|
opts.banner = "mspec [COMMAND] [options] (FILE|DIRECTORY|GLOB)+"
opts.separator ""

opts.on("-t", "--target TARGET", String,
"Implementation to run the specs: r:ruby|r19:ruby19|x:rbx|j:jruby") do |t|
case t
when 'r', 'ruby'
target = 'ruby'
when 'r19', 'ruby19'
target = 'ruby19'
when 'x', 'rbx', 'rubinius'
target = 'shotgun/rubinius'
when 'X'
target = "rbx"
when 'j', 'jruby'
target = 'jruby'
else
target = t
end
end
opts.on("-T", "--target-opt OPT", String,
"Pass OPT as a flag to the target implementation") do |t|
flags << t
end
opts.on("-I", "--include DIR", String,
"Pass DIR through as the -I option to the target") do |d|
includes << "-I#{d}"
end
opts.on("-r", "--require LIBRARY", String,
"Pass LIBRARY through as the -r option to the target") do |f|
requires << "-r#{f}"
end
opts.on("-n", "--name RUBY_NAME", String,
"Override the name used to determine the implementation") do |n|
name = "RUBY_NAME = \"#{n}\";"
end
opts.on("-X", "--tags-dir DIR", String,
"Use DIR as the path prefix for locating spec tag files") do |d|
ENV['TAGS_DIR'] = d
end
opts.on("-D", "--gdb", "Run under gdb") do
flags << '--gdb'
end
opts.on("-A", "--valgrind", "Run under valgrind") do
flags << '--valgrind'
end
opts.on("-w", "--warnings", "Don't supress warnings") do
flags << '-w'
ENV['OUTPUT_WARNINGS'] = '1'
end
opts.on("-v", "--version", "Show version") do
puts "MSpec #{MSpec::VERSION}"
exit
end
opts.on("-h", "--help", "Show this message") do
puts opts
puts %[
where COMMAND is one of:
run - Run the specified specs (default)
ci - Run the known good specs
tag - Add or remove tags
mspec COMMAND -h for more options
]
exit
end
end

options += opts.filter! ARGV
opts.parse ARGV

ENV['MSPEC_RUNNER'] = '1'
ENV['MSPEC_OPTIONS'] = options.join("\n")
exec %[#{target} \
#{flags.join(" ")} \
#{includes.join(" ")} \
#{requires.join(" ")} \
mspec/bin/mspec-#{command || "run"}
]
MSpecMain.main

0 comments on commit bfa449f

Please sign in to comment.