fix 0.4.0 → 0.5.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: af4f77c8e0f75a3f88d2bdb4757cf1f7d122ed3e
4
- data.tar.gz: f80e87cf83afcdcb0f731941769c6f7b95730887
3
+ metadata.gz: 1964b909221b9e975cd2bbaf03ebfe3fbe748fb1
4
+ data.tar.gz: bedaf49f6b9f171ad4abb82817d391b97beafabc
5
5
  SHA512:
6
- metadata.gz: b75d2c1f34987e4bd05bf660ae656ba28cf9e09c4366c21a2a3dbff84e0852a3a8be7d6987451f8596bcfd3ddd73574492639d27bce1bb263780d2195a059b3f
7
- data.tar.gz: ac75678ec541a7481ea4579d5cd0d34a3c0d112a6275021abfa42ab52622bac881ff41ec9f1970cac9066ce851583cd215a7ac6af0baa657809f53b094d4f096
6
+ metadata.gz: fc69cd9da13b6e207270601c1f71a9d489473991edbaa077901cc60a9faec0b53d31db95c57d3174f5f57b8610a4dee551a00ab148a290a2aeb37652c85a9013
7
+ data.tar.gz: fc477c8a857a294bb3a31413f64147b427c5ea7eeb6d53c69f4da37223a012f3339dd4b21bddf3716f15a08185ef02fbfe0dea73ade0b1b1570953a6e85cbd9c
Binary file
data.tar.gz.sig CHANGED
Binary file
data/README.md CHANGED
@@ -1,20 +1,19 @@
1
1
  # Fix
2
2
 
