matest 1.1.4 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0ef04c88e374d09410f7e524b755d30d75508ef5
4
- data.tar.gz: 505f6005edf5df314ffa8b78177e888fa1388bf6
3
+ metadata.gz: 92cbe87cbc8b53490086b363cba0466107e906a5
4
+ data.tar.gz: 545125006ca70b8c13ca8b5632183e3832731898
5
5
  SHA512:
6
- metadata.gz: f364ae4bf2c77c7b1f07abd412306370a824c0d68ae92dbd5092a23f17133ebf176aada8e2a62b33a326d90012286508493d23c297394f00321a1f1fa985e306
7
- data.tar.gz: f792735a4069cbc2e2581a16c4cd620e3f8e7d66c360576a58adbdb4a1c90b7e47221a9192054d5e3cb47e5bb32ad10a38394b68d3d781885facdb58ddeed130
6
+ metadata.gz: d743771c606770155e344f1bdc5c13118a386bdbc1e28ced772ff97108bbc27482772e5e3a69fec9d9c764d2ed637915cf48974aa48ca45f29d74b22dd6567ed
7
+ data.tar.gz: 686cbd8a5ef598ee08564e4c27ce575895b5f30ed6a044f2dfdb79199ef76876cff2da3b133f168a16a5f6f9c48fc9729f04f98cbe2c5aa70d666b3f8e84e7c8
data/.gitignore CHANGED
@@ -12,3 +12,5 @@
12
12
  *.o
13
13
  *.a
14
14
  mkmf.log
