TwP-turn 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +23 -0
- data/README.txt +117 -0
- data/Rakefile +32 -0
- data/Release.txt +29 -0
- data/VERSION +1 -0
- data/bin/turn +4 -0
- data/lib/turn.rb +80 -0
- data/lib/turn/colorize.rb +29 -0
- data/lib/turn/command.rb +159 -0
- data/lib/turn/components/case.rb +98 -0
- data/lib/turn/components/method.rb +32 -0
- data/lib/turn/components/suite.rb +82 -0
- data/lib/turn/controller.rb +146 -0
- data/lib/turn/reporter.rb +56 -0
- data/lib/turn/reporters/dot_reporter.rb +78 -0
- data/lib/turn/reporters/marshal_reporter.rb +64 -0
- data/lib/turn/reporters/outline_reporter.rb +85 -0
- data/lib/turn/reporters/progress_reporter.rb +117 -0
- data/lib/turn/runners/crossrunner.rb +40 -0
- data/lib/turn/runners/isorunner.rb +129 -0
- data/lib/turn/runners/loadrunner.rb +48 -0
- data/lib/turn/runners/solorunner.rb +8 -0
- data/lib/turn/runners/testrunner.rb +154 -0
- data/test/test_example.rb +15 -0
- data/test/test_sample.rb +15 -0
- data/turn.gemspec +37 -0
- data/work/quicktest.rb +42 -0
- data/work/turn.rb +119 -0
- metadata +94 -0
@@ -0,0 +1,98 @@
|
|
1
|
+
module Turn
|
2
|
+
|
3
|
+
#
|
4
|
+
class TestCase
|
5
|
+
|
6
|
+
# Name of test case.
|
7
|
+
attr_accessor :name
|
8
|
+
|
9
|
+
# Test methods.
|
10
|
+
attr_accessor :tests
|
11
|
+
|
12
|
+
# Some runners marshal tests per file.
|
13
|
+
attr_accessor :files
|
14
|
+
|
15
|
+
#attr_accessor :count_passes
|
16
|
+
#attr_accessor :count_failures
|
17
|
+
#attr_accessor :count_errors
|
18
|
+
#attr_accessor :count_tests
|
19
|
+
|
20
|
+
# This can;t be calculated, so it must be
|
21
|
+
# assigned by the runner.
|
22
|
+
attr_accessor :count_assertions
|
23
|
+
|
24
|
+
# Holds dump of test output (optional depending on runner).
|
25
|
+
attr_accessor :message
|
26
|
+
|
27
|
+
# Command used to run test (optional depending on runner).
|
28
|
+
#attr_accessor :command
|
29
|
+
|
30
|
+
#
|
31
|
+
def initialize(name, *files)
|
32
|
+
@name = name
|
33
|
+
@files = (files.empty? ? [name] : files)
|
34
|
+
@tests = []
|
35
|
+
|
36
|
+
@message = nil
|
37
|
+
@count_assertions = 0
|
38
|
+
|
39
|
+
#@count_tests = 0
|
40
|
+
#@count_failures = 0
|
41
|
+
#@count_errors = 0
|
42
|
+
|
43
|
+
#@command = command
|
44
|
+
end
|
45
|
+
|
46
|
+
def new_test(name)
|
47
|
+
c = TestMethod.new(name)
|
48
|
+
@tests << c
|
49
|
+
c
|
50
|
+
end
|
51
|
+
|
52
|
+
# Whne used by a per-file runner.
|
53
|
+
#alias_method :file, :name
|
54
|
+
|
55
|
+
# Were there any errors?
|
56
|
+
def error?
|
57
|
+
count_errors != 0
|
58
|
+
end
|
59
|
+
|
60
|
+
# Were there any failures?
|
61
|
+
def fail?
|
62
|
+
count_failures != 0
|
63
|
+
end
|
64
|
+
|
65
|
+
# Did all tests/assertion pass?
|
66
|
+
def pass?
|
67
|
+
not(fail? or error?)
|
68
|
+
end
|
69
|
+
|
70
|
+
def count_tests
|
71
|
+
tests.size
|
72
|
+
end
|
73
|
+
|
74
|
+
def count_failures
|
75
|
+
sum = 0; tests.each{ |t| sum += 1 if t.fail? }; sum
|
76
|
+
end
|
77
|
+
|
78
|
+
def count_errors
|
79
|
+
sum = 0; tests.each{ |t| sum += 1 if t.error? }; sum
|
80
|
+
end
|
81
|
+
|
82
|
+
def count_passes
|
83
|
+
sum = 0; tests.each{ |t| sum += 1 if t.pass? }; sum
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
def counts
|
88
|
+
return count_tests, count_assertions, count_failures, count_errors
|
89
|
+
end
|
90
|
+
|
91
|
+
def message
|
92
|
+
tests.collect{ |t| t.message }.join("\n")
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Turn
|
2
|
+
|
3
|
+
#
|
4
|
+
class TestMethod
|
5
|
+
attr_accessor :name
|
6
|
+
attr_accessor :file
|
7
|
+
attr_accessor :message
|
8
|
+
|
9
|
+
def initialize(name)
|
10
|
+
@name = name
|
11
|
+
@fail = false
|
12
|
+
@error = false
|
13
|
+
@message = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def fail!(message=nil)
|
17
|
+
@fail, @error = true, false
|
18
|
+
@message = message if message
|
19
|
+
end
|
20
|
+
|
21
|
+
def error!(message=nil)
|
22
|
+
@fail, @error = false, true
|
23
|
+
@message = message if message
|
24
|
+
end
|
25
|
+
|
26
|
+
def fail? ; @fail ; end
|
27
|
+
def error? ; @error ; end
|
28
|
+
def pass? ; !(@fail or @error) ; end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Turn
|
2
|
+
|
3
|
+
#
|
4
|
+
class TestSuite
|
5
|
+
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
attr_accessor :name
|
9
|
+
attr_accessor :size
|
10
|
+
attr_accessor :cases
|
11
|
+
|
12
|
+
# This one can be set manually since it
|
13
|
+
# is not calculatable (beyond the case level).
|
14
|
+
attr_accessor :count_assertions
|
15
|
+
|
16
|
+
#
|
17
|
+
def initialize(name=nil)
|
18
|
+
@name = name
|
19
|
+
@size = nil
|
20
|
+
@cases = []
|
21
|
+
|
22
|
+
#@count_tests = nil
|
23
|
+
#@count_assertions = nil
|
24
|
+
#@count_failures = nil
|
25
|
+
#@count_errors = nil
|
26
|
+
#@count_passes = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
def new_case(name, *files)
|
31
|
+
c = TestCase.new(name, *files)
|
32
|
+
@cases << c
|
33
|
+
c
|
34
|
+
end
|
35
|
+
|
36
|
+
def count_failures
|
37
|
+
#@count_failures ||= (
|
38
|
+
sum = 0; each{ |c| sum += c.count_failures }; sum
|
39
|
+
#)
|
40
|
+
end
|
41
|
+
|
42
|
+
def count_errors
|
43
|
+
#@count_errors ||= (
|
44
|
+
sum = 0; each{ |c| sum += c.count_errors }; sum
|
45
|
+
#)
|
46
|
+
end
|
47
|
+
|
48
|
+
def count_passes
|
49
|
+
#@count_passes ||= (
|
50
|
+
sum = 0; each{ |c| sum += c.count_passes }; sum
|
51
|
+
#)
|
52
|
+
end
|
53
|
+
|
54
|
+
def count_tests
|
55
|
+
#@count_tests ||= (
|
56
|
+
sum = 0; each{ |c| sum += c.count_tests }; sum
|
57
|
+
#)
|
58
|
+
end
|
59
|
+
|
60
|
+
def count_assertions
|
61
|
+
#@count_assertions ||= (
|
62
|
+
sum = 0; each{ |c| sum += c.count_assertions }; sum
|
63
|
+
#)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Convenience methods --this is what is typcially wanted.
|
67
|
+
def counts
|
68
|
+
return count_tests, count_assertions, count_failures, count_errors
|
69
|
+
end
|
70
|
+
|
71
|
+
def each(&block)
|
72
|
+
@cases.each(&block)
|
73
|
+
end
|
74
|
+
|
75
|
+
def size
|
76
|
+
@size ||= @cases.size
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Turn
|
4
|
+
require 'turn/components/suite.rb'
|
5
|
+
require 'turn/components/case.rb'
|
6
|
+
require 'turn/components/method.rb'
|
7
|
+
|
8
|
+
require 'turn/reporters/outline_reporter'
|
9
|
+
require 'turn/reporters/marshal_reporter'
|
10
|
+
require 'turn/reporters/progress_reporter'
|
11
|
+
require 'turn/reporters/dot_reporter'
|
12
|
+
|
13
|
+
require 'turn/runners/testrunner'
|
14
|
+
require 'turn/runners/solorunner'
|
15
|
+
require 'turn/runners/crossrunner'
|
16
|
+
|
17
|
+
# = Controller
|
18
|
+
#
|
19
|
+
#--
|
20
|
+
# TODO: Add support to test run loggging.
|
21
|
+
#++
|
22
|
+
class Controller
|
23
|
+
|
24
|
+
# File glob pattern of tests to run.
|
25
|
+
# Can be an array of files/globs.
|
26
|
+
attr_accessor :tests
|
27
|
+
|
28
|
+
# Files globs to specially exclude.
|
29
|
+
attr_accessor :exclude
|
30
|
+
|
31
|
+
# Add these folders to the $LOAD_PATH.
|
32
|
+
attr_accessor :loadpath
|
33
|
+
|
34
|
+
# Libs to require when running tests.
|
35
|
+
attr_accessor :requires
|
36
|
+
|
37
|
+
# Instance of Reporter.
|
38
|
+
attr_accessor :reporter
|
39
|
+
|
40
|
+
# Insatance of Runner.
|
41
|
+
attr_accessor :runner
|
42
|
+
|
43
|
+
# Test against live install (i.e. Don't use loadpath option)
|
44
|
+
attr_accessor :live
|
45
|
+
|
46
|
+
# Log results? May be true/false or log file name. (TODO)
|
47
|
+
attr_accessor :log
|
48
|
+
|
49
|
+
# Verbose output?
|
50
|
+
attr_accessor :verbose
|
51
|
+
|
52
|
+
def verbose? ; @verbose ; end
|
53
|
+
def live? ; @live ; end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def initialize
|
58
|
+
yield(self) if block_given?
|
59
|
+
initialize_defaults
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
def initialize_defaults
|
64
|
+
@loadpath ||= ['lib']
|
65
|
+
@tests ||= "test/**/{test,}*{,test}"
|
66
|
+
@exclude ||= []
|
67
|
+
@reqiures ||= []
|
68
|
+
@live ||= false
|
69
|
+
@log ||= true
|
70
|
+
@reporter ||= OutlineReporter.new($stdout)
|
71
|
+
@runner ||= TestRunner.new
|
72
|
+
end
|
73
|
+
|
74
|
+
# Collect test configuation.
|
75
|
+
#def test_configuration(options={})
|
76
|
+
# #options = configure_options(options, 'test')
|
77
|
+
# #options['loadpath'] ||= metadata.loadpath
|
78
|
+
# options['tests'] ||= self.tests
|
79
|
+
# options['loadpath'] ||= self.loadpath
|
80
|
+
# options['requires'] ||= self.requires
|
81
|
+
# options['live'] ||= self.live
|
82
|
+
# options['exclude'] ||= self.exclude
|
83
|
+
# #options['tests'] = list_option(options['tests'])
|
84
|
+
# options['loadpath'] = list_option(options['loadpath'])
|
85
|
+
# options['exclude'] = list_option(options['exclude'])
|
86
|
+
# options['require'] = list_option(options['require'])
|
87
|
+
# return options
|
88
|
+
#end
|
89
|
+
|
90
|
+
#
|
91
|
+
def list_option(list)
|
92
|
+
case list
|
93
|
+
when nil
|
94
|
+
[]
|
95
|
+
when Array
|
96
|
+
list
|
97
|
+
else
|
98
|
+
list.split(/[:;]/)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
public
|
103
|
+
|
104
|
+
def loadpath=(paths)
|
105
|
+
@loadpath = list_option(paths)
|
106
|
+
end
|
107
|
+
|
108
|
+
def exclude=(paths)
|
109
|
+
@exclude = list_option(paths)
|
110
|
+
end
|
111
|
+
|
112
|
+
def requries=(paths)
|
113
|
+
@requries = list_option(paths)
|
114
|
+
end
|
115
|
+
|
116
|
+
def files
|
117
|
+
@files ||= (
|
118
|
+
fs = tests.map do |t|
|
119
|
+
File.directory?(t) ? Dir[File.join(t, '**', '*')] : Dir[t]
|
120
|
+
end
|
121
|
+
fs = fs.flatten.reject{ |f| File.directory?(f) }
|
122
|
+
ex = exclude.map do |x|
|
123
|
+
File.directory?(x) ? Dir[File.join(x, '**', '*')] : Dir[x]
|
124
|
+
end
|
125
|
+
ex = ex.flatten.reject{ |f| File.directory?(f) }
|
126
|
+
(fs - ex).uniq
|
127
|
+
)
|
128
|
+
end
|
129
|
+
|
130
|
+
def start
|
131
|
+
@files = nil # reset files just in case
|
132
|
+
|
133
|
+
if files.empty?
|
134
|
+
$stderr.puts "No tests."
|
135
|
+
return
|
136
|
+
end
|
137
|
+
|
138
|
+
testrun = runner.new(self)
|
139
|
+
|
140
|
+
testrun.start
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Turn
|
2
|
+
require 'turn/colorize'
|
3
|
+
|
4
|
+
# = Reporter
|
5
|
+
#
|
6
|
+
# There are two distinct way in which a report may be utilized
|
7
|
+
# by a Runner: per-call or per-file. The method #pass, #fail
|
8
|
+
# and #error are generic, and will be used in either case.
|
9
|
+
# A per-call runner will use all the methods of a Reporter,
|
10
|
+
# while a per-file runner will use start_case per file,
|
11
|
+
# and will not use the start_test and finish_test methods,
|
12
|
+
# since those are beyond it's grainularity.
|
13
|
+
#
|
14
|
+
class Reporter
|
15
|
+
|
16
|
+
include Colorize
|
17
|
+
|
18
|
+
attr :io
|
19
|
+
|
20
|
+
def initialize(io)
|
21
|
+
@io = io || $stdout
|
22
|
+
end
|
23
|
+
|
24
|
+
# These methods are called in the process of running the tests.
|
25
|
+
|
26
|
+
def start_suite(suite)
|
27
|
+
end
|
28
|
+
|
29
|
+
def start_case(kase)
|
30
|
+
end
|
31
|
+
|
32
|
+
def start_test(test)
|
33
|
+
end
|
34
|
+
|
35
|
+
def pass(message=nil)
|
36
|
+
end
|
37
|
+
|
38
|
+
def fail(message=nil)
|
39
|
+
end
|
40
|
+
|
41
|
+
def error(message=nil)
|
42
|
+
end
|
43
|
+
|
44
|
+
def finish_test(test)
|
45
|
+
end
|
46
|
+
|
47
|
+
def finish_case(kase)
|
48
|
+
end
|
49
|
+
|
50
|
+
def finish_suite(suite)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'turn/reporter'
|
2
|
+
|
3
|
+
module Turn
|
4
|
+
|
5
|
+
# = Traditional Dot Reporter
|
6
|
+
#
|
7
|
+
class DotReporter < Reporter
|
8
|
+
|
9
|
+
def start_suite(suite)
|
10
|
+
@time = Time.now
|
11
|
+
io.puts "Loaded suite #{suite.name}"
|
12
|
+
io.puts "Started"
|
13
|
+
end
|
14
|
+
|
15
|
+
def start_case(kase)
|
16
|
+
end
|
17
|
+
|
18
|
+
def start_test(test)
|
19
|
+
end
|
20
|
+
|
21
|
+
def pass(message=nil)
|
22
|
+
io.print '.'; io.flush
|
23
|
+
end
|
24
|
+
|
25
|
+
def fail(message=nil)
|
26
|
+
io.print 'F'; io.flush
|
27
|
+
end
|
28
|
+
|
29
|
+
def error(message=nil)
|
30
|
+
io.print 'E'; io.flush
|
31
|
+
end
|
32
|
+
|
33
|
+
def finish_test(test)
|
34
|
+
end
|
35
|
+
|
36
|
+
def finish_case(kase)
|
37
|
+
end
|
38
|
+
|
39
|
+
def finish_suite(suite)
|
40
|
+
io.puts("\nFinished in %.5f seconds." % [Time.now - @time])
|
41
|
+
io.puts
|
42
|
+
|
43
|
+
report = ''
|
44
|
+
|
45
|
+
fails = suite.select do |testrun|
|
46
|
+
testrun.fail? || testrun.error?
|
47
|
+
end
|
48
|
+
|
49
|
+
unless fails.empty? # or verbose?
|
50
|
+
#report << "\n\n-- Failures and Errors --\n\n"
|
51
|
+
fails.uniq.each do |testrun|
|
52
|
+
message = testrun.message.tabto(0)
|
53
|
+
message = ::ANSICode.magenta(message) if COLORIZE
|
54
|
+
report << message << "\n"
|
55
|
+
end
|
56
|
+
report << "\n"
|
57
|
+
end
|
58
|
+
|
59
|
+
io.puts report
|
60
|
+
|
61
|
+
io.puts "%s tests, %s assetions, %s failures, %s errors" % test_tally(suite)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def test_tally(suite)
|
67
|
+
counts = suite.collect{ |tr| tr.counts }
|
68
|
+
tally = [0,0,0,0]
|
69
|
+
counts.each do |count|
|
70
|
+
4.times{ |i| tally[i] += count[i] }
|
71
|
+
end
|
72
|
+
return tally
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|