3
- [![Build Status](https://travis-ci.org/cyril/fix.rb.svg?branch=master)](https://travis-ci.org/cyril/fix.rb)
4
- [![Coverage Status](http://img.shields.io/coveralls/cyril/fix.rb.svg?branch=master)](https://coveralls.io/r/cyril/fix.rb)
5
- [![Dependency Status](https://gemnasium.com/cyril/fix.rb.svg)](https://gemnasium.com/cyril/fix.rb)
3
+ [![Build Status](https://travis-ci.org/fixrb/fix.svg?branch=master)](https://travis-ci.org/fixrb/fix)
4
+ [![Coverage Status](http://img.shields.io/coveralls/fixrb/fix.svg?branch=master)](https://coveralls.io/r/fixrb/fix)
5
+ [![Dependency Status](https://gemnasium.com/fixrb/fix.svg)](https://gemnasium.com/fixrb/fix)
6
6
  [![Gem Version](http://img.shields.io/gem/v/fix.svg)](https://rubygems.org/gems/fix)
7
- [![Inline docs](http://inch-ci.org/github/cyril/fix.rb.svg?branch=master)](http://inch-ci.org/github/cyril/fix.rb)
7
+ [![Inline docs](http://inch-ci.org/github/fixrb/fix.svg?branch=master)](http://inch-ci.org/github/fixrb/fix)
8
8
  [![Documentation](http://img.shields.io/:yard-docs-38c800.svg)](http://rubydoc.info/gems/fix/frames)
9
- [![License](http://img.shields.io/:license-MIT-38c800.svg)](http://cyril.mit-license.org/)
10
9
 
11
10
  > Specing framework for Ruby.
12
11
 
13
12
  ## Contact
14
13
 
15
14
  * Home page: http://fixrb.org/
16
- * Bugs/issues: https://github.com/cyril/fix.rb/issues
17
- * Support: https://stackoverflow.com/questions/tagged/fix-ruby
15
+ * Bugs/issues: https://github.com/fixrb/fix/issues
16
+ * Support: https://stackoverflow.com/questions/tagged/fixrb
18
17
 
19
18
  ## Rubies
20
19
 
@@ -44,7 +43,7 @@ $ gem install fix
44
43
 
45
44
  ### Minimalist
46
45
 
47
- With ~400 lignes of **simple code** built on top of [Spectus expectation library](cyril/spectus.rb), facilities such as benchmarking and mocking are not supported. Fix offers however a **consistent** syntax to **DRY** and focus your BDD.
46
+ With ~400 lignes of **simple code** built on top of [Spectus expectation library](https://github.com/fixrb/spectus), facilities such as benchmarking and mocking are not supported. Fix offers however a **consistent** syntax to **DRY** and focus your BDD.
48
47
 
49
48
  ### Resistant
50
49
 
@@ -107,11 +106,11 @@ ruby duck_spec.rb
107
106
 
108
107
  .....
109
108
  Finished in 0.001033 seconds.
110
- 5 tests, 0 failures, 0 errors
109
+ 100% compliant (5 specs, 0 infos, 0 failures, 0 errors)
111
110
 
112
111
  ## Contributing
113
112
 
114
- 1. [Fork it](https://github.com/cyril/fix.rb/fork)
113
+ 1. [Fork it](https://github.com/fixrb/fix/fork)
115
114
  2. Create your feature branch (`git checkout -b my-new-feature`)
116
115
  3. Commit your changes (`git commit -am 'Add some feature'`)
117
116
  4. Push to the branch (`git push origin my-new-feature`)
@@ -1 +1 @@
1
- 0.4.0
1
+ 0.5.0
data/bin/fix ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'fix'
4
+
5
+ Fix.run(*ARGV)
@@ -4,5 +4,5 @@
4
4
  $ ./test.rb
5
5
  ......
6
6
  Finished in 0.001181 seconds.
7
- 6 tests, 0 failures, 0 errors
7
+ 100% compliant (6 specs, 0 infos, 0 failures, 0 errors)
8
8
  ```
@@ -9,6 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.license = 'MIT'
10
10
 
11
11
  spec.files = `git ls-files -z`.split("\x0")
12
+ spec.executables = spec.files.grep(%r{^bin/}) {|f| File.basename(f) }
12
13
  spec.test_files = spec.files.grep(%r{^spec/})
13
14
  spec.require_paths = ['lib']
14
15
  spec.required_ruby_version = '>= 2.0.0'
data/lib/fix.rb CHANGED
@@ -1,6 +1,95 @@
1
- require_relative File.join 'fix', 'dsl'
2
1
  require_relative File.join 'fix', 'version'
3
2
 
3
+ require 'optparse'
4
+ require 'set'
5
+
4
6
  # Namespace for the Fix framework.
5
7
  module Fix
8
+
9
+ # This is the command line top-level run method. Everything starts from here.
10
+ def self.run *args
11
+ process_args args
12
+ file_paths = fetch_file_paths args
13
+
14
+ print "> fix --seed #{SEED}"
15
+ print ' --color' if defined? COLOR
16
+ print ' --debug' if $DEBUG
17
+ print ' --warnings' if $VERBOSE
18
+
19
+ puts ' ' + file_paths.to_a.join(' ')
20
+
21
+ require_relative File.join 'fix', 'dsl'
22
+ file_paths.each {|file_path| require file_path }
23
+ end
24
+
25
+ def self.process_args args
26
+ options = {
27
+ seed: Random.new_seed,
28
+ color: false,
29
+ debug: false,
30
+ warnings: false
31
+ }
32
+
33
+ opt_parser = OptionParser.new do |opts|
34
+ opts.banner = 'Usage: fix <files or directories> [options]'
35
+
36
+ opts.separator ''
37
+ opts.separator 'Specific options:'
38
+
39
+ opts.on('-c', '--color', 'Enable color in the output.') do
40
+ options[:color] = (const_set :COLOR, true)
41
+ end
42
+
43
+ opts.on('-s [INTEGER]', '--seed [INTEGER]', Integer, 'Order of the tests') do |seed|
44
+ options[:seed] = seed
45
+ end
46
+
47
+ opts.on('-d', '--debug', 'Enable ruby debug') do
48
+ options[:debug] = $DEBUG = true
49
+ end
50
+
51
+ opts.on('-w', '--warnings', 'Enable ruby warnings') do
52
+ options[:warnings] = $VERBOSE = true
53
+ end
54
+
55
+ opts.separator ''
56
+ opts.separator 'Common options:'
57
+
58
+ opts.on_tail '-h', '--help', 'Show this message' do
59
+ puts opts
60
+ exit
61
+ end
62
+
63
+ opts.on_tail '--version', 'Show the version' do
64
+ puts VERSION
65
+ exit
66
+ end
67
+ end
68
+
69
+ opt_parser.parse! args
70
+ const_set :SEED, options.fetch(:seed)
71
+ options.freeze
72
+ end
73
+
74
+ def self.fetch_file_paths args
75
+ absolute_paths = Set.new
76
+
77
+ args.map do |s|
78
+ s = File.absolute_path s unless s.start_with? '/'
79
+
80
+ if File.directory? s
81
+ spec_files = File.join s, '**', '*_spec.rb'
82
+ Dir.glob(spec_files).each {|spec_file| absolute_paths.add spec_file }
83
+ else
84
+ absolute_paths.add s
85
+ end
86
+ end
87
+
88
+ if absolute_paths.empty?
89
+ warn 'Sorry, files or directories not found.'
90
+ exit 1
91
+ end
92
+
93
+ absolute_paths.freeze
94
+ end
6
95
  end
@@ -1,7 +1,7 @@
1
1
  require 'singleton'
2
2
 
3
3
  module Fix
4
- random = Random.new
4
+ random = Random.new(defined?(SEED) ? SEED : Random.new_seed)
5
5
 
6
6
  db = Class.new Hash do
7
7
  include Singleton
@@ -12,6 +12,6 @@ module Fix
12
12
  @object = object
13
13
  end
14
14
 
15
- at_exit { Test.new if $!.nil? or $!.kind_of?(SystemExit) }
15
+ at_exit { Test.new if $!.nil? }
16
16
  end
17
17
  end
@@ -1,6 +1,7 @@
1
1
  require 'set'
2
2
  require 'spectus'
3
3
  require_relative 'db'
4
+ require_relative 'subject'
4
5
 
5
6
  module Fix
6
7
  class Expectation
@@ -9,10 +10,10 @@ module Fix
9
10
  attr_reader :priority
10
11
 
11
12
  def initialize object, positive, definition, *args
12
- @positive = positive
13
- @matcher, @expected = definition.to_a.flatten 1
14
- @args = args
15
- @priority = DB.instance.random.rand
13
+ @positive = positive
14
+ @definition = definition
15
+ @args = args
16
+ @priority = DB.instance.random.rand
16
17
 
17
18
  freeze
18
19
 
@@ -26,10 +27,6 @@ module Fix
26
27
 
27
28
  private
28
29
 
29
- def pass? result, _subject = nil
30
- result.equal? true
31
- end
32
-
33
30
  def exception result
34
31
  result unless [ true, false ].include? result
35
32
  end
@@ -38,12 +35,11 @@ module Fix
38
35
  !@positive
39
36
  end
40
37
 
41
- def presenter result, got, subject = nil
38
+ def presenter result, got, subject
42
39
  {
43
- pass: pass?(result, subject),
40
+ pass: pass(result, subject),
44
41
  negated: negated?,
45
- matcher: @matcher,
46
- expected: @expected,
42
+ definition: @definition,
47
43
  exception: exception(result),
48
44
  got: got
49
45
  }
@@ -51,10 +47,10 @@ module Fix
51
47
 
52
48
  def meta subject
53
49
  {
54
- level: level,
55
- params: subject.params,
56
- challenge: subject.challenge,
57
- subject_exception: subject.error
50
+ level: level,
51
+ params: subject.params,
52
+ challenge: subject.challenge,
53
+ last_arg: subject.last_arg
58
54
  }
59
55
  end
60
56
 
@@ -1,5 +1,4 @@
1
1
  require_relative 'expectation'
2
- require_relative 'subject'
3
2
 
4
3
  module Fix
5
4
  class ExpectationHigh < Expectation
@@ -8,17 +7,19 @@ module Fix
8
7
  got = nil
9
8
 
10
9
  Thread.new {
11
- report = expect { got = subject.actual }.
12
- public_send target, @matcher => @expected
10
+ report = expect { got = subject.actual }.public_send target, @definition
11
+ data = presenter report, got, subject
13
12
 
14
- data = presenter report, got
15
-
16
- Hash[ data.to_a + meta(subject).to_a ]
13
+ Hash[ data.to_a + meta(subject).to_a ].merge object: front_object
17
14
  }.value
18
15
  end
19
16
 
20
17
  private
21
18
 
19
+ def pass result, _subject
20
+ result.equal? true
21
+ end
22
+
22
23
  def level
23
24
  1
24
25
  end
@@ -7,20 +7,18 @@ module Fix
7
7
  got = nil
8
8
 
9
9
  Thread.new {
10
- report = expect { got = subject.actual }.
11
- public_send target, @matcher => @expected
10
+ report = expect { got = subject.actual }.public_send target, @definition
11
+ data = presenter report, got, subject
12
12
 
13
- data = presenter report, got, subject
14
-
15
- Hash[ data.to_a + meta(subject).to_a ]
13
+ Hash[ data.to_a + meta(subject).to_a ].merge object: front_object
16
14
  }.value
17
15
  end
18
16
 
19
17
  private
20
18
 
21
- def pass? result, subject
19
+ def pass result, subject
22
20
  if subject.implemented?
23
- exception(result).nil?
21
+ super
24
22
  else
25
23
  true
26
24
  end
@@ -7,19 +7,17 @@ module Fix
7
7
  got = nil
8
8
 
9
9
  Thread.new {
10
- report = expect { got = subject.actual }.
11
- public_send target, @matcher => @expected
10
+ report = expect { got = subject.actual }.public_send target, @definition
11
+ data = presenter report, got, subject
12
12
 
13
- data = presenter report, got
14
-
15
- Hash[ data.to_a + meta(subject).to_a ]
13
+ Hash[ data.to_a + meta(subject).to_a ].merge object: front_object
16
14
  }.value
17
15
  end
18
16
 
19
17
  private
20
18
 
21
- def pass? result, _subject
22
- super || exception(result).nil?
19
+ def pass result, _subject
20
+ super || (exception(result).nil? ? nil : false)
23
21
  end
24
22
 
25
23
  def level
@@ -1,36 +1,33 @@
1
1
  module Fix
2
2
  class Subject
3
- attr_reader :error
3
+ attr_reader :last_arg
4
4
 
5
5
  def initialize input, *args
6
- @input = input
7
- @args = args
8
- @error = nil
9
-
10
- @implemented = nil
6
+ @args = args
7
+ @error = nil
8
+ @implemented = nil
9
+ @last_arg = -1
11
10
 
12
11
  begin
13
- param_id = 0
14
12
  @cache_value = params.inject(input) do |mem, param|
15
- param_id = param_id.next
16
- @implemented = mem.respond_to? param.first, false
13
+ @implemented = mem.respond_to? param.first, false
14
+ @last_arg = @last_arg.next
15
+
17
16
  mem.public_send(*param)
18
17
  end
19
- rescue => e
20
- @error = {
21
- exception: e,
22
- param_id: param_id
23
- }
18
+
19
+ @last_arg = @last_arg.next
20
+ rescue => @error
24
21
  end
25
22
 
26
23
  freeze
27
24
  end
28
25
 
29
26
  def actual
30
- if valid?
27
+ if @error.nil?
31
28
  @cache_value.public_send(*challenge)
32
29
  else
33
- raise @error.fetch :exception
30
+ raise @error
34
31
  end
35
32
  end
36
33
 
@@ -43,13 +40,7 @@ module Fix
43
40
  end
44
41
 
45
42
  def implemented?
46
- @implemented && valid? && @cache_value.respond_to?(challenge.first, false)
47
- end
48
-
49
- private
50
-
51
- def valid?
52
- @error.nil?
43
+ @implemented && @error.nil? && @cache_value.respond_to?(challenge.first, false)
53
44
  end
54
45
  end
55
46
  end
@@ -4,35 +4,104 @@ module Fix
4
4
  class Test
5
5
  attr_reader :total_time
6
6
 
7
- def initialize color: true, stdout: $stdout, stderr: $stderr
8
- @options = { color: color, stdout: stdout, stderr: stderr }
7
+ def initialize io = $stdout, color: defined?(COLOR)
8
+ @io = io
9
+ @color = color
9
10
 
10
11
  start_time = Time.now
11
- @results = DB.instance.map do |object, expectations|
12
- expectations.map do |expectation|
13
- log expectation.evaluate object
14
- end
15
- end.flatten 1
12
+
13
+ @results = DB.instance.flat_map do |object, expectations|
14
+ expectations.map {|expectation| log expectation.evaluate(object) }
15
+ end
16
+
16
17
  @total_time = Time.now - start_time
17
18
 
18
- @options.fetch(:stdout).puts about "\nFinished in #{@total_time} seconds."
19
- @options.fetch(:stdout).puts statistics
19
+ @results = @results.sort {|a, b| a.fetch(:level) <=> b.fetch(:level) }
20
+
21
+ @io.puts
22
+
23
+ %i(errors failures infos).each do |results_with_state|
24
+ if reports(results_with_state).any?
25
+ @io.puts
26
+ @io.puts "# #{results_with_state.capitalize}:"
27
+ @io.puts
28
+
29
+ reports(results_with_state).each_with_index do |report, index|
30
+ @io.print about "#{index.next}. "
31
+ @io.puts report
32
+ end
33
+ end
34
+ end
35
+
36
+ @io.puts
37
+ @io.puts about "Finished in #{@total_time} seconds."
38
+ @io.puts statistics
20
39
 
21
40
  freeze
22
41
  end
23
42
 
24
- def pass?
25
- @results.all? {|result| state(result) == :success }
43
+ def errors
44
+ @results.select {|result| state(result) == :error }
26
45
  end
27
46
 
28
- def fail?
29
- !pass?
47
+ def failures
48
+ @results.select {|result| state(result) == :failure }
30
49
  end
31
50
 
32
- def statistics
33
- errors = @results.select {|result| state(result) == :error }
34
- failures = @results.select {|result| state(result) == :failure }
51
+ def infos
52
+ @results.select {|result| state(result) == :info }
53
+ end
54
+
55
+ def reports results_with_state
56
+ __send__(results_with_state).map do |result|
57
+ result.fetch(:object).inspect +
58
+ if result.fetch(:params).any?
59
+ '.' +
60
+ result.fetch(:params).map.with_index do |args, i|
61
+ color = if i == result.fetch(:last_arg)
62
+ state result
63
+ elsif i > result.fetch(:last_arg)
64
+ :pending
65
+ else
66
+ :default
67
+ end
68
+
69
+ __send__(color, "#{args.first}" +
70
+ if args.length > 1
71
+ '(' + args[1..-1].map {|arg| arg.to_s }.join(',') + ')'
72
+ else
73
+ ''
74
+ end
75
+ )
76
+ end.join('.')
77
+ else
78
+ ''
79
+ end +
80
+ '.' +
81
+ (
82
+ color = if result.fetch(:params).length == result.fetch(:last_arg)
83
+ state result
84
+ else
85
+ :pending
86
+ end
87
+
88
+ __send__(color, "#{result.fetch(:challenge).first}" +
89
+ if result.fetch(:challenge).length > 1
90
+ '(' + result.fetch(:challenge)[1..-1].map {|arg| arg.to_s }.join(',') + ')'
91
+ else
92
+ ''
93
+ end
94
+ )
95
+ ) +
96
+ ' ' +
97
+ expectation_level(result) +
98
+ ' ' +
99
+ matcher_with_expected_if_given(result) +
100
+ returned_value(result)
101
+ end
102
+ end
35
103
 
104
+ def statistics
36
105
  color = if errors.any?
37
106
  :error
38
107
  elsif failures.any?
@@ -41,32 +110,74 @@ module Fix
41
110
  :success
42
111
  end
43
112
 
44
- __send__ color, [
45
- "#{@results.length} tests",
113
+ percents = (@results.count {|result| result.fetch(:pass) != false }) / @results.length.to_f * 100
114
+
115
+ __send__ color, "#{percents.round}% compliant (" + [
116
+ "#{@results.length} specs",
117
+ "#{infos.length} infos",
46
118
  "#{failures.length} failures",
47
119
  "#{errors.length} errors"
48
- ].join(', ')
120
+ ].join(', ') + ')'
121
+ end
122
+
123
+ def pass?
124
+ !@results.any? {|result| result.fetch(:pass) == false }
125
+ end
126
+
127
+ def fail?
128
+ !pass?
49
129
  end
50
130
 
51
131
  private
52
132
 
53
- def log result
54
- color = state result
55
- std = (color == :success ? :stdout : :stderr)
133
+ def returned_value result
134
+ if result.fetch(:exception).nil?
135
+ about ' # => got ' + result.fetch(:got).inspect
136
+ else
137
+ about ' # => raised ' + result.fetch(:exception).inspect
138
+ end
139
+ end
56
140
 
57
- @options.fetch(std).print __send__ color, char(result)
141
+ def expectation_level result
142
+ case result.fetch :level
143
+ when 1
144
+ result.fetch(:negated) ? 'MUST_NOT' : 'MUST'
145
+ when 2
146
+ result.fetch(:negated) ? 'SHOULD_NOT' : 'SHOULD'
147
+ when 3
148
+ 'MAY'
149
+ end
150
+ end
151
+
152
+ def matcher_with_expected_if_given result
153
+ definition = Array result.fetch(:definition)
154
+
155
+ definition.flatten(1)[0].to_s +
156
+ if definition.flatten(1).length > 1
157
+ ' ' + definition.flatten(1)[1].inspect
158
+ else
159
+ ''
160
+ end
161
+ end
162
+
163
+ def log result
164
+ @io.print __send__ state(result), char(result)
58
165
 
59
166
  result
60
167
  end
61
168
 
62
169
  def colorize state, string
63
- @options[:color] ? "\e[#{state}m#{string}\e[0m" : string
170
+ @color ? "\e[#{state}m#{string}\e[0m" : string
64
171
  end
65
172
 
66
173
  def about string
67
174
  colorize 37, string
68
175
  end
69
176
 
177
+ def default string
178
+ colorize 30, string
179
+ end
180
+
70
181
  def error string
71
182
  colorize 31, string
72
183
  end
@@ -79,7 +190,7 @@ module Fix
79
190
  colorize 33, string
80
191
  end
81
192
 
82
- def matcher string
193
+ def info string
83
194
  colorize 34, string
84
195
  end
85
196
 
@@ -91,6 +202,8 @@ module Fix
91
202
  def state data
92
203
  if data.fetch :pass
93
204
  :success
205
+ elsif data.fetch(:pass).nil?
206
+ :info
94
207
  elsif data.fetch(:exception).nil?
95
208
  :failure
96
209
  else
@@ -102,6 +215,8 @@ module Fix
102
215
  def char data
103
216
  if data.fetch :pass
104
217
  '.'
218
+ elsif data.fetch(:pass).nil?
219
+ 'I'
105
220
  elsif data.fetch(:exception).nil?
106
221
  'F'
107
222
  else
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fix
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Wack
@@ -30,7 +30,7 @@ cert_chain:
30
30
  sQCgS9KCAyZ+aWNO1bUJcE3Bx1XXkMO3JEyVR1CoEcexg5Ci03/lAm7lL84DmlKR
31
31
  3I7UWtomapPFbzC0J/5jzQ==
32
32
  -----END CERTIFICATE-----
33
- date: 2014-09-29 00:00:00.000000000 Z
33
+ date: 2014-10-05 00:00:00.000000000 Z
34
34
  dependencies:
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: spectus
@@ -105,7 +105,8 @@ dependencies:
105
105
  description: Specing framework for Ruby.
106
106
  email:
107
107
  - cyril@sashite.com
108
- executables: []
108
+ executables:
109
+ - fix
109
110
  extensions: []
110
111
  extra_rdoc_files: []
111
112
  files:
@@ -117,6 +118,7 @@ files:
117
118
  - README.md
118
119
  - Rakefile
119
120
  - VERSION.semver
121
+ - bin/fix
120
122
  - example/duck/README.md
121
123
  - example/duck/app.rb
122
124
  - example/duck/lib.rb
metadata.gz.sig CHANGED
Binary file