attest 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/Gemfile +1 -1
  2. data/Gemfile.lock +6 -1
  3. data/README.rdoc +121 -36
  4. data/Rakefile +12 -1
  5. data/VERSION +1 -1
  6. data/attest.gemspec +61 -36
  7. data/bin/attest +18 -24
  8. data/doodle.txt +73 -29
  9. data/examples/{magic_calculator.rb → basic_functionality_example.rb} +42 -8
  10. data/examples/mocha_example.rb +43 -0
  11. data/examples/module_example.rb +49 -0
  12. data/examples/more/{placeholder.rb → multiple_context_example.rb} +0 -0
  13. data/examples/more/nesting/expectations_as_tests_example.rb +20 -0
  14. data/lib/attest.rb +4 -20
  15. data/lib/attest/config.rb +3 -6
  16. data/lib/attest/core_ext/kernel.rb +19 -1
  17. data/lib/attest/core_ext/object.rb +1 -2
  18. data/lib/attest/core_ext/proc.rb +35 -0
  19. data/lib/attest/execution_context.rb +156 -50
  20. data/lib/attest/expectation_result.rb +22 -1
  21. data/lib/attest/interface/output_writer_configurator.rb +25 -0
  22. data/lib/attest/interface/possible_tests_configurator.rb +35 -0
  23. data/lib/attest/interface/test_double_configurator.rb +40 -0
  24. data/lib/attest/output/basic_output_writer.rb +14 -46
  25. data/lib/attest/output/failures_only_output_writer.rb +47 -0
  26. data/lib/attest/output/output_writer.rb +124 -0
  27. data/lib/attest/output/output_writer_interface.rb +24 -0
  28. data/lib/attest/output/test_unit_output_writer.rb +32 -0
  29. data/lib/attest/proc/proc_source_reader.rb +49 -0
  30. data/lib/attest/rake/attesttask.rb +38 -0
  31. data/lib/attest/test_container.rb +15 -4
  32. data/lib/attest/test_loader.rb +28 -0
  33. data/lib/attest/test_object.rb +35 -29
  34. data/lib/attest/test_parser.rb +69 -11
  35. data/lib/trollop.rb +782 -0
  36. data/spec/interface/output_writer_configurator_test.rb +18 -0
  37. data/spec/interface/possible_tests_configurator_test.rb +55 -0
  38. data/spec/interface/test_double_configurator_test.rb +20 -0
  39. data/spec/output/output_writer_test.rb +20 -0
  40. data/spec/tmp/new_require_test.rb +10 -0
  41. metadata +55 -18
  42. data/.gitignore +0 -23
  43. data/examples/standard_calculator.rb +0 -28
  44. data/lib/attest/attest_error.rb +0 -7
  45. data/lib/attest/itself.rb +0 -34
