lax 0.0.3 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,43 +1,42 @@
1
1
  lax
2
2
  ===
3
- An insouciant smidgen of a testing framework that is not the boss of you.
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
- Lax.test {
6
- calling(:/).on(1).with(0).raises ZeroDivisionError
5
+ Lax.assert do
6
+ let number: 1,
7
+ string: 'Hi There',
8
+ regexp: defer{ /the/ } # lazy evaluation
7
9
 
8
- returns(1) {
9
- calling(:+).on(0).with 1
10
+ number + 1 == 2
11
+ string.downcase =~ regexp
10
12
 
11
- on(1) {
12
- calling(:+).with 0
13
- calling(:*).with 1
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
- yes but why
24
- -----------
25
- * Everything about a test is independently scopeable - methods, arguments, receivers, blocks, expectations, hooks, and any metadata you might care to attach. Testing that one method call satisfies three conditions is as natural as testing that one condition is satisfied by three different method calls. Write tests in whatever way makes sense.
26
- * No hardcoded constraints on terminal output, handling of failed tests, w/e - it's all done with user-configurable hooks.
27
- * Code footprint so small, it's hardly there at all (< 200 SLOC).
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
- mkdir -p lax/test
35
- echo "Lax.test {|that| that.calling(:+).on(1).with(99).returns 100}" > lax/test/test.rb
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/version'
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/**/*.rb','README.md','LICENSE']
11
- spec.test_files = FileList['rakefile','test/**/*.rb']
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 that is not the boss of you.'
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
- require 'lax/version'
2
- module Lax
3
- autoload :Case, 'lax/case'
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
+
@@ -0,0 +1,10 @@
1
+ require 'rspec'
2
+ require 'lax'
3
+ require 'pry'
4
+
5
+ RSpec.configure do |config|
6
+ config.color_enabled = true
7
+ end
8
+
9
+ Lax.config.run.hooks.after = Lax::Hook.noop # suppress output
10
+
@@ -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.0.3
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-14 00:00:00.000000000 Z
13
- dependencies: []
14
- description: A lightweight testing framework that is not the boss of you.
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/version.rb
23
- - lib/lax/task.rb
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
- - rakefile
30
- - test/case/control.rb
31
- - test/case/cases.rb
32
- - test/unit/implicit_receiver.rb
33
- - test/unit/callbacks.rb
34
- - test/unit/test.rb
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
- - rakefile
62
- - test/case/control.rb
63
- - test/case/cases.rb
64
- - test/unit/implicit_receiver.rb
65
- - test/unit/callbacks.rb
66
- - test/unit/test.rb
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
@@ -1,3 +0,0 @@
1
- module Lax
2
- VERSION = '0.0.3'
3
- end
data/rakefile DELETED
@@ -1,6 +0,0 @@
1
- $LOAD_PATH << File.expand_path('../lib', __FILE__)
2
- require 'lax'
3
-
4
- Lax::Task.new
5
- task default: :test
6
-
data/test/case/cases.rb DELETED
@@ -1,5 +0,0 @@
1
- Lax.test {|assert|
2
- assert.on(rand(0..100)).it.satisfies {|n| Fixnum===n}
3
- assert.on(->{QWERTY.uiop[]}).calling(:call).raises
4
- }
5
-
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
-
@@ -1,11 +0,0 @@
1
- @array = []
2
-
3
- Lax.test(obj: @array) {|that|
4
- that.before {@array << 1}.calling(:size) {|_|
5
- _.returns 1
6
- _.returns 2
7
- _.after {@array.shift}.returns 3
8
- _.returns 3
9
- }
10
- }
11
-
@@ -1,5 +0,0 @@
1
- Lax.test {
2
- on(2) { calling(:+) { with(3) { satisfies {odd?} } } }
3
- on(2).calling(:+).with(3).satisfies &:odd?
4
- }
5
-
data/test/unit/test.rb DELETED
@@ -1,8 +0,0 @@
1
- Lax.test {|assert|
2
- assert.calling(:upcase).on('asdf').returns('ASDF')
3
- }
4
-
5
- Lax.test(obj: 'asdf') {|test_that|
6
- test_that.satisfies {|n| n=='ASDF'}.calling(:upcase)
7
- }
8
-