ruby-plsql-spec 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +13 -0
- data/Gemfile +11 -0
- data/History.txt +5 -0
- data/INSTALL-Windows.markdown +55 -0
- data/License.txt +20 -0
- data/README.markdown +91 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/bin/plsql-spec +5 -0
- data/examples/source/award_bonus.rb +29 -0
- data/examples/source/betwnstr.rb +19 -0
- data/examples/source/remove_rooms_by_name.rb +45 -0
- data/examples/source/what_is_profiled.rb +207 -0
- data/examples/spec/award_bonus_spec.rb +35 -0
- data/examples/spec/betwnstr_spec.rb +24 -0
- data/examples/spec/database.yml +16 -0
- data/examples/spec/factories/employee_factory.rb +23 -0
- data/examples/spec/helpers/inspect_helpers.rb +17 -0
- data/examples/spec/helpers/oracle_ebs_helpers.rb +32 -0
- data/examples/spec/helpers/time_helpers.rb +5 -0
- data/examples/spec/oracle_ebs_spec.rb +61 -0
- data/examples/spec/remove_rooms_by_name_spec.rb +51 -0
- data/examples/spec/spec_helper.rb +78 -0
- data/examples/spec/what_is_profiled_spec.rb +12 -0
- data/lib/plsql/coverage.rb +262 -0
- data/lib/plsql/coverage/coverage.css +277 -0
- data/lib/plsql/coverage/details.html.erb +35 -0
- data/lib/plsql/coverage/index.html.erb +71 -0
- data/lib/plsql/coverage/jquery.min.js +154 -0
- data/lib/plsql/coverage/jquery.tablesorter.min.js +2 -0
- data/lib/plsql/coverage/proftab.sql +66 -0
- data/lib/plsql/coverage/rcov.js +43 -0
- data/lib/plsql/coverage/table_line.html.erb +15 -0
- data/lib/plsql/spec.rb +5 -0
- data/lib/plsql/spec/cli.rb +81 -0
- data/lib/plsql/spec/templates/database.yml +16 -0
- data/lib/plsql/spec/templates/helpers/inspect_helpers.rb +17 -0
- data/lib/plsql/spec/templates/helpers/time_helpers.rb +5 -0
- data/lib/plsql/spec/templates/spec_helper.rb +78 -0
- data/lib/plsql/spec/version.rb +5 -0
- data/lib/ruby-plsql-spec.rb +1 -0
- data/ruby-plsql-spec.gemspec +113 -0
- data/spec/plsql/coverage_spec.rb +246 -0
- data/spec/plsql/spec/cli_spec.rb +264 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +61 -0
- 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,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,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
|