@@ -0,0 +1,47 @@
1
+ require 'stringio'
2
+ require 'attest/output/output_writer'
3
+ require 'attest/expectation_result'
4
+
5
+ module Attest
6
+ module Output
7
+ class FailuresOnlyOutputWriter < Attest::Output::OutputWriter
8
+ def initialize
9
+ super()
10
+ @relevant_outputs = StringIO.new
11
+ end
12
+
13
+ def after_all_tests
14
+ super
15
+ @relevant_outputs.rewind
16
+ 2.times {puts}
17
+ puts @relevant_outputs.readlines
18
+ end
19
+
20
+ def before_container(container)
21
+ previous_container = @containers.last
22
+ @containers << container
23
+ end
24
+
25
+ def after_test(test_object)
26
+ relevant_result = determine_relevant_result test_object
27
+ if relevant_result && relevant_result.failure?
28
+ @relevant_outputs.puts "#{@containers.last.file}"
29
+ @relevant_outputs.puts " #{@containers.last.description}"
30
+ @relevant_outputs.puts " - #{test_object.description} [#{relevant_result.status.upcase}]"
31
+ @relevant_outputs.puts " #{relevant_result.source_location}"
32
+ @relevant_outputs.puts
33
+ elsif relevant_result && relevant_result.error?
34
+ e = relevant_result.attributes[:unexpected_error]
35
+ @relevant_outputs.puts "#{@containers.last.file}"
36
+ @relevant_outputs.puts " #{@containers.last.description}"
37
+ @relevant_outputs.puts " - #{test_object.description} [#{relevant_result.status.upcase}]"
38
+ @relevant_outputs.puts " #{e.class}: #{e.message}"
39
+ e.backtrace.each do |line|
40
+ @relevant_outputs.puts " #{line} "
41
+ end
42
+ @relevant_outputs.puts
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,124 @@
1
+ require 'attest/output/output_writer_interface'
2
+ require 'attest/expectation_result'
3
+
4
+ module Attest
5
+ module Output
6
+ class OutputWriter
7
+ include OutputWriterInterface
8
+ def initialize
9
+ self.instance_variable_set("@containers", [])
10
+ self.instance_variable_set("@start_time", nil)
11
+ self.instance_variable_set("@end_time", nil)
12
+ end
13
+
14
+ def before_all_tests
15
+ @start_time = Time.now
16
+ end
17
+ def after_all_tests
18
+ @end_time = Time.now
19
+ end
20
+ def before_container(container)
21
+ end
22
+ def after_container(container)
23
+ end
24
+ def before_test(test_object)
25
+ end
26
+ def after_test(test_object)
27
+ end
28
+ def summary
29
+ return unless @containers.size >= 1
30
+ expectation_status_hash = blank_status_hash
31
+ overall_test_status_hash = blank_status_hash
32
+ test_count = 0
33
+ @containers.each do |container|
34
+ container.test_objects.each do |test_object|
35
+ test_count += 1
36
+ current_test_statuses = determine_test_status test_object
37
+ overall_test_status_hash = merge_counting_hashes(overall_test_status_hash, current_test_statuses[0])
38
+ expectation_status_hash = merge_counting_hashes(expectation_status_hash, current_test_statuses[1])
39
+ end
40
+ end
41
+ puts
42
+ print "#{test_count} tests #{expectation_status_hash.inject(0){|sum, tuple| sum + tuple[1]}} expectations"
43
+ Attest::ExpectationResult.status_weights.sort{|a, b| a[1] <=> b[1]}.each {|status, weight| print " #{expectation_status_hash[status]} #{status.to_s}"}
44
+ puts
45
+ puts "Finished in #{elapsed_time(@end_time, @start_time)}"
46
+ end
47
+ def ignore_container(container)
48
+ if @containers.last.object_id == container.object_id
49
+ @containers.delete @containers.last
50
+ else
51
+ @containers.delete_if {|current_container| current_container.object_id == container.object_id}
52
+ end
53
+ end
54
+ def an_error(error_object)
55
+ puts "#{error_object.class}: #{error_object.message}"
56
+ error_object.backtrace.each do |line|
57
+ puts " #{line} "
58
+ end
59
+ end
60
+
61
+ private
62
+ def elapsed_time(end_time, start_time)
63
+ units = ["milliseconds", "seconds", "minutes", "hours"]
64
+ elapsed_seconds = end_time - start_time
65
+ if elapsed_seconds < 1
66
+ elapsed_time_as_string = "#{round_to(2, (elapsed_seconds * 1000))} #{units[0]}"
67
+ elsif elapsed_seconds >= 1 && elapsed_seconds < 60
68
+ elapsed_time_as_string = "#{round_to(2, elapsed_seconds)} #{units[1]}"
69
+ elsif elapsed_seconds >= 60 && elapsed_seconds < 3600
70
+ minsec = elapsed_seconds.divmod(60).collect{|num| round_to(2, num)}
71
+ elapsed_time_as_string = "#{minsec[0]} #{units[2]}, #{minsec[1]} #{units[1]}"
72
+ else
73
+ minsec = elapsed_seconds.divmod(60).collect{|num| round_to(2, num)}
74
+ hourminsec = minsec[0].divmod(60).collect{|num| round_to(2, num)}
75
+ hourminsec << minsec[1]
76
+ elapsed_time_as_string = "#{hourminsec[0]} #{units[3]}, #{hourminsec[1]} #{units[2]}, #{hourminsec[2]} #{units[1]}"
77
+ end
78
+ elapsed_time_as_string
79
+ end
80
+
81
+ def round_to(decimal_places, number)
82
+ rounded = (number * 10**decimal_places).round.to_f / 10**decimal_places
83
+ rounded_as_int = (rounded == rounded.to_i ? rounded.to_i : rounded)
84
+ rounded_as_int
85
+ end
86
+
87
+ def determine_relevant_result(test_object)
88
+ relevant_result = nil
89
+ test_object.results.each do |result|
90
+ relevant_result = result unless result.success?
91
+ end
92
+ relevant_result
93
+ end
94
+
95
+ def determine_test_status(test_object)
96
+ expectation_status_hash = blank_status_hash
97
+ overall_test_status_hash = blank_status_hash
98
+ dominant_result = nil
99
+ test_object.results.each do |result|
100
+ expectation_status_hash[result.status.to_sym] += 1
101
+ dominant_result = result if result > dominant_result
102
+ end
103
+ raise "Unexpected result status encountered! WTF!!!" if expectation_status_hash.keys.size > Attest::ExpectationResult.status_types.size
104
+ raise "Test without status encountered, all test should have a status!" unless dominant_result
105
+ overall_test_status_hash[dominant_result.status.to_sym] += 1
106
+ [overall_test_status_hash, expectation_status_hash]
107
+ end
108
+
109
+ def merge_counting_hashes(hash1, hash2)
110
+ hash1.inject(hash2) do |accumulator_hash, tuple|
111
+ accumulator_hash[tuple[0]] += tuple[1]
112
+ accumulator_hash
113
+ end
114
+ end
115
+
116
+ def blank_status_hash
117
+ Attest::ExpectationResult.status_types.inject({}) do |accumulator, status|
118
+ accumulator[status] = 0
119
+ accumulator
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,24 @@
1
+ module Attest
2
+ module Output
3
+ module OutputWriterInterface
4
+ def before_all_tests
5
+ end
6
+ def after_all_tests
7
+ end
8
+ def before_container(container)
9
+ end
10
+ def after_container(container)
11
+ end
12
+ def before_test(test_object)
13
+ end
14
+ def after_test(test_object)
15
+ end
16
+ def summary
17
+ end
18
+ def ignore_container(container)
19
+ end
20
+ def an_error(error_object)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,32 @@
1
+ require 'attest/output/output_writer'
2
+ require 'attest/expectation_result'
3
+
4
+ module Attest
5
+ module Output
6
+ class TestUnitOutputWriter < Attest::Output::OutputWriter
7
+ def before_all_tests
8
+ super
9
+ puts
10
+ end
11
+
12
+ def after_all_tests
13
+ super
14
+ puts
15
+ end
16
+
17
+ def before_container(container)
18
+ previous_container = @containers.last
19
+ @containers << container
20
+ end
21
+
22
+ def after_test(test_object)
23
+ relevant_result = determine_relevant_result test_object
24
+ if relevant_result
25
+ print "#{relevant_result.status.upcase[0]}"
26
+ else
27
+ print '.'
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,49 @@
1
+ require 'stringio'
2
+ require 'irb/ruby-lex'
3
+
4
+ module Attest
5
+ class ProcSourceReader
6
+ def initialize(file, line)
7
+ @file = file
8
+ @start_line = line
9
+ end
10
+
11
+ def self.find(file, line)
12
+ source_reader = ProcSourceReader.new(file, line)
13
+ source_reader.read_source
14
+
15
+ end
16
+
17
+ def read_source
18
+ lines_starting_with_proc = read_lines_from_file
19
+ return nil if lines_starting_with_proc.nil?
20
+ lexer = RubyLex.new
21
+ lexer.set_input(StringIO.new(lines_starting_with_proc.join))
22
+ start_token, end_token = nil, nil
23
+ nesting = 0
24
+ while token = lexer.token
25
+ if RubyToken::TkDO === token || RubyToken::TkfLBRACE === token
26
+ nesting += 1
27
+ start_token = token if nesting == 1
28
+ elsif RubyToken::TkEND === token || RubyToken::TkRBRACE === token
29
+ if nesting == 1
30
+ end_token = token
31
+ break
32
+ end
33
+ nesting -= 1
34
+ end
35
+ end
36
+ proc_lines = lines_starting_with_proc[start_token.line_no - 1 .. end_token.line_no - 1]
37
+ proc_lines
38
+ end
39
+
40
+ def read_lines_from_file
41
+ raise "No file for proc where does it come from" unless @file
42
+ begin
43
+ File.readlines(@file)[(@start_line - 1) .. -1]
44
+ rescue
45
+ nil
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,38 @@
1
+ require 'rake'
2
+ require 'rake/tasklib'
3
+
4
+ module Rake
5
+ class AttestTask < TaskLib
6
+ attr_accessor :include, :exclude, :outputwriter, :testdouble
7
+ def initialize
8
+ @include = "attest/"
9
+ @exclude = nil
10
+ @outputwriter = "Basic"
11
+ @testdouble = "mocha"
12
+ yield self if block_given?
13
+ define
14
+ end
15
+
16
+ def define
17
+ desc "Run attest tests"
18
+ task :attest do
19
+ $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), '../../../lib/'))) unless $:.include?(File.expand_path(File.join(File.dirname(__FILE__), '../../../lib')))
20
+ require 'attest'
21
+ require 'attest/interface/output_writer_configurator'
22
+ require 'attest/interface/test_double_configurator'
23
+ require 'attest/interface/possible_tests_configurator'
24
+
25
+ Attest.configure do |config|
26
+ config.output_writer = Attest::OutputWriterConfigurator.configure(@outputwriter)
27
+ config.testdouble = Attest::TestDoubleConfigurator.configure(@testdouble)
28
+ config.possible_tests = Attest::PossibleTestsConfigurator.configure(@include, @exclude)
29
+ end
30
+
31
+ require 'attest/test_loader'
32
+
33
+ Attest::TestLoader.execute(Attest.config.possible_tests, Attest.config.output_writer)
34
+ end
35
+ self
36
+ end
37
+ end
38
+ end
@@ -1,7 +1,10 @@
1
+ require 'attest'
2
+
1
3
  module Attest
