tdi 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.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +27 -0
- data/LICENSE.txt +674 -0
- data/README.md +46 -0
- data/Rakefile +1 -0
- data/bin/tdi +167 -0
- data/helper/acl.rb +38 -0
- data/helper/file.rb +97 -0
- data/helper/http.rb +104 -0
- data/helper/ssh.rb +58 -0
- data/lib/planner.rb +298 -0
- data/lib/rblank.rb +23 -0
- data/lib/rmerge.rb +19 -0
- data/lib/runner.rb +99 -0
- data/lib/tdi.rb +39 -0
- data/lib/tdi/version.rb +3 -0
- data/tdi.gemspec +31 -0
- metadata +170 -0
data/helper/ssh.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
require 'etc'
|
3
|
+
|
4
|
+
class TDIPlan < TDI
|
5
|
+
def ssh(plan)
|
6
|
+
plan.select { |key, val|
|
7
|
+
val.is_a?(Hash)
|
8
|
+
}.each_pair do |case_name, case_content|
|
9
|
+
# Validate.
|
10
|
+
unless /^[^@]+@[^@]+$/.match(case_name)
|
11
|
+
puts "ERR: Invalid ssh plan format \"#{case_name}\". Case name must match pattern \"user@host\".".light_magenta
|
12
|
+
exit 1
|
13
|
+
end
|
14
|
+
|
15
|
+
# Parse.
|
16
|
+
remote_user = case_name.split('@').first
|
17
|
+
host = case_name.split('@').last
|
18
|
+
local_users = [case_content['local_user']].flatten
|
19
|
+
|
20
|
+
# Users.
|
21
|
+
local_users.each do |local_user|
|
22
|
+
# Privileged user.
|
23
|
+
begin
|
24
|
+
Process.euid = 0
|
25
|
+
rescue => e
|
26
|
+
puts "ERR: Must run as root to change user credentials #{e}).".light_magenta
|
27
|
+
exit 1
|
28
|
+
end
|
29
|
+
|
30
|
+
# Change credentials to local user.
|
31
|
+
begin
|
32
|
+
Process.euid = Etc.getpwnam(local_user).uid
|
33
|
+
# SSH needs to know the user homedir in order to use the right
|
34
|
+
# private/public key pair to authenticate.
|
35
|
+
ENV['HOME'] = Etc.getpwnam(local_user).dir
|
36
|
+
rescue => e
|
37
|
+
puts "ERR: User \"#{local_user}\" not found (#{e}).".light_magenta
|
38
|
+
exit 1
|
39
|
+
end
|
40
|
+
|
41
|
+
begin
|
42
|
+
timeout(5) do
|
43
|
+
ssh_session = Net::SSH.start(host,
|
44
|
+
remote_user,
|
45
|
+
:auth_methods => ['publickey'])
|
46
|
+
ssh_session.close
|
47
|
+
success "SSH (#{local_user}): #{remote_user}@#{host}"
|
48
|
+
end
|
49
|
+
rescue
|
50
|
+
failure "SSH (#{local_user}): #{remote_user}@#{host}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Change credentials back to privileged user.
|
56
|
+
Process.euid = 0
|
57
|
+
end
|
58
|
+
end
|
data/lib/planner.rb
ADDED
@@ -0,0 +1,298 @@
|
|
1
|
+
require_relative 'rblank'
|
2
|
+
require_relative 'rmerge'
|
3
|
+
|
4
|
+
# Test plan builder.
|
5
|
+
def planner(opts, plan)
|
6
|
+
# Compila um plano completo.
|
7
|
+
# Processa a herança.
|
8
|
+
# Compila novamente para gerar um plano intermediário.
|
9
|
+
#
|
10
|
+
# Pass 1.
|
11
|
+
if opts[:verbose] > 1
|
12
|
+
puts '* Pass 1...'.cyan
|
13
|
+
puts
|
14
|
+
end
|
15
|
+
compiled_plan1 = plan_compiler(opts, plan)
|
16
|
+
inherited_plan1 = plan_inheriter(opts, compiled_plan1)
|
17
|
+
recompiled_plan1 = plan_compiler(opts, inherited_plan1)
|
18
|
+
if opts[:verbose] > 1
|
19
|
+
puts '* Pass 1... done.'.green
|
20
|
+
puts
|
21
|
+
end
|
22
|
+
|
23
|
+
# Zera o plano, mantendo apenas a estrutura de hashes. Desta forma é possível
|
24
|
+
# gerar um esqueleto de plano com todas as entradas de test cases com valores
|
25
|
+
# vazios (originais e herdados).
|
26
|
+
# Logo em seguida ocorre um merge recursivo com os valores originais para
|
27
|
+
# depois ser processado por completo novamente.
|
28
|
+
# O objetivo é dar precedência aos valores globais locais sobre os valores
|
29
|
+
# herdados.
|
30
|
+
#
|
31
|
+
# Blank and repopulate with original values.
|
32
|
+
blanked_plan = recompiled_plan1.rblank
|
33
|
+
if opts[:verbose] > 2
|
34
|
+
puts 'Blanked plan:'
|
35
|
+
puts "* #{blanked_plan}".yellow
|
36
|
+
end
|
37
|
+
|
38
|
+
repopulated_plan = blanked_plan.rmerge(compiled_plan1)
|
39
|
+
if opts[:verbose] > 2
|
40
|
+
puts 'Repopulated plan:'
|
41
|
+
puts "* #{repopulated_plan}".yellow
|
42
|
+
end
|
43
|
+
|
44
|
+
# Compila um plano completo.
|
45
|
+
# Processa a herança.
|
46
|
+
# Compila novamente para gerar um plano final.
|
47
|
+
#
|
48
|
+
# Pass 2.
|
49
|
+
if opts[:verbose] > 1
|
50
|
+
puts '* Pass 2...'.cyan
|
51
|
+
puts
|
52
|
+
end
|
53
|
+
compiled_plan2 = plan_compiler(opts, repopulated_plan)
|
54
|
+
inherited_plan2 = plan_inheriter(opts, compiled_plan2)
|
55
|
+
recompiled_plan2 = plan_compiler(opts, inherited_plan2)
|
56
|
+
if opts[:verbose] > 1
|
57
|
+
puts '* Pass 2... done.'.green
|
58
|
+
puts
|
59
|
+
end
|
60
|
+
|
61
|
+
# Final plan.
|
62
|
+
plan_filter(opts, recompiled_plan2)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Test plan compile.
|
66
|
+
def plan_compiler(opts, plan)
|
67
|
+
# Gera um plano de teste baseado em todos os valores dos hashes, desde o mais
|
68
|
+
# global (role) até o mais específico (test case).
|
69
|
+
# Ordem de precedência: test case > test plan.
|
70
|
+
|
71
|
+
puts 'Compiling test plan...'.cyan if opts[:verbose] > 1
|
72
|
+
|
73
|
+
compiled_plan = {}
|
74
|
+
|
75
|
+
# Role.
|
76
|
+
# Ex: {"admin": {"desc": "...", "acl": {"domain1": {"port": 80}...}...}...}
|
77
|
+
plan.select { |key, val|
|
78
|
+
val.is_a?(Hash)
|
79
|
+
}.each_with_index do |(role_name, role_content), index|
|
80
|
+
if opts[:verbose] > 2
|
81
|
+
puts '=' * 60
|
82
|
+
puts "Role: #{role_name}"
|
83
|
+
puts 'Role content:'
|
84
|
+
puts "* #{role_content}".yellow
|
85
|
+
end
|
86
|
+
|
87
|
+
# Role (if not already).
|
88
|
+
compiled_plan[role_name] ||= role_content
|
89
|
+
|
90
|
+
# Test plan.
|
91
|
+
# Ex: {"acl": {"domain1": {"port": 80}...}...}
|
92
|
+
role_content.select { |key, val|
|
93
|
+
val.is_a?(Hash)
|
94
|
+
}.each_pair do |plan_name, plan_content|
|
95
|
+
if opts[:verbose] > 2
|
96
|
+
puts "Plan: #{plan_name}"
|
97
|
+
puts 'Plan content:'
|
98
|
+
puts "* #{plan_content}".yellow
|
99
|
+
end
|
100
|
+
|
101
|
+
# Test plan (if not already).
|
102
|
+
compiled_plan[role_name][plan_name] ||= plan_content
|
103
|
+
|
104
|
+
# Test case.
|
105
|
+
# Ex: {"domain1": {"port": 80}...}
|
106
|
+
plan_content.select { |key, val|
|
107
|
+
val.is_a?(Hash)
|
108
|
+
}.each_pair do |case_name, case_content|
|
109
|
+
if opts[:verbose] > 2
|
110
|
+
puts "Case: #{case_name}"
|
111
|
+
puts 'Case content:'
|
112
|
+
puts "* #{case_content}".yellow
|
113
|
+
end
|
114
|
+
|
115
|
+
# Test case compile.
|
116
|
+
new_case_content = role_content.reject { |key, val| UNMERGEABLE_KEY_LIST.include?(key) or val.is_a?(Hash) }
|
117
|
+
new_case_content.merge!(plan_content.reject { |key, val| UNMERGEABLE_KEY_LIST.include?(key) or val.is_a?(Hash) })
|
118
|
+
new_case_content.merge!(case_content.reject { |key, val| UNMERGEABLE_KEY_LIST.include?(key) or val.is_a?(Hash) })
|
119
|
+
|
120
|
+
# Test case (new, merged).
|
121
|
+
compiled_plan[role_name][plan_name][case_name] = new_case_content
|
122
|
+
|
123
|
+
if opts[:verbose] > 2
|
124
|
+
puts 'Compiled case content:'
|
125
|
+
puts "* #{new_case_content}".yellow
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
if opts[:verbose] > 2
|
131
|
+
puts '=' * 60
|
132
|
+
puts unless index == plan.size - 1
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
if opts[:verbose] > 1
|
137
|
+
puts 'Compiling test plan... done.'.green
|
138
|
+
puts
|
139
|
+
end
|
140
|
+
|
141
|
+
compiled_plan
|
142
|
+
end
|
143
|
+
|
144
|
+
# Poor's man test plan inheritance.
|
145
|
+
def plan_inheriter(opts, plan)
|
146
|
+
# Processa a herança entre roles e test plans.
|
147
|
+
|
148
|
+
puts 'Inheriting test plan...'.cyan if opts[:verbose] > 1
|
149
|
+
|
150
|
+
inherited_plan = {}
|
151
|
+
|
152
|
+
# Role may inherit from role.
|
153
|
+
# Ex: {"admin": {"desc": "...", "inherits": "other_role", "acl": {"domain1": {"port": 80}...}...}...}
|
154
|
+
plan.select { |key, val|
|
155
|
+
val.is_a?(Hash)
|
156
|
+
}.each_with_index do |(role_name, role_content), index|
|
157
|
+
if opts[:verbose] > 2
|
158
|
+
puts '=' * 60
|
159
|
+
puts "Role: #{role_name}"
|
160
|
+
puts 'Role content:'
|
161
|
+
puts "* #{role_content}".yellow
|
162
|
+
end
|
163
|
+
|
164
|
+
# Role (if not already).
|
165
|
+
inherited_plan[role_name] ||= role_content
|
166
|
+
|
167
|
+
# Inheritance present?
|
168
|
+
i_role_name = role_content['inherits']
|
169
|
+
unless i_role_name.nil?
|
170
|
+
puts "Role #{role_name} inherits #{i_role_name}" if opts[:verbose] > 2
|
171
|
+
inherited_plan[role_name] = plan[i_role_name].rmerge(role_content)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Plan may inherit from plan.
|
175
|
+
# Ex: {"acl": {"inherits": "other_role::other_plan", "domain1": {"port": 80}...}...}
|
176
|
+
role_content.select { |key, val|
|
177
|
+
val.is_a?(Hash)
|
178
|
+
}.each_pair do |plan_name, plan_content|
|
179
|
+
if opts[:verbose] > 2
|
180
|
+
puts "Plan: #{plan_name}"
|
181
|
+
puts 'Plan content:'
|
182
|
+
puts "* #{plan_content}".yellow
|
183
|
+
end
|
184
|
+
|
185
|
+
# Test plan (if not already).
|
186
|
+
inherited_plan[role_name][plan_name] ||= plan_content
|
187
|
+
|
188
|
+
# Inheritance present?
|
189
|
+
i_plan = plan_content['inherits']
|
190
|
+
unless i_plan.nil?
|
191
|
+
i_role_name, i_plan_name = role_plan_split(i_plan)
|
192
|
+
|
193
|
+
if i_role_name.nil? or i_plan_name.nil?
|
194
|
+
puts "ERR: Invalid inheritance \"#{i_plan}\". Must match pattern \"role::plan\".".light_magenta
|
195
|
+
exit 1
|
196
|
+
end
|
197
|
+
|
198
|
+
# TODO: Tratar quando chave não existe.
|
199
|
+
puts "Plan #{plan_name} inherits #{i_plan}" if opts[:verbose] > 2
|
200
|
+
inherited_plan[role_name][plan_name] = plan[i_role_name][i_plan_name].rmerge(plan_content)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
if opts[:verbose] > 2
|
205
|
+
puts '=' * 60
|
206
|
+
puts unless index == plan.size - 1
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
if opts[:verbose] > 1
|
211
|
+
puts 'Inheriting test plan... done.'.green
|
212
|
+
puts
|
213
|
+
end
|
214
|
+
|
215
|
+
inherited_plan
|
216
|
+
end
|
217
|
+
|
218
|
+
# Test plan filter.
|
219
|
+
def plan_filter(opts, plan)
|
220
|
+
# Filtra roles e test plans desejados a partir do plano fornecido.
|
221
|
+
|
222
|
+
puts 'Filtering test plan...'.cyan if opts[:verbose] > 1
|
223
|
+
|
224
|
+
filtered_plan = {}
|
225
|
+
flag_err = false
|
226
|
+
|
227
|
+
# Ex: tdi -p admin
|
228
|
+
# Ex: tdi -p admin::acl
|
229
|
+
# Ex: tdi --plan fe
|
230
|
+
# Ex: tdi --plan admin::acl,solr,be
|
231
|
+
if opts.plan?
|
232
|
+
# Do filter.
|
233
|
+
puts 'Filtering following test plan from input file:'.cyan if opts[:verbose] > 0
|
234
|
+
opts[:plan].each do |plan_name|
|
235
|
+
puts " - #{plan_name}".cyan if opts[:verbose] > 0
|
236
|
+
|
237
|
+
# Pattern from command line is already validate by validate_args().
|
238
|
+
# Does not need to check for nil.
|
239
|
+
f_role_name, f_plan_name = role_plan_split(plan_name)
|
240
|
+
|
241
|
+
if plan.has_key?(f_role_name)
|
242
|
+
unless f_plan_name.nil?
|
243
|
+
# Test plan only.
|
244
|
+
if plan[f_role_name].has_key?(f_plan_name)
|
245
|
+
# Initialize hash key if not present.
|
246
|
+
filtered_plan[f_role_name] ||= {}
|
247
|
+
filtered_plan[f_role_name][f_plan_name] = plan[f_role_name][f_plan_name]
|
248
|
+
puts " Test plan \"#{plan_name}\" included.".green if opts[:verbose] > 0
|
249
|
+
else
|
250
|
+
puts "ERR: Test plan \"#{plan_name}\" not found in input file. This test plan can not be included.".light_magenta
|
251
|
+
flag_err = true
|
252
|
+
end
|
253
|
+
else
|
254
|
+
# Role test plan (entire).
|
255
|
+
filtered_plan[f_role_name] = plan[f_role_name]
|
256
|
+
puts " Role \"#{plan_name}\" included.".green if opts[:verbose] > 0
|
257
|
+
end
|
258
|
+
else
|
259
|
+
puts "ERR: Role \"#{f_role_name}\" not found in input file. Test plan \"#{plan_name}\" can not be included.".light_magenta
|
260
|
+
flag_err = true
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
puts if opts[:verbose] > 0
|
265
|
+
else
|
266
|
+
# No filter.
|
267
|
+
filtered_plan = plan
|
268
|
+
end
|
269
|
+
|
270
|
+
if opts[:verbose] > 2
|
271
|
+
puts 'Filtered test plan:'.cyan
|
272
|
+
puts "* #{filtered_plan}".yellow
|
273
|
+
end
|
274
|
+
|
275
|
+
exit 1 if flag_err
|
276
|
+
|
277
|
+
if opts[:verbose] > 1
|
278
|
+
puts 'Filtering test plan... done.'.green
|
279
|
+
puts
|
280
|
+
end
|
281
|
+
|
282
|
+
filtered_plan
|
283
|
+
end
|
284
|
+
|
285
|
+
# Split role_name and plan_name.
|
286
|
+
def role_plan_split(name)
|
287
|
+
if name.include?('::')
|
288
|
+
# Test plan only.
|
289
|
+
f_role_name = name.split('::').first
|
290
|
+
f_plan_name = name.split('::').last
|
291
|
+
else
|
292
|
+
# Role test plan (entire).
|
293
|
+
f_role_name = name
|
294
|
+
f_plan_name = nil
|
295
|
+
end
|
296
|
+
|
297
|
+
return f_role_name, f_plan_name
|
298
|
+
end
|
data/lib/rblank.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module HashRecursiveBlank
|
2
|
+
def rblank
|
3
|
+
r = {}
|
4
|
+
|
5
|
+
each_pair do |key, val|
|
6
|
+
r[key] = val.rblank if val.is_a?(Hash)
|
7
|
+
end
|
8
|
+
|
9
|
+
r.keep_if { |key, val| val.is_a?(Hash) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def rblank!
|
13
|
+
each_pair do |key, val|
|
14
|
+
self[key] = val.rblank! if val.is_a?(Hash)
|
15
|
+
end
|
16
|
+
|
17
|
+
keep_if { |key, val| val.is_a?(Hash) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Hash
|
22
|
+
include HashRecursiveBlank
|
23
|
+
end
|
data/lib/rmerge.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module HashRecursiveMerge
|
2
|
+
def rmerge(other_hash)
|
3
|
+
r = {}
|
4
|
+
|
5
|
+
merge(other_hash) do |key, oldval, newval|
|
6
|
+
r[key] = oldval.is_a?(Hash) ? oldval.rmerge(newval) : newval
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def rmerge!(other_hash)
|
11
|
+
merge!(other_hash) do |key, oldval, newval|
|
12
|
+
oldval.is_a?(Hash) ? oldval.rmerge!(newval) : newval
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Hash
|
18
|
+
include HashRecursiveMerge
|
19
|
+
end
|
data/lib/runner.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
require_relative 'tdi'
|
2
|
+
|
3
|
+
# Run tests.
|
4
|
+
def runner(opts, filename, plan)
|
5
|
+
puts 'Running tests...'.cyan if opts[:verbose] > 1
|
6
|
+
|
7
|
+
# Skip reserved roles.
|
8
|
+
# Ex: {"global": {"desc": "...", "acl": {"domain1": {"port": 80}...}...}...}
|
9
|
+
# Ex: {"common": {"desc": "...", "acl": {"domain1": {"port": 80}...}...}...}
|
10
|
+
plan.select { |role_name, role_content|
|
11
|
+
if role_content.is_a?(Hash)
|
12
|
+
UNTESTABLE_ROLE_LIST.include?(role_name) or role_content['notest'].eql?('true')
|
13
|
+
end
|
14
|
+
}.each_pair do |role_name, role_content|
|
15
|
+
puts "Skipping reserved or disabled role: #{role_name}".yellow if opts[:verbose] > 0
|
16
|
+
end
|
17
|
+
|
18
|
+
# Remove untestable roles.
|
19
|
+
plan.reject! { |role_name, role_content|
|
20
|
+
if role_content.is_a?(Hash)
|
21
|
+
UNTESTABLE_ROLE_LIST.include?(role_name) or role_content['notest'].eql?('true')
|
22
|
+
end
|
23
|
+
}
|
24
|
+
total_roles = plan.select { |key, val| val.is_a?(Hash) }.size
|
25
|
+
puts "Total roles to run: #{total_roles}".cyan if opts[:verbose] > 1
|
26
|
+
|
27
|
+
# Run the rest.
|
28
|
+
tdiplan = TDIPlan.new
|
29
|
+
|
30
|
+
# Role.
|
31
|
+
# Ex: {"admin": {"desc": "...", "acl": {"domain1": {"port": 80}...}...}...}
|
32
|
+
plan.select { |key, val|
|
33
|
+
val.is_a?(Hash)
|
34
|
+
}.each_with_index do |(role_name, role_content), index|
|
35
|
+
total_plans = role_content.select { |key, val| val.is_a?(Hash) }.size
|
36
|
+
|
37
|
+
if role_content['desc'].nil?
|
38
|
+
puts "* #{role_name.capitalize}".cyan
|
39
|
+
else
|
40
|
+
puts "* #{role_name.capitalize} - #{role_content['desc']}".cyan
|
41
|
+
end
|
42
|
+
# puts "Running tests for role: #{role_name}".cyan if opts[:verbose] > 0
|
43
|
+
puts "Total test plans to run for this role: #{total_plans}".cyan if opts[:verbose] > 1
|
44
|
+
|
45
|
+
# Test plan.
|
46
|
+
# Ex: {"acl": {"domain1": {"port": 80}...}...}
|
47
|
+
role_content.select { |key, val|
|
48
|
+
val.is_a?(Hash)
|
49
|
+
}.each_pair do |plan_name, plan_content|
|
50
|
+
total_cases = plan_content.select { |key, val| val.is_a?(Hash) }.size
|
51
|
+
|
52
|
+
puts "* #{plan_name.upcase}".cyan
|
53
|
+
# puts "Test plan: #{role_name}::#{plan_name}".cyan if opts[:verbose] > 0
|
54
|
+
puts "Total test cases to run for this plan: #{total_cases}".cyan if opts[:verbose] > 1
|
55
|
+
|
56
|
+
if opts[:verbose] > 3
|
57
|
+
puts "Plan: #{plan_name}"
|
58
|
+
puts 'Plan content:'
|
59
|
+
puts "* #{plan_content}".yellow
|
60
|
+
end
|
61
|
+
|
62
|
+
# Test plan content (test cases).
|
63
|
+
# Ex: {"domain1": {"port": 80}, "domain2": {"port": 80}...}
|
64
|
+
if tdiplan.respond_to?(plan_name)
|
65
|
+
tdiplan.send(plan_name, plan_content)
|
66
|
+
else
|
67
|
+
puts "Skipping not supported test plan type \"#{plan_name}\" for \"#{role_name}::#{plan_name}\".".yellow
|
68
|
+
tdiplan.skip = tdiplan.skip + total_cases
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
puts unless index == plan.size - 1
|
73
|
+
end
|
74
|
+
|
75
|
+
# Summary.
|
76
|
+
summary(opts, tdiplan)
|
77
|
+
|
78
|
+
# Shred.
|
79
|
+
if opts.shred?
|
80
|
+
puts "Shreding and removing test plan file: \"#{filename}\"...".cyan if opts[:verbose] > 2
|
81
|
+
if system("shred -f -n 38 -u -z #{filename}")
|
82
|
+
puts "Shreding and removing test plan file: \"#{filename}\"... done.".green if opts[:verbose] > 2
|
83
|
+
else
|
84
|
+
puts "ERR: Shreding and removing test plan file: \"#{filename}\".".light_magenta
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
puts 'Running tests... done.'.green if opts[:verbose] > 1
|
89
|
+
|
90
|
+
ret = tdiplan.passed? ? 0 : 1
|
91
|
+
ret = 0 if opts.nofail?
|
92
|
+
ret
|
93
|
+
end
|
94
|
+
|
95
|
+
# Display test summary.
|
96
|
+
def summary(opts, tdiplan)
|
97
|
+
puts '=' * 79
|
98
|
+
puts "Total: #{tdiplan.total} | Skip: #{tdiplan.skip} | Pass: #{tdiplan.pass} | Warn: #{tdiplan.warn} | Fail: #{tdiplan.fail}"
|
99
|
+
end
|