exemplor 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ pkg
data/README ADDED
@@ -0,0 +1,152 @@
1
+ Examplor (the exemplar)
2
+ =======================
3
+
4
+ An example-based test framework inspired by [testy](http://github.com/ahoward/testy).
5
+
6
+ Simpler that BDD/TDD, fancier than a file with a bunch of print statements.
7
+
8
+ Very tiny api. Here it is
9
+
10
+ eg.helpers do
11
+ ... your helpers ...
12
+ end
13
+
14
+ eg.setup { ... setup codez ... }
15
+
16
+ eg "an example" do
17
+ ... example code ...
18
+ end
19
+
20
+ eg "another example" do
21
+ ... etc ...
22
+ end
23
+
24
+ The output is both human readable and machine parsable, which means you can test your tests.
25
+
26
+ Writing Examples
27
+ ----------------
28
+
29
+ See `examples.rb` and `/examples` for more examples.
30
+
31
+ The simplest possible example:
32
+
33
+ eg 'An example block without any checks prints the value of the block' do
34
+ "foo"
35
+ end
36
+
37
+ #=>
38
+
39
+ (i) An example block without any checks prints the value of the block: foo
40
+
41
+ The 'i' stands for 'info' which means the example ran without error.
42
+
43
+ Inspecting a few values, by default `Check` prints it's argument and the value of the argument:
44
+
45
+ eg 'Accessing different parts of an array' do
46
+ list = [1, 2, 3]
47
+ Check(list.first)
48
+ Check(list[1])
49
+ Check(list.last)
50
+ end
51
+
52
+ #=>
53
+
54
+ (I) Accessing different parts of an array:
55
+ (i) list.first: 1
56
+ (i) list[1]: 2
57
+ (i) list.last: 3
58
+
59
+ The capital 'I' means all checks were 'info' checks.
60
+
61
+ Call's to `Check` with the same argument can be disambiguated with `[]`:
62
+
63
+ eg 'Array appending' do
64
+ list = [1, 42]
65
+ Check(list.last)["before append"]
66
+ list << 2
67
+ Check(list.last)["after append"]
68
+ end
69
+
70
+ #=>
71
+
72
+ (I) Array appending:
73
+ (i) list.last before append: 42
74
+ (i) list.last after append: 2
75
+
76
+ Errors are caught and reported nicely:
77
+
78
+ eg 'Raising an error' do
79
+ raise "boom!"
80
+ end
81
+
82
+ #=>
83
+
84
+ (e) Raising an error:
85
+ class: RuntimeError
86
+ message: boom!
87
+ backtrace:
88
+ - examples/an_error.rb:4
89
+ # ... more backtrace lines
90
+
91
+ Once you're happy with how your code is running you can make some assertions about its behaviour by adding `is()` calls after your `Check()` statements:
92
+
93
+
94
+ eg 'Asserting first is first' do
95
+ list = [1, 2, 3]
96
+ Check(list.first).is(1)
97
+ end
98
+
99
+ #=>
100
+
101
+ (s) Asserting first is first:
102
+ (s) list.first: 1
103
+
104
+ 's' stands for 'success' and 'f' for failure:
105
+
106
+ eg 'Assertion failure' do
107
+ list = [1, 2, 3]
108
+ Check(list.first).is(2)
109
+ end
110
+
111
+ #=>
112
+
113
+ (f) Assertion failure:
114
+ (f) list.first:
115
+ expected: 2
116
+ actual: 1
117
+
118
+
119
+ Running Examples
120
+ ----------------
121
+
122
+ Running with `--list` or `-l` lists all examples:
123
+
124
+ $> ruby examples.rb -l
125
+ - errors are caught and nicely displayed
126
+ - check_output_matches_expected_for :no_checks
127
+ - check_output_matches_expected_for :oneliner
128
+ - check_output_matches_expected_for :no_checks_non_string
129
+ - check_output_matches_expected_for :with_checks
130
+ - check_output_matches_expected_for :check_with_disambiguation
131
+ - check_output_matches_expected_for :assertion_success
132
+ - check_output_matches_expected_for :assertion_failure
133
+ - check_output_matches_expected_for :assertion_success_and_failure
134
+ - check_output_matches_expected_for :helpers
135
+ - check_output_matches_expected_for :with_setup
136
+ - called with --list arg
137
+ - called with --l arg
138
+ - called with some other arg (always interpreted as a regex)
139
+
140
+ Otherwise the first argument is taken as a regex and only examples who's titles match are run:
141
+
142
+ $> ruby examples.rb called
143
+ (s) called with --list arg:
144
+ (s) list:
145
+ - Modified env
146
+ - Unmodified env
147
+ (s) called with --l arg:
148
+ (s) list:
149
+ - Modified env
150
+ - Unmodified env
151
+ (s) called with some other arg (always interpreted as a regex):
152
+ (s) tests_run: 1
@@ -0,0 +1,16 @@
1
+ # require "mg"
2
+ # MG.new("exemplor.gemspec")
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gs|
7
+ gs.name = "exemplor"
8
+ gs.homepage = "http://github.com/quackingduck/exemplor"
9
+ gs.summary = "A light-weight, low-fi way to provide executable usage examples or your code."
10
+ gs.email = "myles@myles.id.au"
11
+ gs.authors = ["Myles Byrne"]
12
+ end
13
+ Jeweler::GemcutterTasks.new
14
+ rescue LoadError
15
+ puts "Install jeweler to build gem"
16
+ end
data/TODO ADDED
@@ -0,0 +1,6 @@
1
+ * support multiple groups
2
+ * #teardown
3
+ * Use fancy unicode characters for tests status:
4
+ # ‣
5
+ # ✓
6
+ # ✗
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2.2.0
@@ -0,0 +1,59 @@
1
+ # TODO: switch to gem version
2
+ require 'lib/exemplor'
3
+
4
+ # slow because each test runs in a subshell
5
+
6
+ eg.helpers do
7
+
8
+ def run_example(name, args = nil)
9
+ `ruby -Ilib examples/#{name}.rb#{' ' + args if args}`
10
+ end
11
+
12
+ def extract_expected(name)
13
+ File.read("examples/#{name}.rb").split('__END__').last
14
+ end
15
+
16
+ def expected_and_actual(example_name)
17
+ [extract_expected(example_name).strip, run_example(example_name).strip]
18
+ end
19
+
20
+ def check_output_matches_expected_for(example_name)
21
+ expected_output, output = expected_and_actual(example_name)
22
+ Check(output).is(expected_output)
23
+ end
24
+
25
+ end
26
+
27
+ eg "errors are caught and nicely displayed" do
28
+ actual_yaml = YAML.load run_example(:an_error)
29
+ error_hash = actual_yaml[actual_yaml.keys.first]
30
+ # the rest of the bactrace is platform & exemplor version specific
31
+ error_hash['backtrace'] = error_hash['backtrace'][0...1]
32
+ Check(actual_yaml).is(YAML.load(extract_expected(:an_error)))
33
+ end
34
+
35
+ eg { check_output_matches_expected_for :no_checks }
36
+ eg { check_output_matches_expected_for :oneliner }
37
+ eg { check_output_matches_expected_for :no_checks_non_string }
38
+ eg { check_output_matches_expected_for :with_checks }
39
+ eg { check_output_matches_expected_for :check_with_disambiguation }
40
+ eg { check_output_matches_expected_for :assertion_success }
41
+ eg { check_output_matches_expected_for :assertion_failure }
42
+ eg { check_output_matches_expected_for :assertion_success_and_failure }
43
+ eg { check_output_matches_expected_for :helpers }
44
+ eg { check_output_matches_expected_for :with_setup }
45
+
46
+ eg "called with --list arg" do
47
+ list = YAML.load(run_example(:with_setup, '--list'))
48
+ Check(list).is(["Modified env", "Unmodified env"])
49
+ end
50
+
51
+ eg "called with --l arg" do
52
+ list = YAML.load(run_example(:with_setup, '--list'))
53
+ Check(list).is(["Modified env", "Unmodified env"])
54
+ end
55
+
56
+ eg "called with some other arg (always interpreted as a regex)" do
57
+ tests_run = YAML.load(run_example(:with_setup, 'Unmodified')).size
58
+ Check(tests_run).is(1)
59
+ end
@@ -0,0 +1,14 @@
1
+ require 'exemplor'
2
+
3
+ eg 'Raising an error' do
4
+ raise "boom!"
5
+ end
6
+
7
+ __END__
8
+
9
+ (e) Raising an error:
10
+ class: RuntimeError
11
+ message: boom!
12
+ backtrace:
13
+ - examples/an_error.rb:4
14
+ # ... more backtrace lines
@@ -0,0 +1,13 @@
1
+ require 'exemplor'
2
+
3
+ eg 'Assertion failure' do
4
+ list = [1, 2, 3]
5
+ Check(list.first).is(2)
6
+ end
7
+
8
+ __END__
9
+
10
+ (f) Assertion failure:
11
+ (f) list.first:
12
+ expected: 2
13
+ actual: 1
@@ -0,0 +1,11 @@
1
+ require 'exemplor'
2
+
3
+ eg 'Asserting first is first' do
4
+ list = [1, 2, 3]
5
+ Check(list.first).is(1)
6
+ end
7
+
8
+ __END__
9
+
10
+ (s) Asserting first is first:
11
+ (s) list.first: 1
@@ -0,0 +1,18 @@
1
+ require 'exemplor'
2
+
3
+ eg 'Some successes, then a fail' do
4
+ list = [1, 2, 3]
5
+ Check(list.first).is(1)
6
+ Check(list[1]).is(2)
7
+ Check(list.last).is(1)
8
+ Check(list[2]).is(3) # would be successful but we never get here
9
+ end
10
+
11
+ __END__
12
+
13
+ (f) Some successes, then a fail:
14
+ (s) list.first: 1
15
+ (s) list[1]: 2
16
+ (f) list.last:
17
+ expected: 1
18
+ actual: 3
@@ -0,0 +1,14 @@
1
+ require 'exemplor'
2
+
3
+ eg 'Array appending' do
4
+ list = [1, 42]
5
+ Check(list.last)["before append"]
6
+ list << 2
7
+ Check(list.last)["after append"]
8
+ end
9
+
10
+ __END__
11
+
12
+ (I) Array appending:
13
+ (i) list.last before append: 42
14
+ (i) list.last after append: 2
@@ -0,0 +1,17 @@
1
+ require 'exemplor'
2
+
3
+ eg.helpers do
4
+
5
+ def foo
6
+ "foo"
7
+ end
8
+
9
+ end
10
+
11
+ eg 'Example calling helper' do
12
+ foo
13
+ end
14
+
15
+ __END__
16
+
17
+ (i) Example calling helper: foo
@@ -0,0 +1,9 @@
1
+ require 'exemplor'
2
+
3
+ eg 'An example block without any checks prints the value of the block' do
4
+ "foo"
5
+ end
6
+
7
+ __END__
8
+
9
+ (i) An example block without any checks prints the value of the block: foo
@@ -0,0 +1,15 @@
1
+ require 'exemplor'
2
+
3
+ class MyClass
4
+ def inspect
5
+ "<MyClass instance>"
6
+ end
7
+ end
8
+
9
+ eg 'Non-string return values get inspected' do
10
+ MyClass.new
11
+ end
12
+
13
+ __END__
14
+
15
+ (i) Non-string return values get inspected: <MyClass instance>
@@ -0,0 +1,7 @@
1
+ require 'exemplor'
2
+
3
+ eg { 1+2 }
4
+
5
+ __END__
6
+
7
+ (i) 1+2: 3
@@ -0,0 +1,15 @@
1
+ require 'exemplor'
2
+
3
+ eg 'Accessing different parts of an array' do
4
+ list = [1, 2, 3]
5
+ Check(list.first)
6
+ Check(list[1])
7
+ Check(list.last)
8
+ end
9
+
10
+ __END__
11
+
12
+ (I) Accessing different parts of an array:
13
+ (i) list.first: 1
14
+ (i) list[1]: 2
15
+ (i) list.last: 3
@@ -0,0 +1,20 @@
1
+ require 'exemplor'
2
+
3
+
4
+ eg.setup { @str = "foo" }
5
+
6
+ eg 'Modified env' do
7
+ @str << " bar"
8
+ Check(@str).is("foo bar")
9
+ end
10
+
11
+ eg 'Unmodified env' do
12
+ Check(@str).is("foo")
13
+ end
14
+
15
+ __END__
16
+
17
+ (s) Modified env:
18
+ (s) @str: foo bar
19
+ (s) Unmodified env:
20
+ (s) @str: foo
@@ -0,0 +1,66 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{exemplor}
8
+ s.version = "2.2.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Myles Byrne"]
12
+ s.date = %q{2009-10-12}
13
+ s.email = %q{myles@myles.id.au}
14
+ s.extra_rdoc_files = [
15
+ "README"
16
+ ]
17
+ s.files = [
18
+ ".gitignore",
19
+ "README",
20
+ "Rakefile",
21
+ "TODO",
22
+ "VERSION",
23
+ "examples.rb",
24
+ "examples/an_error.rb",
25
+ "examples/assertion_failure.rb",
26
+ "examples/assertion_success.rb",
27
+ "examples/assertion_success_and_failure.rb",
28
+ "examples/check_with_disambiguation.rb",
29
+ "examples/helpers.rb",
30
+ "examples/no_checks.rb",
31
+ "examples/no_checks_non_string.rb",
32
+ "examples/oneliner.rb",
33
+ "examples/with_checks.rb",
34
+ "examples/with_setup.rb",
35
+ "exemplor.gemspec",
36
+ "lib/exemplor.rb"
37
+ ]
38
+ s.homepage = %q{http://github.com/quackingduck/exemplor}
39
+ s.rdoc_options = ["--charset=UTF-8"]
40
+ s.require_paths = ["lib"]
41
+ s.rubygems_version = %q{1.3.5}
42
+ s.summary = %q{A light-weight, low-fi way to provide executable usage examples or your code.}
43
+ s.test_files = [
44
+ "examples/an_error.rb",
45
+ "examples/assertion_failure.rb",
46
+ "examples/assertion_success.rb",
47
+ "examples/assertion_success_and_failure.rb",
48
+ "examples/check_with_disambiguation.rb",
49
+ "examples/helpers.rb",
50
+ "examples/no_checks.rb",
51
+ "examples/no_checks_non_string.rb",
52
+ "examples/oneliner.rb",
53
+ "examples/with_checks.rb",
54
+ "examples/with_setup.rb"
55
+ ]
56
+
57
+ if s.respond_to? :specification_version then
58
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
59
+ s.specification_version = 3
60
+
61
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
62
+ else
63
+ end
64
+ else
65
+ end
66
+ end
@@ -0,0 +1,224 @@
1
+ require 'rubygems' # TODO: remove
2
+ require 'orderedhash'
3
+ require 'yaml'
4
+
5
+ module Exemplor
6
+
7
+ class ExampleDefinitionError < StandardError ; end
8
+
9
+ class Check
10
+
11
+ attr_reader :expectation, :value
12
+
13
+ def initialize(name, value)
14
+ @name = name
15
+ @value = value
16
+ end
17
+
18
+ def [](disambiguate)
19
+ @disambiguate = disambiguate
20
+ end
21
+
22
+ def name
23
+ @name + (@disambiguate ? " #{@disambiguate}" : '')
24
+ end
25
+
26
+ def is(expectation)
27
+ @expectation = expectation
28
+ end
29
+
30
+ def status
31
+ return :info if !@expectation
32
+ @value == @expectation ? :success : :failure
33
+ end
34
+
35
+ def success?
36
+ status == :success
37
+ end
38
+
39
+ def failure?
40
+ status == :failure
41
+ end
42
+
43
+ def info?
44
+ status == :info
45
+ end
46
+
47
+ end
48
+
49
+ class Example
50
+
51
+ class << self
52
+
53
+ alias_method :helpers, :class_eval
54
+ attr_accessor :setup_block
55
+
56
+ def setup(&blk) self.setup_block = blk end
57
+
58
+ end
59
+
60
+ attr_accessor :_checks
61
+
62
+ def initialize
63
+ @_checks = []
64
+ end
65
+
66
+ # fragile. doesnt work with calls like
67
+ # Check(get('/'))
68
+ # Check foo
69
+ def Check(value)
70
+ file, line_number = caller.first.match(/^(.+):(\d+)/).captures
71
+ line = File.read(file).map[line_number.to_i - 1]
72
+ name = line[/Check\((.+?)\)/,1]
73
+ check = Check.new(name, value)
74
+ _checks << check
75
+ check
76
+ end
77
+
78
+ end
79
+
80
+ class Examples
81
+
82
+ attr_writer :setup_block
83
+
84
+ def initialize
85
+ @examples = OrderedHash.new
86
+ end
87
+
88
+ def add(name, &body)
89
+ @examples[name] = body
90
+ end
91
+
92
+ def run(patterns)
93
+ # unoffically supports multiple patterns
94
+ patterns = Regexp.new(patterns.join('|'))
95
+ @examples.each do |name, body|
96
+ if name =~ patterns
97
+ status, out = run_example(body)
98
+ print_yaml("#{status_icon(status)} #{name}" => out)
99
+ end
100
+ end
101
+ end
102
+
103
+ def list(patterns)
104
+ patterns = Regexp.new(patterns.join('|'))
105
+ list = @examples.keys.select { |name| name =~ patterns }
106
+ print_yaml list
107
+ end
108
+
109
+ def print_yaml(obj)
110
+ out = obj.to_yaml.match(/^--- \n/).post_match
111
+ out = colorize out if $stdout.tty?
112
+ print(out)
113
+ end
114
+
115
+ # hacky
116
+ def colorize(out)
117
+ require 'term/ansicolor'
118
+ out.split("\n").map do |line|
119
+ case line
120
+ when /^(?:\s{2})?(\(s\))/
121
+ start_color(line, :green)
122
+ when /^(?:\s{2})?(\(f\))/
123
+ start_color(line, :red)
124
+ when /^(?:\s{2})?(\(e\))/
125
+ start_color(line, :red)
126
+ when /^(?:\s{2})?(\(i\))/i
127
+ start_color(line, :blue)
128
+ else
129
+ line
130
+ end
131
+ end.join("\n") + "\n#{Term::ANSIColor.reset}"
132
+ end
133
+
134
+ def start_color(line, color)
135
+ "#{Term::ANSIColor.reset}#{Term::ANSIColor.send(color)}#{line}"
136
+ end
137
+
138
+ def run_example(code)
139
+ status = :info
140
+ env = Example.new
141
+ out = begin
142
+ env.instance_eval(&Example.setup_block) if Example.setup_block
143
+ value = env.instance_eval(&code)
144
+ if env._checks.empty?
145
+ render_value(value)
146
+ else
147
+ status = :infos if env._checks.all? { |check| check.info? }
148
+ status = :success if env._checks.all? { |check| check.success? }
149
+ status = :fail if env._checks.any? { |check| check.failure? }
150
+ render_checks(env._checks)
151
+ end
152
+ rescue Object => error
153
+ status = :error
154
+ render_error(error)
155
+ end
156
+ [status, out]
157
+ end
158
+
159
+ def render_value(value)
160
+ out = case value
161
+ when String, Numeric : value
162
+ else ; value.inspect ; end
163
+ end
164
+
165
+ def render_checks(checks)
166
+ failure = nil
167
+ out = OrderedHash.new
168
+ checks.each do |check|
169
+ failure = check if check.failure?
170
+ break if failure
171
+
172
+ out["#{status_icon(check.status)} #{check.name}"] = check.value
173
+ end
174
+ if failure
175
+ fail_out = out["#{status_icon(failure.status)} #{failure.name}"] = OrderedHash.new
176
+ fail_out['expected'] = failure.expectation
177
+ fail_out['actual'] = failure.value
178
+ end
179
+ out
180
+ end
181
+
182
+ def render_error(error)
183
+ out = OrderedHash.new
184
+ out['class'] = error.class.name
185
+ out['message'] = error.message
186
+ out['backtrace'] = error.backtrace
187
+ out
188
+ end
189
+
190
+ def status_icon(status)
191
+ icon = status == :infos ? '(I)' : "(#{status.to_s.slice(0,1)})"
192
+ end
193
+
194
+ end
195
+
196
+ class << self
197
+
198
+ def examples
199
+ @examples ||= Examples.new
200
+ end
201
+
202
+ end
203
+
204
+ end
205
+
206
+ def eg(name = nil, &example)
207
+ return Exemplor::Example if name.nil? && example.nil?
208
+ if name.nil?
209
+ file, line_number = caller.first.match(/^(.+):(\d+)/).captures
210
+ line = File.read(file).map[line_number.to_i - 1]
211
+ name = line[/^\s*eg\s*\{\s*(.+?)\s*\}\s*$/,1] if name.nil?
212
+ raise Exemplor::ExampleDefinitionError, "example at #{caller.first} has no name so must be on one line" if name.nil?
213
+ end
214
+ Exemplor.examples.add(name, &example)
215
+ end
216
+
217
+ at_exit do
218
+ args = ARGV.dup
219
+ if args.delete('--list') || args.delete('-l')
220
+ Exemplor.examples.list(args)
221
+ else
222
+ Exemplor.examples.run(args)
223
+ end
224
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: exemplor
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Myles Byrne
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-12 00:00:00 +11:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: myles@myles.id.au
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ files:
25
+ - .gitignore
26
+ - README
27
+ - Rakefile
28
+ - TODO
29
+ - VERSION
30
+ - examples.rb
31
+ - examples/an_error.rb
32
+ - examples/assertion_failure.rb
33
+ - examples/assertion_success.rb
34
+ - examples/assertion_success_and_failure.rb
35
+ - examples/check_with_disambiguation.rb
36
+ - examples/helpers.rb
37
+ - examples/no_checks.rb
38
+ - examples/no_checks_non_string.rb
39
+ - examples/oneliner.rb
40
+ - examples/with_checks.rb
41
+ - examples/with_setup.rb
42
+ - exemplor.gemspec
43
+ - lib/exemplor.rb
44
+ has_rdoc: true
45
+ homepage: http://github.com/quackingduck/exemplor
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options:
50
+ - --charset=UTF-8
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ requirements: []
66
+
67
+ rubyforge_project:
68
+ rubygems_version: 1.3.5
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: A light-weight, low-fi way to provide executable usage examples or your code.
72
+ test_files:
73
+ - examples/an_error.rb
74
+ - examples/assertion_failure.rb
75
+ - examples/assertion_success.rb
76
+ - examples/assertion_success_and_failure.rb
77
+ - examples/check_with_disambiguation.rb
78
+ - examples/helpers.rb
79
+ - examples/no_checks.rb
80
+ - examples/no_checks_non_string.rb
81
+ - examples/oneliner.rb
82
+ - examples/with_checks.rb
83
+ - examples/with_setup.rb