lax 0.0.3 → 0.2.1
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.
- data/README.md +25 -26
- data/lax.gemspec +5 -4
- data/lib/lax/rake_task.rb +20 -0
- data/lib/lax/source.rb +288 -0
- data/lib/lax.rb +3 -35
- data/spec/acceptance/acceptance_spec.rb +26 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/unit/fixture/hashable_spec.rb +27 -0
- data/spec/unit/hook_spec.rb +40 -0
- data/spec/unit/lax_spec.rb +20 -0
- data/spec/unit/target_spec.rb +25 -0
- data/test/control.rb +28 -0
- metadata +36 -21
- data/lib/lax/case.rb +0 -17
- data/lib/lax/hook.rb +0 -24
- data/lib/lax/task.rb +0 -36
- data/lib/lax/tree.rb +0 -60
- data/lib/lax/version.rb +0 -3
- data/rakefile +0 -6
- data/test/case/cases.rb +0 -5
- data/test/case/control.rb +0 -32
- data/test/unit/callbacks.rb +0 -11
- data/test/unit/implicit_receiver.rb +0 -5
- data/test/unit/test.rb +0 -8
data/README.md
CHANGED
@@ -1,43 +1,42 @@
|
|
1
1
|
lax
|
2
2
|
===
|
3
|
-
|
3
|
+
Lax is an insouciant smidgen of a testing framework that tries to be an invisible wrapper around your ideas about how your code should work.
|
4
4
|
```ruby
|
5
|
-
|
6
|
-
|
5
|
+
Lax.assert do
|
6
|
+
let number: 1,
|
7
|
+
string: 'Hi There',
|
8
|
+
regexp: defer{ /the/ } # lazy evaluation
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
+
number + 1 == 2
|
11
|
+
string.downcase =~ regexp
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
13
|
+
assert do
|
14
|
+
let number: 2
|
15
|
+
number - 1 == 1
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Lax::Run[ Lax ] #=> pass, pass, pass
|
14
20
|
|
15
|
-
0.upto(10) { |n|
|
16
|
-
calling(:**).with n
|
17
|
-
}
|
18
|
-
}
|
19
|
-
}
|
20
|
-
}
|
21
|
-
Lax.test!
|
22
21
|
```
|
23
|
-
|
24
|
-
|
25
|
-
*
|
26
|
-
*
|
27
|
-
*
|
22
|
+
how come lax is neat
|
23
|
+
--------------------
|
24
|
+
* Minimal bullshit legalese.
|
25
|
+
* Easy-to-define custom matchers and hooks.
|
26
|
+
* Hackable with a tiny code footprint (< 300 SLOC).
|
28
27
|
* Does not pollute your toplevel namespace or infect the entire Ruby object hierarchy with its code.
|
29
28
|
|
30
|
-
make it do it
|
31
|
-
|
29
|
+
how to make it do it
|
30
|
+
--------------------
|
32
31
|
```shell
|
32
|
+
gem install lax
|
33
33
|
cd my/project/root
|
34
|
-
|
35
|
-
|
36
|
-
echo "require 'lax'; Lax::RakeTask.new(:dir=>'lax')" >> rakefile
|
34
|
+
echo "require 'lax/rake_task'; Lax::RakeTask.new" >> Rakefile
|
35
|
+
# write yr tests in the test directory (default test)
|
37
36
|
rake lax
|
38
37
|
```
|
39
38
|
|
40
39
|
license
|
41
40
|
-------
|
42
|
-
X11. See LICENSE for details.
|
41
|
+
MIT/X11. See LICENSE for details.
|
43
42
|
|
data/lax.gemspec
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
$LOAD_PATH << File.expand_path('../lib', __FILE__)
|
2
2
|
require 'rake'
|
3
|
-
require 'lax
|
3
|
+
require 'lax'
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = 'lax'
|
7
7
|
spec.version = Lax::VERSION
|
8
8
|
spec.author = 'feivel jellyfish'
|
9
9
|
spec.email = 'feivel@sdf.org'
|
10
|
-
spec.files = FileList['lax.gemspec','lib
|
11
|
-
spec.test_files = FileList['
|
10
|
+
spec.files = FileList['lax.gemspec','lib/**/*','README.md','LICENSE']
|
11
|
+
spec.test_files = FileList['test/**/*.rb','spec/**/*.rb']
|
12
12
|
spec.license = 'MIT/X11'
|
13
13
|
spec.homepage = 'http://github.com/gwentacle/lax'
|
14
14
|
spec.summary = 'An insouciant smidgen of a testing framework.'
|
15
|
-
spec.description = 'A lightweight testing framework
|
15
|
+
spec.description = 'A lightweight testing framework.'
|
16
|
+
spec.add_development_dependency 'rspec'
|
16
17
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'lax'
|
3
|
+
class Lax
|
4
|
+
module RakeTask
|
5
|
+
class << self
|
6
|
+
include Rake::DSL
|
7
|
+
def new(opts = {})
|
8
|
+
o = Lax.config.task.merge opts
|
9
|
+
namespace o[:name] do
|
10
|
+
task(:load) { Dir["./#{o[:dir]}/**/*.rb"].each {|f| load f} }
|
11
|
+
task(:run) do
|
12
|
+
Lax::Run[ Lax ]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
task o[:name] => ["#{o[:name]}:load", "#{o[:name]}:run"]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
data/lib/lax/source.rb
ADDED
@@ -0,0 +1,288 @@
|
|
1
|
+
VERSION = '0.2.1'
|
2
|
+
|
3
|
+
class Assertion < Struct.new :name, :subject, :condition, :src, :matcher, :args, :hooks
|
4
|
+
def pass?
|
5
|
+
memoize(:pass) { condition.call value }
|
6
|
+
end
|
7
|
+
|
8
|
+
def value
|
9
|
+
memoize(:value) { subject.call }
|
10
|
+
end
|
11
|
+
|
12
|
+
def validate
|
13
|
+
memoize(:validate) do
|
14
|
+
hooks.before.call self
|
15
|
+
pass?
|
16
|
+
self.tap { hooks.after.call self }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def memoize(key)
|
22
|
+
@memo ||= {}
|
23
|
+
@memo.has_key?(key) ? @memo[key] : @memo[key] = yield
|
24
|
+
end
|
25
|
+
|
26
|
+
class Xptn < Struct.new :assertion, :exception
|
27
|
+
attr_accessor :name, :src, :matcher, :args
|
28
|
+
|
29
|
+
def pass?
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def value
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(a, x)
|
38
|
+
super
|
39
|
+
%w{name src matcher args}.each {|m| send "#{m}=", a.send(m)}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module Fixture
|
45
|
+
def self.new(hash)
|
46
|
+
klass = Struct.new(*hash.keys)
|
47
|
+
klass.send :include, self
|
48
|
+
klass.new *hash.values
|
49
|
+
end
|
50
|
+
|
51
|
+
module Hashable
|
52
|
+
def self.new(hashable)
|
53
|
+
hash = hashable.to_hash
|
54
|
+
klass = Struct.new(*hash.keys)
|
55
|
+
klass.send :include, self, Fixture
|
56
|
+
klass.new(*hash.values.map do |val|
|
57
|
+
(Hash===val) ? new(val) : val
|
58
|
+
end)
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_hash
|
62
|
+
Hash[
|
63
|
+
members.zip entries.map {|e| e.kind_of?(Hashable) ? e.to_hash : e }
|
64
|
+
]
|
65
|
+
end
|
66
|
+
|
67
|
+
def merge(hashable)
|
68
|
+
Hashable.new to_hash.merge hashable
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class Target < BasicObject
|
74
|
+
def self.define_matcher(sym, &p)
|
75
|
+
define_method(sym) do |*a,&b|
|
76
|
+
p ?
|
77
|
+
satisfies(sym) do |o|
|
78
|
+
p[*a.map {|v| resolve v},&b][o]
|
79
|
+
end :
|
80
|
+
satisfies(sym,*a) do |o|
|
81
|
+
o.__send__ sym,*a.map {|v| resolve v},&b
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.define_predicate(sym)
|
87
|
+
if sym =~ /(.*)(\?|_?p)$/
|
88
|
+
define_method($1) { satisfies($1) {|o| o.__send__ sym} }
|
89
|
+
else
|
90
|
+
raise ArgumentError, "#{sym} does not appear to be a predicate"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
%w{== === != =~ !~ < > <= >=}.each {|m| define_matcher m}
|
95
|
+
%w{odd? even? is_a? kind_of? include?}.each {|m| define_predicate m}
|
96
|
+
|
97
|
+
def initialize(node, subj, name, src)
|
98
|
+
@node, @subj, @name, @src = node, subj, name, src
|
99
|
+
end
|
100
|
+
|
101
|
+
def satisfies(matcher=nil, *args, &cond)
|
102
|
+
assert!(cond, *[ matcher, args ])
|
103
|
+
end
|
104
|
+
|
105
|
+
def method_missing(sym, *args, &blk)
|
106
|
+
Target.new @node, ->{@subj.call.__send__(sym, *args, &blk)}, @name, @src
|
107
|
+
end
|
108
|
+
|
109
|
+
def __val__
|
110
|
+
@subj.call
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
def assert!(cond, matcher=nil, args=nil)
|
115
|
+
name, subj, src, hooks = @name, @subj, @src, @node.hooks
|
116
|
+
ord = @node.instance_methods.size.to_s
|
117
|
+
@node.send(:include, ::Module.new do
|
118
|
+
define_method(ord) do
|
119
|
+
::Lax::Assertion.new name, subj, cond, src, matcher, args, hooks
|
120
|
+
end
|
121
|
+
end)
|
122
|
+
end
|
123
|
+
|
124
|
+
def resolve(v)
|
125
|
+
::Lax::Target === v ? v.__val__ : v
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
module Run
|
131
|
+
def self.[](lax)
|
132
|
+
hook = lax.config.run.hooks
|
133
|
+
hook.start[ as = lax.map(&:new).flatten ]
|
134
|
+
as.map do |assertion|
|
135
|
+
hook.before[ assertion ]
|
136
|
+
validate_protect(assertion).tap {|v| hook.after[v]}
|
137
|
+
end.tap {|vs| hook.finish[vs]}
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
def self.validate_protect(a)
|
142
|
+
begin
|
143
|
+
a.validate
|
144
|
+
rescue => e
|
145
|
+
Assertion::Xptn.new(a, e)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
class Hook < Proc
|
151
|
+
class << self
|
152
|
+
def _resolve(hook)
|
153
|
+
if hook.is_a? Hook
|
154
|
+
hook
|
155
|
+
elsif hook.is_a? Proc
|
156
|
+
new &hook
|
157
|
+
elsif hook.is_a?(Symbol) and self.respond_to?(hook)
|
158
|
+
send hook
|
159
|
+
else
|
160
|
+
raise NameError, "Unable to resolve hook `#{hook}'"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def noop
|
165
|
+
new {|*a|}
|
166
|
+
end
|
167
|
+
|
168
|
+
def output
|
169
|
+
new {|tc| print tc.pass? ? "\x1b[32m.\x1b[0m" : "\x1b[31mX\x1b[0m"}
|
170
|
+
end
|
171
|
+
|
172
|
+
# Returns a hook for generating terminal output from test cases.
|
173
|
+
def summary
|
174
|
+
new {|cs| puts "\nFinished #{cs.size} tests with #{cs.reject(&:pass?).size} failures"}
|
175
|
+
end
|
176
|
+
|
177
|
+
# Returns a hook for generating terminal output from test cases.
|
178
|
+
def failures
|
179
|
+
new do |cs|
|
180
|
+
cs.reject(&:pass?).each do |f|
|
181
|
+
puts " #{f.src}\n " <<
|
182
|
+
"#{f.exception ?
|
183
|
+
"(raised an unhandled #{f.exception.class})" :
|
184
|
+
"(got #{f.subject})"}"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def define(sym, &p)
|
190
|
+
define_singleton_method(sym) {new &p}
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def <<(hook)
|
195
|
+
Hook.new {|*a,&b| call(Hook._resolve(hook)[*a,&b])}
|
196
|
+
end
|
197
|
+
|
198
|
+
def +(hook)
|
199
|
+
Hook.new {|*a,&b| call(*a,&b); Hook._resolve(hook)[*a,&b]}
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
CONFIG = Fixture::Hashable.new(
|
204
|
+
task: { dir: :test, name: :lax },
|
205
|
+
node: {
|
206
|
+
hooks: {
|
207
|
+
before: Hook.noop,
|
208
|
+
after: Hook.noop
|
209
|
+
}
|
210
|
+
},
|
211
|
+
run: {
|
212
|
+
hooks: {
|
213
|
+
start: Hook.noop,
|
214
|
+
before: Hook.noop,
|
215
|
+
after: Hook.output,
|
216
|
+
finish: Hook.summary
|
217
|
+
}
|
218
|
+
}
|
219
|
+
)
|
220
|
+
|
221
|
+
@hooks = CONFIG.node.hooks
|
222
|
+
@children = []
|
223
|
+
|
224
|
+
def self.inherited(child)
|
225
|
+
@children << child
|
226
|
+
child.hooks = @hooks.dup
|
227
|
+
child.children = []
|
228
|
+
end
|
229
|
+
|
230
|
+
extend Enumerable
|
231
|
+
|
232
|
+
class << self
|
233
|
+
attr_accessor :hooks, :children
|
234
|
+
|
235
|
+
def reboot(suppress_warning = true)
|
236
|
+
(stderr, $stderr = $stderr, StringIO.new) if suppress_warning
|
237
|
+
Object.const_set :Lax, Class.new(Array)
|
238
|
+
Lax.const_set :SOURCE, SOURCE
|
239
|
+
Lax.class_eval SOURCE
|
240
|
+
($stderr = stderr) if suppress_warning
|
241
|
+
Lax
|
242
|
+
end
|
243
|
+
|
244
|
+
def config
|
245
|
+
block_given? ? yield(CONFIG) : CONFIG
|
246
|
+
end
|
247
|
+
|
248
|
+
def matcher(sym, &b)
|
249
|
+
Target.define_matcher(sym, &b)
|
250
|
+
end
|
251
|
+
|
252
|
+
def hook(sym, &b)
|
253
|
+
Hook.define(sym, &b)
|
254
|
+
end
|
255
|
+
|
256
|
+
def each(&b)
|
257
|
+
yield self
|
258
|
+
children.each {|c| c.each(&b)}
|
259
|
+
end
|
260
|
+
|
261
|
+
def let(h)
|
262
|
+
h.each do |key, value|
|
263
|
+
val = value.is_a?(Hook) ? value : ->{value}
|
264
|
+
define_singleton_method(key) do
|
265
|
+
Target.new(self, val, key, caller[0])
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def defer(&v)
|
271
|
+
Hook.new(&v)
|
272
|
+
end
|
273
|
+
alias _ defer
|
274
|
+
|
275
|
+
def fix(hash)
|
276
|
+
Fixture.new(hash)
|
277
|
+
end
|
278
|
+
|
279
|
+
def assert(*vals, &b)
|
280
|
+
Class.new(self).tap {|node| node.class_eval(&b)}
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def initialize
|
285
|
+
ks = methods - self.class.superclass.instance_methods
|
286
|
+
ks.map {|k| send k}.each {|k| self << k}
|
287
|
+
end
|
288
|
+
|
data/lib/lax.rb
CHANGED
@@ -1,36 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
autoload :Tree, 'lax/tree'
|
5
|
-
autoload :Hook, 'lax/hook'
|
6
|
-
autoload :Task, 'lax/task'
|
7
|
-
|
8
|
-
class << self
|
9
|
-
@@cases = []
|
10
|
-
def test(c={},&b)
|
11
|
-
preproc(b)[t=Tree.new(c)]
|
12
|
-
t.leaves.tap {|cs|@@cases+=cs}
|
13
|
-
end
|
14
|
-
|
15
|
-
def test!(opts={})
|
16
|
-
cases = opts[:cases] || @@cases
|
17
|
-
call opts[:start], cases
|
18
|
-
cases.each do |c|
|
19
|
-
call opts[:before], c
|
20
|
-
c.test
|
21
|
-
call opts[:after], c
|
22
|
-
end
|
23
|
-
call opts[:finish], cases
|
24
|
-
cases
|
25
|
-
end
|
26
|
-
|
27
|
-
def call(p, *as)
|
28
|
-
p[*as] if p
|
29
|
-
end
|
30
|
-
|
31
|
-
def preproc(p)
|
32
|
-
p.parameters.any?? p : ->(o) { o.instance_exec &p }
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
1
|
+
Object::Lax = Class.new(Array)
|
2
|
+
Lax::SOURCE = File.new(File.expand_path('../lax/source.rb', __FILE__)).read
|
3
|
+
Lax.class_eval Lax::SOURCE
|
36
4
|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Lax do
|
4
|
+
|
5
|
+
let :simple_case do
|
6
|
+
Lax.assert do
|
7
|
+
let number: 19,
|
8
|
+
string: 'asdf',
|
9
|
+
symbol: :symbol
|
10
|
+
|
11
|
+
number.odd? == true
|
12
|
+
number == 19
|
13
|
+
string == 'asdf'
|
14
|
+
number == 20
|
15
|
+
string.upcase == 'ASDF'
|
16
|
+
symbol.to_s == 'symbol'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
subject { simple_case.new }
|
21
|
+
it { should have(6).things }
|
22
|
+
specify { subject.select(&:pass?).should have(5).things }
|
23
|
+
specify { subject.reject(&:pass?).should have(1).things }
|
24
|
+
|
25
|
+
end
|
26
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Lax::Fixture::Hashable do
|
4
|
+
let(:hash) { { name: 'phyllis', fears: { mild: ['spiders'], severe: ['manatees'] } } }
|
5
|
+
let(:config) { Lax::Fixture::Hashable.new hash }
|
6
|
+
|
7
|
+
describe '::new' do
|
8
|
+
specify { ->{Lax::Fixture::Hashable.new}.should raise_error ArgumentError }
|
9
|
+
specify { [:name, :fears].each {|msg| config.should respond_to msg } }
|
10
|
+
specify { config.name.should == 'phyllis' }
|
11
|
+
specify { config.fears.should be_a_kind_of Lax::Fixture::Hashable }
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#merge' do
|
15
|
+
subject { config.merge manners: 'impeccable' }
|
16
|
+
it { should be_a_kind_of Lax::Fixture::Hashable }
|
17
|
+
specify { subject.name.should == 'phyllis' }
|
18
|
+
specify { subject.manners.should == 'impeccable' }
|
19
|
+
specify { subject.fears.should be_a_kind_of Lax::Fixture::Hashable }
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#to_hash' do
|
23
|
+
subject { config.to_hash }
|
24
|
+
it { should == hash }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Lax::Hook do
|
4
|
+
let(:one) { Lax::Hook.new {1} }
|
5
|
+
specify { one.should be_a_kind_of Proc }
|
6
|
+
|
7
|
+
describe '#<<' do
|
8
|
+
let(:doubler) { Lax::Hook.new {|n| 2*n} }
|
9
|
+
subject { doubler << one }
|
10
|
+
its(:call) { should == 2 }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#+' do
|
14
|
+
let(:thing) { Object.new }
|
15
|
+
let(:hash) { Lax::Hook.new { thing.hash } }
|
16
|
+
subject { hash + one }
|
17
|
+
its(:call) { should == 1 }
|
18
|
+
specify do
|
19
|
+
thing.should_receive :hash
|
20
|
+
subject.call
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '::_resolve' do
|
25
|
+
let(:resolve) { Lax::Hook.method :_resolve }
|
26
|
+
context 'given a hook' do
|
27
|
+
let(:hook) { Lax::Hook.new {:surprise!} }
|
28
|
+
subject { resolve[hook] }
|
29
|
+
it { should == hook }
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'given a symbol' do
|
33
|
+
let(:a_hook) { :noop }
|
34
|
+
let(:random_sym) { :asdfqwer }
|
35
|
+
specify { resolve[a_hook].call.should == nil }
|
36
|
+
specify { -> {resolve[random_sym]}.should raise_error NameError }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Lax do
|
4
|
+
subject { Lax }
|
5
|
+
|
6
|
+
describe '::let' do
|
7
|
+
before do
|
8
|
+
Lax.let number: 1
|
9
|
+
Lax.number == 1
|
10
|
+
end
|
11
|
+
subject { Lax.instance_methods - Lax.superclass.instance_methods }
|
12
|
+
it { should have(1).thing }
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#initialize' do
|
16
|
+
subject { Lax.reboot.new }
|
17
|
+
it { should be_empty }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Lax::Target do
|
4
|
+
let(:node) { Lax.assert { let num: 1 } }
|
5
|
+
|
6
|
+
describe 'establishing a target' do
|
7
|
+
subject { node.num }
|
8
|
+
specify { (Lax::Target === subject).should == true }
|
9
|
+
specify { subject.__val__.should == 1 }
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'when specifying a condition' do
|
13
|
+
before { node.num == 1234 }
|
14
|
+
specify { node.new.should have(1).thing }
|
15
|
+
describe 'the entailed assertion' do
|
16
|
+
subject { node.new.first }
|
17
|
+
it { should be_an_instance_of Lax::Assertion }
|
18
|
+
its(:subject) { should be_an_instance_of Proc }
|
19
|
+
its(:name) { should == :num }
|
20
|
+
its(:matcher) { should == '==' }
|
21
|
+
its(:pass?) { should == false }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
data/test/control.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
Lax.assert do
|
2
|
+
let number: 1,
|
3
|
+
string: 'asdf',
|
4
|
+
symbol: :a_sym,
|
5
|
+
regexp: _{/asd/}
|
6
|
+
|
7
|
+
assert do
|
8
|
+
number == 1
|
9
|
+
assert do
|
10
|
+
let number: 2
|
11
|
+
number == 2
|
12
|
+
|
13
|
+
string.upcase.downcase == 'asdf'
|
14
|
+
string =~ regexp
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
Lax.assert do
|
19
|
+
let number: 1,
|
20
|
+
string: 'Hi There',
|
21
|
+
regexp: defer{ /the/ }
|
22
|
+
number + 1 == 2
|
23
|
+
string.downcase =~ regexp
|
24
|
+
assert do
|
25
|
+
let number: 2
|
26
|
+
number - 1 == 1
|
27
|
+
end
|
28
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lax
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,9 +9,25 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-12-
|
13
|
-
dependencies:
|
14
|
-
|
12
|
+
date: 2012-12-22 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: A lightweight testing framework.
|
15
31
|
email: feivel@sdf.org
|
16
32
|
executables: []
|
17
33
|
extensions: []
|
@@ -19,19 +35,17 @@ extra_rdoc_files: []
|
|
19
35
|
files:
|
20
36
|
- lax.gemspec
|
21
37
|
- lib/lax.rb
|
22
|
-
- lib/lax/
|
23
|
-
- lib/lax/
|
24
|
-
- lib/lax/case.rb
|
25
|
-
- lib/lax/tree.rb
|
26
|
-
- lib/lax/hook.rb
|
38
|
+
- lib/lax/rake_task.rb
|
39
|
+
- lib/lax/source.rb
|
27
40
|
- README.md
|
28
41
|
- LICENSE
|
29
|
-
-
|
30
|
-
-
|
31
|
-
-
|
32
|
-
-
|
33
|
-
-
|
34
|
-
-
|
42
|
+
- test/control.rb
|
43
|
+
- spec/spec_helper.rb
|
44
|
+
- spec/acceptance/acceptance_spec.rb
|
45
|
+
- spec/unit/hook_spec.rb
|
46
|
+
- spec/unit/target_spec.rb
|
47
|
+
- spec/unit/lax_spec.rb
|
48
|
+
- spec/unit/fixture/hashable_spec.rb
|
35
49
|
homepage: http://github.com/gwentacle/lax
|
36
50
|
licenses:
|
37
51
|
- MIT/X11
|
@@ -58,9 +72,10 @@ signing_key:
|
|
58
72
|
specification_version: 3
|
59
73
|
summary: An insouciant smidgen of a testing framework.
|
60
74
|
test_files:
|
61
|
-
-
|
62
|
-
-
|
63
|
-
-
|
64
|
-
-
|
65
|
-
-
|
66
|
-
-
|
75
|
+
- test/control.rb
|
76
|
+
- spec/spec_helper.rb
|
77
|
+
- spec/acceptance/acceptance_spec.rb
|
78
|
+
- spec/unit/hook_spec.rb
|
79
|
+
- spec/unit/target_spec.rb
|
80
|
+
- spec/unit/lax_spec.rb
|
81
|
+
- spec/unit/fixture/hashable_spec.rb
|
data/lib/lax/case.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
module Lax
|
2
|
-
class Case < Hash
|
3
|
-
def test
|
4
|
-
Lax.call self[:before], self
|
5
|
-
self[:pass] = begin
|
6
|
-
self[:cond][self[:value]=self[:obj].__send__(self[:msg],*self[:args],&self[:blk])]
|
7
|
-
rescue self[:xptn] => e
|
8
|
-
true
|
9
|
-
rescue => e
|
10
|
-
self[:xptn] = e
|
11
|
-
false
|
12
|
-
end
|
13
|
-
Lax.call self[:after], self
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
data/lib/lax/hook.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
module Lax
|
2
|
-
class Hook < Proc
|
3
|
-
def <<(cb); Hook.new {|e| self[cb[e]]} end
|
4
|
-
def +(cb); Hook.new {|e| self[e]; cb[e]} end
|
5
|
-
|
6
|
-
StartTime = Hook.new { @start = Time.now }
|
7
|
-
StopTime = Hook.new { @stop = Time.now }
|
8
|
-
PassFail = Hook.new {|tc| $stdout.write(tc[:pass] ? "\x1b[32m=\x1b[0m" : "\x1b[31m#\x1b[0m")}
|
9
|
-
|
10
|
-
Summary = Hook.new do |cases|
|
11
|
-
puts "\nFinished #{cases.size} tests" <<
|
12
|
-
" in #{(@stop - @start).round 10} seconds" <<
|
13
|
-
" with #{(cases.reject{|c|c[:pass]}).size} failures"
|
14
|
-
end
|
15
|
-
|
16
|
-
Failures = Hook.new do |cases|
|
17
|
-
cases.reject {|c|c[:pass]}.each do |f|
|
18
|
-
puts " #{Module===f[:obj] ? "#{f[:obj]}::" : "#{f[:obj].class}#"}#{f[:msg]}" <<
|
19
|
-
"#{?(+[*f[:args]]*', '+?) if f[:args]} " << (f.has_key?(:value) ? "#=> #{f[:value]}" : "raised unhandled #{f[:xptn].class}")
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
data/lib/lax/task.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
require 'rake'
|
2
|
-
module Lax
|
3
|
-
class Task
|
4
|
-
include Rake::DSL
|
5
|
-
def initialize(opts = {})
|
6
|
-
dir = opts.delete(:dir) || :test
|
7
|
-
make_tasks dir, {
|
8
|
-
start: Hook::StartTime,
|
9
|
-
after: Hook::PassFail,
|
10
|
-
finish: Hook::StopTime + Hook::Summary + Hook::Failures
|
11
|
-
}.merge(opts)
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
def make_tasks(dir, opts)
|
16
|
-
namespace dir do
|
17
|
-
desc "[Lax] load all test files"
|
18
|
-
task load: make_groups(dir)
|
19
|
-
desc "[Lax] run all loaded tests"
|
20
|
-
task(:run) { Lax.test! opts }
|
21
|
-
end
|
22
|
-
desc "[Lax] load and run all tests"
|
23
|
-
task dir => ["#{dir}:load","#{dir}:run"]
|
24
|
-
end
|
25
|
-
|
26
|
-
def make_groups(dir)
|
27
|
-
FileList["#{dir}/**/*"].select {|f| File.directory? f}.map do |group|
|
28
|
-
name = group.sub(/^#{dir}\//,'').gsub(/\//,?:)
|
29
|
-
desc "[Lax] load files in #{group}"
|
30
|
-
task(name) { Dir["#{group}/*.rb"].each {|file| load file} }
|
31
|
-
[dir,name]*?:
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
data/lib/lax/tree.rb
DELETED
@@ -1,60 +0,0 @@
|
|
1
|
-
module Lax
|
2
|
-
class Tree < Array
|
3
|
-
attr_reader :tc
|
4
|
-
def initialize(tc={})
|
5
|
-
@tc = Case.new.merge tc
|
6
|
-
end
|
7
|
-
|
8
|
-
def on(obj,&b)
|
9
|
-
where({obj: obj},&b)
|
10
|
-
end
|
11
|
-
|
12
|
-
def calling(msg,&b)
|
13
|
-
where({msg: msg},&b)
|
14
|
-
end
|
15
|
-
|
16
|
-
def with(*args,&b)
|
17
|
-
where({args: args},&b)
|
18
|
-
end
|
19
|
-
|
20
|
-
def with_block(blk=nil,&b)
|
21
|
-
blk ? where({blk: blk},&b) : where(blk: b)
|
22
|
-
end
|
23
|
-
|
24
|
-
def satisfies(cond=nil,&b)
|
25
|
-
cond ? where({cond: cond},&b) : where(cond: Lax.preproc(b))
|
26
|
-
end
|
27
|
-
|
28
|
-
def before(bef=nil,&b)
|
29
|
-
bef ? where({before: bef},&b) : where(before: b)
|
30
|
-
end
|
31
|
-
|
32
|
-
def after(aft=nil,&b)
|
33
|
-
aft ? where({after: aft},&b) : where(after: b)
|
34
|
-
end
|
35
|
-
|
36
|
-
def raises(xptn=StandardError,&b)
|
37
|
-
where({xptn: xptn},&b)
|
38
|
-
end
|
39
|
-
|
40
|
-
def it
|
41
|
-
calling(:tap).with_block {}
|
42
|
-
end
|
43
|
-
|
44
|
-
def returns(v,&b)
|
45
|
-
satisfies ->(e){e==v}, &b
|
46
|
-
end
|
47
|
-
|
48
|
-
def where(h,&b)
|
49
|
-
t=Tree.new tc.merge h
|
50
|
-
Lax.preproc(b)[t] if b
|
51
|
-
push(t).last
|
52
|
-
end
|
53
|
-
|
54
|
-
def leaves
|
55
|
-
any?? map(&:leaves).flatten : [tc]
|
56
|
-
end
|
57
|
-
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
data/lib/lax/version.rb
DELETED
data/rakefile
DELETED
data/test/case/cases.rb
DELETED
data/test/case/control.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
Lax.test(obj: 1) {|that|
|
2
|
-
that.calling(:/).with(0).raises ZeroDivisionError
|
3
|
-
that.calling(:**) {|exponentiation|
|
4
|
-
exponentiation.with(1).satisfies {|n|n==1}
|
5
|
-
exponentiation.with(2).satisfies ->(n){n==1}
|
6
|
-
}
|
7
|
-
}
|
8
|
-
|
9
|
-
Lax.test {
|
10
|
-
calling(:/).on(1).with(0).raises ZeroDivisionError
|
11
|
-
returns(1) {
|
12
|
-
on(0).calling(:+).with 1
|
13
|
-
on(1) {
|
14
|
-
calling(:* ).with 1
|
15
|
-
calling(:**).with 2
|
16
|
-
calling(:+ ).with 0
|
17
|
-
}
|
18
|
-
}
|
19
|
-
}
|
20
|
-
|
21
|
-
Lax.test(msg: :object_id, cond: ->(v){Fixnum===v}) {
|
22
|
-
on 1
|
23
|
-
on 'asdf'
|
24
|
-
on String
|
25
|
-
on Lax
|
26
|
-
calling(:size).on([1,2,3])
|
27
|
-
}
|
28
|
-
|
29
|
-
Lax.test(obj: 222) {|that|
|
30
|
-
that.it.returns 222
|
31
|
-
}
|
32
|
-
|
data/test/unit/callbacks.rb
DELETED