ruby-plsql-spec 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. data/.gitignore +13 -0
  2. data/Gemfile +11 -0
  3. data/History.txt +5 -0
  4. data/INSTALL-Windows.markdown +55 -0
  5. data/License.txt +20 -0
  6. data/README.markdown +91 -0
  7. data/Rakefile +49 -0
  8. data/VERSION +1 -0
  9. data/bin/plsql-spec +5 -0
  10. data/examples/source/award_bonus.rb +29 -0
  11. data/examples/source/betwnstr.rb +19 -0
  12. data/examples/source/remove_rooms_by_name.rb +45 -0
  13. data/examples/source/what_is_profiled.rb +207 -0
  14. data/examples/spec/award_bonus_spec.rb +35 -0
  15. data/examples/spec/betwnstr_spec.rb +24 -0
  16. data/examples/spec/database.yml +16 -0
  17. data/examples/spec/factories/employee_factory.rb +23 -0
  18. data/examples/spec/helpers/inspect_helpers.rb +17 -0
  19. data/examples/spec/helpers/oracle_ebs_helpers.rb +32 -0
  20. data/examples/spec/helpers/time_helpers.rb +5 -0
  21. data/examples/spec/oracle_ebs_spec.rb +61 -0
  22. data/examples/spec/remove_rooms_by_name_spec.rb +51 -0
  23. data/examples/spec/spec_helper.rb +78 -0
  24. data/examples/spec/what_is_profiled_spec.rb +12 -0
  25. data/lib/plsql/coverage.rb +262 -0
  26. data/lib/plsql/coverage/coverage.css +277 -0
  27. data/lib/plsql/coverage/details.html.erb +35 -0
  28. data/lib/plsql/coverage/index.html.erb +71 -0
  29. data/lib/plsql/coverage/jquery.min.js +154 -0
  30. data/lib/plsql/coverage/jquery.tablesorter.min.js +2 -0
  31. data/lib/plsql/coverage/proftab.sql +66 -0
  32. data/lib/plsql/coverage/rcov.js +43 -0
  33. data/lib/plsql/coverage/table_line.html.erb +15 -0
  34. data/lib/plsql/spec.rb +5 -0
  35. data/lib/plsql/spec/cli.rb +81 -0
  36. data/lib/plsql/spec/templates/database.yml +16 -0
  37. data/lib/plsql/spec/templates/helpers/inspect_helpers.rb +17 -0
  38. data/lib/plsql/spec/templates/helpers/time_helpers.rb +5 -0
  39. data/lib/plsql/spec/templates/spec_helper.rb +78 -0
  40. data/lib/plsql/spec/version.rb +5 -0
  41. data/lib/ruby-plsql-spec.rb +1 -0
  42. data/ruby-plsql-spec.gemspec +113 -0
  43. data/spec/plsql/coverage_spec.rb +246 -0
  44. data/spec/plsql/spec/cli_spec.rb +264 -0
  45. data/spec/spec.opts +6 -0
  46. data/spec/spec_helper.rb +61 -0
  47. metadata +177 -0
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ # load 'award_bonus' procedure into the database
4
+ require 'award_bonus'
5
+
6
+ describe "Award bonus" do
7
+ include EmployeeFactory
8
+
9
+ [ [1000, 1234.55, 0.10, 1123.46],
10
+ [nil, 1234.56, 0.10, 123.46],
11
+ [1000, 1234.54, 0.10, 1123.45]
12
+ ].each do |salary, sales_amt, commission_pct, result|
13
+ it "should calculate base salary #{salary.inspect} + sales amount #{sales_amt} * commission percentage #{commission_pct} = salary #{result.inspect}" do
14
+ employee = create_employee(
15
+ :commission_pct => commission_pct,
16
+ :salary => salary
17
+ )
18
+ plsql.award_bonus(employee[:employee_id], sales_amt)
19
+ get_employee(employee[:employee_id])[:salary].should == result
20
+ end
21
+ end
22
+
23
+ it "should raise ORA-06510 exception if commission percentage is missing" do
24
+ salary, sales_amt, commission_pct = 1000, 1234.55, NULL
25
+ employee = create_employee(
26
+ :commission_pct => commission_pct,
27
+ :salary => salary
28
+ )
29
+ lambda {
30
+ plsql.award_bonus(employee[:employee_id], sales_amt)
31
+ }.should raise_error(/ORA-06510/)
32
+ end
33
+
34
+ end
35
+
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ # load 'betwnstr' procedure into the database
4
+ require "betwnstr"
5
+
6
+ describe "Between string" do
7
+ it "should be correct in normal case" do
8
+ plsql.betwnstr('abcdefg', 2, 5).should == 'bcde'
9
+ end
10
+
11
+ it "should be correct with zero start value" do
12
+ plsql.betwnstr('abcdefg', 0, 5).should == 'abcde'
13
+ end
14
+
15
+ it "should be correct with way big end value" do
16
+ plsql.betwnstr('abcdefg', 5, 500).should == 'efg'
17
+ end
18
+
19
+ it "should be correct with NULL string" do
20
+ plsql.betwnstr(NULL, 5, 500).should == NULL
21
+ end
22
+
23
+ end
24
+
@@ -0,0 +1,16 @@
1
+ # Change default connection username, password and database.
2
+ # Specify also host and port if not using tnsnames.ora connection.
3
+ default:
4
+ username: hr
5
+ password: hr
6
+ database: orcl
7
+ # host: localhost
8
+ # port: 1521
9
+
10
+ # Add other connection if needed.
11
+ # You can access them with plsql(:other) where :other is connection name specified below.
12
+
13
+ # other:
14
+ # username: scott
15
+ # password: tiger
16
+ # database: xe
@@ -0,0 +1,23 @@
1
+ module EmployeeFactory
2
+ # Creates new employee with valid field values.
3
+ # Pass in parameters only field values that you want to override.
4
+ def create_employee(params)
5
+ employee = {
6
+ :employee_id => plsql.employees2_seq.nextval,
7
+ :last_name => 'Last',
8
+ :email => 'last@example.com',
9
+ :hire_date => Date.today,
10
+ :job_id => plsql.jobs.first[:job_id],
11
+ :commission_pct => nil,
12
+ :salary => nil
13
+ }.merge(params)
14
+ plsql.employees2.insert employee
15
+ get_employee employee[:employee_id]
16
+ end
17
+
18
+ # Select employee by primary key
19
+ def get_employee(employee_id)
20
+ plsql.employees2.first :employee_id => employee_id
21
+ end
22
+
23
+ end
@@ -0,0 +1,17 @@
1
+ # As ruby-plsql returns NUMBER values as BigDecimal values in Ruby then
2
+ # change format how they are by default displayed
3
+ BigDecimal.class_eval do
4
+ def inspect
5
+ to_s('F')
6
+ end
7
+ end
8
+
9
+ # As ruby-plsql returns NULL as Ruby nil then change nil.inspect to return 'NULL'
10
+ NilClass.class_eval do
11
+ def inspect
12
+ 'NULL'
13
+ end
14
+ end
15
+
16
+ # NULL looks more like SQL NULL than nil
17
+ NULL = nil
@@ -0,0 +1,32 @@
1
+ # initialize Oracle E-Business Suite session with specified user and responsibility
2
+ def init_ebs_user(params={})
3
+ # replace with default user name and responsibility and then it will not be necessary
4
+ # to specify default value as parameter
5
+ params = {
6
+ :user_name => "default user name",
7
+ :responsibility_name => "default responsibility"
8
+ }.merge(params)
9
+
10
+ row = plsql.select(:first, "SELECT usr.user_name, res.responsibility_name, usr.user_id,
11
+ urg.responsibility_id, urg.responsibility_application_id resp_appl_id
12
+ FROM apps.fnd_user_resp_groups urg,
13
+ applsys.fnd_user usr,
14
+ fnd_responsibility_vl res
15
+ WHERE usr.user_name = :user_name
16
+ AND res.responsibility_name = :responsibility_name
17
+ AND urg.user_id = usr.user_id
18
+ AND res.responsibility_id = urg.responsibility_id",
19
+ params[:user_name], params[:responsibility_name])
20
+
21
+ raise ArgumentError, "Wrong user name or responsibility name" unless row
22
+
23
+ plsql.fnd_global.apps_initialize(
24
+ :user_id => row[:user_id],
25
+ :resp_id => row[:responsibility_id],
26
+ :resp_appl_id => row[:resp_appl_id]
27
+ )
28
+
29
+ # uncomment if logging to dbms_output is necessary
30
+ # plsql.dbms_output.put_line("Initialized " + params[:user_name] + " / " + params[:responsibility_name])
31
+
32
+ end
@@ -0,0 +1,5 @@
1
+ # return current date with time 00:00:00
2
+ def Time.today
3
+ t = Time.now
4
+ Time.local(t.year, t.month, t.day)
5
+ end unless Time.respond_to?(:today)
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Oracle E-Business Suite" do
4
+ before(:all) do
5
+ # @old_connection = plsql.connection
6
+ # plsql.connect! "APPS", "APPS", "VIS"
7
+ @user_name = "OPERATIONS"
8
+ @responsibility_name = "System Administrator"
9
+ end
10
+
11
+ after(:all) do
12
+ # plsql.connection = @old_connection
13
+ end
14
+
15
+ if plsql.schema_name == 'APPS'
16
+
17
+ describe "Session initialization" do
18
+ it "should initialize session with valid user and responsibility" do
19
+ lambda {
20
+ init_ebs_user(:user_name => @user_name, :responsibility_name => @responsibility_name)
21
+ }.should_not raise_error
22
+ end
23
+
24
+ it "should raise error with invalid user" do
25
+ lambda {
26
+ init_ebs_user(:user_name => "INVALID", :responsibility_name => @responsibility_name)
27
+ }.should raise_error(/Wrong user name or responsibility name/)
28
+ end
29
+
30
+ it "should raise error with invalid responsibility" do
31
+ lambda {
32
+ init_ebs_user(:user_name => @user_name, :responsibility_name => "INVALID")
33
+ }.should raise_error(/Wrong user name or responsibility name/)
34
+ end
35
+
36
+ it "should raise error with default username and responsibility parameters" do
37
+ lambda {
38
+ init_ebs_user
39
+ }.should raise_error(/Wrong user name or responsibility name/)
40
+ end
41
+
42
+ end
43
+
44
+ describe "Session information" do
45
+ before(:all) do
46
+ init_ebs_user(:user_name => @user_name, :responsibility_name => @responsibility_name)
47
+ end
48
+
49
+ it "should return user name" do
50
+ plsql.fnd_global.user_name.should == @user_name
51
+ end
52
+
53
+ it "should return responsibility name" do
54
+ plsql.fnd_global.resp_name.should == @responsibility_name
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+
61
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ require 'remove_rooms_by_name'
4
+
5
+ describe "Remove rooms by name" do
6
+ before(:all) do
7
+ plsql.rooms.insert_values(
8
+ [:room_key, :name],
9
+ [1, 'Dining Room'],
10
+ [2, 'Living Room'],
11
+ [3, 'Office'],
12
+ [4, 'Bathroom'],
13
+ [5, 'Bedroom']
14
+ )
15
+ plsql.room_contents.insert_values(
16
+ [:contents_key, :room_key, :name],
17
+ [1, 1, 'Table'],
18
+ [2, 1, 'Hutch'],
19
+ [3, 1, 'Chair'],
20
+ [4, 2, 'Sofa'],
21
+ [5, 2, 'Lamp'],
22
+ [6, 3, 'Desk'],
23
+ [7, 3, 'Chair'],
24
+ [8, 3, 'Computer'],
25
+ [9, 3, 'Whiteboard']
26
+ )
27
+ end
28
+
29
+ it "should remove a room without furniture" do
30
+ rooms_without_b = plsql.rooms.all("WHERE name NOT LIKE 'B%'")
31
+ plsql.remove_rooms_by_name('B%')
32
+ plsql.rooms.all.should == rooms_without_b
33
+ end
34
+
35
+ it "should not remove a room with furniture" do
36
+ lambda {
37
+ lambda {
38
+ plsql.remove_rooms_by_name('Living Room')
39
+ }.should raise_error(/ORA-02292/)
40
+ }.should_not change { plsql.rooms.all }
41
+ end
42
+
43
+ it "should raise exception when NULL value passed" do
44
+ lambda {
45
+ lambda {
46
+ plsql.remove_rooms_by_name(NULL)
47
+ }.should raise_error(/program error/)
48
+ }.should_not change { plsql.rooms.all }
49
+ end
50
+
51
+ end
@@ -0,0 +1,78 @@
1
+ require "rubygems"
2
+ require "ruby-plsql-spec"
3
+ require "yaml"
4
+
5
+ # create all connections specified in database.yml file
6
+ database_config_file = File.expand_path('../database.yml', __FILE__)
7
+ database_config = YAML.load(File.read(database_config_file))
8
+ database_config = {} unless database_config.is_a?(Hash)
9
+ database_connections = database_config.keys.map{|k| k.to_sym}
10
+
11
+ database_config.each do |name, params|
12
+ # change all keys to symbols
13
+ name = name.to_sym
14
+ symbol_params = Hash[*params.map{|k,v| [k.to_sym, v]}.flatten]
15
+
16
+ plsql(name).connect! symbol_params
17
+
18
+ # Set autocommit to false so that automatic commits after each statement are _not_ performed
19
+ plsql(name).connection.autocommit = false
20
+ # reduce network traffic in case of large resultsets
21
+ plsql(name).connection.prefetch_rows = 100
22
+ # log DBMS_OUTPUT to standard output
23
+ if ENV['PLSQL_DBMS_OUTPUT']
24
+ plsql(name).dbms_output_stream = STDOUT
25
+ end
26
+
27
+ # start code coverage collection
28
+ if ENV['PLSQL_COVERAGE']
29
+ PLSQL::Coverage.start(name)
30
+ end
31
+ end
32
+
33
+ # Do logoff when exiting to ensure that session temporary tables
34
+ # (used when calling procedures with table types defined in packages)
35
+ at_exit do
36
+ database_connections.each do |name|
37
+ if ENV['PLSQL_COVERAGE']
38
+ PLSQL::Coverage.stop(name)
39
+ coverage_directory = name == :default ? ENV['PLSQL_COVERAGE'] : "#{ENV['PLSQL_COVERAGE']}/#{name}"
40
+ options = {:directory => coverage_directory}
41
+ options[:ignore_schemas] = ENV['PLSQL_COVERAGE_IGNORE_SCHEMAS'].split(',') if ENV['PLSQL_COVERAGE_IGNORE_SCHEMAS']
42
+ options[:like] = ENV['PLSQL_COVERAGE_LIKE'].split(',') if ENV['PLSQL_COVERAGE_LIKE']
43
+ PLSQL::Coverage.report name, options
44
+ PLSQL::Coverage.cleanup name
45
+ end
46
+ plsql(name).logoff
47
+ end
48
+ end
49
+
50
+ Spec::Runner.configure do |config|
51
+ config.before(:each) do
52
+ database_connections.each do |name|
53
+ plsql(name).savepoint "before_each"
54
+ end
55
+ end
56
+ config.after(:each) do
57
+ # Always perform rollback to savepoint after each test
58
+ database_connections.each do |name|
59
+ plsql(name).rollback_to "before_each"
60
+ end
61
+ end
62
+ config.after(:all) do
63
+ # Always perform rollback after each describe block
64
+ database_connections.each do |name|
65
+ plsql(name).rollback
66
+ end
67
+ end
68
+ end
69
+
70
+ # require all helper methods which are located in any helpers subdirectories
71
+ Dir[File.dirname(__FILE__) + '/**/helpers/*.rb'].each {|f| require f}
72
+
73
+ # require all factory modules which are located in any factories subdirectories
74
+ Dir[File.dirname(__FILE__) + '/**/factories/*.rb'].each {|f| require f}
75
+
76
+ # If necessary add source directory to load path where PL/SQL procedures are defined.
77
+ # It is not required if PL/SQL procedures are already loaded in test database in some other way.
78
+ $:.push File.dirname(__FILE__) + '/../source'
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ require 'what_is_profiled'
4
+
5
+ describe "what is profiled" do
6
+ it "should run driver" do
7
+ lambda {
8
+ plsql.what_is_profiled.driver
9
+ }.should_not raise_error
10
+ end
11
+
12
+ end
@@ -0,0 +1,262 @@
1
+ module PLSQL
2
+ class Coverage
3
+ @@coverages = {}
4
+
5
+ attr_accessor :directory
6
+
7
+ # used in tests to reset coverages cache
8
+ def self.reset_cache
9
+ @@coverages = {}
10
+ end
11
+
12
+ def self.start(connection_alias = nil)
13
+ find_or_new(connection_alias).start
14
+ end
15
+
16
+ def initialize(connection_alias)
17
+ @connection_alias = connection_alias
18
+ end
19
+
20
+ def start
21
+ # ignore repeated invocation
22
+ return false if @started
23
+ create_profiler_tables
24
+ result = plsql(@connection_alias).dbms_profiler.start_profiler(
25
+ :run_comment => "ruby-plsql-spec #{Time.now.xmlschema}",
26
+ :run_number => nil
27
+ )
28
+ @run_number = result[1][:run_number]
29
+ @coverages = nil
30
+ @started = true
31
+ end
32
+
33
+ def self.stop(connection_alias = nil)
34
+ find_or_new(connection_alias).stop
35
+ end
36
+
37
+ def stop
38
+ # ignore repeated invocation
39
+ return false unless @started
40
+ plsql(@connection_alias).dbms_profiler.stop_profiler
41
+ @started = false
42
+ true
43
+ end
44
+
45
+ def self.cleanup(connection_alias = nil)
46
+ find(connection_alias).cleanup
47
+ end
48
+
49
+ def cleanup
50
+ return false if @started
51
+ drop_or_delete_profiler_tables
52
+ true
53
+ end
54
+
55
+ def self.find(connection_alias = nil)
56
+ connection_alias ||= :default
57
+ @@coverages[connection_alias]
58
+ end
59
+
60
+ def self.report(connection_alias = nil, options = {})
61
+ # if first parameter is Hash then consider it as options and use default connection
62
+ if connection_alias.is_a?(Hash)
63
+ options = connection_alias
64
+ connection_alias = nil
65
+ end
66
+ find(connection_alias).report(options)
67
+ end
68
+
69
+ def report(options={})
70
+ # prevent repeated invocation after coverage is reported
71
+ # return if @coverages
72
+
73
+ @directory = options.delete(:directory)
74
+ coverage_data(options)
75
+ create_static_files
76
+ read_templates
77
+
78
+ # Loop through each database object, evaluating it along with the template
79
+ @coverage_data.keys.sort.each do |schema|
80
+ @coverage_data[schema].keys.sort.each do |object|
81
+ details_report(schema, object)
82
+ end
83
+ end
84
+
85
+ index_report
86
+ true
87
+ end
88
+
89
+ def coverage_data(options={})
90
+ quoted_ignore_schemas = if options[:ignore_schemas]
91
+ options[:ignore_schemas].map{|schema| quote_condition_string(schema)}
92
+ else
93
+ %w('SYS')
94
+ end
95
+ quoted_ignore_schemas << "'<anonymous>'"
96
+ like_condition = if options[:like]
97
+ 'AND ((' << Array(options[:like]).map do |like|
98
+ like_schema, like_object = like.split('.')
99
+ condition = "u.unit_owner LIKE #{quote_condition_string(like_schema)}"
100
+ condition << " AND u.unit_name LIKE #{quote_condition_string(like_object)}" if like_object
101
+ end.join(') OR (') << '))'
102
+ else
103
+ nil
104
+ end
105
+ data = {}
106
+ rows = plsql(@connection_alias).select_all <<-EOS
107
+ SELECT u.unit_owner, u.unit_name, d.line# line_number, d.total_occur
108
+ FROM plsql_profiler_units u, plsql_profiler_data d
109
+ WHERE u.runid = #{@run_number}
110
+ AND u.unit_owner NOT IN (#{quoted_ignore_schemas.join(',')})
111
+ AND u.runid = d.runid
112
+ AND u.unit_number = d.unit_number
113
+ #{like_condition}
114
+ ORDER BY u.unit_owner, u.unit_name, d.line#
115
+ EOS
116
+ rows.each do |row|
117
+ unit_owner, unit_name, line_number, total_occur = row
118
+ data[unit_owner] ||= {}
119
+ data[unit_owner][unit_name] ||= {}
120
+ data[unit_owner][unit_name][line_number] = total_occur
121
+ end
122
+ @coverage_data = data
123
+ end
124
+
125
+ private
126
+
127
+ def self.find_or_new(connection_alias) #:nodoc:
128
+ connection_alias ||= :default
129
+ if @@coverages[connection_alias]
130
+ @@coverages[connection_alias]
131
+ else
132
+ @@coverages[connection_alias] = self.new(connection_alias)
133
+ end
134
+ end
135
+
136
+ def create_profiler_tables
137
+ unless PLSQL::Table.find(plsql(@connection_alias), 'plsql_profiler_data')
138
+ proftab_file = File.expand_path('../coverage/proftab.sql', __FILE__)
139
+ File.read(proftab_file).split(";\n").each do |sql|
140
+ if sql =~ /^drop/i
141
+ plsql(@connection_alias).execute sql rescue nil
142
+ elsif sql =~ /^(create|comment)/i
143
+ plsql(@connection_alias).execute sql
144
+ end
145
+ end
146
+ @created_profiler_tables = true
147
+ end
148
+ end
149
+
150
+ def drop_or_delete_profiler_tables
151
+ if @created_profiler_tables
152
+ %w(plsql_profiler_data plsql_profiler_units plsql_profiler_runs).each do |table|
153
+ plsql(@connection_alias).execute "DROP TABLE #{table} CASCADE CONSTRAINTS"
154
+ end
155
+ plsql(@connection_alias).execute "DROP SEQUENCE plsql_profiler_runnumber"
156
+ else @run_number
157
+ plsql(@connection_alias).execute <<-SQL, @run_number
158
+ DECLARE
159
+ PRAGMA AUTONOMOUS_TRANSACTION;
160
+ v_runid BINARY_INTEGER := :runid;
161
+ BEGIN
162
+ DELETE FROM plsql_profiler_data WHERE runid = v_runid;
163
+ DELETE FROM plsql_profiler_units WHERE runid = v_runid;
164
+ DELETE FROM plsql_profiler_runs WHERE runid = v_runid;
165
+ COMMIT;
166
+ END;
167
+ SQL
168
+ end
169
+ end
170
+
171
+ def quote_condition_string(string)
172
+ "'#{string.to_s.upcase.gsub(/[^\w\d\$\%\_]/,'')}'"
173
+ end
174
+
175
+ def create_static_files
176
+ FileUtils.mkdir_p("#{@directory}")
177
+ %w(coverage.css jquery.min.js jquery.tablesorter.min.js rcov.js).each do |file|
178
+ FileUtils.cp File.expand_path("../coverage/#{file}", __FILE__), "#{@directory}/#{file}"
179
+ end
180
+ end
181
+
182
+ def read_templates
183
+ %w(details table_line index).each do |template|
184
+ template_erb = File.read(File.expand_path("../coverage/#{template}.html.erb", __FILE__))
185
+ instance_variable_set("@#{template}_template", ERB.new(template_erb, nil, '><'))
186
+ end
187
+
188
+ @table_lines = []
189
+ @total_lines = @analyzed_lines = @executed_lines = 0
190
+ end
191
+
192
+ def details_report(schema, object)
193
+ source = plsql(@connection_alias).select_all <<-EOS, schema, object
194
+ SELECT s.line, s.text
195
+ FROM all_source s
196
+ WHERE s.owner = :owner
197
+ AND s.name = :name
198
+ AND s.type NOT IN ('PACKAGE')
199
+ ORDER BY s.line
200
+ EOS
201
+ coverage = (@coverage_data[schema]||{})[object]||{}
202
+
203
+ total_lines = source.length
204
+ # return if no access to source of database object
205
+ # or if package body is wrapped
206
+ return if total_lines == 0 || source[0][1] =~ /^\s*PACKAGE BODY .* WRAPPED/i
207
+
208
+ # sometimes first PROCEDURE or FUNCTION line is reported as not executed, force ignoring it
209
+ source.each do |line, text|
210
+ if text =~ /^\s*(PROCEDURE|FUNCTION)/ && coverage[line] == 0
211
+ coverage.delete(line)
212
+ end
213
+ end
214
+
215
+ @total_lines += total_lines
216
+ analyzed_lines = executed_lines = 0
217
+ coverage.each do |line, value|
218
+ analyzed_lines += 1
219
+ executed_lines += 1 if value > 0
220
+ end
221
+ @analyzed_lines += analyzed_lines
222
+ @executed_lines += executed_lines
223
+ total_coverage = (total_lines - analyzed_lines + executed_lines).to_f / total_lines * 100
224
+ code_coverage = analyzed_lines > 0 ? executed_lines.to_f / analyzed_lines * 100 : 0
225
+
226
+ file_name = "#{schema}-#{object}.html"
227
+ object_name = "#{schema}.#{object}"
228
+
229
+ table_line_html = @table_line_template.result binding
230
+ @table_lines << table_line_html
231
+ html = @details_template.result binding
232
+
233
+ File.open("#{@directory}/#{file_name}", "w") do |file|
234
+ file.write html
235
+ end
236
+ end
237
+
238
+ def index_report
239
+ schemas = @coverage_data.keys.sort
240
+ table_lines_html = @table_lines.join("\n")
241
+
242
+ total_lines, analyzed_lines, executed_lines = @total_lines, @analyzed_lines, @executed_lines
243
+ # return if no access to source of database objects
244
+ return if total_lines == 0
245
+
246
+ total_coverage = (total_lines - analyzed_lines + executed_lines).to_f / total_lines * 100
247
+ code_coverage = analyzed_lines > 0 ? executed_lines.to_f / analyzed_lines * 100 : 0
248
+
249
+ schema = file_name = nil
250
+ object_name = 'TOTAL'
251
+
252
+ table_footer_html = @table_line_template.result binding
253
+
254
+ html = @index_template.result binding
255
+
256
+ File.open("#{@directory}/index.html", "w") do |file|
257
+ file.write html
258
+ end
259
+ end
260
+
261
+ end
262
+ end