2
4
  class TestContainer
3
5
 
4
6
  attr_reader :description, :test_objects, :file
7
+ attr_accessor :before, :after
5
8
 
6
9
  def initialize(description)
7
10
  @file = Attest.current_file
@@ -14,11 +17,19 @@ module Attest
14
17
  end
15
18
 
16
19
  def execute_all
17
- Attest.output_writer.before_context(self)
18
- @test_objects.each do |test_object|
19
- test_object.run
20
+ Attest.output_writer.before_container(self)
21
+ container_context = Attest::ExecutionContext.new
22
+ begin
23
+ container_context.instance_eval(&@before) if @before
24
+ @test_objects.each do |test_object|
25
+ test_object.run container_context
26
+ end
27
+ container_context.instance_eval(&@after) if @after
28
+ rescue => e
29
+ Attest.output_writer.an_error(e)
30
+ Attest.output_writer.ignore_container(self)
20
31
  end
21
- Attest.output_writer.after_context
32
+ Attest.output_writer.after_container(self)
22
33
  end
23
34
  end
24
35
  end
@@ -0,0 +1,28 @@
1
+ require 'attest'
2
+ require 'attest/core_ext/kernel'
3
+
4
+ module Attest
5
+ class TestLoader
6
+ class << self
7
+ def execute(possible_tests, output_writer)
8
+ switch_on_attest_mode
9
+ output_writer.before_all_tests
10
+ possible_tests.each do |ruby_file|
11
+ Attest.config.current_file = ruby_file
12
+ load ruby_file
13
+ end
14
+ output_writer.after_all_tests
15
+ output_writer.summary
16
+ switch_off_attest_mode
17
+ end
18
+
19
+ def switch_on_attest_mode
20
+ ENV["attest"] = "true"
21
+ end
22
+
23
+ def switch_off_attest_mode
24
+ ENV["attest"] = nil
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,58 +1,64 @@
1
+ require 'attest'
2
+ require 'attest/execution_context'
3
+ require 'attest/expectation_result'
4
+
1
5
  module Attest
