peck 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/COPYING ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c)
2
+ 2007 - 2008 Christian Neukirchen <purl.org/net/chneukirchen>
3
+ 2011 - 2012 Eloy Durán <eloy.de.enige@gmail.com>
4
+ 2012 Manfred Stienstra, Fingertips <manfred@fngtps.com>
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
7
+ this software and associated documentation files (the "Software"), to deal in
8
+ the Software without restriction, including without limitation the rights to
9
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10
+ the Software, and to permit persons to whom the Software is furnished to do so,
11
+ subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,127 @@
1
+ # Peck
2
+
3
+ Peck is a concurrent spec framework.
4
+
5
+ [![Build Status](https://secure.travis-ci.org/Fingertips/Peck.png?branch=master)](http://travis-ci.org/Fingertips/Peck)
6
+
7
+ ## Getting Started
8
+
9
+ You can install Peck as a gem.
10
+
11
+ $ gem install peck
12
+
13
+ Write a little test.
14
+
15
+ require 'peck/flavors/vanilla'
16
+
17
+ describe MicroMachine do
18
+ it "drives really fast" do
19
+ MicroMachine.should.drives(:fast)
20
+ end
21
+ end
22
+
23
+ And enjoy the output.
24
+
25
+ ruby -rubygems spec/micro_machine_spec.rb -e ''
26
+ .
27
+ 1 spec, 0 failures, finished in 0.0 seconds.
28
+
29
+ ## Why another framework/test library/spec language?
30
+
31
+ I guess that's something you will have to find out for yourself. For us a spec
32
+ framework needs two things: a good way to describe intention of the specs and
33
+ flexibility to work with the different types of project we work on.
34
+
35
+ We really like Bacon and test/spec but we've found that they're to limiting
36
+ in some of our projects. Bacon doesn't work really well with Rails and
37
+ test/spec needs test/unit, which will be gone in Ruby 1.9.
38
+
39
+ In some projects we find that we have an enormous amount of little specs which
40
+ beg to be run concurrently.
41
+
42
+ Peck tries to be what we, and maybe you, need it to be. Within reason of
43
+ course.
44
+
45
+ ### Peck can be concurrent
46
+
47
+ Peck has two run modes: serial and concurrent. Right now that's a global
48
+ setting for the whole suite.
49
+
50
+ Peck.concurrency = 9
51
+
52
+ Some projects don't allow concurrency because either the code or the test
53
+ suite aren't thread safe. We've also found that some suites actually run
54
+ slower in threads.
55
+
56
+ ### Peck can host your own spec syntax
57
+
58
+ When implementing your own spec syntax you only have to add expectations
59
+ to a little accessor when they run and raise Peck::Error when something
60
+ fails.
61
+
62
+ describe "Fish" do
63
+ it "breathes with water" do
64
+ # If you don't like .should, you can write your own assertion
65
+ # DSL
66
+ expects {
67
+ Fish.breathes == water
68
+ }
69
+ end
70
+ end
71
+
72
+ With a bit of work you could make Peck run your Rspec tests.
73
+
74
+ ### Peck has a pluggable notification system
75
+
76
+ You can write your own notifiers and register them with Peck's delegate
77
+ system.
78
+
79
+ class Remotifier < Peck::Notifiers::Base
80
+ def finished
81
+ HTTP.post("https://ci.lan/runs?ran=#{Peck.counter.ran}" +
82
+ "&failures=#{Peck.counter.failed}")
83
+ end
84
+ end
85
+ Remotifier.use
86
+
87
+ ### Peck is extensible
88
+
89
+ Expect opening up Peck classes you can also extend during runtime with the
90
+ `once` callback. This callback is ran when a new context (describe) is
91
+ created.
92
+
93
+ Peck::Context.once do |context|
94
+ context.class_eval do
95
+ attr_accessor :controller_class
96
+
97
+ before do
98
+ @controller = controller_class.new
99
+ end
100
+ emd
101
+ end
102
+
103
+ ## Documentation
104
+
105
+ Peck is still very much in flux and will probably change a lot in the coming
106
+ months. Currently we support a small number of assertions:
107
+
108
+ * should, should.not, and should.be
109
+ * should.equal
110
+ * should.raise([exception]) { }
111
+ * should.change([expression]) { }
112
+ * should.satisfy { |object| }
113
+
114
+ If you want to learn more you're probably best of reading the code
115
+ documentation.
116
+
117
+ ## Copying
118
+
119
+ Peck inherits a lot of ideas, concepts and even some implementation from both
120
+ Bacon and MacBacon. Both of these projects have been released under the terms
121
+ of an MIT-style license.
122
+
123
+ Copyright (C) 2007 - 2012 Christian Neukirchen http://purl.org/net/chneukirchen
124
+ Copyright (C) 2011 - 2012 Eloy Durán eloy.de.enige@gmail.com
125
+ Copyright (C) 2012 Manfred Stienstra, Fingertips <manfred@fngtps.com>
126
+
127
+ Peck is freely distributable under the terms of an MIT-style license. See COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -0,0 +1,106 @@
1
+ class Peck
2
+ class Context
3
+ attr_reader :specification
4
+
5
+ def initialize(specification)
6
+ @specification = specification
7
+ end
8
+
9
+ def describe(*args, &block)
10
+ self.class.describe(*args, &block)
11
+ end
12
+
13
+ class << self
14
+ FILENAME_WITHOUT_LINE_RE = /^(.+?):\d+/
15
+
16
+ attr_reader :description, :block, :specs, :source_file
17
+ attr_accessor :timeout
18
+
19
+ def init(before, after, *description, &block)
20
+ # Find the first file in the backtrace which is not this file
21
+ source_file = caller.find { |line| line[0,__FILE__.size] != __FILE__ }
22
+ if source_file
23
+ source_file = source_file.match(FILENAME_WITHOUT_LINE_RE)[1]
24
+ source_file = File.expand_path(source_file)
25
+ else
26
+ log("Unable to determine the file in which the context is defined.")
27
+ end
28
+
29
+ context = Class.new(self) do
30
+ @before = before.dup
31
+ @after = after.dup
32
+ @source_file = source_file
33
+ @description = description
34
+ @block = block
35
+ @specs = []
36
+ end
37
+
38
+ if @setup
39
+ @setup.each { |b| b.call(context) }
40
+ end
41
+
42
+ Peck.contexts << context
43
+ context.class_eval(&block)
44
+ context
45
+ end
46
+
47
+ def label
48
+ Peck.join_description(description)
49
+ end
50
+
51
+ def describe(*description, &block)
52
+ if Peck.context_selector.match(Peck.join_description(*description))
53
+ init(@before, @after, *description, &block)
54
+ end
55
+ end
56
+
57
+ # Is only ran once for every context when it's initialized. Great place
58
+ # to hook in test suite specific functionality.
59
+ #
60
+ # Peck::Context.once { |context| context.before { @name = 'Mary' } }
61
+ def once(&block)
62
+ @setup ||= []
63
+ @setup << block
64
+ end
65
+
66
+ def before(*args, &block)
67
+ add_callback(@before, *args, &block)
68
+ end
69
+ alias setup before
70
+
71
+ def after(*args, &block)
72
+ add_callback(@after, *args, &block)
73
+ end
74
+ alias teardown after
75
+
76
+ private
77
+
78
+ def add_callback(chain, *args, &block)
79
+ args.each do |method|
80
+ Peck.log("Adding method `#{method}' to callback chain")
81
+ chain << Proc.new { send(method) }
82
+ end
83
+ if block_given?
84
+ chain << block
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ PECK_PART_RE = /Peck/
91
+ def self.join_description(description)
92
+ description.map do |part|
93
+ part = part.to_s
94
+ part = nil if part =~ PECK_PART_RE
95
+ part
96
+ end.compact.join(' ')
97
+ end
98
+ end
99
+
100
+ module Kernel
101
+ private
102
+
103
+ def describe(*description, &block)
104
+ Peck::Context.init([], [], *description, &block)
105
+ end
106
+ end
@@ -0,0 +1,48 @@
1
+ class Peck
2
+ class << self
3
+ attr_accessor :counter
4
+ end
5
+
6
+ class Counter
7
+ def self.instance
8
+ @instance ||= new
9
+ end
10
+
11
+ attr_accessor :ran, :passed, :failed, :pending, :missing, :events
12
+
13
+ def initialize
14
+ @ran = @passed = @failed = 0
15
+ @pending = []
16
+ @missing = []
17
+ @events = []
18
+ $stdout.sync = true
19
+ end
20
+
21
+ def finished_specification(spec)
22
+ @ran += 1
23
+ if spec.passed?
24
+ @passed += 1
25
+ elsif spec.failed?
26
+ @failed += 1
27
+ end
28
+ end
29
+
30
+ def received_pending(label)
31
+ @pending << label
32
+ end
33
+
34
+ def received_missing(spec)
35
+ @missing << spec
36
+ end
37
+
38
+ def received_exception(spec, exception)
39
+ @events << exception
40
+ end
41
+ end
42
+
43
+ self.counter = Counter.instance
44
+
45
+ if respond_to?(:delegates)
46
+ self.delegates << self.counter
47
+ end
48
+ end
data/lib/peck/debug.rb ADDED
@@ -0,0 +1,14 @@
1
+ class Peck
2
+ def self.logger
3
+ @logger ||= begin
4
+ require 'logger'
5
+ logger = Logger.new($stdout)
6
+ logger.formatter = Logger::Formatter.new
7
+ logger
8
+ end
9
+ end
10
+
11
+ def self.log(message)
12
+ logger.debug(message)
13
+ end
14
+ end
@@ -0,0 +1,44 @@
1
+ require 'set'
2
+
3
+ class Peck
4
+ class Delegates < Set
5
+ def self.instance
6
+ @instance ||= new
7
+ end
8
+
9
+ MESSAGES = %w(
10
+ started
11
+ finished
12
+ started_specification
13
+ finished_specification
14
+ received_missing
15
+ received_exception
16
+ ).freeze
17
+
18
+ def supported_messages
19
+ MESSAGES
20
+ end
21
+
22
+ def method_missing(method, *args, &block)
23
+ method = method.to_s
24
+ if supported_messages.include?(method)
25
+ each do |delegate|
26
+ if delegate.respond_to?(method)
27
+ delegate.send(method, *args, &block)
28
+ end
29
+ end
30
+ else
31
+ super
32
+ end
33
+ end
34
+ end
35
+
36
+ class << self
37
+ # This can be used by a `client' to receive status updates.
38
+ #
39
+ # Peck.delegates << Notifier.new
40
+ attr_reader :delegates
41
+ end
42
+
43
+ @delegates = Peck::Delegates.instance
44
+ end
data/lib/peck/error.rb ADDED
@@ -0,0 +1,14 @@
1
+ class Peck
2
+ class Error < RuntimeError
3
+ attr_accessor :type
4
+
5
+ def initialize(type, message)
6
+ @type = type.to_s
7
+ super message
8
+ end
9
+
10
+ def count_as?(type)
11
+ @type == type.to_s
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,115 @@
1
+ require 'peck/error'
2
+
3
+ class Peck
4
+ class Should
5
+ # Kills ==, ===, =~, eql?, equal?, frozen?, instance_of?, is_a?,
6
+ # kind_of?, nil?, respond_to?, tainted?
7
+ KILL_METHODS_RE = /\?|^\W+$/
8
+ instance_methods.each do |name|
9
+ undef_method(name) if name =~ KILL_METHODS_RE
10
+ end
11
+
12
+ def initialize(this)
13
+ @this = this
14
+ @negated = false
15
+ Thread.current['peck-spec'].expectations << self
16
+ end
17
+
18
+ def not
19
+ @negated = !@negated
20
+ self
21
+ end
22
+
23
+ def be(*args, &block)
24
+ if args.empty?
25
+ self
26
+ else
27
+ block = args.shift unless block_given?
28
+ satisfy(*args, &block)
29
+ end
30
+ end
31
+
32
+ def satisfy(*args, &block)
33
+ if args.size == 1 && String === args.first
34
+ description = args.shift
35
+ else
36
+ description = ""
37
+ end
38
+
39
+ result = yield(@this, *args)
40
+ unless @negated ^ result
41
+ Kernel.raise Peck::Error.new(:failed, description)
42
+ end
43
+ result
44
+ end
45
+
46
+ def change(expression, change=nil)
47
+ if @negated
48
+ description = "#{expression} changed"
49
+ description << " by #{actual}" if change
50
+ else
51
+ description = "#{expression} didn't change"
52
+ description << " by #{change}" if change
53
+ end
54
+
55
+ satisfy(description) do |x|
56
+ difference = change || 1
57
+ binding = x.send(:binding)
58
+
59
+ before = eval(expression, binding)
60
+ result = @this.call
61
+ after = eval(expression, binding)
62
+
63
+ after == before + difference
64
+ end
65
+ end
66
+
67
+ def raise(exception_class=nil)
68
+ exception = nil
69
+ begin
70
+ @this.call
71
+ rescue Exception => e
72
+ exception = e
73
+ end
74
+
75
+ description = if exception_class
76
+ if @negated
77
+ "expected `#{exception_class}' to not be raised"
78
+ else
79
+ "expected `#{exception_class}' to be raised, but got a `#{exception.class}'"
80
+ end
81
+ else
82
+ if @negated
83
+ "expected nothing to be raised, but got `#{exception.inspect}'"
84
+ else
85
+ "expected an exception, but nothing was raised"
86
+ end
87
+ end
88
+
89
+ satisfy(description) do
90
+ if exception_class
91
+ exception.kind_of?(exception_class)
92
+ else
93
+ !exception.nil?
94
+ end
95
+ end
96
+ end
97
+
98
+ PREDICATE_METHOD_RE = /\w[^?]\z/
99
+ def method_missing(name, *args, &block)
100
+ name = "#{name}?" if name.to_s =~ PREDICATE_METHOD_RE
101
+
102
+ desc = @negated ? "not " : ""
103
+ desc << @this.inspect << "." << name.to_s
104
+ desc << "(" << args.map{|x|x.inspect}.join(", ") << ") failed"
105
+
106
+ satisfy(desc) { |x| x.__send__(name, *args, &block) }
107
+ end
108
+ end
109
+ end
110
+
111
+ class Object
112
+ def should(*args, &block)
113
+ Peck::Should.new(self).be(*args, &block)
114
+ end
115
+ end
@@ -0,0 +1,8 @@
1
+ require 'peck'
2
+ require 'peck/delegates'
3
+ require 'peck/counter'
4
+ require 'peck/context'
5
+ require 'peck/specification'
6
+ require 'peck/expectations'
7
+
8
+ Peck.run_at_exit
@@ -0,0 +1,10 @@
1
+ require 'peck'
2
+ require 'peck/delegates'
3
+ require 'peck/counter'
4
+ require 'peck/context'
5
+ require 'peck/specification'
6
+ require 'peck/expectations'
7
+ require 'peck/notifiers/default'
8
+
9
+ Peck::Notifiers::Default.use
10
+ Peck.run_at_exit
@@ -0,0 +1,39 @@
1
+ class Peck
2
+ class Notifiers
3
+ class Base
4
+ # When a file starts with this path, it's in the Peck library
5
+ PECK_PATH = File.expand_path('../../../../', __FILE__)
6
+
7
+ # Matches: `block (2 levels) in <top (required)>'
8
+ ANONYMOUS_BLOCK_RE = /`block/
9
+
10
+ def self.use
11
+ @instance ||= begin
12
+ notifier = new
13
+ notifier.install_at_exit
14
+ Peck.delegates << notifier
15
+ notifier
16
+ end
17
+ end
18
+
19
+ protected
20
+
21
+ def pluralize(count, stem)
22
+ count == 1 ? stem : "#{stem}s"
23
+ end
24
+
25
+ def clean_backtrace(backtrace)
26
+ stripped = []
27
+ backtrace.each do |line|
28
+ if line.start_with?(PECK_PATH) || line.start_with?("<")
29
+ elsif line =~ ANONYMOUS_BLOCK_RE
30
+ stripped << line.split(':')[0,2].join(':')
31
+ else
32
+ stripped << line
33
+ end
34
+ end
35
+ stripped.empty? ? backtrace : stripped
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,68 @@
1
+ require 'peck/notifiers/base'
2
+
3
+ class Peck
4
+ class Notifiers
5
+ class Default < Peck::Notifiers::Base
6
+ def initialize
7
+ @started_at = @finished_at = Time.now
8
+ end
9
+
10
+ def started
11
+ @started_at = Time.now
12
+ end
13
+
14
+ def finished
15
+ @finished_at = Time.now
16
+ end
17
+
18
+ def finished_specification(spec)
19
+ if spec.passed?
20
+ $stdout.write('.')
21
+ elsif spec.failed?
22
+ $stdout.write('f')
23
+ end
24
+ end
25
+
26
+ def write_exeception(number, event)
27
+ puts " #{number}) #{event.spec.label}\n\n"
28
+ backtrace = clean_backtrace(event.exception.backtrace)
29
+ puts " #{event.exception.message}\n\n\t#{backtrace.join("\n\t")}\n\n"
30
+ end
31
+
32
+ def write_event(number, event)
33
+ case event.exception
34
+ when Exception
35
+ write_exeception(number, event)
36
+ else
37
+ raise ArgumentError, "Don't know how to display event `#{event.expectation.class.name}'"
38
+ end
39
+ end
40
+
41
+ def write_events
42
+ Peck.all_events.each_with_index do |event, index|
43
+ number = index + 1
44
+ write_event(number, event)
45
+ end
46
+ end
47
+
48
+ def runtime_in_seconds
49
+ runtime = @finished_at - @started_at
50
+ (runtime * 100).round / 100.0
51
+ end
52
+
53
+ def write_stats
54
+ puts "#{Peck.counter.ran} #{pluralize(Peck.counter.ran, 'spec')}, #{Peck.counter.failed} #{pluralize(Peck.counter.failed, 'failure')}, finished in #{runtime_in_seconds} seconds."
55
+ end
56
+
57
+ def write
58
+ puts if Peck.counter.ran > 0
59
+ write_events
60
+ write_stats
61
+ end
62
+
63
+ def install_at_exit
64
+ at_exit { write }
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,85 @@
1
+ class Peck
2
+ class Context
3
+ def self.it(description, &block)
4
+ return unless Peck.spec_selector.match(label)
5
+ specification = Specification.new(self, @before, @after, description, &block)
6
+ @specs << specification
7
+ specification
8
+ end
9
+
10
+ def self.pending(description)
11
+ return unless Peck.spec_selector.match(label)
12
+ delegates.received_pending(description)
13
+ end
14
+ end
15
+
16
+ class Event
17
+ attr_accessor :exception, :spec
18
+
19
+ def initialize(exception, spec)
20
+ @exception = exception
21
+ @spec = spec
22
+ end
23
+ end
24
+
25
+ class Specification
26
+ attr_reader :description, :context
27
+ attr_reader :expectations, :events
28
+
29
+ def initialize(context, before, after, description, &block)
30
+ @context = context.new(self)
31
+ @before = before.dup
32
+ @after = after.dup
33
+ @description = description
34
+ @block = block
35
+
36
+ @expectations = []
37
+ @events = []
38
+ end
39
+
40
+ def label
41
+ "#{@context.class.label} #{@description}"
42
+ end
43
+
44
+ def synchronized(&block)
45
+ if semaphore = Thread.current['peck-semaphore']
46
+ semaphore.synchronize(&block)
47
+ else
48
+ block.call
49
+ end
50
+ end
51
+
52
+ def run
53
+ if @block
54
+ @before.each { |cb| @context.instance_eval(&cb) }
55
+ begin
56
+ synchronized do
57
+ Thread.current['peck-spec'] = self
58
+ @context.instance_eval(&@block)
59
+ Thread.current['peck-spec'] = nil
60
+ end
61
+ Peck.delegates.received_missing(self) if empty?
62
+ ensure
63
+ @after.each { |cb| @context.instance_eval(&cb) }
64
+ end
65
+ else
66
+ Peck.delegates.received_missing(self)
67
+ end
68
+ rescue Object => e
69
+ Peck.delegates.received_exception(self, e)
70
+ @events << Event.new(e, self)
71
+ end
72
+
73
+ def empty?
74
+ @expectations.empty?
75
+ end
76
+
77
+ def failed?
78
+ !@events.empty?
79
+ end
80
+
81
+ def passed?
82
+ !failed? && !empty?
83
+ end
84
+ end
85
+ end
data/lib/peck.rb ADDED
@@ -0,0 +1,124 @@
1
+ class Peck
2
+ VERSION = "0.1.0"
3
+
4
+ class << self
5
+ # Returns all the defined contexts.
6
+ attr_reader :contexts
7
+
8
+ # Used to select which contexts should be run. The match method will be
9
+ # called on these with the label of the context as argument. You can use
10
+ # a regular expression or a custom class to match what needs to be run.
11
+ #
12
+ # module ContextMatcher
13
+ # def self.match(label)
14
+ # label =~ /^Birds/
15
+ # end
16
+ # end
17
+ # Peck.context_selector = ContextMatcher
18
+ attr_accessor :context_selector
19
+
20
+ # Used to select which specs should be run. See Peck.select_context
21
+ # for more information.
22
+ attr_accessor :spec_selector
23
+
24
+ # Sets the level of concurrency.
25
+ attr_accessor :concurrency
26
+ end
27
+
28
+ # A no-op unless you require 'peck/debug'
29
+ def self.log(message)
30
+ end
31
+
32
+ # Returns true if the suite should run concurrent.
33
+ def self.concurrent?
34
+ concurrency && concurrency > 1
35
+ end
36
+
37
+ def self.reset!
38
+ @contexts = []
39
+ end
40
+
41
+ @context_selector = //
42
+ @spec_selector = //
43
+
44
+ reset!
45
+
46
+ def self.all_specs
47
+ contexts.inject([]) do |all, context|
48
+ all.concat(context.specs)
49
+ end
50
+ end
51
+
52
+ def self.all_events
53
+ contexts.inject([]) do |all, context|
54
+ context.specs.inject(all) do |events, spec|
55
+ events.concat(spec.events)
56
+ end
57
+ end
58
+ end
59
+
60
+ def self.run
61
+ delegates.started
62
+ concurrent? ? run_concurrent : run_serial
63
+ delegates.finished
64
+ end
65
+
66
+ def self.run_at_exit
67
+ at_exit do
68
+ run
69
+ exit Peck.counter.failed
70
+ end
71
+ end
72
+
73
+ def self.run_serial
74
+ Peck.log("Running specs in serial")
75
+ Thread.current['peck-semaphore'] = Mutex.new
76
+ contexts.each do |context|
77
+ context.specs.each do |specification|
78
+ delegates.started_specification(specification)
79
+ specification.run
80
+ delegates.finished_specification(specification)
81
+ end
82
+ end
83
+ rescue Exception => e
84
+ log("An error bubbled up from the context, this should never happen and is possibly a bug.")
85
+ raise e
86
+ end
87
+
88
+ def self.run_concurrent
89
+ Peck.log("Running specs concurrently")
90
+ current_spec = -1
91
+ specs = all_specs
92
+ threaded do |nr|
93
+ Thread.current['peck-semaphore'] = Mutex.new
94
+ loop do
95
+ spec_index = Thread.exclusive { current_spec += 1 }
96
+ if specification = specs[spec_index]
97
+ delegates.started_specification(specification)
98
+ specification.run
99
+ delegates.finished_specification(specification)
100
+ else
101
+ break
102
+ end
103
+ end
104
+ end
105
+
106
+ delegates.finished
107
+ end
108
+
109
+ def self.threaded
110
+ threads = []
111
+ Peck.concurrency.times do |nr|
112
+ threads[nr] = Thread.new do
113
+ yield nr
114
+ end
115
+ end
116
+
117
+ threads.compact.each do |thread|
118
+ begin
119
+ thread.join
120
+ rescue Interrupt
121
+ end
122
+ end
123
+ end
124
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: peck
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Manfred Stienstra
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-26 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: ! ' Concurrent spec framework.
15
+
16
+ '
17
+ email: manfred@fngtps.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files:
21
+ - COPYING
22
+ files:
23
+ - lib/peck/context.rb
24
+ - lib/peck/counter.rb
25
+ - lib/peck/debug.rb
26
+ - lib/peck/delegates.rb
27
+ - lib/peck/error.rb
28
+ - lib/peck/expectations.rb
29
+ - lib/peck/flavors/quiet.rb
30
+ - lib/peck/flavors/vanilla.rb
31
+ - lib/peck/notifiers/base.rb
32
+ - lib/peck/notifiers/default.rb
33
+ - lib/peck/specification.rb
34
+ - lib/peck.rb
35
+ - COPYING
36
+ - README.md
37
+ homepage:
38
+ licenses: []
39
+ post_install_message:
40
+ rdoc_options:
41
+ - --charset=utf-8
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ requirements: []
57
+ rubyforge_project:
58
+ rubygems_version: 1.8.11
59
+ signing_key:
60
+ specification_version: 3
61
+ summary: Peck is a concurrent spec framework which inherits a lot from the fabulous
62
+ Bacon and MacBacon. We call it a framework because it was designed to be used in
63
+ parts and is easily extended.
64
+ test_files: []