eetee 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,62 @@
1
+ module EEtee
2
+ class Reporter
3
+ attr_reader :failures, :errors, :assertion_count, :test_count
4
+
5
+ def initialize
6
+ @errors = []
7
+ @failures = []
8
+ @empty = []
9
+
10
+ @indent = 0
11
+ @assertion_count = 0
12
+ @test_count = 0
13
+
14
+ @started_at = Time.now
15
+ end
16
+
17
+ def increment_assertions
18
+ @assertion_count += 1
19
+ end
20
+
21
+ def around_context(ctx, &block)
22
+ @indent += 1
23
+ block.call()
24
+ ensure
25
+ @indent -= 1
26
+ end
27
+
28
+ def around_test(test, &block)
29
+ before_assertions = @assertion_count
30
+
31
+ block.call()
32
+
33
+ if before_assertions == @assertion_count
34
+ @empty << test
35
+ :empty
36
+ else
37
+ @test_count += 1
38
+ :ok
39
+ end
40
+
41
+ rescue EEtee::AssertionFailed => err
42
+ @failures << err
43
+ :failed
44
+
45
+ rescue EEtee::Error => err
46
+ @errors << err
47
+ :error
48
+ end
49
+
50
+ end
51
+
52
+
53
+ def report_results
54
+ # no op
55
+ end
56
+
57
+ private
58
+ def elapsed_time
59
+ Time.now.to_i - @started_at.to_i
60
+ end
61
+
62
+ end
@@ -0,0 +1,123 @@
1
+ # encoding: utf-8
2
+
3
+ require 'term/ansicolor'
4
+
5
+ module EEtee
6
+ module Reporters
7
+
8
+ ##
9
+ # Advanced text output designed to be used in a open
10
+ # console.
11
+ #
12
+ class Console < Reporter
13
+ Color = Term::ANSIColor
14
+
15
+ def around_context(ctx, &block)
16
+ puts "" if @indent == 0
17
+ # iputs "# #{ctx.description}:"
18
+
19
+ iputs "#{Color.underscore}#{ctx.description}#{Color.reset}"
20
+ super
21
+
22
+ puts "" if @indent == 0
23
+ end
24
+
25
+ def around_test(test, &block)
26
+ iprint " ~ #{test.label}"
27
+
28
+ ret = super
29
+
30
+ # goto beginning of line
31
+ print "\e[G\e[K"
32
+
33
+
34
+ case ret
35
+ when :empty then iputs " #{Color.red}✘#{Color.reset} #{test.label}"
36
+ when :ok then iputs " #{Color.green}✔#{Color.reset} #{test.label}"
37
+ when :failed then iputs " #{Color.red}✘#{Color.reset} #{test.label}"
38
+ when :error then iputs " #{Color.red}☁ #{test.label}#{Color.reset} [#{@errors[-1].class}]"
39
+ end
40
+ end
41
+
42
+ def report_results
43
+ report_failures() unless @failures.empty?
44
+ report_errors() unless @errors.empty?
45
+ report_empty() unless @empty.empty?
46
+
47
+ puts ""
48
+ puts "#{@test_count} Tests / #{@assertion_count} Assertions"
49
+ puts "#{@errors.size} Errors / #{@failures.size} failures"
50
+ if elapsed_time() == 0
51
+ puts "Execution time: < 1s"
52
+ else
53
+ puts "Execution time: #{human_duration(elapsed_time)}"
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def spaces(str = " ")
60
+ str * @indent
61
+ end
62
+
63
+ def iprint(msg)
64
+ tmp = ' ' * @indent
65
+ Kernel.print("#{tmp}#{msg}")
66
+ end
67
+
68
+ def iputs(msg)
69
+ iprint("#{msg}\n")
70
+ end
71
+
72
+ def human_duration(secs)
73
+ [[60, 'seconds'], [60, 'minutes'], [24, 'hours'], [1000, 'days']].map do |count, name|
74
+ if secs > 0
75
+ secs, n = secs.divmod(count)
76
+ (n > 0) ? "#{n.to_i} #{name}" : nil
77
+ end
78
+ end.compact.reverse.join(' ')
79
+ end
80
+
81
+ def report_empty
82
+ puts "\nEmpty tests:"
83
+ @empty.each do |test|
84
+ puts "- #{test.label}"
85
+ end
86
+ end
87
+
88
+ def report_failures
89
+ puts "\nFailures:"
90
+ @failures.each do |f|
91
+ puts "- #{f.test.label}: #{f.message}"
92
+ dump_trace(f)
93
+ end
94
+ end
95
+
96
+ def report_errors
97
+ puts "\nErrors:"
98
+ @errors.each do |err|
99
+ puts "#{err.test.label}:"
100
+ puts " #{err.error.class} #{err.error}"
101
+ dump_trace(err.error)
102
+ end
103
+ end
104
+
105
+ def dump_trace(err, cleanup = true)
106
+ lines = err.backtrace
107
+
108
+ if cleanup
109
+ lines.reject! do |line|
110
+ line.include?('.rbenv')
111
+ end
112
+ end
113
+
114
+ lines.each do |line|
115
+ puts " #{line}"
116
+ end
117
+
118
+ end
119
+
120
+ end
121
+
122
+ end
123
+ end
@@ -0,0 +1,80 @@
1
+ module EEtee
2
+ module Reporters
3
+
4
+ ##
5
+ # Basic text output.
6
+ #
7
+ class Text < Reporter
8
+
9
+
10
+ def around_context(ctx, &block)
11
+ puts "" if @indent == 0
12
+ iputs "# #{ctx.description}:"
13
+ super
14
+ end
15
+
16
+ def around_test(test, &block)
17
+ iprint "- #{test.label}... "
18
+ case super
19
+ when :empty then puts "EMPTY"
20
+ when :ok then puts "OK"
21
+ when :failed then puts "FAILED"
22
+ when :error then puts "ERROR"
23
+ end
24
+ end
25
+
26
+ def report_results
27
+ report_failures() unless @failures.empty?
28
+ report_errors() unless @errors.empty?
29
+
30
+ puts ""
31
+ puts "#{@test_count} Tests / #{@assertion_count} Assertions"
32
+ puts "#{@errors.size} Errors / #{@failures.size} failures"
33
+ end
34
+
35
+ private
36
+ def iprint(msg)
37
+ tmp = ' ' * @indent
38
+ Kernel.print("#{tmp}#{msg}")
39
+ end
40
+
41
+ def iputs(msg)
42
+ iprint("#{msg}\n")
43
+ end
44
+
45
+ def report_failures
46
+ puts "\nFailures:"
47
+ @failures.each do |f|
48
+ puts "- #{f.test.label}: #{f.message}"
49
+ dump_trace(f)
50
+ end
51
+ end
52
+
53
+ def report_errors
54
+ puts "\nErrors:"
55
+ @errors.each do |err|
56
+ puts "#{err.test.label}:"
57
+ puts " #{err.error.class} #{err.error}"
58
+ dump_trace(err.error)
59
+ end
60
+ end
61
+
62
+ def dump_trace(err, cleanup = true)
63
+ lines = err.backtrace
64
+
65
+ if cleanup
66
+ lines.reject! do |line|
67
+ line.include?('gems')
68
+ end
69
+ end
70
+
71
+ lines.each do |line|
72
+ puts " #{line}"
73
+ end
74
+
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,62 @@
1
+ require 'forwardable'
2
+
3
+ module EEtee
4
+
5
+ class Runner
6
+ extend Forwardable
7
+ attr_reader :focus_mode
8
+
9
+ def_delegators :@reporter, :failures, :errors, :assertions, :test_count
10
+
11
+ def initialize
12
+ @reporter = EEtee.default_reporter_class.new
13
+ @focus_mode = false
14
+ end
15
+
16
+ def run(&block)
17
+ instance_eval(&block)
18
+ @reporter.report_results()
19
+ end
20
+
21
+ def run_pattern(pattern)
22
+ paths = []
23
+ caller_path = caller[0].split(':').first
24
+ pattern = File.expand_path("../#{pattern}", caller_path)
25
+
26
+ paths = Dir[pattern].to_a
27
+ run_files(paths)
28
+ end
29
+
30
+ ##
31
+ # run the files relative to the caller.
32
+ #
33
+ def run_files(paths)
34
+ paths.each do |path|
35
+ if File.exist?(path)
36
+ data = File.read(path)
37
+ if data =~ /^\s+should.*?,\s+:focus => true do$/
38
+ puts "Focus enabled."
39
+ @focus_mode = true
40
+ else
41
+ @focus_mode = false
42
+ end
43
+ instance_eval(data, path)
44
+ else
45
+ puts "!!! file does not exists: #{path}"
46
+ end
47
+ end
48
+
49
+ rescue SyntaxError => ex
50
+ puts ex.message
51
+ end
52
+
53
+ def report_results
54
+ @reporter.report_results()
55
+ end
56
+
57
+ def describe(description, &block)
58
+ Context.new(description, 0, @reporter, {}, @focus_mode, &block)
59
+ end
60
+ end
61
+
62
+ end
@@ -0,0 +1,27 @@
1
+ module EEtee
2
+ class Shared
3
+ attr_accessor :_reporter, :_level
4
+
5
+ include SharedContextMethods
6
+
7
+ @@shared = {}
8
+
9
+ def self.run(name, reporter, level, *args)
10
+ sh = @@shared[name]
11
+ sh._reporter = reporter
12
+ sh._level = level
13
+ sh.run(*args)
14
+ end
15
+
16
+
17
+ def initialize(name, &block)
18
+ @@shared[name] = self
19
+ @_block = block
20
+ end
21
+
22
+ def run(*args)
23
+ instance_exec(*args, &@_block)
24
+ end
25
+
26
+ end
27
+ end
data/lib/eetee/test.rb ADDED
@@ -0,0 +1,40 @@
1
+ require 'thread'
2
+
3
+ module EEtee
4
+
5
+ module TestBaseExtension
6
+ def run(&block)
7
+ block.call()
8
+ end
9
+ end
10
+
11
+ class Test
12
+ attr_reader :label, :reporter
13
+
14
+ include TestBaseExtension
15
+
16
+ def initialize(label, reporter, &block)
17
+ @label = label
18
+ @reporter = reporter || raise('missing reporter')
19
+ @reporter.around_test(self) do
20
+ run(&block)
21
+ end
22
+ end
23
+
24
+ def run(&block)
25
+ EEtee.current_test = self
26
+ super(&block)
27
+
28
+ rescue AssertionFailed => err
29
+ err.test = self
30
+ ::Kernel.raise
31
+
32
+ rescue => err
33
+ ::Kernel.raise Error.new(err, self)
34
+ ensure
35
+ EEtee.current_test = nil
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,3 @@
1
+ module EEtee
2
+ VERSION = "0.0.1"
3
+ end
data/lib/eetee.rb ADDED
@@ -0,0 +1,65 @@
1
+ require_relative 'eetee/version'
2
+
3
+ require_relative 'eetee/reporter'
4
+ require_relative 'eetee/reporters/text'
5
+ require_relative 'eetee/reporters/console'
6
+
7
+ require_relative 'eetee/errors'
8
+ require_relative 'eetee/assertion_wrapper'
9
+ require_relative 'eetee/test'
10
+ require_relative 'eetee/context'
11
+ require_relative 'eetee/shared'
12
+ require_relative 'eetee/runner'
13
+
14
+
15
+
16
+ module EEtee
17
+ class << self
18
+ def default_reporter_class=(reporter)
19
+ @reporter = reporter
20
+ end
21
+
22
+ def default_reporter_class
23
+ @reporter
24
+ end
25
+
26
+ def enable_focus_mode=(value)
27
+ @enable_focus_mode = value
28
+ end
29
+
30
+ def enable_focus_mode
31
+ @enable_focus_mode
32
+ end
33
+ end
34
+
35
+
36
+ def self.current_test
37
+ Thread.current[:eetee_test]
38
+ end
39
+
40
+ def self.current_test=(test)
41
+ Thread.current[:eetee_test] = test
42
+ end
43
+
44
+
45
+ module AssertionHelpers
46
+ def should()
47
+ EEtee::AssertionWrapper.new(self)
48
+ end
49
+ end
50
+
51
+ def describe(description, enable_focus_mode = EEtee.enable_focus_mode, &block)
52
+ reporter = EEtee.default_reporter_class.new
53
+ Context.new(description, 0, reporter, {}, enable_focus_mode, &block)
54
+ reporter.report_results()
55
+ end
56
+
57
+ def shared(name, &block)
58
+ Shared.new(name, &block)
59
+ end
60
+
61
+ self.default_reporter_class = Reporters::Console
62
+ end
63
+
64
+ include EEtee
65
+ Object.__send__(:include, EEtee::AssertionHelpers)
@@ -0,0 +1,77 @@
1
+ require 'guard'
2
+ require 'guard/guard'
3
+
4
+ gem 'guard', '~> 1.5.3'
5
+
6
+ module Guard
7
+ class EEtee < Guard
8
+
9
+ # Initialize a Guard.
10
+ # @param [Array<Guard::Watcher>] watchers the Guard file watchers
11
+ # @param [Hash] options the custom Guard options
12
+ def initialize(watchers = [], options = {})
13
+ super
14
+ end
15
+
16
+ # Call once when Guard starts. Please override initialize method to init stuff.
17
+ # @raise [:task_has_failed] when start has failed
18
+ def start
19
+ puts "Guard::EEtee started."
20
+ end
21
+
22
+ # Called when `stop|quit|exit|s|q|e + enter` is pressed (when Guard quits).
23
+ # @raise [:task_has_failed] when stop has failed
24
+ def stop
25
+ puts "Guard::EEtee stopped."
26
+ end
27
+
28
+ # Called when `reload|r|z + enter` is pressed.
29
+ # This method should be mainly used for "reload" (really!) actions like reloading passenger/spork/bundler/...
30
+ # @raise [:task_has_failed] when reload has failed
31
+ def reload
32
+
33
+ end
34
+
35
+ # Called when just `enter` is pressed
36
+ # This method should be principally used for long action like running all specs/tests/...
37
+ # @raise [:task_has_failed] when run_all has failed
38
+ def run_all
39
+ end
40
+
41
+ # Called on file(s) modifications that the Guard watches.
42
+ # @param [Array<String>] paths the changes files or paths
43
+ # @raise [:task_has_failed] when run_on_change has failed
44
+ def run_on_changes(paths)
45
+ pid = Kernel.fork do
46
+ require 'eetee'
47
+ runner = EEtee::Runner.new
48
+ runner.run_files(paths)
49
+ runner.report_results()
50
+
51
+ tests = runner.test_count
52
+ failures = runner.failures.size + runner.errors.size
53
+
54
+ focus_mode = runner.focus_mode
55
+
56
+ if failures > 0
57
+ Notifier.notify("Specs: #{failures} Failures (#{tests} tests) #{focus_mode ? '(focus)' : ''}",
58
+ :image => :failed
59
+ )
60
+ else
61
+ Notifier.notify("Specs: OK (#{tests} tests) #{focus_mode ? '(focus)' : ''}",
62
+ :image => :success
63
+ )
64
+ end
65
+ end
66
+
67
+ Process.wait(pid)
68
+ end
69
+
70
+ # Called on file(s) deletions that the Guard watches.
71
+ # @param [Array<String>] paths the deleted files or paths
72
+ # @raise [:task_has_failed] when run_on_change has failed
73
+ def run_on_removals(paths)
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'eetee'
5
+
6
+ require 'eetee/ext/mocha'
7
+
8
+ Thread.abort_on_exception = true
9
+
10
+ # examples
11
+ require_relative '../examples/extensions/extension'
@@ -0,0 +1,134 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ module Helpers
4
+ def ensure_error_not_raised
5
+ ret = nil
6
+ begin
7
+ yield
8
+ rescue => err
9
+ ret = err
10
+ end
11
+
12
+ ret.should == nil
13
+ end
14
+
15
+ def ensure_error_raised(error_class)
16
+ ret = nil
17
+ begin
18
+ yield
19
+ rescue error_class => err
20
+ ret = err
21
+ end
22
+
23
+ ret.class.should == error_class
24
+ end
25
+ end
26
+
27
+ EEtee::Context.__send__(:include, Helpers)
28
+
29
+ describe 'AssertionWrapper' do
30
+ before do
31
+ @obj = stub('Object')
32
+ @wrapper = EEtee::AssertionWrapper.new(@obj)
33
+ end
34
+
35
+ should 'call method on the targeted object' do
36
+ @obj.expects(:to_zebra).returns(3)
37
+ @wrapper.to_zebra
38
+ end
39
+
40
+ should 'not raise an error if methods return anything else' do
41
+ @obj.expects(:to_zebra).returns(3)
42
+ ensure_error_not_raised do
43
+ @wrapper.to_zebra
44
+ end
45
+ end
46
+
47
+
48
+ describe 'assertions' do
49
+ describe 'any method' do
50
+ should 'not raise an error if it returns a non false value' do
51
+ @obj.expects(:to_zebra).returns(3)
52
+ @wrapper.to_zebra
53
+ end
54
+
55
+ should 'raise an error if it returns a false/nil value' do
56
+ @obj.expects(:to_zebra).returns(nil)
57
+ ensure_error_raised(EEtee::AssertionFailed) do
58
+ @wrapper.to_zebra
59
+ end
60
+ end
61
+
62
+ should '[inverted] not raise an error if it returns a false/nil value' do
63
+ @obj.expects(:to_zebra).returns(nil)
64
+ ensure_error_not_raised do
65
+ @wrapper.not.to_zebra
66
+ end
67
+ end
68
+ end
69
+
70
+ describe 'raise' do
71
+ should 'raise an error if block do not raises expected error' do
72
+ @obj.expects(:to_zebra).raises("damn !")
73
+ ensure_error_raised(EEtee::AssertionFailed) do
74
+ ->{ @wrapper.to_zebra }.should.raise(NoMethodError)
75
+ end
76
+ end
77
+
78
+ should 'not raise an error if block raises expected error' do
79
+ @obj.expects(:to_zebra).raises("damn !")
80
+ ensure_error_not_raised do
81
+ ->{ @wrapper.to_zebra }.should.raise(RuntimeError)
82
+ end
83
+ end
84
+
85
+ should '[inverted] raise an error if block raises expected error' do
86
+ @obj.expects(:to_zebra).raises("damn !")
87
+ ensure_error_raised(EEtee::AssertionFailed) do
88
+ ->{ @wrapper.to_zebra }.should.not.raise(RuntimeError)
89
+ end
90
+ end
91
+
92
+ end
93
+
94
+ describe 'be_a' do
95
+ should 'not raise an error if class match' do
96
+ w1 = EEtee::AssertionWrapper.new("string")
97
+ w1.be_a String
98
+
99
+ w1 = EEtee::AssertionWrapper.new(4)
100
+ w1.be_a Fixnum
101
+ end
102
+
103
+ should 'raise an error unless class match' do
104
+ w1 = EEtee::AssertionWrapper.new("string")
105
+ ->{ w1.be_a Fixnum }.should.raise(EEtee::AssertionFailed)
106
+
107
+ w1 = EEtee::AssertionWrapper.new(3)
108
+ ->{ w1.be_a String }.should.raise(EEtee::AssertionFailed)
109
+ end
110
+
111
+ end
112
+
113
+ describe 'be' do
114
+ should 'be transparent (syntax sugar)' do
115
+ w1 = EEtee::AssertionWrapper.new("string")
116
+ w1.be == "string"
117
+ end
118
+ end
119
+
120
+ describe 'close?' do
121
+ should 'not raise an error if number in range' do
122
+ w1 = EEtee::AssertionWrapper.new(3.4)
123
+ w1.close?(3, 0.4)
124
+ end
125
+
126
+ should 'raise an error if number outside range' do
127
+ w1 = EEtee::AssertionWrapper.new(3.4)
128
+ ->{ w1.close?(3, 0.3) }.should.raise(EEtee::AssertionFailed)
129
+ end
130
+ end
131
+
132
+ end
133
+
134
+ end