2
6
  class TestObject
3
7
  attr_reader :description, :results
4
- attr_accessor :nosetup
8
+ attr_accessor :nosetup, :disabled, :before, :after
5
9
  def initialize(description, test_block)
6
10
  @description = description
7
11
  @test_block = test_block
8
- @before = nil
9
- @after = nil
10
- @results = nil
11
- end
12
-
13
- def add_setup(block)
14
- @before = block
12
+ @results = []
15
13
  end
16
14
 
17
- def add_cleanup(block)
18
- @after = block
19
- end
20
-
21
- def run
15
+ def run(persistent_context)
22
16
  Attest.output_writer.before_test(self)
23
17
  error = nil
24
- context = Attest::ExecutionContext.new
18
+ context = Attest::ExecutionContext.new(persistent_context)
25
19
  begin
26
- Object.class_eval do
27
- define_method :itself do
28
- subject = self
29
- context.instance_eval {@subject = subject}
30
- context
31
- end
32
- end
33
- context.instance_eval(&@before) if @before && !nosetup
34
- context.instance_eval(&@test_block) if @test_block
35
- context.instance_eval(&@after) if @after && !nosetup
20
+ #Object.class_eval do
21
+ #define_method :itself do
22
+ #subject = self
23
+ #context.instance_eval {@subject = subject}
24
+ #context
25
+ #end
26
+ #end
27
+ context.instance_eval(&@before) if @before && !nosetup && !disabled
28
+ context.instance_eval(&@test_block) if @test_block && !disabled
29
+ context.instance_eval(&@after) if @after && !nosetup && !disabled
36
30
  rescue => e
37
31
  error = e
38
32
  ensure
39
33
  @results = context.results
40
34
  add_unexpected_error_result(error) if error
41
35
  add_pending_result unless @test_block
36
+ add_disabled_result if disabled
37
+ add_success_result if @results.size == 0
42
38
  end
43
39
  Attest.output_writer.after_test(self)
44
40
  end
45
41
 
46
42
  private
47
43
  def add_unexpected_error_result(error)
48
- result = Attest::ExpectationResult.new(:unexpected_error => error)
49
- result.error
50
- @results << result
44
+ create_and_add_result(:unexpected_error => error) {|result| result.error}
51
45
  end
52
46
 
53
47
  def add_pending_result
54
- result = Attest::ExpectationResult.new
55
- result.pending
48
+ create_and_add_result{|result| result.pending}
49
+ end
50
+
51
+ def add_disabled_result
52
+ create_and_add_result{|result| result.disabled}
53
+ end
54
+
55
+ def add_success_result
56
+ create_and_add_result{|result| result.success}
57
+ end
58
+
59
+ def create_and_add_result(opts={})
60
+ result = Attest::ExpectationResult.new(opts)
61
+ yield result
56
62
  @results << result
57
63
  end
58
64
  end