matest 1.2.0 → 1.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 92cbe87cbc8b53490086b363cba0466107e906a5
4
- data.tar.gz: 545125006ca70b8c13ca8b5632183e3832731898
3
+ metadata.gz: 84452e41b4f3cd9abc5a1bbc3f908700a9cc8861
4
+ data.tar.gz: 68d2dabb2017c46bc31eb36d8b67e918e8eddab7
5
5
  SHA512:
6
- metadata.gz: d743771c606770155e344f1bdc5c13118a386bdbc1e28ced772ff97108bbc27482772e5e3a69fec9d9c764d2ed637915cf48974aa48ca45f29d74b22dd6567ed
7
- data.tar.gz: 686cbd8a5ef598ee08564e4c27ce575895b5f30ed6a044f2dfdb79199ef76876cff2da3b133f168a16a5f6f9c48fc9729f04f98cbe2c5aa70d666b3f8e84e7c8
6
+ metadata.gz: 16fa8463a914188a962999097d5c9b54182c56dc5ed3f528cf2a010dcf1bf75ae9b62dfa9d8c61ab6402c8116e6fce2181d2398afb2db80c35db6c822f7abd70
7
+ data.tar.gz: 35dcf6b249937b2a4eed2491dd8d4f2d5ed5c319a1973b45d64189bd38991f4f3127692e084ecb0e0d555810184c3dcffd395159490b7aa676971f6de91d28ad
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
1
  source "https://rubygems.org"
2
- gemspec
2
+ gemspec
3
3
 
data/README.md CHANGED
@@ -74,15 +74,6 @@ This is important, because the assertion will be evaluated and it will provide t
74
74
  The assertion can be any expression that returns a boolean value, BUT IT CANNOT CONTAIN LOCAL VARIABLES.
75
75
  If the assertion contains a local variable and it fails, the code that explains it bit by bit will throw an error.
76
76
 
77
- ### The assertion MUST be idempotent
78
-
79
- Once the code is run, the state of the block gets saved.
80
- If the test fails, the assertion will be re-run a couple of times.
81
- So if you are popping the last element of an array inside the assertion, each time it gets run, you'll be popping the next element on the chain.
82
- The reason for this is that Matest will show you the result of each part of the expression and for that, it needs to re-run each part.
83
-
84
- Removing this constraint is a high priority on my todo list. But I'm considering the threadoffs of doing it. I'll be happy to pair or discuss on this matter.
85
-
86
77
  ## Raising Errors
87
78
 
88
79
  If your test raises an error during the run, youll get an `ERROR` status and you'll see the backtrace.
@@ -162,9 +153,9 @@ In case the test fails, you'll get an extensive explanation about why.
162
153
  To show a trivial example:
163
154
 
