matest 1.1.4 → 1.2.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: 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