rutema_web 1.0.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/COPYING.txt +340 -0
- data/History.txt +34 -0
- data/Manifest.txt +26 -0
- data/README.txt +88 -0
- data/Rakefile +26 -0
- data/bin/rutema_web +6 -0
- data/lib/rutema_web/gems.rb +10 -0
- data/lib/rutema_web/main.rb +44 -0
- data/lib/rutema_web/model.rb +78 -0
- data/lib/rutema_web/public/css/style.css +105 -0
- data/lib/rutema_web/public/images/bg_menu.gif +0 -0
- data/lib/rutema_web/public/images/bg_submenu.gif +0 -0
- data/lib/rutema_web/public/images/left.png +0 -0
- data/lib/rutema_web/public/images/right.png +0 -0
- data/lib/rutema_web/public/images/run_error.png +0 -0
- data/lib/rutema_web/public/images/run_ok.png +0 -0
- data/lib/rutema_web/public/images/run_warn.png +0 -0
- data/lib/rutema_web/public/images/step_error.png +0 -0
- data/lib/rutema_web/public/images/step_ok.png +0 -0
- data/lib/rutema_web/public/images/step_warn.png +0 -0
- data/lib/rutema_web/public/images/tie_logo.gif +0 -0
- data/lib/rutema_web/public/js/folding.js +16 -0
- data/lib/rutema_web/public/js/jquery.js +4376 -0
- data/lib/rutema_web/ruport_formatter.rb +31 -0
- data/lib/rutema_web/sinatra.rb +467 -0
- data/lib/rutema_web/views/layout.erb +54 -0
- data/test/test_model.rb +48 -0
- data/test/test_rutema_web.rb +107 -0
- metadata +185 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__),"..")
|
2
|
+
require 'ruport/acts_as_reportable'
|
3
|
+
module Rutema
|
4
|
+
module UI
|
5
|
+
# Formats the test scenario data into a vertical folding structure
|
6
|
+
class VerticalTableFormatter < Ruport::Formatter::HTML
|
7
|
+
|
8
|
+
renders :vhtml, :for => Ruport::Controller::Table
|
9
|
+
|
10
|
+
def build_table_body
|
11
|
+
data.each do |row|
|
12
|
+
build_row(row)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def build_table_header
|
17
|
+
end
|
18
|
+
|
19
|
+
def build_table_footer
|
20
|
+
end
|
21
|
+
|
22
|
+
def build_row(data = self.data)
|
23
|
+
output << "<table class=\"vtable\"><colgroup><col width=\"100\"><col></colgroup>\n"
|
24
|
+
output << "<tr><td>#{data['status']}</td><td colspan=\"2\"><h3>#{data['number']} - #{data['name']}</h3></td></tr>"
|
25
|
+
output << "<tr><td>duration:</td><td>#{data['duration']}</td></tr>\n"
|
26
|
+
%w(output error).each { |k| output << "<tr><td colspan=\"2\"><div class=\"scenario_#{k}\"><pre>#{data.get(k)}</pre></div></td></tr>\n" if data.get(k).size > 0 }
|
27
|
+
output << "</table>\n"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,467 @@
|
|
1
|
+
# Copyright (c) 2008 Vassilis Rizopoulos. All rights reserved.
|
2
|
+
$:.unshift File.join(File.dirname(__FILE__),"..")
|
3
|
+
require 'sinatra/base'
|
4
|
+
require 'erb'
|
5
|
+
require 'patir/configuration'
|
6
|
+
require 'patir/command'
|
7
|
+
require 'patir/base'
|
8
|
+
require 'rutema_web/model'
|
9
|
+
require 'rutema_web/ruport_formatter'
|
10
|
+
|
11
|
+
module RutemaWeb
|
12
|
+
module UI
|
13
|
+
#Helper methods that create HTML snippets
|
14
|
+
module ViewUtilities
|
15
|
+
#image filename to use for succesfull scenarios
|
16
|
+
IMG_SCE_OK="/images/run_ok.png"
|
17
|
+
#image filename to use for failed scenarios
|
18
|
+
IMG_SCE_ERROR="/images/run_error.png"
|
19
|
+
#image filename to use for unexecuted scenarios
|
20
|
+
IMG_SCE_WARN="/images/run_warn.png"
|
21
|
+
#image filename to use for succesfull steps
|
22
|
+
IMG_STEP_OK="/images/step_ok.png"
|
23
|
+
#image filename to use for failed steps
|
24
|
+
IMG_STEP_ERROR="/images/step_error.png"
|
25
|
+
#image filename to use for unexecuted steps
|
26
|
+
IMG_STEP_WARN="/images/step_warn.png"
|
27
|
+
#Time format to use for the start and stop times
|
28
|
+
TIME_FORMAT="%d/%m/%Y\n%H:%M:%S"
|
29
|
+
#The left arrow
|
30
|
+
IMG_LEFT="/images/left.png"
|
31
|
+
#The right arrow
|
32
|
+
IMG_RIGHT="images/right.png"
|
33
|
+
# returns the image tag appropriate for the given status
|
34
|
+
def status_icon status
|
35
|
+
return case status
|
36
|
+
when :warning ,"not_executed"
|
37
|
+
"<img src=\"#{IMG_STEP_WARN}\" align=\"center\"/>"
|
38
|
+
when :success, "success"
|
39
|
+
"<img src=\"#{IMG_STEP_OK}\" align=\"center\"/>"
|
40
|
+
when :error, "error"
|
41
|
+
"<img src=\"#{IMG_STEP_ERROR}\" align=\"center\"/>"
|
42
|
+
else
|
43
|
+
"<img src=\"#{IMG_STEP_WARN}\" align=\"center\"/>"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
# Returns a string with the correct time format for display
|
47
|
+
def time_formatted time
|
48
|
+
time.strftime(TIME_FORMAT)
|
49
|
+
end
|
50
|
+
#Returns the URL of details page for a run
|
51
|
+
def run_url run
|
52
|
+
"/run/#{run.id}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def run_summary r
|
56
|
+
#Ramaze::Log.debug("Summary snippet for #{r}") if @logger
|
57
|
+
msg="#{run_link(r)}"
|
58
|
+
if r.context.is_a?(Hash)
|
59
|
+
msg<<" started at #{time_formatted(r.context[:start_time])}"
|
60
|
+
end
|
61
|
+
return msg
|
62
|
+
end
|
63
|
+
|
64
|
+
def run_link r
|
65
|
+
"<a class=\"smallgreytext\" href=\"#{run_url(r)}\">Run #{r.id}</a>"
|
66
|
+
end
|
67
|
+
def cfg_link cfg
|
68
|
+
"<a class=\"smallgreytext\" href=\"/statistics/config_report/#{cfg}\">#{cfg}</a>"
|
69
|
+
end
|
70
|
+
#will render a hash in a table of key||value rows
|
71
|
+
def context_table context
|
72
|
+
ret=""
|
73
|
+
if context.is_a?(Hash)
|
74
|
+
ret="<p>"
|
75
|
+
ret<<"Run configured from #{context[:config_file]}<br/>" if context[:config_file]
|
76
|
+
ret<<"Started at #{time_formatted(context[:start_time])}<br/>"
|
77
|
+
ret<<"Ended at #{time_formatted(context[:end_time])}<br/>"
|
78
|
+
ret<<"Total duration #{context[:end_time]-context[:start_time]} seconds</p>"
|
79
|
+
end
|
80
|
+
return ret
|
81
|
+
end
|
82
|
+
|
83
|
+
#returns the pagination HTML for runs
|
84
|
+
def run_page_link page_number,page_total
|
85
|
+
ret=""
|
86
|
+
unless page_total==1
|
87
|
+
ret<<"<a href=\"/runs/#{page_number-1}\">Previous</a>" unless page_number==0
|
88
|
+
ret<<" | Page #{page_number+1}"
|
89
|
+
ret<<" | <a href=\"/runs/#{page_number+1}\">Next</a>" unless page_number==page_total-1
|
90
|
+
end
|
91
|
+
return ret
|
92
|
+
end
|
93
|
+
#returns the pagination HTML for scenarios
|
94
|
+
def scenario_page_link page_number,page_total
|
95
|
+
ret=""
|
96
|
+
unless page_total==1
|
97
|
+
ret<<"<a href=\"/scenarios/#{page_number-1}\">Previous</a>" unless page_number==0
|
98
|
+
ret<<" | Page #{page_number+1}"
|
99
|
+
ret<<" | <a href=\"/scenarios/#{page_number+1}\">Next</a>" unless page_number==page_total-1
|
100
|
+
end
|
101
|
+
return ret
|
102
|
+
end
|
103
|
+
|
104
|
+
#Gives the HTML to use for the status column of a scenario list
|
105
|
+
def scenario_status r
|
106
|
+
img_src=IMG_SCE_WARN
|
107
|
+
img_src=IMG_SCE_OK if "success"==r.status
|
108
|
+
img_src=IMG_SCE_ERROR if "error"==r.status
|
109
|
+
"<img src=\"#{img_src}\" align=\"center\"/>"
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
#Contains all the settings that control the display of information for RutemaWeb's controllers
|
115
|
+
module Settings
|
116
|
+
DEFAULT_PAGE_SIZE=10
|
117
|
+
@@rutemaweb_settings||=Hash.new
|
118
|
+
#Set to true to show all setup and teardown scenarios
|
119
|
+
def show_setup_teardown= v
|
120
|
+
@@rutemaweb_settings[:show_setup_teardown]= v
|
121
|
+
end
|
122
|
+
|
123
|
+
def show_setup_teardown?
|
124
|
+
return @@rutemaweb_settings[:show_setup_teardown]
|
125
|
+
end
|
126
|
+
|
127
|
+
def page_size= v
|
128
|
+
@@rutemaweb_settings[:page_size]=v
|
129
|
+
end
|
130
|
+
|
131
|
+
def page_size
|
132
|
+
@@rutemaweb_settings[:page_size]||=DEFAULT_PAGE_SIZE
|
133
|
+
return @@rutemaweb_settings[:page_size]
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
module Statistics
|
138
|
+
def self.gruff_working= v
|
139
|
+
@@gruff_working= v
|
140
|
+
end
|
141
|
+
def self.gruff_working?
|
142
|
+
return @@gruff_working
|
143
|
+
end
|
144
|
+
begin
|
145
|
+
require 'gruff'
|
146
|
+
self.gruff_working=true
|
147
|
+
rescue LoadError
|
148
|
+
self.gruff_working=false
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
#returns a jpg blob
|
153
|
+
def runs_graph_jpg successful,failed,labels
|
154
|
+
graph=Gruff::StackedBar.new(640)
|
155
|
+
graph.theme = {
|
156
|
+
:colors => %w(green red yellow blue),
|
157
|
+
:marker_color => 'black',
|
158
|
+
:background_colors => %w(white grey)
|
159
|
+
}
|
160
|
+
graph.x_axis_label="#{successful.size} runs"
|
161
|
+
graph.data("successful",successful)
|
162
|
+
graph.data("failed",failed)
|
163
|
+
graph.labels=labels
|
164
|
+
graph.marker_font_size=12
|
165
|
+
return graph.to_blob("PNG")
|
166
|
+
end
|
167
|
+
#extract all the configuration names
|
168
|
+
def configurations
|
169
|
+
runs=Rutema::Model::Run.find(:all)
|
170
|
+
return runs.map{|r| r.context[:config_file] if r.context.is_a?(Hash)}.compact.uniq
|
171
|
+
end
|
172
|
+
def panel_configurations
|
173
|
+
ret="<a href=\"/statistics/config_report\">all</a><br/>"
|
174
|
+
configurations.each do |cfg|
|
175
|
+
ret<<cfg_link(cfg)
|
176
|
+
ret<<"<br/>"
|
177
|
+
end
|
178
|
+
return ret
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
class SinatraApp<Sinatra::Base
|
183
|
+
include ViewUtilities
|
184
|
+
include Settings
|
185
|
+
include Statistics
|
186
|
+
enable :logging
|
187
|
+
enable :run
|
188
|
+
enable :static
|
189
|
+
set :server, %w[thin mongrel webrick]
|
190
|
+
set :port, 7000
|
191
|
+
set :root, File.dirname(__FILE__)
|
192
|
+
set :public, File.dirname(__FILE__) + '/public'
|
193
|
+
|
194
|
+
@@logger = Patir.setup_logger
|
195
|
+
|
196
|
+
get '/' do
|
197
|
+
@title="Rutema"
|
198
|
+
@panel_content=panel_runs
|
199
|
+
@content_title="Welcome to Rutema"
|
200
|
+
@content="<p>This is the rutema web interface.<br/>It allows you to browse the contents of the test results database.</p><p>Currently you can view the results for each separate run, the results for a specific scenario (a complete list of all steps executed in the scenario with standard and error output logs) or the complete execution history of a scenario.</p><p>The panel on the left shows a list of the ten most recent runs.</p>"
|
201
|
+
erb :layout
|
202
|
+
end
|
203
|
+
|
204
|
+
get '/runs/:page' do |page|
|
205
|
+
runs(page)
|
206
|
+
erb :layout
|
207
|
+
end
|
208
|
+
#Displays the details of a run
|
209
|
+
#
|
210
|
+
#Routes to /runs if no id is provided
|
211
|
+
get '/run/:run_id' do |run_id|
|
212
|
+
@panel_content=nil
|
213
|
+
if !run_id.empty?
|
214
|
+
@panel_content=panel_runs
|
215
|
+
@title="Run #{run_id}"
|
216
|
+
@content_title="Summary of run #{run_id}"
|
217
|
+
@content=single_run(run_id)
|
218
|
+
end
|
219
|
+
erb :layout
|
220
|
+
end
|
221
|
+
|
222
|
+
get '/run/?' do
|
223
|
+
runs(0)
|
224
|
+
erb :layout
|
225
|
+
end
|
226
|
+
|
227
|
+
get '/runs/?' do
|
228
|
+
runs(0)
|
229
|
+
erb :layout
|
230
|
+
end
|
231
|
+
#Displays a paginated list of scenarios
|
232
|
+
get '/scenarios/:page' do |page|
|
233
|
+
scenarios(page)
|
234
|
+
erb :layout
|
235
|
+
end
|
236
|
+
#Displays the details of a scenario
|
237
|
+
get '/scenario/:scenario_id' do |scenario_id|
|
238
|
+
@panel_content=""
|
239
|
+
if !scenario_id.empty?
|
240
|
+
if scenario_id.to_i==0
|
241
|
+
@content=scenario_by_name(scenario_id)
|
242
|
+
else
|
243
|
+
@content=scenario_in_a_run(scenario_id.to_i)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
erb :layout
|
247
|
+
end
|
248
|
+
|
249
|
+
get '/scenario/?' do
|
250
|
+
scenarios(0)
|
251
|
+
erb :layout
|
252
|
+
end
|
253
|
+
|
254
|
+
get '/scenarios/?' do
|
255
|
+
scenarios(0)
|
256
|
+
erb :layout
|
257
|
+
end
|
258
|
+
|
259
|
+
get '/statistics/?' do
|
260
|
+
@title="Rutema"
|
261
|
+
@panel_content=panel_configurations
|
262
|
+
@content_title="Statistics"
|
263
|
+
@content="<p>rutema statistics provide reports that present the results on a time axis<br/>At present you can see the ratio of successful vs. failed test cases over time grouped per configuration file.</p>"
|
264
|
+
@content<<"statistics reports require the gruff gem which in turn depends on RMagick. gruff does not appear to be available!<br/>rutemaweb will not be able to produce statistics reports" unless Statistics.gruff_working?
|
265
|
+
erb :layout
|
266
|
+
end
|
267
|
+
|
268
|
+
get '/statistics/config_report/:configuration' do |configuration|
|
269
|
+
@title=configuration || "All configurations"
|
270
|
+
@panel_content=panel_configurations
|
271
|
+
@content_title= configuration || "All configurations"
|
272
|
+
if Statistics.gruff_working?
|
273
|
+
@content="<img src=\"/statistics/graph/#{configuration}\"/>"
|
274
|
+
else
|
275
|
+
@content="Could not generate graph.<p>This is probably due to a missing gruff/RMagick installation.</p><p>You will need to restart rutemaweb once the issue is resolved.</p>"
|
276
|
+
end
|
277
|
+
erb :layout
|
278
|
+
end
|
279
|
+
|
280
|
+
get '/statistics/graph/:configuration' do |configuration|
|
281
|
+
content_type "image/png"
|
282
|
+
successful=[]
|
283
|
+
failed=[]
|
284
|
+
labels=Hash.new
|
285
|
+
runs=Rutema::Model::Run.find(:all)
|
286
|
+
#find all runs beloging to this configuration
|
287
|
+
runs=runs.select{|r| r.context[:config_file]==configuration if r.context.is_a?(Hash)} if configuration
|
288
|
+
#now extract the data
|
289
|
+
counter=0
|
290
|
+
#the normalizer thins out the labels on the x axis so that they won't overlap
|
291
|
+
normalizer = 1
|
292
|
+
normalizer=runs.size/11 unless runs.size<=11
|
293
|
+
runs.each do |r|
|
294
|
+
fails=r.number_of_failed
|
295
|
+
#the scenarios array includes setup and teardown scripts as well - we want only the actual testcases
|
296
|
+
#so we use the added number_of_tests method that filters setup and test scripts
|
297
|
+
successful<<r.number_of_tests-fails
|
298
|
+
failed<<fails
|
299
|
+
#every Nth label
|
300
|
+
labels[counter]="R#{r.id}" if counter%normalizer==0
|
301
|
+
counter+=1
|
302
|
+
end
|
303
|
+
runs_graph_jpg(successful,failed,labels)
|
304
|
+
end
|
305
|
+
private
|
306
|
+
def runs page
|
307
|
+
@title="All runs"
|
308
|
+
@content_title="Runs"
|
309
|
+
@content=""
|
310
|
+
dt=[]
|
311
|
+
total_pages=(Rutema::Model::Run.count/page_size)+1
|
312
|
+
page_number=validated_page_number(page,total_pages)
|
313
|
+
|
314
|
+
runs=Rutema::Model::Run.find_on_page(page_number,page_size)
|
315
|
+
runs.each do |r|
|
316
|
+
dt<<[status_icon(r.status),run_summary(r),r.config_file]
|
317
|
+
end
|
318
|
+
@content<< Ruport::Data::Table.new(:data=>dt,:column_names=>["status","description","configuration"]).to_html
|
319
|
+
@content<<"<br/>"
|
320
|
+
@content<<run_page_link(page_number,total_pages)
|
321
|
+
end
|
322
|
+
|
323
|
+
def scenarios page
|
324
|
+
@title="All scenarios"
|
325
|
+
@content_title="Scenarios"
|
326
|
+
@content=""
|
327
|
+
@panel_content=panel_runs
|
328
|
+
runs=Hash.new
|
329
|
+
#find which runs contain each scenario with the same name
|
330
|
+
#Ramaze::Log.debug("Getting the runs for each scenario")
|
331
|
+
conditions="name NOT LIKE '%_teardown' AND name NOT LIKE '%_setup'"
|
332
|
+
Rutema::Model::Scenario.find(:all, :conditions=>conditions).each do |sc|
|
333
|
+
nm=sc.name
|
334
|
+
runs[nm]||=[]
|
335
|
+
runs[nm]<<sc.run.id
|
336
|
+
end
|
337
|
+
#the size of the hash is also the number of unique scenario names
|
338
|
+
total_pages=(runs.size / page_size)+1
|
339
|
+
page_number=validated_page_number(page,total_pages)
|
340
|
+
#Ramaze::Log.debug("Getting scenarios for page #{page_number}")
|
341
|
+
scens=Rutema::Model::Scenario.find_on_page(page_number,page_size,conditions)
|
342
|
+
#and now build the table data
|
343
|
+
dt=Array.new
|
344
|
+
scens.each do |sc|
|
345
|
+
nm=sc.name
|
346
|
+
#sort the run ids
|
347
|
+
runs[nm]=runs[nm].sort.reverse[0..4]
|
348
|
+
dt<<["<a href=\"/scenario/#{nm}\">#{nm}</a> : ",sc.title,runs[nm].map{|r| " <a href=\"/run/#{r}\">#{r}</a>"}.join(" "),"#{failure_rate(sc.name)}%"]
|
349
|
+
end
|
350
|
+
@content<<Ruport::Data::Table.new(:data=>dt,:column_names=>["name","title","last 5 runs","failure rate"]).to_html
|
351
|
+
@content<<"<br/>"
|
352
|
+
@content<<scenario_page_link(page_number,total_pages)
|
353
|
+
end
|
354
|
+
#Returns a valid page number no matter what __page__ is.
|
355
|
+
def validated_page_number page,total_pages
|
356
|
+
page_number=page.to_i if page
|
357
|
+
page_number||=0
|
358
|
+
#Ramaze::Log.debug("Total number of run pages is #{total_pages}")
|
359
|
+
if page_number<0 || page_number>total_pages-1
|
360
|
+
#Ramaze::Log.warn("Page number out of bounds: #{page_number}. Reseting")
|
361
|
+
page_number=0
|
362
|
+
end
|
363
|
+
return page_number
|
364
|
+
end
|
365
|
+
|
366
|
+
#Renders the summary of all runs for a single scenario
|
367
|
+
def scenario_by_name scenario_id
|
368
|
+
ret=""
|
369
|
+
@title="Runs for #{scenario_id}"
|
370
|
+
@content_title="Scenario #{scenario_id} runs"
|
371
|
+
begin
|
372
|
+
table=Rutema::Model::Scenario.report_table(:all,:conditions=>["name = :spec_name",{:spec_name=>scenario_id}],
|
373
|
+
:order=>"run_id DESC")
|
374
|
+
if table.empty?
|
375
|
+
ret="<p>no results for the given name</p>"
|
376
|
+
else
|
377
|
+
table.replace_column("status"){|r| scenario_status(r)}
|
378
|
+
table.replace_column("name") { |r| "<a href=\"/scenario/#{r}\">#{r}</a>"}
|
379
|
+
table.replace_column("start_time"){|r| r.stop_time ? r.start_time.strftime(TIME_FORMAT) : nil}
|
380
|
+
table.replace_column("stop_time"){|r| r.stop_time ? r.stop_time.strftime(TIME_FORMAT) : nil}
|
381
|
+
table.replace_column("run_id"){|r| "<a class=\"smallgreytext\" href=\"/run/#{r.run_id}\">Run #{r.run_id}</a>"}
|
382
|
+
table.reorder("status","run_id","title","start_time","stop_time")
|
383
|
+
table.column_names=["status","run","title","started at","ended at"]
|
384
|
+
ret<<table.to_html
|
385
|
+
end
|
386
|
+
rescue
|
387
|
+
@content_title="Error"
|
388
|
+
@title=@content_title
|
389
|
+
#Ramaze::Log.error("Could not retrieve data for the scenario name '#{scenario_id}'")
|
390
|
+
#Ramaze::Log.debug("#{$!.message}:\n#{$!.backtrace}")
|
391
|
+
ret="<p>could not retrieve data for the given scenario name</p>"
|
392
|
+
end
|
393
|
+
return ret
|
394
|
+
end
|
395
|
+
#Renders the information for a specific executed scenario
|
396
|
+
#giving a detailed list of the steps, with status and output
|
397
|
+
def scenario_in_a_run scenario_id
|
398
|
+
@panel_content=panel_runs
|
399
|
+
begin
|
400
|
+
scenario=Rutema::Model::Scenario.find(scenario_id)
|
401
|
+
@content_title="Summary for #{scenario.name} in run #{scenario.run_id}"
|
402
|
+
@title=@content_title
|
403
|
+
table=Rutema::Model::Step.report_table(:all,
|
404
|
+
:conditions=>["scenario_id = :scenario_id",{:scenario_id=>scenario_id}],
|
405
|
+
:order=>"number ASC")
|
406
|
+
if table.empty?
|
407
|
+
ret="<p>no results for the given id</p>"
|
408
|
+
else
|
409
|
+
table.replace_column("status"){|r| status_icon(r.status)}
|
410
|
+
ret=table.to_vhtml
|
411
|
+
end
|
412
|
+
rescue
|
413
|
+
@content_title="Error"
|
414
|
+
@title=@content_title
|
415
|
+
#Ramaze::Log.error("Could not find scenario with the id '#{scenario_id}'")
|
416
|
+
@@logger.warn("#{$!.message}:\n#{$!.backtrace}")
|
417
|
+
ret="<p>Could not find scenario with the given id.</p>"
|
418
|
+
end
|
419
|
+
return ret
|
420
|
+
end
|
421
|
+
|
422
|
+
#Renders the information for a single run providing the context and a list of the scenarios
|
423
|
+
def single_run run_id
|
424
|
+
ret=""
|
425
|
+
begin
|
426
|
+
run=Rutema::Model::Run.find(run_id)
|
427
|
+
rescue
|
428
|
+
return "Could not find #{run_id}"
|
429
|
+
end
|
430
|
+
if run.context
|
431
|
+
ret<<context_table(run.context)
|
432
|
+
end
|
433
|
+
conditions="run_id = :run_id AND name NOT LIKE '%_teardown' AND name NOT LIKE '%_setup'"
|
434
|
+
table=Rutema::Model::Scenario.report_table(:all,
|
435
|
+
:conditions=>[conditions,{:run_id=>run_id}],:except=>["run_id"],
|
436
|
+
:order=>"name ASC")
|
437
|
+
if table.empty?
|
438
|
+
ret<<"No scenarios for run #{run_id}"
|
439
|
+
else
|
440
|
+
table.replace_column("status") {|r| scenario_status(r) }
|
441
|
+
table.replace_column("name"){ |r| "<a href=\"/scenario/#{r.id}\">#{r.name}</a>" }
|
442
|
+
table.replace_column("start_time"){|r| r.start_time ? r.start_time.strftime(TIME_FORMAT) : nil}
|
443
|
+
table.replace_column("stop_time"){|r| r.stop_time ? r.stop_time.strftime(TIME_FORMAT) : nil}
|
444
|
+
table.reorder("status","name","title","start_time","stop_time")
|
445
|
+
table.column_names=["status","name","title","started at","ended at"]
|
446
|
+
ret<<table.to_html
|
447
|
+
end
|
448
|
+
return ret
|
449
|
+
end
|
450
|
+
|
451
|
+
def panel_runs
|
452
|
+
ret=""
|
453
|
+
Rutema::Model::Run.find(:all,:limit=>10,:order=>"id DESC").each do |r|
|
454
|
+
ret<<"#{status_icon(r.status)} #{run_link(r)}<br/>"
|
455
|
+
end
|
456
|
+
return ret
|
457
|
+
end
|
458
|
+
|
459
|
+
def failure_rate scenario_name
|
460
|
+
scenarios=Rutema::Model::Scenario.find(:all,:conditions=>["name = :spec_name",{:spec_name=>scenario_name}])
|
461
|
+
failures=0
|
462
|
+
scenarios.each{|sc| failures+=1 unless sc.status=="success" }
|
463
|
+
return ((failures.to_f/scenarios.size)*100).round
|
464
|
+
end
|
465
|
+
end#SinatraApp
|
466
|
+
end#UI module
|
467
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
2
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
3
|
+
<head>
|
4
|
+
<meta name="author" content="Vassilis Rizopoulos" />
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf8" />
|
6
|
+
<link rel="stylesheet" href="/css/style.css" type="text/css" />
|
7
|
+
<script src="/js/jquery.js"></script>
|
8
|
+
<script src="/js/folding.js"></script>
|
9
|
+
<script type="text/javascript">
|
10
|
+
$(document).ready(toggleFolding);
|
11
|
+
</script>
|
12
|
+
<title><%=@title%></title>
|
13
|
+
</head>
|
14
|
+
<body>
|
15
|
+
<div id="page" align="center">
|
16
|
+
<div id="content" style="width:800px">
|
17
|
+
<div id="logo">
|
18
|
+
<div style="margin-top:70px" class="whitetitle">rutema</div>
|
19
|
+
</div>
|
20
|
+
<div id="topheader">
|
21
|
+
<div align="left" class="bodytext">
|
22
|
+
</div>
|
23
|
+
<div id="toplinks" class="smallgraytext">
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
<div id="menu">
|
27
|
+
<div align="right" class="smallwhitetext" style="padding:9px;">
|
28
|
+
<a href="/">Home</a> | <a href="/run">Runs</a> | <a href="/scenario">Scenarios</a> | <a href="/statistics">Statistics</a>
|
29
|
+
</div>
|
30
|
+
</div>
|
31
|
+
<div id="submenu">
|
32
|
+
<div align="right" class="smallgraytext" style="padding:9px;"></div>
|
33
|
+
</div>
|
34
|
+
<div id="contenttext">
|
35
|
+
<div style="padding:10px">
|
36
|
+
<span class="titletext"><%=@content_title%></span>
|
37
|
+
</div>
|
38
|
+
<div class="bodytext" style="padding:12px;" align="justify"><%=@content%></div>
|
39
|
+
</div>
|
40
|
+
<div id="leftpanel">
|
41
|
+
<% if @panel_content && !@panel_content.empty? %>
|
42
|
+
<div align="justify" class="graypanel">
|
43
|
+
<%=@panel_content%>
|
44
|
+
</div>
|
45
|
+
<% end %>
|
46
|
+
</div>
|
47
|
+
<div id="footer" class="smallgraytext">
|
48
|
+
rutemaweb v<%= RutemaWeb::Version::STRING %> | <a href="/">Home</a> | <a href="http://patir.rubyforge.org/rutema">about rutema</a> |
|
49
|
+
© 2008 - 2009 <a href="http://www.braveworld.net/riva" target="_blank">Vassilis Rizopoulos</a>
|
50
|
+
</div>
|
51
|
+
</div>
|
52
|
+
</div>
|
53
|
+
</body>
|
54
|
+
</html>
|
data/test/test_model.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'rutema_web/ramaze_controller'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'mocha'
|
6
|
+
|
7
|
+
class TestModel <Test::Unit::TestCase
|
8
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3",:database =>":memory:")
|
9
|
+
Rutema::Model::Schema.up
|
10
|
+
def setup
|
11
|
+
@stp=Rutema::Model::Scenario.new
|
12
|
+
@stp.name="test_setup"
|
13
|
+
@trd=Rutema::Model::Scenario.new
|
14
|
+
@trd.name="test_teardown"
|
15
|
+
@tst=Rutema::Model::Scenario.new
|
16
|
+
@tst.name="test"
|
17
|
+
@r=Rutema::Model::Run.new
|
18
|
+
@r.scenarios=[@stp,@tst,@trd]
|
19
|
+
end
|
20
|
+
def test_status
|
21
|
+
assert_equal(:success,@r.status)
|
22
|
+
t1=Rutema::Model::Scenario.new
|
23
|
+
t1.name="failed"
|
24
|
+
t1.stubs(:status).returns("error")
|
25
|
+
@r.scenarios<<t1
|
26
|
+
assert_equal(:error,@r.status)
|
27
|
+
end
|
28
|
+
def test_is_test?
|
29
|
+
assert(!@stp.is_test?, "Setup as test")
|
30
|
+
assert(!@trd.is_test?, "Teardown as test")
|
31
|
+
assert(@tst.is_test?, "Test not a test")
|
32
|
+
end
|
33
|
+
def test_number_of_tests
|
34
|
+
assert_equal(1, @r.number_of_tests)
|
35
|
+
end
|
36
|
+
def test_number_of_failed
|
37
|
+
t1=Rutema::Model::Scenario.new
|
38
|
+
t1.name="failed"
|
39
|
+
t1.stubs(:status).returns("error")
|
40
|
+
t2=Rutema::Model::Scenario.new
|
41
|
+
t2.name="not executed"
|
42
|
+
t2.stubs(:status).returns("not_executed")
|
43
|
+
@tst.stubs(:status).returns("success")
|
44
|
+
@r.scenarios<<t1
|
45
|
+
@r.scenarios<<t2
|
46
|
+
assert_equal(2,@r.number_of_failed)
|
47
|
+
end
|
48
|
+
end
|