15
+ tags
16
+ .tags
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  # Matest
2
+ [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/iachettifederico/matest?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
2
3
 
3
4
  Tests Gasoleros (Very cheap tests)
4
5
 
@@ -59,6 +60,29 @@ scope "a description" do
59
60
  end
60
61
  ```
61
62
 
63
+ ## Constraints
64
+
65
+ A couple of constraints must b taken into account, and this is extremely important.
66
+
67
+ ### The assertion MUST return a boolean
68
+
69
+ The assertion is the last statement of the block and it must return either true or false.
70
+ This is important, because the assertion will be evaluated and it will provide the status o the test.
71
+
72
+ ### Assertion expression
73
+
74
+ The assertion can be any expression that returns a boolean value, BUT IT CANNOT CONTAIN LOCAL VARIABLES.
75
+ If the assertion contains a local variable and it fails, the code that explains it bit by bit will throw an error.
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
+
62
86
  ## Raising Errors
63
87
 
64
88
  If your test raises an error during the run, youll get an `ERROR` status and you'll see the backtrace.
@@ -133,7 +157,47 @@ end
133
157
 
134
158
  ## The output
135
159
 
136
- In case the test fails, the instance variables you define inside it as well as the ones defined by `let` and `let!` are tracked and you'll see their final values in the output.
160
+ In case the test fails, you'll get an extensive explanation about why.
161
+
162
+ To show a trivial example:
163
+
164
+ ```ruby
165
+ scope "hola" do
166
+ let(:three) { 3 }
167
+ spec "chau" do
168
+ one = 2
169
+ two = 2
170
+ @one_plus_two_plus_three = one + two + three
171
+ @res = 3
172
+
173
+ @one_plus_two_plus_three.to_i == @res
174
+ end
175
+ end
176
+ ```
177
+
178
+ It fails and the output will be
179
+
180
+ ```
181
+ F
182
+
183
+ ### Messages ###
184
+
185
+ [FAILING] chau
186
+ Location:
187
+ spec/matest_specs/printing_assertion_spec.rb:3:
188
+ Assertion:
189
+ @one_plus_two_plus_three.to_i == @res
190
+ Variables:
191
+ @one_plus_two_plus_three: 7
192
+ @res: 3
193
+ Lets:
194
+ three: 3
195
+ Explanation:
196
+ "@one_plus_two_plus_three.to_i == @res" =>
197
+ false
198
+ "@one_plus_two_plus_three.to_i" =>
199
+ 7
200
+ ```
137
201
 
138
202
  ## Matchers
139
203
 
data/lib/matest.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "matest/version"
2
+ require "matest/example_block"
2
3
  require "matest/spec_status"
3
4
  require "matest/spec_printer"
4
5
 
@@ -38,35 +39,65 @@ module Matest
38
39
  class SkipMe; end
39
40
 
40
41
  class Example
41
- def example_block
42
- @__example_block
43
- end
44
- def description
45
- @__description
46
- end
47
-
48
42
  def initialize(example_block, description, lets)
49
- @__example_block = example_block
50
- @__description = description
43
+ @example_block__for_internal_use = ExampleBlock.new(example_block)
44
+ @description__for_internal_use = description
51
45
  lets.each do |let|
52
46
  self.class.let(let.var_name, &let.block)
53
47
  send(let.var_name) if let.bang
54
48
  end
55
49
  end
56
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
+
57
59
  def call
58
- instance_eval(&example_block)
60
+ instance_eval(&example_block.block)
59
61
  end
60
62
 
61
63
  def self.let(var_name, &block)
62
64
  define_method(var_name) do
63
- instance_variable_set(:"@#{var_name}", block.call)
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)
64
75
  end
65
76
  end
66
77
 
67
- def track
68
- instance_variables.reject {|i| i.to_s =~ /\A@__/}.map {|i| [i, instance_variable_get(i)] }
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)] }
69
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
+
70
101
  end
71
102
 
72
103
  class Let
@@ -0,0 +1,50 @@
1
+ require "ripper"
2
+ require "sorcerer"
3
+
4
+ class ExampleBlock
5
+ attr_reader :block
6
+ attr_reader :code
7
+ attr_reader :sexp
8
+ attr_reader :assertion
9
+ attr_reader :assertion_sexp
10
+
11
+ def initialize(block)
12
+ @block = block
13
+
14
+ @code = generate_code
15
+
16
+ @sexp = Ripper::SexpBuilder.new(code).parse.last
17
+ @assertion_sexp = @sexp.last
18
+ @assertion = Sorcerer.source(assertion_sexp)
19
+ end
20
+
21
+ def call
22
+ block.call
23
+ end
24
+
25
+ def source_location
26
+ block.source_location
27
+ end
28
+
29
+ private
30
+
31
+ def generate_code
32
+ file = File.open(block.source_location.first)
33
+ source = file.read
34
+ lines = source.each_line.to_a
35
+
36
+ lineno = block.source_location.last
37
+
38
+ current_line = lineno-1
39
+ valid_lines = [lines[current_line]]
40
+
41
+ valid_lines
42
+
43
+ until Ripper::SexpBuilder.new(valid_lines.join("\n")).parse
44
+ current_line += 1
45
+ valid_lines << lines[current_line]
46
+ end
47
+ code_array = valid_lines[1..-2]
48
+ code_array.join
49
+ end
50
+ end
@@ -1,7 +1,52 @@
1
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
2
16
 
3
- class SpecPrinter
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
4
48
 
49
+ class SpecPrinter
5
50
  def print(runner)
6
51
  puts "\n\n### Messages ###"
7
52
 
@@ -16,15 +61,27 @@ module Matest
16
61
  runner.info[:num_specs][status.name] ||= 0
17
62
  runner.info[:num_specs][status.name] += 1
18
63
 
19
- if status.is_a?(Matest::SpecPassed)
20
- else
64
+ if !status.is_a?(Matest::SpecPassed)
21
65
  puts "\n[#{status.name}] #{status.description}"
66
+ puts "Location:\n #{status.location}:"
67
+
22
68
  if status.is_a?(Matest::SpecFailed)
23
69
  runner.info[:success] = false
70
+ puts "Assertion: \n #{status.example.example_block.assertion}"
24
71
  puts "Variables: "
25
- status.example.track.each do |var, val|
72
+ status.example.track_variables.each do |var, val|
26
73
  puts " #{var}: #{val.inspect}"
27
74
  end
75
+ puts "Lets: "
76
+ status.example.track_lets.each do |var, val|
77
+ puts " #{var}: #{val.inspect}"
78
+ end
79
+
80
+ puts "Explanation:"
81
+ subexpressions = Sorcerer.subexpressions(status.example.example_block.assertion_sexp).reverse.uniq.reverse
82
+ subexpressions.each do |code|
83
+ print_subexpression(code, status)
84
+ end
28
85
  end
29
86
  if status.is_a?(Matest::NotANaturalAssertion)
30
87
  runner.info[:success] = false
@@ -38,10 +95,26 @@ module Matest
38
95
  end
39
96
 
40
97
  end
41
- puts " #{status.location}:"
42
98
  end
43
99
  end
44
100
  end
45
101
  end
102
+
103
+ def print_subexpression(code, status)
104
+ result = Evaluator.new(status.example, status.example.example_block.block).eval_string(code)
105
+ if result.class != Matest::EvalErr
106
+ puts <<-CODE
107
+ "#{code}" =>
108
+ #{result}
109
+ CODE
110
+ else
111
+ puts <<-CODE
112
+ The assertion couldn't be explained.
113
+ The error message was:
114
+ #{result}
115
+ Make sure you are not calling any local vaiables on your code assertion.
116
+ CODE
117
+ end
118
+ end
46
119
  end
47
120
  end
@@ -1,3 +1,3 @@
1
1
  module Matest
2
- VERSION = "1.1.4"
2
+ VERSION = "1.2.0"
3
3
  end
data/matest.gemspec CHANGED
@@ -20,4 +20,6 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.7"
22
22
  spec.add_development_dependency "rake", "~> 10.0"
23
+
24
+ spec.add_dependency "sorcerer", "~> 1.0.2"
23
25
  end
@@ -1,65 +1,65 @@
1
1
  scope do
2
2
  spec { true }
3
- xspec { true }
3
+ xspec { false }
4
4
 
5
5
  example { true }
6
- xexample { true }
6
+ xexample { false }
7
7
 
8
8
  it { true }
9
- xit { true }
9
+ xit { false }
10
10
  end
11
11
 
12
12
  describe do
13
13
  spec { true }
14
- xspec { true }
14
+ xspec { false }
15
15
 
16
16
  example { true }
17
- xexample { true }
17
+ xexample { false }
18
18
 
19
19
  it { true }
20
- xit { true }
20
+ xit { false }
21
21
  end
22
22
 
23
23
  context do
24
24
  spec { true }
25
- xspec { true }
25
+ xspec { false }
26
26
 
27
27
  example { true }
28
- xexample { true }
28
+ xexample { false }
29
29
 
30
30
  it { true }
31
- xit { true }
31
+ xit { false }
32
32
  end
33
33
 
34
34
  xscope do
35
35
  spec { true }
36
- xspec { true }
36
+ xspec { false }
37
37
 
38
38
  example { true }
39
- xexample { true }
39
+ xexample { false }
40
40
 
41
41
  it { true }
42
- xit { true }
42
+ xit { false }
43
43
  end
44
44
 
45
45
  xdescribe do
46
46
  spec { true }
47
- xspec { true }
47
+ xspec { false }
48
48
 
49
49
  example { true }
50
- xexample { true }
50
+ xexample { false }
51
51
 
52
52
  it { true }
53
- xit { true }
53
+ xit { false }
54
54
  end
55
55
 
56
56
  xcontext do
57
57
  spec { true }
58
- xspec { true }
58
+ xspec { false }
59
59
 
60
60
  example { true }
61
- xexample { true }
61
+ xexample { false }
62
62
 
63
63
  it { true }
64
- xit { true }
64
+ xit { false }
65
65
  end
@@ -0,0 +1,35 @@
1
+ scope "hola" do
2
+ let(:three) { 3 }
3
+ xspec "chau" do
4
+ one = 2
5
+ two = 2
6
+
7
+ @one_plus_two_plus_three = one + two + three
8
+ @res = 3
9
+ @one_plus_two_plus_three.to_i == @res
10
+ end
11
+
12
+ xspec "again?" do
13
+ @arr = %w[a b c d e]
14
+
15
+ @arr.pop == "k"
16
+ end
17
+
18
+ spec do
19
+ a = 4
20
+
21
+ a == 5
22
+ end
23
+ end
24
+
25
+ # @one_plus_two == @res.to_i => false
26
+ # @one_plus_two => 4
27
+ # @res => "3"
28
+ # @res.to_i => 3
29
+
30
+
31
+ ## ON EXAMPLE
32
+ # on call:
33
+ # - run the spec without the assertion
34
+ # - save the state
35
+ # - and then run the assertion.
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.1.4
4
+ version: 1.2.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: 2014-12-02 00:00:00.000000000 Z
11
+ date: 2015-01-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sorcerer
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.0.2
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.0.2
41
55
  description: Natural assertions test suite.
42
56
  email:
43
57
  - iachetti.federico@gmail.com
@@ -53,6 +67,7 @@ files:
53
67
  - Rakefile
54
68
  - bin/matest
55
69
  - lib/matest.rb
70
+ - lib/matest/example_block.rb
56
71
  - lib/matest/spec_printer.rb
57
72
  - lib/matest/spec_status.rb
58
73
  - lib/matest/version.rb
@@ -63,6 +78,7 @@ files:
63
78
  - spec/matest_specs/matchers_spec.rb
64
79
  - spec/matest_specs/nested_scopes_spec.rb
65
80
  - spec/matest_specs/passing_spec.rb
81
+ - spec/matest_specs/printing_assertion_spec.rb
66
82
  - spec/matest_specs/scope_spec.rb
67
83
  - spec/matest_specs/scoping_stuff_spec.rb
68
84
  - spec/matest_specs/spec_helper.rb
@@ -87,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
87
103
  version: '0'
88
104
  requirements: []
89
105
  rubyforge_project:
90
- rubygems_version: 2.4.3
106
+ rubygems_version: 2.4.5
91
107
  signing_key:
92
108
  specification_version: 4
93
109
  summary: Tests gasoleros (cheap tests).
@@ -98,6 +114,7 @@ test_files:
98
114
  - spec/matest_specs/matchers_spec.rb
99
115
  - spec/matest_specs/nested_scopes_spec.rb
100
116
  - spec/matest_specs/passing_spec.rb
117
+ - spec/matest_specs/printing_assertion_spec.rb
101
118
  - spec/matest_specs/scope_spec.rb
102
119
  - spec/matest_specs/scoping_stuff_spec.rb
103
120
  - spec/matest_specs/spec_helper.rb