rutema 0.3 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,15 @@
1
+ == 0.4 / 2007-07-05
2
+ * rutemaweb, a web interface for the db contents added
3
+ * uses WEBRick, Camping and Ruport
4
+ * offers views on all runs in the System, detailed scenario views and ccenario-over-time views
5
+ * Runner now rescues all failures in scenarios
6
+ * Bugfix in setting status of attended scenario in unattended mode
7
+ * Better ruport usage in the historian
8
+ * rutemah added to the gem and manifest
9
+ * rutemax help message complete with commands
10
+ * split the AR reporter and the model
11
+ * expanded the unit tests
12
+
1
13
  == 0.3 / 2007-05-30
2
14
  * Reporter implementations moved to own file
3
15
  * Reporter interface changed (Runner stati now passed directly as a name indexed Hash, specifications passed as well)
data/Manifest.txt CHANGED
@@ -5,12 +5,26 @@ COPYING.txt
5
5
  Rakefile
6
6
  bin/rutemax
7
7
  bin/rutemah
8
+ bin/rutemaweb
8
9
  lib/rutema/configuration.rb
9
10
  lib/rutema/specification.rb
10
11
  lib/rutema/system.rb
11
12
  lib/rutema/reporter.rb
12
13
  lib/rutema/reporter_ar.rb
13
14
  lib/rutema/historian.rb
15
+ lib/rutema/model.rb
16
+ lib/rutemaweb/htdocs/bg_menu.gif
17
+ lib/rutemaweb/htdocs/bg_submenu.gif
18
+ lib/rutemaweb/htdocs/run_error.png
19
+ lib/rutemaweb/htdocs/run_ok.png
20
+ lib/rutemaweb/htdocs/run_warn.png
21
+ lib/rutemaweb/htdocs/step_error.png
22
+ lib/rutemaweb/htdocs/step_ok.png
23
+ lib/rutemaweb/htdocs/step_warn.png
24
+ lib/rutemaweb/htdocs/style.css
25
+ lib/rutemaweb/htdocs/tie_logo.gif
26
+ lib/rutemaweb/htdocs/index.html
27
+ lib/rutemaweb/rutemaweb.rb
14
28
  test/test_configuration.rb
15
29
  test/test_specification.rb
16
30
  test/test_system.rb
data/README.txt CHANGED
@@ -95,4 +95,9 @@ You can redistribute it and/or modify it under either the terms of the GPL
95
95
  6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
96
96
  IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
97
97
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
98
- PURPOSE.
98
+ PURPOSE.
99
+
100
+ == CREDITS
101
+ RutemaWeb uses icons from
102
+ Fast Icon (Dirceu Veiga)
103
+ Homepage: http://www.fasticon.com
data/Rakefile CHANGED
@@ -19,7 +19,8 @@ Hoe.new('rutema', "#{Rutema::VERSION_MAJOR}.#{Rutema::VERSION_MINOR}") do |p|
19
19
  p.extra_deps<<['mailfactory']
20
20
  p.extra_deps<<['activerecord']
21
21
  p.extra_deps<<['ruport']
