uncool 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY.rdoc +10 -0
- data/LICENSE +204 -0
- data/README.rdoc +90 -0
- data/bin/uncool-ruby +3 -0
- data/lib/uncool.rb +8 -0
- data/lib/uncool/analysis.rb +94 -0
- data/lib/uncool/app.rb +85 -0
- data/lib/uncool/autolog.rb +14 -0
- data/lib/uncool/cli.rb +168 -0
- data/lib/uncool/generator/abstract.rb +95 -0
- data/lib/uncool/generator/ko.rb +32 -0
- data/lib/uncool/generator/lemon.rb +31 -0
- data/lib/uncool/generator/qed.rb +30 -0
- data/lib/uncool/lemon.svg +39 -0
- data/lib/uncool/log.rb +17 -0
- data/lib/uncool/meta/data.rb +29 -0
- data/lib/uncool/meta/gemfile.yml +11 -0
- data/lib/uncool/meta/profile.yml +18 -0
- data/lib/uncool/report.rb +79 -0
- data/lib/uncool/report.rhtml +37 -0
- data/lib/uncool/syntax/ko.rb +9 -0
- data/lib/uncool/trace.rb +70 -0
- data/lib/uncool/unit.rb +73 -0
- data/meta/data.rb +29 -0
- data/meta/gemfile.yml +11 -0
- data/meta/profile.yml +18 -0
- data/test/features/coverage_feature.rb +14 -0
- data/test/features/generate_feature.rb +16 -0
- data/test/fixtures/example.rb +20 -0
- data/test/fixtures/example_run.rb +7 -0
- metadata +169 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
#if conf = Dir['{,.}config/uncool.yml'].first
|
2
|
+
# require 'yaml'
|
3
|
+
# opts = YAML.load(File.new(conf))
|
4
|
+
# output = opts['output'] || opts['out']
|
5
|
+
# namespaces = opts['spaces'] || opts['namespaces'] || opts['ns']
|
6
|
+
#else
|
7
|
+
output = ENV['out']
|
8
|
+
namespaces = ENV['ns'].split(',')
|
9
|
+
#end
|
10
|
+
|
11
|
+
require 'uncool/log'
|
12
|
+
|
13
|
+
Uncool.log(:output=>output, :namespaces=>namespaces)
|
14
|
+
|
data/lib/uncool/cli.rb
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
module Uncool
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
# CLI Interface handle all lemon sub-commands.
|
6
|
+
class CLI
|
7
|
+
|
8
|
+
COMMANDS = ['coverage', 'generate']
|
9
|
+
|
10
|
+
#
|
11
|
+
def self.run(argv=ARGV)
|
12
|
+
new.run(argv)
|
13
|
+
end
|
14
|
+
|
15
|
+
#
|
16
|
+
def initialize(argv=ARGV)
|
17
|
+
@options = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
def options
|
22
|
+
@options
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
def run(argv)
|
27
|
+
if argv.include?('-g') or argv.include?('--generate')
|
28
|
+
cmd = 'generate'
|
29
|
+
else
|
30
|
+
cmd = 'coverage'
|
31
|
+
end
|
32
|
+
cmd = COMMANDS.find{ |c| /^#{cmd}/ =~ c }
|
33
|
+
__send__("#{cmd}_parse", argv)
|
34
|
+
__send__("#{cmd}", argv)
|
35
|
+
end
|
36
|
+
|
37
|
+
# C O V E R A G E
|
38
|
+
|
39
|
+
#
|
40
|
+
def coverage(scripts)
|
41
|
+
require 'uncool/app'
|
42
|
+
|
43
|
+
app = App.new(options)
|
44
|
+
|
45
|
+
app.log
|
46
|
+
|
47
|
+
scripts.each do |file|
|
48
|
+
require(file)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
def coverage_parse(argv)
|
54
|
+
option_namespaces
|
55
|
+
option_private
|
56
|
+
option_output
|
57
|
+
option_format
|
58
|
+
#option_uncovered
|
59
|
+
option_loadpath
|
60
|
+
option_requires
|
61
|
+
|
62
|
+
option_parser.parse!(argv)
|
63
|
+
end
|
64
|
+
|
65
|
+
# G E N E R A T E
|
66
|
+
|
67
|
+
#
|
68
|
+
def generate(scripts)
|
69
|
+
require 'uncool/app'
|
70
|
+
|
71
|
+
app = App.new(options)
|
72
|
+
|
73
|
+
output = app.generate(scripts)
|
74
|
+
|
75
|
+
$stdout.puts(output)
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
def generate_parse(argv)
|
80
|
+
option_generate
|
81
|
+
option_namespaces
|
82
|
+
option_framework
|
83
|
+
option_private
|
84
|
+
option_loadpath
|
85
|
+
option_requires
|
86
|
+
|
87
|
+
option_parser.parse!(argv)
|
88
|
+
end
|
89
|
+
|
90
|
+
# P A R S E R O P T I O N S
|
91
|
+
|
92
|
+
def option_namespaces
|
93
|
+
option_parser.on('-n', '--namespace NAME', 'add a namespace to output') do |name|
|
94
|
+
options[:namespaces] ||= []
|
95
|
+
options[:namespaces] << name
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def option_framework
|
100
|
+
option_parser.on('-f', '--framework NAME', 'framework syntax to output') do |name|
|
101
|
+
options[:framework] = name.to_sym
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# TODO: How feasible is it to parse tests of various frameworks to check "writ" coverage?
|
106
|
+
#def option_uncovered
|
107
|
+
# option_parser.on('-u', '--uncovered', 'include only uncovered tests') do
|
108
|
+
# options[:uncovered] = true
|
109
|
+
# end
|
110
|
+
#end
|
111
|
+
|
112
|
+
def option_private
|
113
|
+
option_parser.on('-p', '--private', 'include private and protected methods') do
|
114
|
+
options[:private] = true
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def option_output
|
119
|
+
option_parser.on('-o', '--output DIRECTORY', 'log directory') do |dir|
|
120
|
+
options[:output] = dir
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def option_format
|
125
|
+
option_parser.on('--format', '-f NAME', 'output format') do |name|
|
126
|
+
options[:format] = name
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def option_loadpath
|
131
|
+
option_parser.on("-I PATH" , 'add directory to $LOAD_PATH') do |path|
|
132
|
+
paths = path.split(/[:;]/)
|
133
|
+
options[:loadpath] ||= []
|
134
|
+
options[:loadpath].concat(paths)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def option_requires
|
139
|
+
option_parser.on("-r FILE" , 'require file(s) (before doing anything else)') do |files|
|
140
|
+
files = files.split(/[:;]/)
|
141
|
+
options[:requires] ||= []
|
142
|
+
options[:requires].concat(files)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def option_generate
|
147
|
+
option_parser.on('-g' , '--generate', 'code generation mode') do
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def option_parser
|
152
|
+
@option_parser ||= (
|
153
|
+
OptionParser.new do |opt|
|
154
|
+
opt.on_tail("--debug" , 'turn on debugging mode') do
|
155
|
+
$DEBUG = true
|
156
|
+
end
|
157
|
+
opt.on_tail('-h', '--help', 'display this help messae') do
|
158
|
+
puts opt
|
159
|
+
exit 0
|
160
|
+
end
|
161
|
+
end
|
162
|
+
)
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'uncool/unit'
|
2
|
+
|
3
|
+
module Uncool
|
4
|
+
|
5
|
+
# Generator base class.
|
6
|
+
class GeneratorAbstract
|
7
|
+
|
8
|
+
def initialize(options={})
|
9
|
+
@namespaces = options[:namespaces] || []
|
10
|
+
@checklist = options[:checklist]
|
11
|
+
@options = options || {}
|
12
|
+
end
|
13
|
+
|
14
|
+
#
|
15
|
+
def namespaces
|
16
|
+
@namespaces
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
def checklist
|
21
|
+
@checklist ||= default_checklist
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
def targets
|
26
|
+
@targets ||= namespaces.map{ |ns| eval(ns, TOPLEVEL_BINDING) }
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
def options
|
31
|
+
@options
|
32
|
+
end
|
33
|
+
|
34
|
+
# Include already covered methods.
|
35
|
+
def covered?
|
36
|
+
options[:covered]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Include private and protected methods?
|
40
|
+
def private?
|
41
|
+
options[:private]
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
def mapping
|
46
|
+
checklist.sort.group_by{ |mp, yes| mp.target }
|
47
|
+
end
|
48
|
+
|
49
|
+
# Override this method in subclasses.
|
50
|
+
def generate
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
def default_checklist
|
55
|
+
list = []
|
56
|
+
targets.each do |target|
|
57
|
+
target.public_instance_methods(false).each do |meth|
|
58
|
+
list << Unit.new(target, meth)
|
59
|
+
end
|
60
|
+
if private?
|
61
|
+
target.protected_instance_methods(false).each do |meth|
|
62
|
+
list << Unit.new(target, meth, :access=>:protected)
|
63
|
+
end
|
64
|
+
target.private_instance_methods(false).each do |meth|
|
65
|
+
list << Unit.new(target, meth, :access=>:private)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
list
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
#
|
78
|
+
# # Generate code template.
|
79
|
+
# #
|
80
|
+
# def generate
|
81
|
+
# code = []
|
82
|
+
# system.each do |ofmod|
|
83
|
+
# next if ofmod.base.is_a?(Lemon::Test::Suite)
|
84
|
+
# code << "TestCase #{ofmod.base} do"
|
85
|
+
# ofmod.class_methods(public_only?).each do |meth|
|
86
|
+
# code << "\n MetaUnit :#{meth} => '' do\n raise Pending\n end"
|
87
|
+
# end
|
88
|
+
# ofmod.instance_methods(public_only?).each do |meth|
|
89
|
+
# code << "\n Unit :#{meth} => '' do\n raise Pending\n end"
|
90
|
+
# end
|
91
|
+
# code << "\nend\n"
|
92
|
+
# end
|
93
|
+
# code.join("\n")
|
94
|
+
# end
|
95
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'uncool/generator/abstract'
|
2
|
+
|
3
|
+
module Uncool
|
4
|
+
|
5
|
+
# KO test generator.
|
6
|
+
class GeneratorKO < GeneratorAbstract
|
7
|
+
|
8
|
+
#
|
9
|
+
def generate
|
10
|
+
code = []
|
11
|
+
mapping.each do |target, units|
|
12
|
+
#next if /Lemon::Test::Suite/ =~ target.to_s
|
13
|
+
code << "require 'lemon/syntax/ko'\n"
|
14
|
+
code << "testcase #{target} do"
|
15
|
+
units.each do |(unit, yes)|
|
16
|
+
next if unit.covered? and !covered?
|
17
|
+
next if unit.private? and !private?
|
18
|
+
if unit.function?
|
19
|
+
code << "\n metaunit :#{unit.method} do\n\n end"
|
20
|
+
else
|
21
|
+
code << "\n unit :#{unit.method} do\n\n end"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
code << "\nend\n"
|
25
|
+
end
|
26
|
+
code.join("\n")
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'uncool/generator/abstract'
|
2
|
+
|
3
|
+
module Uncool
|
4
|
+
|
5
|
+
# Lemon test generator.
|
6
|
+
class GeneratorLemon < GeneratorAbstract
|
7
|
+
|
8
|
+
#
|
9
|
+
def generate
|
10
|
+
code = []
|
11
|
+
mapping.each do |target, units|
|
12
|
+
#next if /Lemon::Test::Suite/ =~ target.to_s
|
13
|
+
code << "TestCase #{target} do"
|
14
|
+
units.each do |unit|
|
15
|
+
next if unit.covered? and !covered?
|
16
|
+
next if unit.private? and !private?
|
17
|
+
if unit.function?
|
18
|
+
code << "\n MetaUnit :#{unit.method} => '' do\n\n end"
|
19
|
+
else
|
20
|
+
code << "\n Unit :#{unit.method} => '' do\n\n end"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
code << "\nend\n"
|
24
|
+
end
|
25
|
+
code.join("\n")
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'uncool/generator/abstract'
|
2
|
+
|
3
|
+
module Uncool
|
4
|
+
|
5
|
+
# QED test generator.
|
6
|
+
class GeneratorQED < GeneratorAbstract
|
7
|
+
|
8
|
+
#
|
9
|
+
def generate
|
10
|
+
code = []
|
11
|
+
mapping.each do |target, units|
|
12
|
+
#next if /Lemon::Test::Suite/ =~ target.to_s
|
13
|
+
code << "= #{target}\n"
|
14
|
+
units.each do |(unit, yes)|
|
15
|
+
next if unit.covered? and !covered?
|
16
|
+
next if unit.private? and !private?
|
17
|
+
if unit.function?
|
18
|
+
code << "== ::#{unit.method}\n\n"
|
19
|
+
else
|
20
|
+
code << "== ##{unit.method}\n\n"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
code.join("\n")
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
2
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
3
|
+
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
4
|
+
<svg id="svg1" sodipodi:version="0.32" inkscape:version="0.38.1" width="400.00000pt" height="400.00000pt" sodipodi:docbase="/var/www/html/svg_gallery/svg/fruits" sodipodi:docname="lemon.svg" xmlns="http://www.w3.org/2000/svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:xlink="http://www.w3.org/1999/xlink">
|
5
|
+
<defs id="defs3">
|
6
|
+
<linearGradient id="linearGradient831">
|
7
|
+
<stop style="stop-color: rgb(255, 255, 0); stop-opacity: 1;" offset="0.0000000" id="stop832"/>
|
8
|
+
<stop style="stop-color: rgb(255, 227, 0); stop-opacity: 1;" offset="1.0000000" id="stop833"/>
|
9
|
+
</linearGradient>
|
10
|
+
<radialGradient xlink:href="#linearGradient831" id="radialGradient834" cx="0.33703703" cy="0.28358209" r="0.38183698" fx="0.33703703" fy="0.28358209"/>
|
11
|
+
</defs>
|
12
|
+
<sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="1.3195079" inkscape:cx="120.13018" inkscape:cy="204.09312" inkscape:window-width="910" inkscape:window-height="775" inkscape:window-x="119" inkscape:window-y="24"/>
|
13
|
+
<g id="g838">
|
14
|
+
<path style="fill: url(#radialGradient834) rgb(0, 0, 0); fill-rule: evenodd; stroke: rgb(0, 0, 0); stroke-width: 10; stroke-linejoin: round; stroke-dasharray: none;" d="M 68.708186,148.12939 C 228.69852,9.5810597 474.45687,146.15839 471.15810,289.65487 C 497.54826,315.22034 467.85933,343.25988 451.36548,350.68211 C 309.51838,525.51691 17.577254,382.02043 34.895796,237.69925 C 12.847026,174.32754 32.421718,155.55162 68.708186,148.12939 z " id="path827" sodipodi:nodetypes="ccccc"/>
|
15
|
+
<path style="fill-opacity: 0.133333; fill-rule: evenodd; stroke-width: 1pt;" d="M 44.330330,267.99099 C 65.580330,246.74099 68.080330,411.74099 378.08033,350.49099 C 371.83033,344.24099 338.08033,201.74099 458.08033,249.24099 C 504.95533,263.61599 358.70533,239.55349 410.58033,339.24099 C 418.62433,352.33698 416.20533,352.36599 438.08033,357.99099 C 305.58033,500.49099 46.830330,390.49099 44.330330,267.99099 z " id="path828" sodipodi:nodetypes="cccccc"/>
|
16
|
+
<path style="fill: rgb(255, 255, 255); fill-opacity: 0.7; fill-rule: evenodd; stroke-width: 1pt;" d="M 68.080330,155.83295 C 124.36962,90.490990 313.68568,43.977604 438.08033,203.33295 C 395.58033,249.58295 275.58033,49.582958 68.080330,155.83295 z " id="path829" sodipodi:nodetypes="ccc"/>
|
17
|
+
<path style="fill: rgb(255, 255, 255); fill-opacity: 0.7; fill-rule: evenodd; stroke-width: 1pt;" d="M 469.33033,295.49099 C 483.64639,312.99099 470.58033,332.99099 460.58033,330.49099 C 381.83033,309.24099 424.33033,287.99099 469.33033,295.49099 z " id="path830" sodipodi:nodetypes="ccc"/>
|
18
|
+
</g>
|
19
|
+
|
20
|
+
<rdf:RDF xmlns="http://web.resource.org/cc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
21
|
+
<Work rdf:about="">
|
22
|
+
<dc:title>Clipart by Nicu Buculei - pear</dc:title>
|
23
|
+
<dc:rights>
|
24
|
+
<Agent>
|
25
|
+
<dc:title>Nicu Buculei</dc:title>
|
26
|
+
</Agent>
|
27
|
+
</dc:rights>
|
28
|
+
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
29
|
+
<license rdf:resource="http://web.resource.org/cc/PublicDomain"/>
|
30
|
+
</Work>
|
31
|
+
|
32
|
+
<License rdf:about="http://web.resource.org/cc/PublicDomain">
|
33
|
+
<permits rdf:resource="http://web.resource.org/cc/Reproduction"/>
|
34
|
+
<permits rdf:resource="http://web.resource.org/cc/Distribution"/>
|
35
|
+
<permits rdf:resource="http://web.resource.org/cc/DerivativeWorks"/>
|
36
|
+
</License>
|
37
|
+
|
38
|
+
</rdf:RDF>
|
39
|
+
</svg>
|
data/lib/uncool/log.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'uncool/app'
|
2
|
+
|
3
|
+
module Uncool
|
4
|
+
|
5
|
+
# Run coverage trace and log results.
|
6
|
+
#
|
7
|
+
# targets = ENV['squeeze'].split(',')
|
8
|
+
# Lemon.log(targets, :output=>'log')
|
9
|
+
#
|
10
|
+
# NOTE: This sets up an at_exit routine.
|
11
|
+
def self.log(options={})
|
12
|
+
app = App.new(options)
|
13
|
+
app.log
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
Object.__send__(:remove_const, :VERSION) if Object.const_defined?(:VERSION) # becuase Ruby 1.8~ gets in the way
|
2
|
+
|
3
|
+
module Uncool
|
4
|
+
|
5
|
+
def self.__DIR__
|
6
|
+
File.dirname(__FILE__)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.gemfile
|
10
|
+
@gemfile ||= (
|
11
|
+
require 'yaml'
|
12
|
+
YAML.load(File.new(__DIR__ + '/gemfile.yml'))
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.profile
|
17
|
+
@profile ||= (
|
18
|
+
require 'yaml'
|
19
|
+
YAML.load(File.new(__DIR__ + '/profile.yml'))
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.const_missing(name)
|
24
|
+
name = name.to_s.downcase
|
25
|
+
gemfile[name] || profile[name]
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|