164
155
  ```ruby
165
- scope "hola" do
156
+ scope do
166
157
  let(:three) { 3 }
167
- spec "chau" do
158
+ spec "Printing Failing Specs" do
168
159
  one = 2
169
160
  two = 2
170
161
  @one_plus_two_plus_three = one + two + three
@@ -182,7 +173,7 @@ F
182
173
 
183
174
  ### Messages ###
184
175
 
185
- [FAILING] chau
176
+ [FAILING] Printing Failing Specs
186
177
  Location:
187
178
  spec/matest_specs/printing_assertion_spec.rb:3:
188
179
  Assertion:
@@ -1,208 +1,15 @@
1
1
  require "matest/version"
2
+
3
+ require "matest/runner"
4
+ require "matest/example_group"
5
+ require "matest/example"
2
6
  require "matest/example_block"
7
+ require "matest/let"
8
+ require "matest/skip_me"
3
9
  require "matest/spec_status"
4
10
  require "matest/spec_printer"
5
-
11
+ require "matest/top_level_methods"
6
12
 
7
13
  module Matest
8
- class Runner
9
- attr_reader :example_groups
10
- attr_reader :info
11
- attr_reader :printer
12
-
13
- def initialize(options={})
14
- @example_groups = []
15
- @info = {}
16
- @printer = options[:printer] || SpecPrinter.new
17
- end
18
-
19
- def self.runner
20
- @runner ||= new
21
- end
22
-
23
- def <<(example_group)
24
- example_groups << example_group
25
- end
26
-
27
- def load_file(file)
28
- load(file)
29
- end
30
-
31
- def execute!
32
- example_groups.each do |current_group|
33
- current_group.execute!
34
- end
35
- printer.print(self)
36
- end
37
- end
38
-
39
- class SkipMe; end
40
-
41
- class Example
42
- def initialize(example_block, description, lets)
43
- @example_block__for_internal_use = ExampleBlock.new(example_block)
44
- @description__for_internal_use = description
45
- lets.each do |let|
46
- self.class.let(let.var_name, &let.block)
47
- send(let.var_name) if let.bang
48
- end
49
- end
50
-
51
- def example_block
52
- @example_block__for_internal_use
53
- end
54
-
55
- def description
56
- @description__for_internal_use
57
- end
58
-
59
- def call
60
- instance_eval(&example_block.block)
61
- end
62
-
63
- def self.let(var_name, &block)
64
- define_method(var_name) do
65
- instance_variable_set(:"@#{var_name}__from_let", block.call)
66
- end
67
- end
68
-
69
- def self.local_var(var_name)
70
- define_method(var_name) do
71
- instance_variable_get(:"@#{var_name}")
72
- end
73
- define_method("#{var_name}=") do |value|
74
- instance_variable_set(:"@#{var_name}", value)
75
- end
76
- end
77
-
78
- def track_variables
79
- instance_variables.reject {|var|
80
- var.to_s =~ /__for_internal_use\Z/ || var.to_s =~ /__from_let\Z/
81
- }.map {|var| [var, instance_variable_get(var)] }
82
- end
83
-
84
- def track_lets
85
- instance_variables.select {|var|
86
- var.to_s =~ /__from_let\Z/
87
- }.map {|var|
88
- name = var.to_s
89
- name["__from_let"] = ""
90
- name[0] = ""
91
- [name, instance_variable_get(var)]
92
- }
93
- end
94
-
95
- def without_block
96
- the_new = self.clone
97
- the_new.instance_variable_set(:@example_block__for_internal_use, nil)
98
- the_new
99
- end
100
-
101
- end
102
-
103
- class Let
104
- attr_reader :var_name
105
- attr_reader :block
106
- attr_reader :bang
107
-
108
- def initialize(var_name, block, bang=false)
109
- @var_name = var_name
110
- @block = block
111
- @bang = bang
112
- end
113
- end
114
-
115
- class ExampleGroup
116
- attr_reader :scope_block
117
- attr_reader :specs
118
- attr_reader :lets
119
- attr_reader :statuses
120
-
121
- def initialize(scope_block)
122
- @scope_block = scope_block
123
- @specs = []
124
- @lets = []
125
- @statuses = []
126
- end
127
-
128
- def spec(description=nil, &block)
129
- current_example = block_given? ? block : ->(*) { Matest::SkipMe.new }
130
- specs << Example.new(current_example, description, lets)
131
- end
132
-
133
- def execute!
134
- instance_eval(&scope_block)
135
- specs.shuffle.each do |spec, desc|
136
- res = run_spec(spec)
137
- print res
138
- end
139
-
140
- end
141
-
142
- def xspec(description=nil, &block)
143
- spec(description)
144
- end
145
-
146
- alias :it :spec
147
- alias :xit :xspec
148
-
149
- alias :test :spec
150
- alias :xtest :xspec
151
-
152
- alias :example :spec
153
- alias :xexample :xspec
154
-
155
-
156
- def self.let(var_name, &block)
157
- define_method(var_name) do
158
- instance_variable_set(:"@#{var_name}", block.call)
159
- end
160
- end
161
-
162
- def let(var_name, bang=false, &block)
163
- lets << Let.new(var_name, block, bang=false)
164
- end
165
-
166
- def let!(var_name, &block)
167
- lets << Let.new(var_name, block, bang=true)
168
- end
169
-
170
- def run_spec(spec)
171
- status = begin
172
- result = spec.call
173
- status_class = case result
174
- when true
175
- Matest::SpecPassed
176
- when false
177
- Matest::SpecFailed
178
- when Matest::SkipMe
179
- Matest::SpecSkipped
180
- else
181
- Matest::NotANaturalAssertion
182
- end
183
- status_class.new(spec, result)
184
- rescue Exception => e
185
- Matest::ExceptionRaised.new(spec, e, spec.description)
186
- end
187
- @statuses << status
188
- status
189
- end
190
- end
191
- end
192
-
193
- def scope(description=nil, &block)
194
- Matest::Runner.runner << Matest::ExampleGroup.new(block)
195
- end
196
-
197
- def xscope(description=nil, &block)
198
- # no-op
199
14
  end
200
15
 
201
- alias :describe :scope
202
- alias :xdescribe :xscope
203
-
204
- alias :context :scope
205
- alias :xcontext :xscope
206
-
207
- alias :group :scope
208
- alias :xgroup :xscope
@@ -0,0 +1,47 @@
1
+ module Matest
2
+ class EvalErr
3
+ def initialize(str)
4
+ @string = str
5
+ end
6
+ def size
7
+ inspect.size
8
+ end
9
+ def to_s
10
+ @string
11
+ end
12
+ def inspect
13
+ @string
14
+ end
15
+ end
16
+
17
+ class Evaluator
18
+ def initialize(example, block)
19
+ @example = example
20
+ @block = block
21
+ end
22
+
23
+ def eval_string(exp_string)
24
+ limit_length(eval_in_context(exp_string).inspect)
25
+ rescue StandardError => ex
26
+ EvalErr.new("#{ex.class}: #{ex.message}")
27
+ end
28
+
29
+ private
30
+
31
+ MAX_INSPECT_SIZE = 2000
32
+
33
+ def limit_length(string)
34
+ if string.size > MAX_INSPECT_SIZE
35
+ string[0..MAX_INSPECT_SIZE] + " (...truncated...)"
36
+ else
37
+ string
38
+ end
39
+ end
40
+
41
+ def eval_in_context(exp_string)
42
+ exp_proc = "proc { #{exp_string} }"
43
+ blk = eval(exp_proc, @block.binding)
44
+ @example.instance_eval(&blk)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,92 @@
1
+ module Matest
2
+ class Example
3
+ def initialize(example_block, description, lets)
4
+ @example_block__for_internal_use = ExampleBlock.new(example_block)
5
+ @description__for_internal_use = description
6
+ @lets__for_internal_use = lets
7
+ lets.each do |let|
8
+ self.class.let(let.var_name, &let.block)
9
+ send(let.var_name) if let.bang
10
+ end
11
+ end
12
+
13
+ def lets
14
+ @lets__for_internal_use
15
+ end
16
+
17
+ def example_block
18
+ @example_block__for_internal_use
19
+ end
20
+
21
+ def description
22
+ @description__for_internal_use
23
+ end
24
+
25
+ def call
26
+ instance_eval(&example_block.block)
27
+ end
28
+
29
+ def self.let(var_name, &block)
30
+ define_method(var_name) do
31
+ instance_variable_set(:"@#{var_name}__from_let", block.call)
32
+ end
33
+ end
34
+
35
+ def self.local_var(var_name)
36
+ define_method(var_name) do
37
+ instance_variable_get(:"@#{var_name}")
38
+ end
39
+ define_method("#{var_name}=") do |value|
40
+ instance_variable_set(:"@#{var_name}", value)
41
+ end
42
+ end
43
+
44
+ def track_variables
45
+ instance_variables.reject {|var|
46
+ var.to_s =~ /__for_internal_use\Z/ || var.to_s =~ /__from_let\Z/
47
+ }.map {|var| [var, instance_variable_get(var)] }
48
+ end
49
+
50
+ def track_lets
51
+ instance_variables.select {|var|
52
+ var.to_s =~ /__from_let\Z/
53
+ }.map {|var|
54
+ name = var.to_s
55
+ name["__from_let"] = ""
56
+ name[0] = ""
57
+ [name, instance_variable_get(var)]
58
+ }
59
+ end
60
+
61
+ # def without_block
62
+ # the_new = self.clone
63
+ # the_new.instance_variable_set(:@example_block__for_internal_use, nil)
64
+ # the_new
65
+ # end
66
+
67
+ def just_before_assertion
68
+ # return a clone of self, but with
69
+ ExampleBeforeAssertion.new(example_block.block, description, lets)
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ class ExampleBeforeAssertion < Example
76
+ def initialize(example_block, description, lets)
77
+ super
78
+ set_state
79
+ end
80
+
81
+ def set_state
82
+ before_sexp = example_block.sexp[0..-2]
83
+ @code = Sorcerer.source(before_sexp)
84
+ eval(@code)
85
+ end
86
+
87
+ def before_assertion_block
88
+ eval("proc { #{@code} }")
89
+ end
90
+
91
+ end
92
+ end
@@ -0,0 +1,80 @@
1
+ module Matest
2
+ class ExampleGroup
3
+ attr_reader :scope_block
4
+ attr_reader :specs
5
+ attr_reader :lets
6
+ attr_reader :statuses
7
+
8
+ attr_accessor :printer
9
+
10
+ def initialize(scope_block)
11
+ @scope_block = scope_block
12
+ @specs = []
13
+ @lets = []
14
+ @statuses = []
15
+ end
16
+
17
+ def spec(description=nil, &block)
18
+ current_example = block_given? ? block : ->(*) { Matest::SkipMe.new }
19
+ specs << Example.new(current_example, description, lets)
20
+ end
21
+
22
+ def execute!
23
+ instance_eval(&scope_block)
24
+ specs.shuffle.each do |spec, desc|
25
+ res = run_spec(spec)
26
+ printer.print(res)
27
+ end
28
+
29
+ end
30
+
31
+ def xspec(description=nil, &block)
32
+ spec(description)
33
+ end
34
+
35
+ alias :it :spec
36
+ alias :xit :xspec
37
+
38
+ alias :test :spec
39
+ alias :xtest :xspec
40
+
41
+ alias :example :spec
42
+ alias :xexample :xspec
43
+
44
+
45
+ def self.let(var_name, &block)
46
+ define_method(var_name) do
47
+ instance_variable_set(:"@#{var_name}", block.call)
48
+ end
49
+ end
50
+
51
+ def let(var_name, bang=false, &block)
52
+ lets << Let.new(var_name, block, bang=false)
53
+ end
54
+
55
+ def let!(var_name, &block)
56
+ lets << Let.new(var_name, block, bang=true)
57
+ end
58
+
59
+ def run_spec(spec)
60
+ status = begin
61
+ result = spec.call
62
+ status_class = case result
63
+ when true
64
+ Matest::SpecPassed
65
+ when false
66
+ Matest::SpecFailed
67
+ when Matest::SkipMe
68
+ Matest::SpecSkipped
69
+ else
70
+ Matest::NotANaturalAssertion
71
+ end
72
+ status_class.new(spec, result)
73
+ rescue Exception => e
74
+ Matest::ExceptionRaised.new(spec, e, spec.description)
75
+ end
76
+ @statuses << status
77
+ status
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,13 @@
1
+ module Matest
2
+ class Let
3
+ attr_reader :var_name
4
+ attr_reader :block
5
+ attr_reader :bang
6
+
7
+ def initialize(var_name, block, bang=false)
8
+ @var_name = var_name
9
+ @block = block
10
+ @bang = bang
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,34 @@
1
+ module Matest
2
+ class Runner
3
+ attr_reader :example_groups
4
+ attr_reader :info
5
+ attr_reader :printer
6
+
7
+ def initialize(options={})
8
+ @example_groups = []
9
+ @info = {}
10
+ @printer = options[:printer] || SpecPrinter.new
11
+ end
12
+
13
+ def self.runner
14
+ @runner ||= new
15
+ end
16
+
17
+ def <<(example_group)
18
+ example_group.printer = printer
19
+ example_groups << example_group
20
+ end
21
+
22
+ def load_file(file)
23
+ load(file)
24
+ end
25
+
26
+ def execute!
27
+ example_groups.each do |current_group|
28
+ current_group.execute!
29
+ end
30
+ printer.print_messages(self)
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,3 @@
1
+ module Matest
2
+ class SkipMe; end
3
+ end
@@ -1,53 +1,12 @@
1
- module Matest
2
- class EvalErr
3
- def initialize(str)
4
- @string = str
5
- end
6
- def size
7
- inspect.size
8
- end
9
- def to_s
10
- @string
11
- end
12
- def inspect
13
- @string
14
- end
15
- end
16
-
17
- class Evaluator
18
- def initialize(example, block)
19
- # @example = Marshal.load( Marshal.dump(example) )
20
- @example = example
21
- @block = block
22
- end
23
-
24
- def eval_string(exp_string)
25
- limit_length(eval_in_context(exp_string).inspect)
26
- rescue StandardError => ex
27
- EvalErr.new("#{ex.class}: #{ex.message}")
28
- end
29
-
30
- private
31
-
32
- MAX_INSPECT_SIZE = 2000
33
-
34
- def limit_length(string)
35
- if string.size > MAX_INSPECT_SIZE
36
- string[0..MAX_INSPECT_SIZE] + " (...truncated...)"
37
- else
38
- string
39
- end
40
- end
41
-
42
- def eval_in_context(exp_string)
43
- exp_proc = "proc { #{exp_string} }"
44
- blk = eval(exp_proc, @block.binding)
45
- @example.instance_eval(&blk)
46
- end
47
- end
1
+ require "matest/evaluator"
48
2
 
3
+ module Matest
49
4
  class SpecPrinter
50
- def print(runner)
5
+ def print(res)
6
+ super res
7
+ end
8
+
9
+ def print_messages(runner)
51
10
  puts "\n\n### Messages ###"
52
11
 
53
12
  statuses = []
@@ -86,6 +45,11 @@ module Matest
86
45
  if status.is_a?(Matest::NotANaturalAssertion)
87
46
  runner.info[:success] = false
88
47
  puts " # => #{status.result.inspect}"
48
+ puts "Explanation:"
49
+ subexpressions = Sorcerer.subexpressions(status.example.example_block.assertion_sexp).reverse.uniq.reverse
50
+ subexpressions.each do |code|
51
+ print_subexpression(code, status)
52
+ end
89
53
  end
90
54
  if status.is_a?(Matest::ExceptionRaised)
91
55
  runner.info[:success] = false
@@ -101,16 +65,17 @@ module Matest
101
65
  end
102
66
 
103
67
  def print_subexpression(code, status)
104
- result = Evaluator.new(status.example, status.example.example_block.block).eval_string(code)
68
+ just_before_assertion = status.example.just_before_assertion
69
+ result = Evaluator.new(just_before_assertion, just_before_assertion.before_assertion_block).eval_string(code)
105
70
  if result.class != Matest::EvalErr
106
71
  puts <<-CODE
107
- "#{code}" =>
108
- #{result}
72
+ #{code}
73
+ # => #{result}
109
74
  CODE
110
75
  else
111
76
  puts <<-CODE
112
77
  The assertion couldn't be explained.
113
- The error message was:
78
+ The error message was:
114
79
  #{result}
115
80
  Make sure you are not calling any local vaiables on your code assertion.
116
81
  CODE
@@ -0,0 +1,16 @@
1
+ def scope(description=nil, &block)
2
+ Matest::Runner.runner << Matest::ExampleGroup.new(block)
3
+ end
4
+
5
+ def xscope(description=nil, &block)
6
+ # no-op
7
+ end
8
+
9
+ alias :describe :scope
10
+ alias :xdescribe :xscope
11
+
12
+ alias :context :scope
13
+ alias :xcontext :xscope
14
+
15
+ alias :group :scope
16
+ alias :xgroup :xscope
@@ -1,3 +1,3 @@
1
1
  module Matest
2
- VERSION = "1.2.0"
2
+ VERSION = "1.3.0"
3
3
  end
@@ -1,35 +1,29 @@
1
- scope "hola" do
1
+ scope do
2
2
  let(:three) { 3 }
3
- xspec "chau" do
3
+ spec "variables and lets" do
4
4
  one = 2
5
5
  two = 2
6
6
 
7
7
  @one_plus_two_plus_three = one + two + three
8
8
  @res = 3
9
+
9
10
  @one_plus_two_plus_three.to_i == @res
10
11
  end
11
12
 
12
- xspec "again?" do
13
+ spec "again?" do
13
14
  @arr = %w[a b c d e]
14
15
 
15
16
  @arr.pop == "k"
16
17
  end
17
18
 
18
- spec do
19
- a = 4
20
-
21
- a == 5
22
- end
23
- end
19
+ spec "not natural" do
20
+ one = 2
21
+ two = 2
24
22
 
25
- # @one_plus_two == @res.to_i => false
26
- # @one_plus_two => 4
27
- # @res => "3"
28
- # @res.to_i => 3
23
+ @one_plus_two_plus_three = one + two + three
24
+ @res = 3
29
25
 
26
+ @res = @one_plus_two_plus_three.to_i + @res.to_i
27
+ end
30
28
 
31
- ## ON EXAMPLE
32
- # on call:
33
- # - run the spec without the assertion
34
- # - save the state
35
- # - and then run the assertion.
29
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: matest
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Federico Iachetti
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-11 00:00:00.000000000 Z
11
+ date: 2015-01-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -67,9 +67,16 @@ files:
67
67
  - Rakefile
68
68
  - bin/matest
69
69
  - lib/matest.rb
70
+ - lib/matest/evaluator.rb
71
+ - lib/matest/example.rb
70
72
  - lib/matest/example_block.rb
73
+ - lib/matest/example_group.rb
74
+ - lib/matest/let.rb
75
+ - lib/matest/runner.rb
76
+ - lib/matest/skip_me.rb
71
77
  - lib/matest/spec_printer.rb
72
78
  - lib/matest/spec_status.rb
79
+ - lib/matest/top_level_methods.rb
73
80
  - lib/matest/version.rb
74
81
  - matest.gemspec
75
82
  - spec/matest_spec.rb