soda 0.0.1
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/lib/FieldUtils.rb +102 -0
- data/lib/Soda.rb +2628 -0
- data/lib/SodaCSV.rb +88 -0
- data/lib/SodaFireFox.rb +118 -0
- data/lib/SodaLogReporter.rb +810 -0
- data/lib/SodaReportSummery.rb +470 -0
- data/lib/SodaReporter.rb +452 -0
- data/lib/SodaTestCheck.rb +347 -0
- data/lib/SodaUtils.rb +931 -0
- data/lib/SodaXML.rb +129 -0
- data/lib/fields/CheckBoxField.rb +87 -0
- data/lib/fields/FileField.rb +37 -0
- data/lib/fields/LiField.rb +40 -0
- data/lib/fields/RadioField.rb +44 -0
- data/lib/fields/SelectField.rb +59 -0
- data/lib/fields/SodaField.rb +363 -0
- data/lib/fields/TextField.rb +31 -0
- data/lib/soda.rb +2 -0
- data/lib/utils/sodalookups.rb +465 -0
- metadata +114 -0
@@ -0,0 +1,470 @@
|
|
1
|
+
###############################################################################
|
2
|
+
# Copyright (c) 2010, SugarCRM, Inc.
|
3
|
+
# 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
|
+
# * Redistributions of source code must retain the above copyright
|
8
|
+
# notice, this list of conditions and the following disclaimer.
|
9
|
+
# * Redistributions in binary form must reproduce the above copyright
|
10
|
+
# notice, this list of conditions and the following disclaimer in the
|
11
|
+
# documentation and/or other materials provided with the distribution.
|
12
|
+
# * Neither the name of SugarCRM, Inc. nor the
|
13
|
+
# names of its contributors may be used to endorse or promote products
|
14
|
+
# derived from this software without specific prior written permission.
|
15
|
+
#
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
17
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
18
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
19
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL SugarCRM, Inc. BE LIABLE FOR ANY
|
20
|
+
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
21
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
22
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
23
|
+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
24
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
25
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
26
|
+
###############################################################################
|
27
|
+
|
28
|
+
###############################################################################
|
29
|
+
# Needed Ruby libs:
|
30
|
+
###############################################################################
|
31
|
+
require 'getoptlong'
|
32
|
+
require 'date'
|
33
|
+
|
34
|
+
class SodaReportSummery
|
35
|
+
|
36
|
+
###############################################################################
|
37
|
+
# initialize -- constructor
|
38
|
+
# This is the class constructor. Really this does all the needed work.
|
39
|
+
#
|
40
|
+
# Params:
|
41
|
+
# dir: This is the dorectory with raw soda logs in it.
|
42
|
+
# outfile: This is the new summery html file to create.
|
43
|
+
# create_links: This will create links to the soda report files in the
|
44
|
+
# summery.
|
45
|
+
#
|
46
|
+
# Results:
|
47
|
+
# Creates a new class and html summery file. Will raise and exception on
|
48
|
+
# any errors.
|
49
|
+
#
|
50
|
+
###############################################################################
|
51
|
+
def initialize(dir ="", outfile = "", create_links = false)
|
52
|
+
log_files = nil
|
53
|
+
report_data = nil
|
54
|
+
result = 0
|
55
|
+
html_tmp_file = ""
|
56
|
+
timout = true
|
57
|
+
|
58
|
+
if (dir.empty?)
|
59
|
+
raise "Empty 'dir' param!\n"
|
60
|
+
elsif (outfile.empty?)
|
61
|
+
raise "Empty 'outfile param!"
|
62
|
+
end
|
63
|
+
|
64
|
+
html_tmp_file = File.dirname(outfile)
|
65
|
+
html_tmp_file += "/summery.tmp"
|
66
|
+
|
67
|
+
for i in 0..120
|
68
|
+
if (!File.exist?(html_tmp_file))
|
69
|
+
timeout = false
|
70
|
+
break
|
71
|
+
end
|
72
|
+
|
73
|
+
timeout = true
|
74
|
+
sleep(1)
|
75
|
+
end
|
76
|
+
|
77
|
+
# if (timeout != false)
|
78
|
+
# raise "Timed out waiting for lock to be released on file:"+
|
79
|
+
# " \"#{html_tmp_file}\"!\n"
|
80
|
+
# end
|
81
|
+
|
82
|
+
log_files = GetLogFiles(dir)
|
83
|
+
if ( (log_files == nil) || (log_files.length < 1) )
|
84
|
+
raise "Failed calling: GetLogFiles(#{dir})!"
|
85
|
+
end
|
86
|
+
|
87
|
+
report_data = GenerateReportData(log_files)
|
88
|
+
if (report_data.length < 1)
|
89
|
+
raise "No report data found when calling: GenerateReportData()!"
|
90
|
+
end
|
91
|
+
|
92
|
+
result = GenHtmlReport(report_data, html_tmp_file, create_links)
|
93
|
+
if (result != 0)
|
94
|
+
raise "Failed calling: GenHtmlReport()!"
|
95
|
+
end
|
96
|
+
|
97
|
+
File.rename(html_tmp_file, outfile)
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
###############################################################################
|
102
|
+
# GetLogFiles -- method
|
103
|
+
# This function gets all the log files in a given dir puts them in a list.
|
104
|
+
#
|
105
|
+
# Params:
|
106
|
+
# dir: this is the directory that holds the log files.
|
107
|
+
#
|
108
|
+
# Results:
|
109
|
+
# returns nil on error, else a list of all the log files in the dir.
|
110
|
+
#
|
111
|
+
###############################################################################
|
112
|
+
def GetLogFiles(dir)
|
113
|
+
files = nil
|
114
|
+
|
115
|
+
if (!File.directory?(dir))
|
116
|
+
print "(!)Error: #{dir} is not a directory!\n"
|
117
|
+
return nil
|
118
|
+
end
|
119
|
+
|
120
|
+
files = File.join("#{dir}", "*.log")
|
121
|
+
files = Dir.glob(files)
|
122
|
+
files = files.sort()
|
123
|
+
return files
|
124
|
+
end
|
125
|
+
|
126
|
+
private :GetLogFiles
|
127
|
+
|
128
|
+
###############################################################################
|
129
|
+
# GenerateReportData -- method
|
130
|
+
# This function generates needed data from each file passed in.
|
131
|
+
#
|
132
|
+
# Params:
|
133
|
+
# files: This is a list of files to read data from.
|
134
|
+
#
|
135
|
+
# Results:
|
136
|
+
# returns an array of hashed data.
|
137
|
+
#
|
138
|
+
###############################################################################
|
139
|
+
def GenerateReportData(files)
|
140
|
+
test_info = []
|
141
|
+
|
142
|
+
files.each do |f|
|
143
|
+
line = ""
|
144
|
+
hash = Hash.new()
|
145
|
+
hash['test_start_time'] = ""
|
146
|
+
hash['test_end_time'] = ""
|
147
|
+
hash['test_report_line'] = ""
|
148
|
+
hash['test_file'] = ""
|
149
|
+
hash['log_file'] = "#{f}"
|
150
|
+
hash['report_hash'] = nil
|
151
|
+
hash['total_tests'] = 0
|
152
|
+
|
153
|
+
print "(*)Opening file: #{f}\n"
|
154
|
+
logfd = File.open(f, "r")
|
155
|
+
while ( (line = logfd.gets) != nil)
|
156
|
+
line = line.chomp()
|
157
|
+
case line
|
158
|
+
when /\[new\s+test\]/i
|
159
|
+
if (!hash['test_start_time'].empty?)
|
160
|
+
next
|
161
|
+
end
|
162
|
+
|
163
|
+
line =~ /^\[(\d+\/\d+\/\d+-\d+:\d+:\d+)\]/
|
164
|
+
hash['test_start_time'] = "#{$1}"
|
165
|
+
when /starting\s+soda\s+test:/i
|
166
|
+
if (!hash['test_file'].empty?)
|
167
|
+
next
|
168
|
+
end
|
169
|
+
|
170
|
+
data = line.split(/:/)
|
171
|
+
test_file = "#{data[data.length() -1]}"
|
172
|
+
test_file = test_file.gsub(/^\s+/, "")
|
173
|
+
hash['test_file'] = "#{test_file}"
|
174
|
+
when /\[end\s+test\]/i
|
175
|
+
line =~ /^\[(\d+\/\d+\/\d+-\d+:\d+:\d+)\]/
|
176
|
+
hash['test_end_time'] = "#{$1}"
|
177
|
+
when /soda\s+test\s+report:/i
|
178
|
+
report_hash = Hash.new()
|
179
|
+
line = line.gsub(/^(\[\d+\/\d+\/\d+-\d+:\d+:\d+\])\(\*\)/, "")
|
180
|
+
line = line.gsub(/^soda\s+test\s+report:/i, "")
|
181
|
+
line_data = line.split("--")
|
182
|
+
line_data.each do |data|
|
183
|
+
if (data.empty?)
|
184
|
+
next
|
185
|
+
end
|
186
|
+
|
187
|
+
data = data.split(/:/)
|
188
|
+
data[1] = data[1].gsub(/^\s+/, "")
|
189
|
+
report_hash["#{data[0]}"] = data[1]
|
190
|
+
end
|
191
|
+
hash['report_hash'] = report_hash
|
192
|
+
test_info.push(hash)
|
193
|
+
end # end case #
|
194
|
+
end
|
195
|
+
logfd.close()
|
196
|
+
|
197
|
+
end
|
198
|
+
|
199
|
+
return test_info
|
200
|
+
end
|
201
|
+
|
202
|
+
private :GenerateReportData
|
203
|
+
|
204
|
+
###############################################################################
|
205
|
+
# GenHtmlReport -- method
|
206
|
+
# This function generates an html report from an array of hashed data.
|
207
|
+
#
|
208
|
+
# Params:
|
209
|
+
# data: An array of hashs
|
210
|
+
# reportfile: This is the html file to create.
|
211
|
+
#
|
212
|
+
# Results:
|
213
|
+
# Creates an html report file. Retruns -1 on error, else 0 on success.
|
214
|
+
#
|
215
|
+
###############################################################################
|
216
|
+
def GenHtmlReport(data, reportfile, create_links = false)
|
217
|
+
fd = nil
|
218
|
+
result = 0
|
219
|
+
totals = {}
|
220
|
+
log_file_td = ""
|
221
|
+
report_file = ""
|
222
|
+
now = nil
|
223
|
+
|
224
|
+
totals['Test Failure Count'] = 0
|
225
|
+
totals['Test CSS Error Count'] = 0
|
226
|
+
totals['Test JavaScript Error Count'] = 0
|
227
|
+
totals['Test Assert Failures'] = 0
|
228
|
+
totals['Test Event Count'] = 0
|
229
|
+
totals['Test Assert Count'] = 0
|
230
|
+
totals['Test Exceptions'] = 0
|
231
|
+
totals['Test Major Exceptions'] = 0
|
232
|
+
totals['Test Count'] = 0
|
233
|
+
totals['Test Skip Count'] = 0
|
234
|
+
totals['running_time'] = nil
|
235
|
+
|
236
|
+
begin
|
237
|
+
fd = File.new(reportfile, "w+")
|
238
|
+
rescue Exception => e
|
239
|
+
fd = nil
|
240
|
+
result = -1
|
241
|
+
print "Error: trying to open file!\n"
|
242
|
+
print "Exception: #{e.message}\n"
|
243
|
+
print "StackTrace: #{e.backtrace.join("\n")}\n"
|
244
|
+
ensure
|
245
|
+
if (result != 0)
|
246
|
+
return -1
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
now = Time.now.getlocal()
|
251
|
+
html_header = <<HTML
|
252
|
+
<html>
|
253
|
+
<style type="text/css">
|
254
|
+
body
|
255
|
+
{
|
256
|
+
margin: 0px;
|
257
|
+
font-family: Arial, Verdana, Helvetica, sans-serif;
|
258
|
+
}
|
259
|
+
|
260
|
+
a:hover
|
261
|
+
{
|
262
|
+
color: #24f938;
|
263
|
+
}
|
264
|
+
|
265
|
+
fieldset, table, pre
|
266
|
+
{
|
267
|
+
margin-bottom:0;
|
268
|
+
}
|
269
|
+
|
270
|
+
p
|
271
|
+
{
|
272
|
+
margin-top: 0px;
|
273
|
+
margin-bottom: 0px;
|
274
|
+
font-family: Arial, Verdana, Helvetica, sans-serif;
|
275
|
+
font-size: 11px;
|
276
|
+
}
|
277
|
+
|
278
|
+
li
|
279
|
+
{
|
280
|
+
margin-top: 0px;
|
281
|
+
margin-bottom: 0px;
|
282
|
+
font-family: Arial, Verdana, Helvetica, sans-serif;
|
283
|
+
font-size: 11px;
|
284
|
+
}
|
285
|
+
|
286
|
+
td
|
287
|
+
{
|
288
|
+
text-align: center;
|
289
|
+
vertical-align: middle;
|
290
|
+
}
|
291
|
+
|
292
|
+
.td_file
|
293
|
+
{
|
294
|
+
text-align: left;
|
295
|
+
vertical-align: middle;
|
296
|
+
white-space: nowrap;
|
297
|
+
}
|
298
|
+
|
299
|
+
.tr_normal
|
300
|
+
{
|
301
|
+
background: #e5eef3;
|
302
|
+
}
|
303
|
+
|
304
|
+
.highlight {
|
305
|
+
background-color: #8888FF;
|
306
|
+
}
|
307
|
+
|
308
|
+
.tr_header
|
309
|
+
{
|
310
|
+
white-space: nowrap;
|
311
|
+
background: #a4a4a4;
|
312
|
+
font-weight: bold;
|
313
|
+
}
|
314
|
+
|
315
|
+
table
|
316
|
+
{
|
317
|
+
background: #ffff;
|
318
|
+
border: 1px solid black;
|
319
|
+
border-bottom: 1px solid #0000;
|
320
|
+
border-right: 1px solid #0000;
|
321
|
+
color: #0000;
|
322
|
+
padding: 4px;
|
323
|
+
font-size: 11px;
|
324
|
+
}
|
325
|
+
</style>
|
326
|
+
<title>Soda Global Report Summery: #{now}</title>
|
327
|
+
<body>
|
328
|
+
<li>#{now}</li>
|
329
|
+
<table>
|
330
|
+
<tr class="tr_header">
|
331
|
+
\t<td>Test File:<br>
|
332
|
+
\tClick link for full report</td>
|
333
|
+
\t<td>Test Count:</td>
|
334
|
+
\t<td>Test Skip Count:</td>
|
335
|
+
\t<td>Failure Count:</td>
|
336
|
+
\t<td>CSS Error Count:</td>
|
337
|
+
\t<td>JavaScript Error Count:</td>
|
338
|
+
\t<td>Assert Failures:</td>
|
339
|
+
\t<td>Event Count:</td>
|
340
|
+
\t<td>Assert Count:</td>
|
341
|
+
\t<td>Exceptions:</td>
|
342
|
+
\t<td>Major Exceptions:</td>
|
343
|
+
\t<td>Running Time:<br>(hh:mm:ss):</td>
|
344
|
+
</tr>
|
345
|
+
HTML
|
346
|
+
|
347
|
+
fd.write(html_header)
|
348
|
+
|
349
|
+
data.each do |rpt|
|
350
|
+
totals['Test Failure Count'] +=
|
351
|
+
rpt['report_hash']['Test Failure Count'].to_i()
|
352
|
+
totals['Test CSS Error Count'] +=
|
353
|
+
rpt['report_hash']['Test CSS Error Count'].to_i()
|
354
|
+
totals['Test JavaScript Error Count'] +=
|
355
|
+
rpt['report_hash']['Test JavaScript Error Count'].to_i()
|
356
|
+
totals['Test Assert Failures'] +=
|
357
|
+
rpt['report_hash']['Test Assert Failures'].to_i()
|
358
|
+
totals['Test Event Count'] +=
|
359
|
+
rpt['report_hash']['Test Event Count'].to_i()
|
360
|
+
totals['Test Assert Count'] +=
|
361
|
+
rpt['report_hash']['Test Assert Count'].to_i()
|
362
|
+
totals['Test Exceptions'] +=
|
363
|
+
rpt['report_hash']['Test Exceptions'].to_i()
|
364
|
+
totals['Test Major Exceptions'] +=
|
365
|
+
rpt['report_hash']['Test Major Exceptions'].to_i()
|
366
|
+
totals['Test Count'] += rpt['report_hash']['Test Count'].to_i()
|
367
|
+
totals['Test Skip Count'] += rpt['report_hash']['Test Skip Count'].to_i()
|
368
|
+
|
369
|
+
start_time = DateTime.strptime("#{rpt['test_start_time']}",
|
370
|
+
"%m/%d/%Y-%H:%M:%S")
|
371
|
+
stop_time = DateTime.strptime("#{rpt['test_end_time']}",
|
372
|
+
"%m/%d/%Y-%H:%M:%S")
|
373
|
+
time_diff = stop_time - start_time
|
374
|
+
if (totals['running_time'] == nil)
|
375
|
+
totals['running_time'] = time_diff
|
376
|
+
else
|
377
|
+
totals['running_time'] += time_diff
|
378
|
+
end
|
379
|
+
hours,minutes,seconds,frac = Date.day_fraction_to_time(time_diff)
|
380
|
+
|
381
|
+
rpt['report_hash'].each do |k,v|
|
382
|
+
if ( (v.to_i > 0) && (k !~ /test\s+assert\s+count/i) &&
|
383
|
+
(k !~ /test\s+event\s+count/i) &&
|
384
|
+
(k !~ /css\s+error\s+count/i) &&
|
385
|
+
(k !~ /test\s+count/i))
|
386
|
+
tmp = '<font color="#FF0000"><b>'
|
387
|
+
tmp += "#{v}</b></font>"
|
388
|
+
rpt['report_hash'][k] = tmp
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
report_file = File.basename(rpt['log_file'], ".log")
|
393
|
+
report_file = "Report-#{report_file}.html"
|
394
|
+
|
395
|
+
if (create_links)
|
396
|
+
rerun = ""
|
397
|
+
if (report_file =~ /-SodaRerun/i)
|
398
|
+
rerun = "<b> :Rerun</b>"
|
399
|
+
end
|
400
|
+
log_file_td = "<a href=\"#{report_file}\">#{rpt['test_file']}</a>"+
|
401
|
+
"#{rerun}"
|
402
|
+
else
|
403
|
+
log_file_td = "#{rpt['test_file']}"
|
404
|
+
end
|
405
|
+
|
406
|
+
str = "<tr class=\"tr_normal\" "+
|
407
|
+
"onMouseOver=\"this.className='highlight'\" "+
|
408
|
+
"onMouseOut=\"this.className='tr_normal'\">\n" +
|
409
|
+
"\t<td class=\"td_file\">#{log_file_td}</td>\n" +
|
410
|
+
"\t<td>#{rpt['report_hash']['Test Count']}</td>\n"+
|
411
|
+
"\t<td>#{rpt['report_hash']['Test Skip Count']}</td>\n"+
|
412
|
+
"\t<td>#{rpt['report_hash']['Test Failure Count']}</td>\n"+
|
413
|
+
"\t<td>#{rpt['report_hash']['Test CSS Error Count']}</td>\n" +
|
414
|
+
"\t<td>#{rpt['report_hash']['Test JavaScript Error Count']}</td>\n" +
|
415
|
+
"\t<td>#{rpt['report_hash']['Test Assert Failures']}</td>\n" +
|
416
|
+
"\t<td>#{rpt['report_hash']['Test Event Count']}</td>\n" +
|
417
|
+
"\t<td>#{rpt['report_hash']['Test Assert Count']}</td>\n" +
|
418
|
+
"\t<td>#{rpt['report_hash']['Test Exceptions']}</td>\n" +
|
419
|
+
"\t<td>#{rpt['report_hash']['Test Major Exceptions']}</td>\n" +
|
420
|
+
"\t<td>#{hours}:#{minutes}:#{seconds}</td>\n</tr>\n"
|
421
|
+
fd.write(str)
|
422
|
+
end
|
423
|
+
|
424
|
+
hours,minutes,seconds,frac =
|
425
|
+
Date.day_fraction_to_time(totals['running_time'])
|
426
|
+
|
427
|
+
totals.each do |k,v|
|
428
|
+
if ( (v.to_i > 0) && (k !~ /test\s+assert\s+count/i) &&
|
429
|
+
(k !~ /test\s+event\s+count/i) &&
|
430
|
+
(k !~ /css\s+error\s+count/i) &&
|
431
|
+
(k !~ /test\s+count/i) )
|
432
|
+
|
433
|
+
tmp = '<font color="#FF0000"><b>'
|
434
|
+
tmp += "#{v}</b></font>"
|
435
|
+
totals[k] = tmp
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
totals['Test Skip Count'] = totals['Test Skip Count'].to_i()
|
440
|
+
test_totals = totals['Test Count'] + totals['Test Skip Count']
|
441
|
+
sub_totals = "<tr class=\"tr_header\">\n"+
|
442
|
+
"\t<td>Totals:</td>\n"+
|
443
|
+
"\t<td>#{totals['Test Count']}</td>\n"+
|
444
|
+
"\t<td>#{totals['Test Skip Count']}</td>\n"+
|
445
|
+
"\t<td>#{totals['Test Failure Count']}</td>\n"+
|
446
|
+
"\t<td>#{totals['Test CSS Error Count']}</td>\n"+
|
447
|
+
"\t<td>#{totals['Test JavaScript Error Count']}</td>\n"+
|
448
|
+
"\t<td>#{totals['Test Assert Failures']}</td>\n"+
|
449
|
+
"\t<td>#{totals['Test Event Count']}</td>\n"+
|
450
|
+
"\t<td>#{totals['Test Assert Count']}</td>\n"+
|
451
|
+
"\t<td>#{totals['Test Exceptions']}</td>\n"+
|
452
|
+
"\t<td>#{totals['Test Major Exceptions']}</td>\n"+
|
453
|
+
"\t<td>#{hours}:#{minutes}:#{seconds}</td>\n"+
|
454
|
+
"</tr>\n" +
|
455
|
+
"<tr class=\"tr_header\">\n"+
|
456
|
+
"\t<td>Total Test Count:</td>\n"+
|
457
|
+
"\t<td colspan=\"2\">#{test_totals}</td>\n</tr>\n"
|
458
|
+
|
459
|
+
fd.write(sub_totals)
|
460
|
+
fd.write("</table>\n</body>\n</html>\n")
|
461
|
+
fd.close()
|
462
|
+
|
463
|
+
return result
|
464
|
+
|
465
|
+
end
|
466
|
+
private :GenHtmlReport
|
467
|
+
|
468
|
+
end
|
469
|
+
|
470
|
+
|