citron 0.1.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.
- data/.gemspec +152 -0
- data/.gitignore +6 -0
- data/.ruby +50 -0
- data/.yardopts +7 -0
- data/Assembly +46 -0
- data/COPYING.rdoc +31 -0
- data/LICENSE.txt +25 -0
- data/MANIFEST +12 -0
- data/PROFILE +29 -0
- data/README.md +58 -0
- data/VERSION +1 -0
- data/lib/citron.rb +23 -0
- data/lib/citron/test_advice.rb +59 -0
- data/lib/citron/test_case.rb +297 -0
- data/lib/citron/test_setup.rb +51 -0
- data/lib/citron/test_unit.rb +123 -0
- data/try/case_example.rb +30 -0
- metadata +127 -0
data/.gemspec
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
Gem::Specification.new do |gemspec|
|
6
|
+
|
7
|
+
manifest = Dir.glob('manifest{,.txt)', File::FNM_CASEFOLD).first
|
8
|
+
|
9
|
+
scm = case
|
10
|
+
when File.directory?('.git')
|
11
|
+
:git
|
12
|
+
end
|
13
|
+
|
14
|
+
files = case
|
15
|
+
when manifest
|
16
|
+
File.readlines(manifest).
|
17
|
+
map{ |line| line.srtip }.
|
18
|
+
reject{ |line| line.empty? || line[0,1] == '#' }
|
19
|
+
when scm == :git
|
20
|
+
`git ls-files -z`.split("\0")
|
21
|
+
else
|
22
|
+
Dir.glob('{**/}{.*,*}') # TODO: be more specific using standard locations ?
|
23
|
+
end.select{ |path| File.file?(path) }
|
24
|
+
|
25
|
+
patterns = {
|
26
|
+
:bin_files => 'bin/*',
|
27
|
+
:lib_files => 'lib/{**/}*.rb',
|
28
|
+
:ext_files => 'ext/{**/}extconf.rb',
|
29
|
+
:doc_files => '*.{txt,rdoc,md,markdown,tt,textile}',
|
30
|
+
:test_files => '{test/{**/}*_test.rb,spec/{**/}*_spec.rb}'
|
31
|
+
}
|
32
|
+
|
33
|
+
glob_files = lambda { |pattern|
|
34
|
+
Dir.glob(pattern).select { |path|
|
35
|
+
File.file?(path) && files.include?(path)
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
#files = glob_files[patterns[:files]]
|
40
|
+
|
41
|
+
executables = glob_files[patterns[:bin_files]].map do |path|
|
42
|
+
File.basename(path)
|
43
|
+
end
|
44
|
+
|
45
|
+
extensions = glob_files[patterns[:ext_files]].map do |path|
|
46
|
+
File.basename(path)
|
47
|
+
end
|
48
|
+
|
49
|
+
metadata = YAML.load_file('.ruby')
|
50
|
+
|
51
|
+
# build-out the gemspec
|
52
|
+
|
53
|
+
case metadata['revision']
|
54
|
+
when 0
|
55
|
+
gemspec.name = metadata['name']
|
56
|
+
gemspec.version = metadata['version']
|
57
|
+
gemspec.summary = metadata['summary']
|
58
|
+
gemspec.description = metadata['description']
|
59
|
+
|
60
|
+
metadata['authors'].each do |author|
|
61
|
+
gemspec.authors << author['name']
|
62
|
+
|
63
|
+
if author.has_key?('email')
|
64
|
+
if gemspec.email
|
65
|
+
gemspec.email << author['email']
|
66
|
+
else
|
67
|
+
gemspec.email = [author['email']]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
gemspec.licenses = metadata['licenses']
|
73
|
+
|
74
|
+
metadata['requirements'].each do |req|
|
75
|
+
name = req['name']
|
76
|
+
version = req['version']
|
77
|
+
groups = req['groups'] || []
|
78
|
+
|
79
|
+
if md = /^(.*?)([+-~])$/.match(version)
|
80
|
+
version = case md[2]
|
81
|
+
when '+' then ">= #{$1}"
|
82
|
+
when '-' then "< #{$1}"
|
83
|
+
when '~' then "~> #{$1}"
|
84
|
+
else version
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
#development = req['development']
|
89
|
+
#if development
|
90
|
+
# # populate development dependencies
|
91
|
+
# if gemspec.respond_to?(:add_development_dependency)
|
92
|
+
# gemspec.add_development_dependency(name,*version)
|
93
|
+
# else
|
94
|
+
# gemspec.add_dependency(name,*version)
|
95
|
+
# end
|
96
|
+
#else
|
97
|
+
# # populate runtime dependencies
|
98
|
+
# if gemspec.respond_to?(:add_runtime_dependency)
|
99
|
+
# gemspec.add_runtime_dependency(name,*version)
|
100
|
+
# else
|
101
|
+
# gemspec.add_dependency(name,*version)
|
102
|
+
# end
|
103
|
+
#end
|
104
|
+
|
105
|
+
if groups.empty? or groups.include?('runtime')
|
106
|
+
# populate runtime dependencies
|
107
|
+
if gemspec.respond_to?(:add_runtime_dependency)
|
108
|
+
gemspec.add_runtime_dependency(name,*version)
|
109
|
+
else
|
110
|
+
gemspec.add_dependency(name,*version)
|
111
|
+
end
|
112
|
+
else
|
113
|
+
# populate development dependencies
|
114
|
+
if gemspec.respond_to?(:add_development_dependency)
|
115
|
+
gemspec.add_development_dependency(name,*version)
|
116
|
+
else
|
117
|
+
gemspec.add_dependency(name,*version)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# convert external dependencies into a requirements
|
123
|
+
if metadata['external_dependencies']
|
124
|
+
##gemspec.requirements = [] unless metadata['external_dependencies'].empty?
|
125
|
+
metadata['external_dependencies'].each do |req|
|
126
|
+
gemspec.requirements << req.to_s
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# determine homepage from resources
|
131
|
+
homepage = metadata['resources'].find{ |key, url| key =~ /^home/ }
|
132
|
+
gemspec.homepage = homepage.last if homepage
|
133
|
+
|
134
|
+
gemspec.require_paths = metadata['load_path'] || ['lib']
|
135
|
+
gemspec.post_install_message = metadata['install_message']
|
136
|
+
|
137
|
+
# RubyGems specific metadata
|
138
|
+
gemspec.files = files
|
139
|
+
gemspec.extensions = extensions
|
140
|
+
gemspec.executables = executables
|
141
|
+
|
142
|
+
if Gem::VERSION < '1.7.'
|
143
|
+
gemspec.default_executable = gemspec.executables.first
|
144
|
+
end
|
145
|
+
|
146
|
+
gemspec.test_files = glob_files[patterns[:test_files]]
|
147
|
+
|
148
|
+
unless gemspec.files.include?('.document')
|
149
|
+
gemspec.extra_rdoc_files = glob_files[patterns[:doc_files]]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
data/.ruby
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
---
|
2
|
+
authors:
|
3
|
+
- name: Thomas Sawyer
|
4
|
+
email: transfire@gmail.com
|
5
|
+
copyrights:
|
6
|
+
- holder: Thomas Sawyer
|
7
|
+
year: "2011"
|
8
|
+
license: BSD-2-Clause
|
9
|
+
replacements: []
|
10
|
+
|
11
|
+
conflicts: []
|
12
|
+
|
13
|
+
requirements:
|
14
|
+
- name: test
|
15
|
+
- name: ae
|
16
|
+
- name: detroit
|
17
|
+
groups:
|
18
|
+
- build
|
19
|
+
development: true
|
20
|
+
- name: reap
|
21
|
+
groups:
|
22
|
+
- build
|
23
|
+
development: true
|
24
|
+
- name: qed
|
25
|
+
groups:
|
26
|
+
- test
|
27
|
+
development: true
|
28
|
+
dependencies: []
|
29
|
+
|
30
|
+
repositories:
|
31
|
+
- uri: git://github.com/proutils/citron.git
|
32
|
+
scm: git
|
33
|
+
name: upstream
|
34
|
+
resources:
|
35
|
+
home: http://rubyworks.github.com/citron
|
36
|
+
code: http://github.com/rubyworks/citron
|
37
|
+
load_path:
|
38
|
+
- lib
|
39
|
+
extra:
|
40
|
+
manifest: MANIFEST
|
41
|
+
alternatives: []
|
42
|
+
|
43
|
+
revision: 0
|
44
|
+
name: citron
|
45
|
+
title: Citron
|
46
|
+
suite: RubyWorks
|
47
|
+
summary: Classic Unit-style Test Framework
|
48
|
+
description: Citron is a unit testing framework with a classic test-case/test-unit style.
|
49
|
+
version: 0.1.0
|
50
|
+
date: "2011-07-29"
|
data/.yardopts
ADDED
data/Assembly
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
---
|
2
|
+
github:
|
3
|
+
active: true
|
4
|
+
|
5
|
+
gem:
|
6
|
+
active: true
|
7
|
+
|
8
|
+
dnote:
|
9
|
+
labels: ~
|
10
|
+
output: log/NOTES.rdoc
|
11
|
+
|
12
|
+
yard:
|
13
|
+
yardopts: true
|
14
|
+
|
15
|
+
qed:
|
16
|
+
files : ~
|
17
|
+
#exclude : ~
|
18
|
+
#loadpath: ~
|
19
|
+
#requires: ~
|
20
|
+
#live : false
|
21
|
+
active : false
|
22
|
+
|
23
|
+
qedoc:
|
24
|
+
files : spec/
|
25
|
+
output: QED.rdoc
|
26
|
+
active: false
|
27
|
+
|
28
|
+
vclog:
|
29
|
+
output: log/ChangeLog.rdoc
|
30
|
+
active: false
|
31
|
+
|
32
|
+
email:
|
33
|
+
service: Email
|
34
|
+
file : ~
|
35
|
+
subject: ~
|
36
|
+
mailto :
|
37
|
+
- ruby-talk@ruby-lang.org
|
38
|
+
- rubyworks-mailinglist@googlegroups.com
|
39
|
+
from : <%= ENV['EMAIL_ACCOUNT'] %>
|
40
|
+
server : <%= ENV['EMAIL_SERVER'] %>
|
41
|
+
port : <%= ENV['EMAIL_PORT'] %>
|
42
|
+
account: <%= ENV['EMAIL_ACCOUNT'] %>
|
43
|
+
domain : <%= ENV['EMAIL_DOMAIN'] %>
|
44
|
+
login : <%= ENV['EMAIL_LOGIN'] %>
|
45
|
+
secure : <%= ENV['EMAIL_SECURE'] %>
|
46
|
+
|
data/COPYING.rdoc
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
= COPYRIGHT NOTICES
|
2
|
+
|
3
|
+
== Citron
|
4
|
+
|
5
|
+
Copyright:: (c) 2011 Thomas Sawyer, RubyWorks
|
6
|
+
License:: BSD-2-Clause
|
7
|
+
Website:: http://rubyworks.github.com/citron
|
8
|
+
|
9
|
+
Copyright 2011 Thomas Sawyer. All rights reserved.
|
10
|
+
|
11
|
+
Redistribution and use in source and binary forms, with or without
|
12
|
+
modification, are permitted provided that the following conditions are met:
|
13
|
+
|
14
|
+
1. Redistributions of source code must retain the above copyright notice,
|
15
|
+
this list of conditions and the following disclaimer.
|
16
|
+
|
17
|
+
2. Redistributions in binary form must reproduce the above copyright
|
18
|
+
notice, this list of conditions and the following disclaimer in the
|
19
|
+
documentation and/or other materials provided with the distribution.
|
20
|
+
|
21
|
+
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
22
|
+
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
23
|
+
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
24
|
+
COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
25
|
+
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
26
|
+
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
27
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
28
|
+
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
29
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
30
|
+
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
31
|
+
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
BSD 2 Clause License
|
2
|
+
|
3
|
+
Copyright 2011 Thomas Sawyer. All rights reserved.
|
4
|
+
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
7
|
+
|
8
|
+
1. Redistributions of source code must retain the above copyright notice,
|
9
|
+
this list of conditions and the following disclaimer.
|
10
|
+
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright
|
12
|
+
notice, this list of conditions and the following disclaimer in the
|
13
|
+
documentation and/or other materials provided with the distribution.
|
14
|
+
|
15
|
+
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
16
|
+
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
18
|
+
COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
19
|
+
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
20
|
+
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
21
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
22
|
+
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
23
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
24
|
+
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
25
|
+
|
data/MANIFEST
ADDED
data/PROFILE
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
---
|
2
|
+
name : citron
|
3
|
+
title : Citron
|
4
|
+
suite : RubyWorks
|
5
|
+
summary: Classic Unit-style Test Framework
|
6
|
+
authors:
|
7
|
+
- Thomas Sawyer <transfire@gmail.com>
|
8
|
+
|
9
|
+
description:
|
10
|
+
Citron is a unit testing framework with a classic
|
11
|
+
test-case/test-unit style.
|
12
|
+
|
13
|
+
resources:
|
14
|
+
home: http://rubyworks.github.com/citron
|
15
|
+
code: http://github.com/rubyworks/citron
|
16
|
+
|
17
|
+
repositories:
|
18
|
+
upstream: git://github.com/proutils/citron.git
|
19
|
+
|
20
|
+
copyrights:
|
21
|
+
- 2011 Thomas Sawyer (BSD-2-Clause)
|
22
|
+
|
23
|
+
requirements:
|
24
|
+
- test
|
25
|
+
- ae
|
26
|
+
- detroit (build)
|
27
|
+
- reap (build)
|
28
|
+
- qed (test)
|
29
|
+
|
data/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# Citron
|
2
|
+
|
3
|
+
<table>
|
4
|
+
<tr><td><b>Author </b></td><td>Thomas Sawyer</td></tr>
|
5
|
+
<tr><td><b>License </b></td><td>FreeBSD</td></tr>
|
6
|
+
<tr><td><b>Copyright</b></td><td>(c) 2011 Thomas Sawyer, Rubyworks</td></tr>
|
7
|
+
</table>
|
8
|
+
|
9
|
+
## Description
|
10
|
+
|
11
|
+
Citron is a classic unit test framework. It defines a simple
|
12
|
+
domain language for create classic-style tests.
|
13
|
+
|
14
|
+
## Example
|
15
|
+
|
16
|
+
Here's a fun example.
|
17
|
+
|
18
|
+
``` ruby
|
19
|
+
TestCase "Show them how to Beat It" do
|
20
|
+
|
21
|
+
# fail
|
22
|
+
test "show them how to funky" do
|
23
|
+
"funky".assert != "funky"
|
24
|
+
end
|
25
|
+
|
26
|
+
# pass
|
27
|
+
test "show them what's right" do
|
28
|
+
"right".assert == "right"
|
29
|
+
end
|
30
|
+
|
31
|
+
# error
|
32
|
+
test "no one wants to be defeated" do
|
33
|
+
raise SyntaxError
|
34
|
+
end
|
35
|
+
|
36
|
+
# todo
|
37
|
+
test "better do what you can" do
|
38
|
+
raise NotImplementedError
|
39
|
+
end
|
40
|
+
|
41
|
+
# omit
|
42
|
+
test "just beat it" do
|
43
|
+
e = NotImplementedError.new
|
44
|
+
e.set_assertion(true)
|
45
|
+
raise e
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
## License
|
52
|
+
|
53
|
+
Copyright (c) 2011 Thomas Sawyer, Rubyworks
|
54
|
+
|
55
|
+
Citron is distributed according to the terms of the FreeBSD license.
|
56
|
+
|
57
|
+
See COPYING.rd for details.
|
58
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/lib/citron.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Citron
|
2
|
+
$TEST_SUITE ||= []
|
3
|
+
|
4
|
+
require 'citron/test_case'
|
5
|
+
require 'citron/test_unit'
|
6
|
+
require 'citron/test_advice'
|
7
|
+
require 'citron/test_setup'
|
8
|
+
end
|
9
|
+
|
10
|
+
module Test
|
11
|
+
extend self
|
12
|
+
|
13
|
+
# Define a general test case.
|
14
|
+
def Case(label, &block)
|
15
|
+
$TEST_SUITE << Citron::TestCase.new(nil, :label=>label, &block)
|
16
|
+
end
|
17
|
+
|
18
|
+
alias :TestCase :Case
|
19
|
+
alias :test_case :Case
|
20
|
+
alias :case :Case
|
21
|
+
end
|
22
|
+
|
23
|
+
extend Test
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Citron
|
2
|
+
|
3
|
+
# Test Advice
|
4
|
+
class TestAdvice
|
5
|
+
|
6
|
+
# The test case to which this advice belongs.
|
7
|
+
#attr :context
|
8
|
+
|
9
|
+
#
|
10
|
+
attr :table
|
11
|
+
|
12
|
+
# New case instance.
|
13
|
+
def initialize
|
14
|
+
@table = Hash.new{ |h,k| h[k] = {} }
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
def initialize_copy(original)
|
19
|
+
@table = original.table.clone
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
def [](type)
|
24
|
+
@table[type.to_sym]
|
25
|
+
end
|
26
|
+
|
27
|
+
=begin
|
28
|
+
#
|
29
|
+
#def teardown=(procedure)
|
30
|
+
# @teardown = procedure
|
31
|
+
#end
|
32
|
+
|
33
|
+
# Setup.
|
34
|
+
def setup(scope=nil)
|
35
|
+
if scope
|
36
|
+
scope.instance_eval(&@setup)
|
37
|
+
else
|
38
|
+
@setup
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Teardown.
|
43
|
+
def teardown(scope=nil)
|
44
|
+
if scope
|
45
|
+
scope.instance_eval(&@teardown) if @teardown
|
46
|
+
else
|
47
|
+
@teardown
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns the description with newlines removed.
|
52
|
+
def to_s
|
53
|
+
description.gsub(/\n/, ' ')
|
54
|
+
end
|
55
|
+
=end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,297 @@
|
|
1
|
+
#require 'citron/pending'
|
2
|
+
#require 'citron/test_context'
|
3
|
+
require 'citron/test_advice'
|
4
|
+
require 'citron/test_setup'
|
5
|
+
|
6
|
+
module Citron
|
7
|
+
|
8
|
+
# Test Case encapsulates a collection of
|
9
|
+
# unit tests organized into groups of contexts.
|
10
|
+
#
|
11
|
+
class TestCase
|
12
|
+
|
13
|
+
# The parent context in which this case resides.
|
14
|
+
attr :context
|
15
|
+
|
16
|
+
# Brief description of the test case.
|
17
|
+
attr :label
|
18
|
+
|
19
|
+
# List of tests and sub-cases.
|
20
|
+
attr :tests
|
21
|
+
|
22
|
+
# The setup and teardown advice.
|
23
|
+
attr :setup
|
24
|
+
|
25
|
+
# Advice are labeled procedures, such as before
|
26
|
+
# and after advice.
|
27
|
+
attr :advice
|
28
|
+
|
29
|
+
# Module for evaluating tests.
|
30
|
+
attr :scope
|
31
|
+
|
32
|
+
# A test case +target+ is a class or module.
|
33
|
+
#
|
34
|
+
# @param [TestSuite] context
|
35
|
+
# The parent case to which this case belongs.
|
36
|
+
#
|
37
|
+
def initialize(context, settings={}, &block)
|
38
|
+
if context
|
39
|
+
@context = context
|
40
|
+
@advice = context.advice.clone
|
41
|
+
else
|
42
|
+
@context = nil
|
43
|
+
@advice = TestAdvice.new
|
44
|
+
end
|
45
|
+
|
46
|
+
@label = settings[:label]
|
47
|
+
@setup = settings[:setup]
|
48
|
+
@skip = settings[:skip]
|
49
|
+
|
50
|
+
@tests = []
|
51
|
+
|
52
|
+
domain = DSL.new(self, &block)
|
53
|
+
@scope = Module.new
|
54
|
+
@scope.extend domain
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
def <<(test_object)
|
59
|
+
@tests << test_object
|
60
|
+
end
|
61
|
+
|
62
|
+
# Iterate over each test and subcase.
|
63
|
+
def each(&block)
|
64
|
+
tests.each(&block)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Number of tests plus subcases.
|
68
|
+
def size
|
69
|
+
tests.size
|
70
|
+
end
|
71
|
+
|
72
|
+
# Subclasses of TestCase can override this to describe
|
73
|
+
# the type of test case they define.
|
74
|
+
def type
|
75
|
+
'Case'
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
def to_s
|
80
|
+
"#{type}: " + @label.to_s
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
def skip?
|
85
|
+
@skip
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
def skip=(boolean)
|
90
|
+
@skip = !!boolean
|
91
|
+
end
|
92
|
+
|
93
|
+
# Run test in the context of this case.
|
94
|
+
#
|
95
|
+
# @param [TestProc] test
|
96
|
+
# The test unit to run.
|
97
|
+
#
|
98
|
+
def run(test, &block)
|
99
|
+
advice[:before].each do |matches, block|
|
100
|
+
if matches.all?{ |match| test.match?(match) }
|
101
|
+
scope.instance_exec(test, &block) #block.call(unit)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
block.call
|
106
|
+
|
107
|
+
advice[:after].each do |matches, block|
|
108
|
+
if matches.all?{ |match| test.match?(match) }
|
109
|
+
scope.instance_exec(test, &block) #block.call(unit)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
#
|
115
|
+
#--
|
116
|
+
# TODO: Change so that the scope is the DSL
|
117
|
+
# and ** includes the DSL of the context ** !!!
|
118
|
+
#++
|
119
|
+
#def scope
|
120
|
+
# @scope ||= (
|
121
|
+
# scope = Object.new
|
122
|
+
# scope.extend(domain)
|
123
|
+
# scope
|
124
|
+
# )
|
125
|
+
#end
|
126
|
+
|
127
|
+
#
|
128
|
+
class DSL < Module
|
129
|
+
|
130
|
+
#
|
131
|
+
def initialize(testcase, &code)
|
132
|
+
@_case = testcase
|
133
|
+
@_setup = testcase.setup
|
134
|
+
|
135
|
+
if testcase.context
|
136
|
+
extend(testcase.context.scope)
|
137
|
+
end
|
138
|
+
|
139
|
+
module_eval(&code)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Create a sub-case.
|
143
|
+
#--
|
144
|
+
# @TODO: Instead of resuing TestCase can we have a TestContext
|
145
|
+
# that more generically mimics it's context context?
|
146
|
+
#++
|
147
|
+
def Context(label, &block)
|
148
|
+
settings = {
|
149
|
+
:label => label,
|
150
|
+
:setup => @_setup
|
151
|
+
}
|
152
|
+
testcase = TestCase.new(@_case, settings, &block)
|
153
|
+
@_case.tests << testcase
|
154
|
+
testcase
|
155
|
+
end
|
156
|
+
alias_method :context, :Context
|
157
|
+
|
158
|
+
# Create a test.
|
159
|
+
def Test(label=nil, &procedure)
|
160
|
+
settings = {
|
161
|
+
:label => label,
|
162
|
+
:setup => @_setup
|
163
|
+
}
|
164
|
+
testunit = TestUnit.new(@_case, settings, &procedure)
|
165
|
+
if procedure.arity == 0
|
166
|
+
@_case.tests << testunit
|
167
|
+
else
|
168
|
+
@_test = testunit
|
169
|
+
end
|
170
|
+
testunit
|
171
|
+
end
|
172
|
+
alias_method :test, :Test
|
173
|
+
|
174
|
+
#
|
175
|
+
#
|
176
|
+
#
|
177
|
+
def Ok(*args)
|
178
|
+
test = @_test
|
179
|
+
test.arguments = args
|
180
|
+
@_case << test
|
181
|
+
@_test = nil
|
182
|
+
return test
|
183
|
+
end
|
184
|
+
|
185
|
+
#
|
186
|
+
#
|
187
|
+
#
|
188
|
+
def No(*args)
|
189
|
+
test = @_test
|
190
|
+
test.arguments = args
|
191
|
+
test.negate = true
|
192
|
+
@_case << test
|
193
|
+
@_test = nil
|
194
|
+
return test
|
195
|
+
end
|
196
|
+
|
197
|
+
# Setup is used to set things up for each unit test.
|
198
|
+
# The setup procedure is run before each unit.
|
199
|
+
#
|
200
|
+
# @param [String] description
|
201
|
+
# A brief description of what the setup procedure sets-up.
|
202
|
+
#
|
203
|
+
def Setup(description=nil, &procedure)
|
204
|
+
if procedure
|
205
|
+
@_setup = TestSetup.new(@_case, description, &procedure)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
alias_method :setup, :Setup
|
210
|
+
|
211
|
+
#alias_method :Concern, :Setup
|
212
|
+
#alias_method :concern, :Setup
|
213
|
+
|
214
|
+
#alias_method :Subject, :Setup
|
215
|
+
#alias_method :subject, :Setup
|
216
|
+
|
217
|
+
# Teardown procedure is used to clean-up after each unit test.
|
218
|
+
#
|
219
|
+
def Teardown(&procedure)
|
220
|
+
@_setup.teardown = procedure
|
221
|
+
end
|
222
|
+
|
223
|
+
alias_method :teardown, :Teardown
|
224
|
+
|
225
|
+
# Define a _complex_ before procedure. The #before method allows
|
226
|
+
# before procedures to be defined that are triggered by a match
|
227
|
+
# against the unit's target method name or _aspect_ description.
|
228
|
+
# This allows groups of tests to be defined that share special
|
229
|
+
# setup code.
|
230
|
+
#
|
231
|
+
# @example
|
232
|
+
# Method :puts do
|
233
|
+
# Test "standard output (@stdout)" do
|
234
|
+
# puts "Hello"
|
235
|
+
# end
|
236
|
+
#
|
237
|
+
# Before /@stdout/ do
|
238
|
+
# $stdout = StringIO.new
|
239
|
+
# end
|
240
|
+
#
|
241
|
+
# After /@stdout/ do
|
242
|
+
# $stdout = STDOUT
|
243
|
+
# end
|
244
|
+
# end
|
245
|
+
#
|
246
|
+
# @param [Array<Symbol,Regexp>] matches
|
247
|
+
# List of match critera that must _all_ be matched
|
248
|
+
# to trigger the before procedure.
|
249
|
+
#
|
250
|
+
def Before(*matches, &procedure)
|
251
|
+
@_case.advice[:before][matches] = procedure
|
252
|
+
end
|
253
|
+
|
254
|
+
alias_method :before, :Before
|
255
|
+
|
256
|
+
# Define a _complex_ after procedure. The #before method allows
|
257
|
+
# before procedures to be defined that are triggered by a match
|
258
|
+
# against the unit's target method name or _aspect_ description.
|
259
|
+
# This allows groups of tests to be defined that share special
|
260
|
+
# teardown code.
|
261
|
+
#
|
262
|
+
# @example
|
263
|
+
# Method :puts do
|
264
|
+
# Test "standard output (@stdout)" do
|
265
|
+
# puts "Hello"
|
266
|
+
# end
|
267
|
+
#
|
268
|
+
# Before /@stdout/ do
|
269
|
+
# $stdout = StringIO.new
|
270
|
+
# end
|
271
|
+
#
|
272
|
+
# After /@stdout/ do
|
273
|
+
# $stdout = STDOUT
|
274
|
+
# end
|
275
|
+
# end
|
276
|
+
#
|
277
|
+
# @param [Array<Symbol,Regexp>] matches
|
278
|
+
# List of match critera that must _all_ be matched
|
279
|
+
# to trigger the after procedure.
|
280
|
+
#
|
281
|
+
def After(*matches, &procedure)
|
282
|
+
@_case.advice[:after][matches] = procedure
|
283
|
+
end
|
284
|
+
|
285
|
+
alias_method :after, :After
|
286
|
+
|
287
|
+
# Mark a test or testcase to be omitted.
|
288
|
+
#
|
289
|
+
def Omit(test_obect)
|
290
|
+
test_object.omit = true
|
291
|
+
end
|
292
|
+
|
293
|
+
end
|
294
|
+
|
295
|
+
end
|
296
|
+
|
297
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Citron
|
2
|
+
|
3
|
+
# Test Setup/Concern - Setup and Teardown code.
|
4
|
+
class TestSetup
|
5
|
+
|
6
|
+
# The test case to which this advice belong.
|
7
|
+
attr :context
|
8
|
+
|
9
|
+
# A brief description of this concern.
|
10
|
+
attr :label
|
11
|
+
|
12
|
+
# The setup procedure.
|
13
|
+
attr :setup
|
14
|
+
|
15
|
+
# The teardown procedure.
|
16
|
+
attr :teardown
|
17
|
+
|
18
|
+
# New case instance.
|
19
|
+
def initialize(context, label, options={}, &setup)
|
20
|
+
@context = context
|
21
|
+
@label = label.to_s
|
22
|
+
@setup = [setup].flatten
|
23
|
+
@teardown = []
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
def teardown=(procedure)
|
28
|
+
@teardown = [procedure]
|
29
|
+
end
|
30
|
+
|
31
|
+
# Setup.
|
32
|
+
def run_setup(scope)
|
33
|
+
setup.each do |proc|
|
34
|
+
scope.instance_eval(&proc)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Teardown.
|
39
|
+
def run_teardown(scope)
|
40
|
+
teardown.each do |proc|
|
41
|
+
scope.instance_eval(&proc)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns the description with newlines removed.
|
46
|
+
def to_s
|
47
|
+
label.gsub(/\n/, ' ')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module Citron
|
2
|
+
|
3
|
+
#
|
4
|
+
class TestUnit
|
5
|
+
|
6
|
+
# New unit test procedure.
|
7
|
+
#
|
8
|
+
def initialize(context, options={}, &procedure)
|
9
|
+
@context = context
|
10
|
+
|
11
|
+
@setup = options[:setup]
|
12
|
+
@label = options[:label]
|
13
|
+
@skip = options[:skip]
|
14
|
+
|
15
|
+
@procedure = procedure
|
16
|
+
@tested = false
|
17
|
+
end
|
18
|
+
|
19
|
+
public
|
20
|
+
|
21
|
+
# The parent testcase to which this test belongs.
|
22
|
+
attr :context
|
23
|
+
|
24
|
+
#
|
25
|
+
alias :parent :context
|
26
|
+
|
27
|
+
# Setup and teardown procedures.
|
28
|
+
attr :setup
|
29
|
+
|
30
|
+
# Description of test.
|
31
|
+
attr :label
|
32
|
+
|
33
|
+
# Test procedure, in which test assertions should be made.
|
34
|
+
attr :procedure
|
35
|
+
|
36
|
+
# The before and after advice from the context.
|
37
|
+
def advice
|
38
|
+
context.advice
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
def type
|
43
|
+
'Unit'
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
def skip? ; @skip ; end
|
48
|
+
|
49
|
+
#
|
50
|
+
def skip=(boolean)
|
51
|
+
@skip = !!boolean
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
def tested?
|
56
|
+
@tested
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
def tested=(boolean)
|
61
|
+
@tested = boolean
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
def to_s
|
66
|
+
label.to_s
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
def setup
|
71
|
+
@setup
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
alias :subtext :setup
|
76
|
+
|
77
|
+
#
|
78
|
+
def scope
|
79
|
+
context.scope
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
def arguments
|
84
|
+
@arguments
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
def arguments=(args)
|
89
|
+
@arguments = args
|
90
|
+
end
|
91
|
+
|
92
|
+
# TODO: how to handle negated tests?
|
93
|
+
def negate
|
94
|
+
@negate
|
95
|
+
end
|
96
|
+
|
97
|
+
#
|
98
|
+
def negate=(boolean)
|
99
|
+
@negate = !!boolean
|
100
|
+
end
|
101
|
+
|
102
|
+
#
|
103
|
+
def to_proc
|
104
|
+
lambda{ call }
|
105
|
+
end
|
106
|
+
|
107
|
+
#
|
108
|
+
def match?(match)
|
109
|
+
match == target || match === description
|
110
|
+
end
|
111
|
+
|
112
|
+
#
|
113
|
+
def call
|
114
|
+
context.run(self) do
|
115
|
+
setup.run_setup(scope) if setup
|
116
|
+
scope.instance_exec(*arguments, &procedure)
|
117
|
+
setup.run_teardown(scope) if setup
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
data/try/case_example.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
TestCase "Show them how to Beat It" do
|
2
|
+
|
3
|
+
# will fail
|
4
|
+
test "show them how to funky" do
|
5
|
+
"funky".assert != "funky"
|
6
|
+
end
|
7
|
+
|
8
|
+
# will pass
|
9
|
+
test "show them what's right" do
|
10
|
+
"right".assert == "right"
|
11
|
+
end
|
12
|
+
|
13
|
+
# will error
|
14
|
+
test "no one wants to be defeated" do
|
15
|
+
raise SyntaxError
|
16
|
+
end
|
17
|
+
|
18
|
+
# pending
|
19
|
+
test "better do what you can" do
|
20
|
+
raise NotImplementedError
|
21
|
+
end
|
22
|
+
|
23
|
+
# omit
|
24
|
+
test "just beat it" do
|
25
|
+
e = NotImplementedError.new
|
26
|
+
e.set_assertion(true)
|
27
|
+
raise e
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: citron
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Thomas Sawyer
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-07-29 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: test
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
type: :runtime
|
25
|
+
version_requirements: *id001
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: ae
|
28
|
+
prerelease: false
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id002
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: detroit
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id003
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: reap
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
type: :development
|
58
|
+
version_requirements: *id004
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: qed
|
61
|
+
prerelease: false
|
62
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
type: :development
|
69
|
+
version_requirements: *id005
|
70
|
+
description: Citron is a unit testing framework with a classic test-case/test-unit style.
|
71
|
+
email:
|
72
|
+
- transfire@gmail.com
|
73
|
+
executables: []
|
74
|
+
|
75
|
+
extensions: []
|
76
|
+
|
77
|
+
extra_rdoc_files:
|
78
|
+
- LICENSE.txt
|
79
|
+
- COPYING.rdoc
|
80
|
+
- README.md
|
81
|
+
files:
|
82
|
+
- .gemspec
|
83
|
+
- .gitignore
|
84
|
+
- .ruby
|
85
|
+
- .yardopts
|
86
|
+
- Assembly
|
87
|
+
- COPYING.rdoc
|
88
|
+
- LICENSE.txt
|
89
|
+
- MANIFEST
|
90
|
+
- PROFILE
|
91
|
+
- README.md
|
92
|
+
- VERSION
|
93
|
+
- lib/citron.rb
|
94
|
+
- lib/citron/test_advice.rb
|
95
|
+
- lib/citron/test_case.rb
|
96
|
+
- lib/citron/test_setup.rb
|
97
|
+
- lib/citron/test_unit.rb
|
98
|
+
- try/case_example.rb
|
99
|
+
homepage: http://rubyworks.github.com/citron
|
100
|
+
licenses: []
|
101
|
+
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options: []
|
104
|
+
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: "0"
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: "0"
|
119
|
+
requirements: []
|
120
|
+
|
121
|
+
rubyforge_project:
|
122
|
+
rubygems_version: 1.8.2
|
123
|
+
signing_key:
|
124
|
+
specification_version: 3
|
125
|
+
summary: Classic Unit-style Test Framework
|
126
|
+
test_files: []
|
127
|
+
|