22
- p.spec_extras={:executables=>["rutemax","rutemah"],
22
+ p.extra_deps<<['camping']
23
+ p.spec_extras={:executables=>["rutemax","rutemah","rutemaweb"],
23
24
  :default_executable=>"rutemax"}
24
25
  end
25
26
 
data/bin/rutemaweb ADDED
@@ -0,0 +1,9 @@
1
+ # Copyright (c) 2007 Vassilis Rizopoulos. All rights reserved.
2
+ begin
3
+ require 'rutemaweb/rutemaweb'
4
+ rescue LoadError
5
+ require 'rubygems'
6
+ require 'rutemaweb/rutemaweb'
7
+ end
8
+
9
+ start_server
@@ -31,12 +31,17 @@ module Rutema
31
31
  private
32
32
  def parse_command_line args
33
33
  args.options do |opt|
34
+ opt.on("Usage:")
35
+ opt.on("rutemah [options] -c CONFIG command")
34
36
  opt.on("Options:")
35
37
  opt.on("--debug", "-d","Turns on debug messages") { $DEBUG=true }
36
38
  opt.on("--config FILE", "-c FILE",String,"Loads the configuration from FILE") { |@config_file|}
37
39
  opt.on("--log FILE", "-l FILE",String,"Redirects the log output to FILE") { |@log_file|}
38
40
  opt.on("-V", "--version","Displays the version") { $stdout.puts("v#{VERSION_MAJOR}.#{VERSION_MINOR}");exit 0 }
39
41
  opt.on("--help", "-h", "-?", "This text") { $stdout.puts opt; exit 0 }
42
+ opt.on("Commands are:")
43
+ opt.on("all - lists all entries in the database")
44
+ opt.on("\"testname\" - lists all tests runs for the given test name")
40
45
  opt.parse!
41
46
  #and now the rest
42
47
  if args.empty?
@@ -47,6 +52,16 @@ module Rutema
47
52
  end
48
53
  end
49
54
  end#parse_command_line
55
+ private
56
+ def application_flow
57
+ logger=Patir.setup_logger(@log_file)
58
+ if @config_file
59
+ cfg=RutemaHConfigurator.new(@config_file,logger).configuration()
60
+ puts Historian.new(cfg,logger).history(@command)
61
+ else
62
+ logger.error("No configuration file specified")
63
+ end
64
+ end
50
65
  end
51
66
 
52
67
  class Historian
@@ -60,35 +75,44 @@ module Rutema
60
75
  def history mode
61
76
  case mode
62
77
  when "all"
63
- DB::Run.find(:all).each do |run|
64
- puts "#{run.id}-----"
65
- table=DB::Scenario.report_table(:all,:except=>["id","run_id","number"],:conditions => "run_id = '#{run.id}'")
66
- puts beautify(table)
67
- end
78
+ table=Model::Scenario.report_table(:all)
79
+ ret=Ruport::Data::Grouping(beautify(table),:by=>"run_id")
68
80
  when String
69
- per_spec(mode)
81
+ ret=per_spec(mode)
70
82
  end
83
+ return ret
71
84
  end
72
85
  private
73
86
  def beautify table
74
- table.reorder("name","status","attended","version","start_time","stop_time")
87
+ table.reorder("run_id","name","status","attended","version","start_time","stop_time")
75
88
  table.replace_column("version") {|r| r.version ? r.version : "N/A"}
76
89
  table.replace_column("attended") {|r| r.attended=="t" ? "yes" : "no"}
77
90
  table.replace_column("start_time") {|r| r.stop_time ? r.start_time : nil}
78
91
  return table
79
92
  end
80
93
  def per_spec spec_name
81
- table=DB::Scenario.report_table(:all,:conditions=>"name = '#{spec_name}'",:except=>["id","run_id","number"])
82
- if table.empty?
83
- @logger.warn("No test run records found for #{spec_name}")
84
- else
85
- puts beautify(table)
94
+ begin
95
+ table=Model::Scenario.report_table(:all,:conditions=>"name = '#{spec_name}'",:except=>["id","number"])
96
+ if table.empty?
97
+ @logger.warn("No test run records found for #{spec_name}")
98
+ nil
99
+ else
100
+ beautify(table)
101
+ end
102
+ rescue
103
+ @logger.debug($!)
104
+ @logger.error("Could not retrieve scenario data from the database")
86
105
  end
87
106
  end
88
107
  def connect
89
108
  ActiveRecord::Base.logger = @logger
90
- @logger.info("Connecting with database '#{@configuration.source}'")
91
- ActiveRecord::Base.establish_connection(:adapter => "sqlite3",:database => @configuration.source)
109
+ begin
110
+ ActiveRecord::Base.connection
111
+ @logger.info("Database connection available: #{ActiveRecord::Base.connection.to_s}")
112
+ rescue
113
+ @logger.info("Connecting with database '#{@configuration.source}'")
114
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3",:database => @configuration.source)
115
+ end
92
116
  end
93
117
  end
94
118
  end
@@ -0,0 +1,78 @@
1
+ require 'rubygems'
2
+ require 'active_record'
3
+ require 'ruport/acts_as_reportable'
4
+ #this fixes the AR Logger hack that annoys me sooooo much
5
+ class Logger
6
+ private
7
+ def format_message(severity, datetime, progname, msg)
8
+ (@formatter || @default_formatter).call(severity, datetime, progname, msg)
9
+ end
10
+ end
11
+ module Rutema
12
+ #This is the ActiveRecord model for Rutema:
13
+ #
14
+ #A Run has n instances of executed scenarios - which in turn have n instances of executed steps -
15
+ #and n instances of parse errors.
16
+ module Model
17
+ #This is the schema for the AR database used to store test results
18
+ #
19
+ #We store the RutemaConfiguration#context for every run so that reports for past runs can be recreated without running the actual tests again.
20
+ class Schema<ActiveRecord::Migration
21
+ def self.up
22
+ create_table :runs do |t|
23
+ t.column :context, :string
24
+ end
25
+
26
+ create_table :scenarios do |t|
27
+ t.column :name, :string, :null=>false
28
+ t.column :run_id,:integer, :null=>false
29
+ t.column :attended,:bool, :null=>false
30
+ t.column :status, :string,:null=>false
31
+ t.column :number,:integer
32
+ t.column :start_time, :datetime,:null=>false
33
+ t.column :stop_time, :datetime
34
+ t.column :version, :string
35
+ end
36
+
37
+ create_table :steps do |t|
38
+ t.column :scenario_id,:integer, :null=>false
39
+ t.column :name, :string
40
+ t.column :number, :integer,:null=>false
41
+ t.column :status, :string,:null=>false
42
+ t.column :output, :text
43
+ t.column :error, :text
44
+ t.column :duration, :time
45
+ end
46
+
47
+ create_table :parse_errors do |t|
48
+ t.column :filename, :string,:null=>false
49
+ t.column :error, :string
50
+ t.column :run_id,:integer, :null=>false
51
+ end
52
+ end
53
+ end
54
+
55
+ class Run<ActiveRecord::Base
56
+ has_many :scenarios
57
+ has_many :parse_errors
58
+ serialize :context
59
+ acts_as_reportable
60
+ end
61
+
62
+ class Scenario<ActiveRecord::Base
63
+ belongs_to :run
64
+ has_many :steps
65
+ acts_as_reportable
66
+ end
67
+
68
+ class Step<ActiveRecord::Base
69
+ belongs_to :scenario
70
+ acts_as_reportable
71
+ end
72
+
73
+ class ParseError<ActiveRecord::Base
74
+ belongs_to :run
75
+ acts_as_reportable
76
+ end
77
+ end
78
+ end
@@ -1,87 +1,14 @@
1
1
  # Copyright (c) 2007 Vassilis Rizopoulos. All rights reserved.
2
- require 'rutema/reporter'
3
2
  require 'yaml'
3
+ require 'rutema/reporter'
4
+ require 'rutema/model'
4
5
  require 'rubygems'
5
- require 'active_record'
6
- require 'ruport/acts_as_reportable'
7
6
  require 'patir/command'
8
7
 
9
- #this fixes the AR Logger hack that annoys me sooooo much
10
- class Logger
11
- private
12
- def format_message(severity, datetime, progname, msg)
13
- (@formatter || @default_formatter).call(severity, datetime, progname, msg)
14
- end
15
- end
16
-
17
- module Rutema
18
- #The DB module provides the ActiveRecord model for Rutema
19
- module DB
20
- #This is the schema for the AR database used to store test results
21
- #
22
- #We store the RutemaConfiguration#context for every run so that reports for past runs can be recreated without running the actual tests again.
23
- class Schema<ActiveRecord::Migration
24
- def self.up
25
- create_table :runs do |t|
26
- t.column :context, :string
27
- end
28
-
29
- create_table :scenarios do |t|
30
- t.column :name, :string, :null=>false
31
- t.column :run_id,:integer, :null=>false
32
- t.column :attended,:bool, :null=>false
33
- t.column :status, :string,:null=>false
34
- t.column :number,:integer
35
- t.column :start_time, :datetime,:null=>false
36
- t.column :stop_time, :datetime
37
- t.column :version, :string
38
- end
39
-
40
- create_table :steps do |t|
41
- t.column :scenario_id,:integer, :null=>false
42
- t.column :name, :string
43
- t.column :number, :integer,:null=>false
44
- t.column :status, :string,:null=>false
45
- t.column :output, :text
46
- t.column :error, :text
47
- t.column :duration, :time
48
- end
49
-
50
- create_table :parse_errors do |t|
51
- t.column :filename, :string,:null=>false
52
- t.column :error, :string
53
- t.column :run_id,:integer, :null=>false
54
- end
55
- end
56
- end
57
-
58
- class Run<ActiveRecord::Base
59
- has_many :scenarios
60
- has_many :parse_errors
61
- acts_as_reportable
62
- end
63
-
64
- class Scenario<ActiveRecord::Base
65
- belongs_to :run
66
- has_many :steps
67
- acts_as_reportable
68
- end
69
-
70
- class Step<ActiveRecord::Base
71
- belongs_to :scenario
72
- acts_as_reportable
73
- end
74
-
75
- class ParseError<ActiveRecord::Base
76
- belongs_to :run
77
- acts_as_reportable
78
- end
79
- end
8
+ module Rutema
80
9
  #The ActiveRecordReporter will store the results of a test run in a database using ActiveRecord.
81
10
  #
82
11
  #The current implementation uses SQLite as the DBMS until we get enough time to implement the rest of the configuration.
83
- #
84
- #We store the RutemaConfiguration#context for every run so that reports for past runs can be recreated without running the actual tests again.
85
12
  class ActiveRecordReporter<Reporter
86
13
  def initialize definition
87
14
  @definition=definition
@@ -90,20 +17,23 @@ module Rutema
90
17
  raise "No database file defined" unless definition[:dbfile]
91
18
  connect
92
19
  end
93
-
20
+
21
+ #We get all the data for a Rutema::Model::Run entry in here.
22
+ #
23
+ #If the configuration is given and there is a context defined, this will be YAML-dumped into Rutema::Model::Run#context
94
24
  def report specifications,runner_states,parse_errors,configuration
95
- run_entry=DB::Run.new
25
+ run_entry=Model::Run.new
96
26
  if configuration && configuration.context
97
- run_entry.context=YAML.dump(configuration.context)
27
+ run_entry.context=configuration.context
98
28
  end
99
29
  parse_errors.each do |pe|
100
- er=DB::ParseError.new()
30
+ er=Model::ParseError.new()
101
31
  er.filename=pe[:filename]
102
32
  er.error=pe[:error]
103
33
  run_entry.parse_errors<<er
104
34
  end
105
35
  runner_states.each do |scenario|
106
- sc=DB::Scenario.new
36
+ sc=Model::Scenario.new
107
37
  sc.name=scenario.sequence_name
108
38
  sc.number=scenario.sequence_id
109
39
  sc.start_time=scenario.start_time
@@ -122,7 +52,7 @@ module Rutema
122
52
  sc.attended=false
123
53
  end
124
54
  scenario.step_states.each do |number,step|
125
- st=DB::Step.new
55
+ st=Model::Step.new
126
56
  st.name=step[:name]
127
57
  st.number=number
128
58
  st.status=step[:status].to_s
@@ -142,7 +72,7 @@ module Rutema
142
72
  @logger.info("Connecting with database '#{@definition[:dbfile]}'")
143
73
  ActiveRecord::Base.establish_connection(:adapter => "sqlite3",
144
74
  :database => @definition[:dbfile])
145
- DB::Schema.migrate(:up) unless File.exists?(@definition[:dbfile])
75
+ Model::Schema.migrate(:up) unless File.exists?(@definition[:dbfile])
146
76
  end
147
77
  end
148
78
 
data/lib/rutema/system.rb CHANGED
@@ -13,7 +13,7 @@ require 'patir/command'
13
13
 
14
14
  module Rutema
15
15
  VERSION_MAJOR=0
16
- VERSION_MINOR=3
16
+ VERSION_MINOR=4
17
17
  #Is raised when an error is found in a specification
18
18
  class ParserError<RuntimeError
19
19
  end
@@ -176,7 +176,7 @@ module Rutema
176
176
  return spec
177
177
  end
178
178
  end
179
-
179
+
180
180
  #This class coordinates parsing, execution and reporting of test specifications
181
181
  class Coordinator
182
182
  attr_accessor :configuration,:parse_errors,:parsed_files
@@ -201,23 +201,28 @@ module Rutema
201
201
  def run mode
202
202
  @configuration.context.start_time=Time.now
203
203
  @logger.info("Run started")
204
- case mode
205
- when :all
206
- specs=parse_all_specifications
207
- run_scenarios(specs)
208
- when :attended
209
- specs=parse_all_specifications
210
- @logger.debug(specs)
211
- run_scenarios(specs.select{|s| s.scenario && s.scenario.attended?})
212
- when :unattended
213
- specs=parse_all_specifications
214
- @logger.debug(specs)
215
- run_scenarios(specs.select{|s| s.scenario && !s.scenario.attended?})
216
- when String
217
- spec=parse_specification(mode)
218
- run_test(spec) if spec
219
- else
220
- @logger.error("Don't know how to run '#{mode}'")
204
+ begin
205
+ case mode
206
+ when :all
207
+ specs=parse_all_specifications
208
+ run_scenarios(specs)
209
+ when :attended
210
+ specs=parse_all_specifications
211
+ @logger.debug(specs)
212
+ run_scenarios(specs.select{|s| s.scenario && s.scenario.attended?})
213
+ when :unattended
214
+ specs=parse_all_specifications
215
+ @logger.debug(specs)
216
+ run_scenarios(specs.select{|s| s.scenario && !s.scenario.attended?})
217
+ when String
218
+ spec=parse_specification(mode)
219
+ run_test(spec) if spec
220
+ else
221
+ @logger.error("Don't know how to run '#{mode}'")
222
+ end
223
+ rescue
224
+ @logger.error("Runner error: #{$!.message}")
225
+ @logger.debug($!)
221
226
  end
222
227
  @configuration.context.end_time=Time.now
223
228
  @logger.info("Run completed in #{@configuration.context.end_time-@configuration.context.start_time}s")
@@ -384,25 +389,31 @@ module Rutema
384
389
  private
385
390
  def run_scenario name,scenario
386
391
  state=Patir::CommandSequenceStatus.new(name,scenario.steps)
387
- if scenario.attended? && !self.attended?
388
- @logger.warn("Attended scenario cannot be run in unattended mode")
389
- state..status=:warning
390
- else
391
- if scenario.attended?
392
- state.strategy=:attended
393
- else
394
- state.strategy=:unattended
395
- end
396
- stps=scenario.steps
397
- if stps.empty?
398
- @logger.warn("Scenario #{name} contains no steps")
392
+ begin
393
+ if scenario.attended? && !self.attended?
394
+ @logger.warn("Attended scenario cannot be run in unattended mode")
399
395
  state.status=:warning
400
396
  else
401
- stps.each do |s|
402
- state.step=run_step(s)
403
- break if !state.success?
397
+ if scenario.attended?a
398
+ state.strategy=:attended
399
+ else
400
+ state.strategy=:unattended
401
+ end
402
+ stps=scenario.steps
403
+ if stps.empty?
404
+ @logger.warn("Scenario #{name} contains no steps")
405
+ state.status=:warning
406
+ else
407
+ stps.each do |s|
408
+ state.step=run_step(s)
409
+ break if !state.success?
410
+ end
404
411
  end
405
412
  end
413
+ rescue
414
+ @logger.error("Encountered error in #{name}: #{$!.message}")
415
+ @logger.debug($!)
416
+ state.status=:error
406
417
  end
407
418
  state.stop_time=Time.now
408
419
  state.sequence_id=@number_of_runs
@@ -474,6 +485,11 @@ module Rutema
474
485
  opt.on("--check","Runs just the check test"){@check=true}
475
486
  opt.on("-V", "--version","Displays the version") { $stdout.puts("v#{VERSION_MAJOR}.#{VERSION_MINOR}");exit 0 }
476
487
  opt.on("--help", "-h", "-?", "This text") { $stdout.puts opt; exit 0 }
488
+ opt.on("The commands are:")
489
+ opt.on("\tall - Runs all tests")
490
+ opt.on("\tattended - Runs all attended tests")
491
+ opt.on("\tunattended - Runs all unattended tests")
492
+ opt.on("You can also provide a specification filename in order to run a single test")
477
493
  opt.parse!
478
494
  #and now the rest
479
495
  if args.empty?
@@ -498,7 +514,7 @@ module Rutema
498
514
  end
499
515
  end
500
516
  end
501
-
517
+
502
518
  def application_flow
503
519
  if @check
504
520
  #run just the check test
Binary file
Binary file
@@ -0,0 +1,50 @@
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="style.css" type="text/css" />
7
+ <title>Rutema Historian Web Interface</title>
8
+ </head>
9
+ <body>
10
+ <div id="page" align="center">
11
+ <div id="content" style="width:800px">
12
+ <div id="logo">
13
+ <div style="margin-top:70px" class="whitetitle">rutema</div>
14
+ </div>
15
+ <div id="topheader">
16
+ <div align="left" class="bodytext">
17
+ <br />
18
+ </div>
19
+ </div>
20
+ <div id="menu">
21
+ </div>
22
+ <div id="submenu">
23
+ <div align="right" class="smallgraytext" style="padding:9px;">
24
+ <!-- <a href="../index.html">patir</a> | <a href="index.html">rutema</a> | <a href="rdoc/index.html">rdoc</a> -->
25
+ </div>
26
+ </div>
27
+ <div id="contenttext">
28
+ <div style="padding:10px">
29
+ <span class="titletext">Intro</span>
30
+ </div>
31
+ <div class="bodytext" style="padding:12px;" align="justify">
32
+ <p>This is the rutema web interface.<br />
33
+ It allows you to browse the contents of the test results database.
34
+ </p>
35
+ <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>
36
+ </div>
37
+ </div>
38
+ <div id="leftpanel">
39
+ <div align="justify" class="graypanel">
40
+ <span class="bodytext"><a href="/rutema" class="smallgraytext">Run overview</a></span><br /><br />
41
+ <span class="bodytext"><a href="/rutema?scenarios=all" class="smallgraytext">Scenario overview</a></span><br /><br />
42
+ </div>
43
+ </div>
44
+ <div id="footer" class="smallgraytext">
45
+ <a href="/index.html">Home</a> | &copy; 2007 Vassilis Rizopoulos</a>
46
+ </div>
47
+ </div>
48
+ </div>
49
+ </body>
50
+ </html>
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,102 @@
1
+ /* CSS Document */
2
+
3
+ /*PAGE LAYOUT*/
4
+ body {
5
+ margin-left: 0px;
6
+ margin-top: 0px;
7
+ margin-right: 0px;
8
+ margin-bottom: 0px;
9
+ }
10
+ table {
11
+ font: 0.9em Tahoma, sans-serif;
12
+ color: #666666;
13
+ text-align: center;
14
+
15
+ }
16
+ #logo{float:left; background-image:url(tie_logo.gif); width:234px; height:213px;}
17
+ #topheader{float:right; width:566px; height:97px; background-color:#F0F0F0; position:relative}
18
+ #toplinks{position:absolute; right:10px; top:10px;}
19
+ #menu{float:right; width:566px; height:32px; background-image:url(bg_menu.gif); background-repeat:repeat-x;}
20
+ #submenu{float:right; width:566px; height:32px; background-image:url(bg_submenu.gif); background-position:top; background-repeat:repeat-x;}
21
+ #contenttext{float:right; width:566px;}
22
+ #contentnopanel{float:left; width:800px}
23
+ #leftpanel{float:left; width:234px;}
24
+ #footer{float:left; width:800px; background-color:#F0F0F0; padding:10px;}
25
+ #footernopanel{float:left; width:800px; background-color:#F0F0F0; padding:10px;}
26
+ /*GRAY PANEL*/
27
+ .graypanel{
28
+ border:solid 1px #666666; background-color:#F7F7F7; padding:10px; margin:10px;
29
+ }
30
+
31
+ /*TEXT STYLES*/
32
+ .bodytext {
33
+ font: 0.9em Tahoma, sans-serif;
34
+ color: #666666;
35
+ }
36
+
37
+ .titletext {
38
+ font: 0.7em Tahoma, sans-serif;
39
+ font-size:26px;
40
+ font-weight:bold;
41
+ color:#CC0000;
42
+ }
43
+ .whitetitle {
44
+ font: 0.7em Tahoma, sans-serif;
45
+ font-size:36px;
46
+ font-weight:bold;
47
+ color:#FFFFFF;
48
+ }
49
+ .smalltitle {
50
+ font: 0.7em Tahoma, sans-serif;
51
+ font-size:20px;
52
+ font-weight:bold;
53
+ color:#CC0000;
54
+ }
55
+ .smallwhitetext {
56
+ font: 0.7em Tahoma, sans-serif;
57
+ font-size: 11px;
58
+ font-weight:bold;
59
+ color: #FFFFFF;
60
+ }
61
+ .smallwhitetext a{
62
+ font: 0.7em Tahoma, sans-serif;
63
+ font-size: 11px;
64
+ font-weight:bold;
65
+ color: #FFFFFF;
66
+ text-decoration:none;
67
+ }
68
+ .smallwhitetext a:hover{
69
+ text-decoration:underline;
70
+ }
71
+ .smallgraytext {
72
+ font: 0.7em Tahoma, sans-serif;
73
+ font-size: 11px;
74
+ font-weight:bold;
75
+ color:#999999;
76
+ }
77
+ .smallgraytext a{
78
+ font: 0.7em Tahoma, sans-serif;
79
+ font-size: 11px;
80
+ font-weight:bold;
81
+ color:#999999;
82
+ text-decoration:none;
83
+ }
84
+ .smallgraytext a:hover{
85
+ text-decoration:underline;
86
+ }
87
+ .smallredtext {
88
+ font: 0.7em Tahoma, sans-serif;
89
+ font-size: 11px;
90
+ font-weight:bold;
91
+ color:#CC0000;
92
+ }
93
+ .smallredtext a{
94
+ font: 0.7em Tahoma, sans-serif;
95
+ font-size: 11px;
96
+ font-weight:bold;
97
+ color:#CC0000;
98
+ text-decoration:none;
99
+ }
100
+ .smallredtext a:hover{
101
+ text-decoration:underline;
102
+ }
Binary file
@@ -0,0 +1,240 @@
1
+ # Copyright (c) 2007 Vassilis Rizopoulos. All rights reserved.
2
+ $:.unshift File.join(File.dirname(__FILE__),"..")
3
+
4
+ require 'rutema/model'
5
+ require 'rutema/historian'
6
+ require 'webrick'
7
+ require 'rubygems'
8
+ require 'camping/webrick'
9
+
10
+
11
+ Camping.goes :RutemaWeb
12
+
13
+ #This is the module containing the Camping views and controllers for the web interface to rutema
14
+ module RutemaWeb
15
+ #The rutema web interface controllers
16
+ module RutemaWeb::Controllers
17
+ #This is the main controller
18
+ #
19
+ #The various views on the data are found through the query keys
20
+ class Index < R '/'
21
+ def get
22
+ @head_title||="Rutema Historian"
23
+ @white_title||="Runs"
24
+ @title||="History"
25
+ @left_panel=:runs
26
+ if @input.empty?
27
+ render :start
28
+ else
29
+ if @input["scenario"]
30
+ @scenario_id=@input[:scenario]
31
+ scenario=Rutema::Model::Scenario.find(@scenario_id)
32
+ @title="#{scenario.name} from Run ##{scenario.run_id}"
33
+ @white_title="Scenario <br/>##{@scenario_id}"
34
+ render :scenario_in_a_run
35
+ elsif @input[:run]
36
+ @run_id=@input[:run]
37
+ @white_title="Run ##{@run_id}"
38
+ @title="Summary"
39
+ render :single_run
40
+ elsif @input["scenario_name"]
41
+ @spec_name=@input["scenario_name"]
42
+ @white_title=@spec_name
43
+ @title="Summary"
44
+ render :scenario_by_name
45
+ elsif @input["scenarios"]
46
+ @white_title="Scenarios"
47
+ @title="Summary"
48
+ @left_panel=:scenarios
49
+ render :start
50
+ elsif @input["runs"]
51
+ @white_title="Runs"
52
+ @title="Summary"
53
+ @left_panel=:runs
54
+ render :start
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ module RutemaWeb::Views
61
+ #Time format to use for the start and stop times
62
+ TIME_FORMAT="%y%m%d - %H:%M:%S"
63
+ #image filename to use for succesfull scenarios
64
+ IMG_SCE_OK="run_ok.png"
65
+ #image filename to use for failed scenarios
66
+ IMG_SCE_ERROR="run_error.png"
67
+ #image filename to use for unexecuted scenarios
68
+ IMG_SCE_WARN="run_warn.png"
69
+ #image filename to use for succesfull steps
70
+ IMG_STEP_OK="step_ok.png"
71
+ #image filename to use for failed steps
72
+ IMG_STEP_ERROR="step_error.png"
73
+ #image filename to use for unexecuted steps
74
+ IMG_STEP_WARN="step_warn.png"
75
+ #The whole page in Markaby
76
+ def layout
77
+ xhtml_transitional do
78
+ head do
79
+ meta(:name=>"author",:content=>"Vassilis Rizopoulos")
80
+ link(:rel=>"stylesheet",:href=>"style.css",:type=>"text/css")
81
+ title { @head_title }
82
+ end
83
+ body do
84
+ div.page!(:align=>"center") do
85
+ div.content!(:style=>"width:800px") do
86
+ div.logo!{div.whitetitle(:style=>"margin-top:70px"){@white_title}}
87
+ div.topheader!{div.bodytext(:align=>"left"){br} }
88
+ div.menu!{}
89
+ div.submenu! do
90
+ div(:align=>"right",:class=>"smallgraytext",:style=>"padding:9px;") do
91
+ a(:href=>"?runs=all"){"runs"}+" | "+a(:href=>"?scenarios=all"){"scenarios"}
92
+ end
93
+ end
94
+ div.contenttext! do
95
+ div(:style=>"padding:10px"){span.titletext{@title}}
96
+ div.bodytext(:style=>"padding:12px;", :align=>"justify"){self<<yield}
97
+ end
98
+ leftpanel
99
+ footer
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ #Start point: No rendering content
107
+ def start
108
+
109
+ end
110
+
111
+ #Renders the information for a specific executed scenario
112
+ #giving a detailed list of the steps, with status and output
113
+ def scenario_in_a_run
114
+ table=Rutema::Model::Step.report_table(:all,:conditions=>["scenario_id = :scenario_id",{:scenario_id=>@scenario_id}])
115
+ table.replace_column("status") do |r|
116
+ img_src=IMG_STEP_WARN if "not_executed"==r.status
117
+ img_src=IMG_STEP_OK if "success"==r.status
118
+ img_src=IMG_STEP_ERROR if "error"==r.status
119
+ img(:src=>img_src,:align=>"center")
120
+ end
121
+ table.reorder("number","status","name","output","error","duration")
122
+ table.to_html
123
+ end
124
+ #Renders the information for a single run providing the context and a list of the scenarios
125
+ def single_run
126
+ ret=""
127
+ run=Rutema::Model::Run.find(@run_id)
128
+ if run.context
129
+ ret<<context_table(run.context)
130
+ end
131
+ table=Rutema::Model::Scenario.report_table(:all,:conditions=>["run_id = :run_id",{:run_id=>@run_id}],:except=>["run_id"])
132
+ table.replace_column("status") do |r|
133
+ if "not_executed"==r.status
134
+ img(:src=>IMG_SCE_WARN,:align=>"center")
135
+ else
136
+ img_src=IMG_SCE_OK if "success"==r.status
137
+ img_src=IMG_SCE_ERROR if "error"==r.status
138
+ a(:href=> "?scenario=#{r.data["id"]}"){img(:src=>img_src,:align=>"center")}
139
+ end
140
+ end
141
+ table.replace_column("version") {|r| r.version ? r.version : "N/A"}
142
+ table.replace_column("start_time"){|r| r.stop_time ? r.start_time.strftime(TIME_FORMAT) : nil}
143
+ table.replace_column("stop_time"){|r| r.stop_time.strftime(TIME_FORMAT)}
144
+ table.reorder("status","name","version","start_time","stop_time")
145
+ ret<<table.to_html
146
+ return ret
147
+ end
148
+
149
+ #Renders the summary of all runs for a single scenario
150
+ def scenario_by_name
151
+ ret=""
152
+ begin
153
+ table=Rutema::Model::Scenario.report_table(:all,:conditions=>["name = :spec_name",{:spec_name=>@spec_name}])
154
+ if table.empty?
155
+ ret=p{"no results for #{@spec_name}"}
156
+ else
157
+ table.replace_column("status") do |r|
158
+ if "not_executed"==r.status
159
+ img(:src=>IMG_SCE_WARN,:align=>"center")
160
+ else
161
+ img_src=IMG_SCE_OK if "success"==r.status
162
+ img_src=IMG_SCE_ERROR if "error"==r.status
163
+ a(:href=> "?scenario=#{r.data["id"]}"){img(:src=>img_src,:align=>"center")}
164
+ end
165
+ end
166
+ table.replace_column("version") {|r| r.version ? r.version : "N/A"}
167
+ table.replace_column("start_time"){|r| r.stop_time ? r.start_time.strftime(TIME_FORMAT) : nil}
168
+ table.replace_column("stop_time"){|r| r.stop_time.strftime(TIME_FORMAT)}
169
+ table.reorder("status","run_id","version","start_time","stop_time")
170
+ ret<<table.to_html
171
+ end
172
+ rescue
173
+ puts $!
174
+ ret=p{"could not retrieve data for #{@spec_name}"}
175
+ end
176
+ return ret
177
+ end
178
+ private
179
+ #Constructs the left panel of the page as a list of the test runs
180
+ def leftpanel
181
+ div.leftpanel! do
182
+ div.graypanel(:align=>"justify") do
183
+ span.bodytext do
184
+ if @left_panel==:runs
185
+ #<img src="run_error.png" align="center"/><a href="index.html" class="smallgraytext">1 - 28.06.2007</a>
186
+ Rutema::Model::Run.find(:all,:select=>"id").reverse.each do |r|
187
+ a.smallgraytext(:href=>"?run=#{r.id}"){"Run ##{r.id}"}
188
+ br
189
+ end
190
+ elsif @left_panel==:scenarios
191
+ result=Rutema::Model::Scenario.find(:all).collect{|r| r.name}
192
+ result.uniq.sort.each do |r|
193
+ a.smallgraytext(:href=>"?scenario_name=#{r}"){"#{r}"}
194
+ br
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
201
+
202
+ #Just a bit of copyright info as a footer
203
+ def footer
204
+ div.footer!(:class=>"smallgraytext") do
205
+ "&copy; 2007 Vassilis Rizopoulos"
206
+ end
207
+ end
208
+
209
+ #will render a hash in a table of key||value rows
210
+ def context_table context
211
+ table do
212
+ tr do
213
+ context.each do |k,v|
214
+ td {k.to_s}
215
+ td {v.to_s}
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
222
+
223
+ #Starts a WEBRick server with the RutemaWeb controller in /rutema
224
+ def start_server (port=3301,docroot=File.join(File.dirname(__FILE__),"htdocs"),mountpoint="/rutema")
225
+ include WEBrick
226
+ dbfile=ARGV.shift
227
+ if !dbfile
228
+ puts "please provide a database filename"
229
+ exit 0
230
+ end
231
+ if File.exist?(dbfile)
232
+ ActiveRecord::Base.establish_connection(:adapter=>"sqlite3", :database=>dbfile )
233
+ s = HTTPServer.new(:Port => port, :DocumentRoot => docroot )
234
+ s.mount mountpoint, WEBrick::CampingHandler, RutemaWeb
235
+ trap("INT") { s.shutdown }
236
+ s.start
237
+ else
238
+ $stderr.puts "Can't find database file '#{dbfile}'"
239
+ end
240
+ end
@@ -7,25 +7,54 @@ require 'rutema/historian'
7
7
  #$DEBUG=true
8
8
  module TestRutema
9
9
  class TestHistorian<Test::Unit::TestCase
10
- DB_FILE="test.db"
10
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3",:database =>":memory:")
11
+ Rutema::Model::Schema.up
11
12
  def setup
12
- @prev_dir=Dir.pwd
13
- Dir.chdir(File.dirname(__FILE__))
14
13
  @configuration=OpenStruct.new
15
- @configuration.source=DB_FILE
16
- #FileUtils.rm_rf(DB_FILE)
14
+ @configuration.source=":memory:"
15
+ a_bunch_of_data
17
16
  end
18
17
  def teardown
19
- Dir.chdir(@prev_dir)
20
18
  end
21
-
22
19
  def test_history
23
20
  assert_nothing_raised() do
24
21
  h=Rutema::Historian.new(@configuration)
25
- h.history(:all)
26
- h.history("test1")
27
- h.history("test5")
22
+ assert_not_nil( h.history("all"), "There should be some history there" )
23
+ assert_not_nil(h.history("TC001"), "TC001 should have some history" )
24
+ assert_nil(h.history("test5"), "NO history expected of test5" )
25
+ puts h.history("all")
26
+ puts h.history("TC002")
27
+ end
28
+ end
29
+
30
+ def a_bunch_of_data
31
+ runs=[]
32
+ runs<<Rutema::Model::Run.new(:context=>{:version=>"0.4a"},:scenarios=>success_scenarios)
33
+ runs<<Rutema::Model::Run.new(:context=>{:version=>"0.4b"},:scenarios=>success_scenarios)
34
+ runs<<Rutema::Model::Run.new(:context=>{:version=>"0.4c"},:scenarios=>failure_scenarios)
35
+ runs.each{|r| r.save}
36
+ end
37
+ def success_scenarios
38
+ scenarios=[]
39
+ 1.upto(2) do |i|
40
+ steps=[]
41
+ steps<<Rutema::Model::Step.new(:name=>"hard",:number=>1,:status=>"success",
42
+ :output=>"the first step is hard",:error=>"",:duration=>1)
43
+ steps<<Rutema::Model::Step.new(:name=>"easy",:number=>2,:status=>"success",
44
+ :output=>"the next step is easy",:error=>"",:duration=>1)
45
+ scenarios<<Rutema::Model::Scenario.new(:name=>"TC00#{i}",:version=>"100",
46
+ :attended=>false,:status=>"success",
47
+ :start_time=>(Time.now-10000*i),:stop_time=>(Time.now-10000*i),
48
+ :steps=>steps)
28
49
  end
50
+ return scenarios
51
+ end
52
+ def failure_scenarios
53
+ failed_step=Rutema::Model::Step.new(:name=>"echo",:number=>1,:status=>"error",
54
+ :output=>"Hel^.",:error=>"IO error",:duration=>1)
55
+ [Rutema::Model::Scenario.new(:name=>"TC003",:version=>"10",
56
+ :attended=>false,:status=>"error",:steps=>[failed_step],
57
+ :start_time=>(Time.now-10000),:stop_time=>Time.now)]
29
58
  end
30
59
 
31
60
  end
@@ -0,0 +1,35 @@
1
+ $:.unshift File.join(File.dirname(__FILE__),"..","lib")
2
+
3
+ require 'test/unit'
4
+ require 'rutema/model'
5
+ require 'rubygems'
6
+ require 'active_record/fixtures'
7
+
8
+ module TestRutema
9
+ class TestModel<Test::Unit::TestCase
10
+ ActiveRecord::Base.establish_connection(:adapter=>"sqlite3",:dbfile=>":memory:")
11
+ Rutema::Model::Schema.up
12
+ #test the CRUD operations
13
+ def test_create_read_update_delete
14
+ #create
15
+ r=Rutema::Model::Run.new
16
+ context={:tester=>"automatopoulos",:version=>"latest"}
17
+ r.context=context
18
+ sc=Rutema::Model::Scenario.new(:name=>"TC000",:attended=>false,:status=>"success",:start_time=>Time.now)
19
+ sc.steps<<Rutema::Model::Step.new(:name=>"echo",:number=>1,:status=>"success",:output=>"testing is nice",:error=>"",:duration=>1)
20
+ r.scenarios<<sc
21
+ assert(r.save, "Failed to save.")
22
+ #read
23
+ run=Rutema::Model::Run.find(r.id)
24
+ assert_equal(context,run.context)
25
+ assert_equal(sc.name, run.scenarios[0].name)
26
+ #update
27
+ new_context={:tester=>"tempelopoulos"}
28
+ run.context=new_context
29
+ assert(run.save, "Failed to update.")
30
+ #delete
31
+ assert(run.destroy, "Failed to delete.")
32
+ assert_raise(ActiveRecord::RecordNotFound) {Rutema::Model::Run.find(r.id)}
33
+ end
34
+ end
35
+ end
@@ -14,7 +14,7 @@ module TestRutema
14
14
  end
15
15
  end
16
16
  class TestActiveRecordReporter<Test::Unit::TestCase
17
- DB_FILE="test.db"
17
+ DB_FILE=":memory:"
18
18
  def setup
19
19
  @prev_dir=Dir.pwd
20
20
  Dir.chdir(File.dirname(__FILE__))
@@ -29,7 +29,6 @@ module TestRutema
29
29
  test2.step=MockCommand.new(2)
30
30
  test2.step=MockCommand.new(3)
31
31
  @status=[test1,test2]
32
- FileUtils.rm_rf(DB_FILE)
33
32
  end
34
33
  def teardown
35
34
  Dir.chdir(@prev_dir)
@@ -41,8 +40,7 @@ module TestRutema
41
40
  specs={"test1"=>spec1,
42
41
  "test2"=>spec2
43
42
  }
44
- definition={:dbfile=>DB_FILE}
45
- r=Rutema::ActiveRecordReporter.new(definition)
43
+ r=Rutema::ActiveRecordReporter.new(:dbfile=>DB_FILE)
46
44
  #without configuration
47
45
  assert_nothing_raised() { r.report(specs,@status,@parse_errors,nil) }
48
46
  configuration=OpenStruct.new
data/test/test_system.rb CHANGED
@@ -2,6 +2,8 @@ $:.unshift File.join(File.dirname(__FILE__),"..","lib")
2
2
  $:.unshift File.join(File.dirname(__FILE__),"..","ext")
3
3
  require 'test/unit'
4
4
  require 'ostruct'
5
+ require 'rubygems'
6
+ require 'patir/command'
5
7
 
6
8
  #$DEBUG=true
7
9
  module TestRutema
@@ -86,7 +88,16 @@ EOT
86
88
  state2=runner.run("test2",scenario)
87
89
  assert_equal(2, runner.states.size)
88
90
  end
89
-
91
+
92
+ def test_run_exceptions
93
+ runner=Rutema::Runner.new
94
+ scenario=Rutema::TestScenario.new
95
+ step=Rutema::TestStep.new("bad",Patir::RubyCommand.new("bad"){|cmd| raise "Bad command"})
96
+ scenario.add_step(step)
97
+ state1=:success
98
+ assert_nothing_raised() { state1=runner.run("test1",scenario) }
99
+ assert_equal(:error, state1.status)
100
+ end
90
101
  def test_reset
91
102
  runner=Rutema::Runner.new
92
103
  scenario=Rutema::TestScenario.new
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.1
3
3
  specification_version: 1
4
4
  name: rutema
5
5
  version: !ruby/object:Gem::Version
6
- version: "0.3"
7
- date: 2007-05-30 00:00:00 +02:00
6
+ version: "0.4"
7
+ date: 2007-07-05 00:00:00 +02:00
8
8
  summary: rutema is a test execution and management framework for heterogeneous testing environments
9
9
  require_paths:
10
10
  - lib
@@ -36,12 +36,26 @@ files:
36
36
  - Rakefile
37
37
  - bin/rutemax
38
38
  - bin/rutemah
39
+ - bin/rutemaweb
39
40
  - lib/rutema/configuration.rb
40
41
  - lib/rutema/specification.rb
41
42
  - lib/rutema/system.rb
42
43
  - lib/rutema/reporter.rb
43
44
  - lib/rutema/reporter_ar.rb
44
45
  - lib/rutema/historian.rb
46
+ - lib/rutema/model.rb
47
+ - lib/rutemaweb/htdocs/bg_menu.gif
48
+ - lib/rutemaweb/htdocs/bg_submenu.gif
49
+ - lib/rutemaweb/htdocs/run_error.png
50
+ - lib/rutemaweb/htdocs/run_ok.png
51
+ - lib/rutemaweb/htdocs/run_warn.png
52
+ - lib/rutemaweb/htdocs/step_error.png
53
+ - lib/rutemaweb/htdocs/step_ok.png
54
+ - lib/rutemaweb/htdocs/step_warn.png
55
+ - lib/rutemaweb/htdocs/style.css
56
+ - lib/rutemaweb/htdocs/tie_logo.gif
57
+ - lib/rutemaweb/htdocs/index.html
58
+ - lib/rutemaweb/rutemaweb.rb
45
59
  - test/test_configuration.rb
46
60
  - test/test_specification.rb
47
61
  - test/test_system.rb
@@ -58,6 +72,7 @@ files:
58
72
  test_files:
59
73
  - test/test_configuration.rb
60
74
  - test/test_historian.rb
75
+ - test/test_model.rb
61
76
  - test/test_reporter.rb
62
77
  - test/test_specification.rb
63
78
  - test/test_system.rb
@@ -72,6 +87,7 @@ extra_rdoc_files:
72
87
  executables:
73
88
  - rutemax
74
89
  - rutemah
90
+ - rutemaweb
75
91
  extensions: []
76
92
 
77
93
  requirements: []
@@ -122,6 +138,15 @@ dependencies:
122
138
  - !ruby/object:Gem::Version
123
139
  version: 0.0.0
124
140
  version:
141
+ - !ruby/object:Gem::Dependency
142
+ name: camping
143
+ version_requirement:
144
+ version_requirements: !ruby/object:Gem::Version::Requirement
145
+ requirements:
146
+ - - ">"
147
+ - !ruby/object:Gem::Version
148
+ version: 0.0.0
149
+ version:
125
150
  - !ruby/object:Gem::Dependency
126
151
  name: hoe
127
152
  version_requirement: