rutema_web 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|