testability-driver 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/tdriver-devtools/behaviour/xml/rdoc_behaviour_xml_generator.rb +2 -2
- data/lib/tdriver-devtools/tdriver-devtools.rb +1 -1
- data/lib/tdriver-devtools/tests/feature_tests/lib/custom_rdoc_generator.rb +3 -3
- data/lib/tdriver/base/behaviour/behaviours/object_behaviour_composition.rb +6 -1
- data/lib/tdriver/base/behaviour/behaviours/object_behaviour_description.rb +5 -3
- data/lib/tdriver/base/behaviour/behaviours/object_composition.rb +1 -1
- data/lib/tdriver/base/behaviour/factory.rb +225 -225
- data/lib/tdriver/base/errors.rb +1 -1
- data/lib/tdriver/base/state_object.rb +227 -179
- data/lib/tdriver/base/sut/controller.rb +2 -2
- data/lib/tdriver/base/sut/factory.rb +190 -182
- data/lib/tdriver/base/sut/generic/behaviours/application.rb +69 -25
- data/lib/tdriver/base/sut/generic/behaviours/controller.rb +1 -1
- data/lib/tdriver/base/sut/generic/behaviours/find.rb +4 -4
- data/lib/tdriver/base/sut/generic/behaviours/flash_behaviour.rb +3 -3
- data/lib/tdriver/base/sut/generic/behaviours/sut.rb +350 -165
- data/lib/tdriver/base/sut/generic/behaviours/switchbox_behaviour.rb +9 -9
- data/lib/tdriver/base/sut/generic/behaviours/verification.rb +191 -103
- data/lib/tdriver/base/sut/generic/commands/application.rb +1 -1
- data/lib/tdriver/base/sut/generic/commands/key_sequence.rb +1 -1
- data/lib/tdriver/base/sut/generic/commands/screen_capture.rb +1 -1
- data/lib/tdriver/base/sut/generic/plugin.rb +1 -1
- data/lib/tdriver/base/sut/sut.rb +5 -1
- data/lib/tdriver/base/test_object/abstract.rb +136 -151
- data/lib/tdriver/base/test_object/adapter.rb +293 -82
- data/lib/tdriver/base/test_object/behaviours/syncronization.rb +20 -17
- data/lib/tdriver/base/test_object/behaviours/test_object.rb +159 -532
- data/lib/tdriver/base/test_object/cache.rb +1 -1
- data/lib/tdriver/base/test_object/factory.rb +254 -605
- data/lib/tdriver/base/test_object/identificator.rb +1 -1
- data/lib/tdriver/base/test_object/loader.rb +1 -1
- data/lib/tdriver/base/test_object/verification.rb +17 -17
- data/lib/tdriver/loader.rb +20 -9
- data/lib/tdriver/report/report.rb +5 -0
- data/lib/tdriver/report/report_creator.rb +2 -2
- data/lib/tdriver/report/report_cucumber_listener.rb +4 -4
- data/lib/tdriver/report/report_cucumber_reporter.rb +4 -4
- data/lib/tdriver/report/report_execution_statistics.rb +22 -22
- data/lib/tdriver/report/report_grouping.rb +2 -2
- data/lib/tdriver/report/report_javascript.rb +11 -4
- data/lib/tdriver/report/report_test_case_run.rb +2 -2
- data/lib/tdriver/report/report_test_run.rb +5 -5
- data/lib/tdriver/report/report_test_unit.rb +74 -26
- data/lib/tdriver/report/report_writer.rb +70 -13
- data/lib/tdriver/tdriver.rb +17 -8
- data/lib/tdriver/util/common/array.rb +1 -1
- data/lib/tdriver/util/common/crc16.rb +1 -1
- data/lib/tdriver/util/common/environment.rb +1 -1
- data/lib/tdriver/util/common/file.rb +18 -9
- data/lib/tdriver/util/common/gem.rb +1 -1
- data/lib/tdriver/util/common/hash.rb +21 -0
- data/lib/tdriver/util/common/kernel.rb +1 -1
- data/lib/tdriver/util/common/loader.rb +5 -2
- data/lib/tdriver/util/common/numeric.rb +54 -3
- data/lib/tdriver/util/common/retryable.rb +30 -12
- data/lib/tdriver/util/common/stackable.rb +185 -0
- data/lib/tdriver/util/common/string.rb +21 -5
- data/lib/tdriver/util/{dbaccess/dbaccess.rb → database/access.rb} +4 -1
- data/lib/tdriver/util/{dbaccess/dbconnection.rb → database/connection.rb} +3 -0
- data/lib/tdriver/util/{dbaccess → database}/error.rb +0 -1
- data/lib/tdriver/util/{dbaccess → database}/loader.rb +5 -6
- data/lib/tdriver/util/{dynamic_attribute_filter.rb → filters/dynamic_attributes.rb} +1 -1
- data/lib/tdriver/util/hooking/hooking.rb +477 -0
- data/lib/tdriver/util/loader.rb +35 -29
- data/lib/tdriver/util/localisation/error.rb +0 -1
- data/lib/tdriver/util/localisation/loader.rb +1 -4
- data/lib/tdriver/util/localisation/localisation.rb +30 -27
- data/lib/tdriver/util/{common.rb → logger/loader.rb} +2 -4
- data/lib/tdriver/util/logger/logger.rb +574 -0
- data/lib/tdriver/util/operator_data/loader.rb +4 -3
- data/lib/tdriver/util/operator_data/operator_data.rb +5 -5
- data/lib/tdriver/util/parameter/parameter.rb +7 -1
- data/lib/tdriver/util/parameter/parameter_hash.rb +1 -1
- data/lib/tdriver/util/parameter/parameter_template.rb +1 -1
- data/lib/tdriver/util/parameter/parameter_user_api.rb +28 -20
- data/lib/tdriver/util/parameter/parameter_xml.rb +1 -1
- data/lib/tdriver/util/plugin/abstract.rb +1 -1
- data/lib/tdriver/util/plugin/service.rb +1 -1
- data/lib/tdriver/util/{localisation.rb → recorder/loader.rb} +4 -3
- data/lib/tdriver/util/recorder/recorder.rb +66 -0
- data/lib/tdriver/util/recorder/scripter.rb +258 -0
- data/lib/tdriver/util/{stats.rb → statistics/statistics.rb} +7 -8
- data/lib/tdriver/util/user_data/error.rb +0 -1
- data/lib/tdriver/util/user_data/loader.rb +1 -2
- data/lib/tdriver/util/user_data/user_data.rb +6 -6
- data/lib/tdriver/util/video/camera.rb +67 -0
- data/lib/tdriver/util/video/camera_linux.rb +139 -0
- data/lib/tdriver/util/video/camera_windows.rb +174 -0
- data/lib/tdriver/util/video/loader.rb +31 -0
- data/lib/tdriver/util/video/video_utils.rb +139 -0
- data/lib/tdriver/util/xml/abstraction.rb +56 -5
- data/lib/tdriver/util/xml/builder.rb +2 -5
- data/lib/tdriver/util/{parameter.rb → xml/comment.rb} +10 -2
- data/lib/tdriver/util/xml/loader.rb +32 -22
- data/lib/tdriver/util/xml/nil_node.rb +2 -2
- data/lib/tdriver/util/xml/parsers/loader.rb +0 -1
- data/lib/tdriver/util/xml/parsers/nokogiri/abstraction.rb +18 -44
- data/lib/tdriver/util/xml/parsers/nokogiri/attribute.rb +9 -13
- data/lib/tdriver/util/xml/parsers/nokogiri/builder.rb +9 -3
- data/lib/tdriver/util/xml/parsers/nokogiri/comment.rb +39 -0
- data/lib/tdriver/util/xml/parsers/nokogiri/document.rb +6 -11
- data/lib/tdriver/util/xml/parsers/nokogiri/element.rb +2 -122
- data/lib/tdriver/util/xml/parsers/nokogiri/loader.rb +26 -16
- data/lib/tdriver/util/xml/parsers/nokogiri/node.rb +203 -0
- data/lib/tdriver/util/xml/parsers/nokogiri/nodeset.rb +1 -2
- data/lib/tdriver/util/xml/parsers/nokogiri/text.rb +2 -20
- data/lib/tdriver/util/xml/xml.rb +52 -20
- data/lib/tdriver/verify/verify.rb +238 -81
- data/xml/behaviours/generic.xml +12 -10
- metadata +156 -180
- data/lib/tdriver/base/test_object/factory_new.rb +0 -202
- data/lib/tdriver/util/hooking.rb +0 -434
- data/lib/tdriver/util/logger.rb +0 -506
- data/lib/tdriver/util/recorder.rb +0 -297
- data/lib/tdriver/util/video_utils.rb +0 -384
- data/lib/tdriver/util/xml/nil_element.rb +0 -89
|
@@ -21,6 +21,13 @@ module MobyUtil
|
|
|
21
21
|
|
|
22
22
|
class Retryable
|
|
23
23
|
|
|
24
|
+
# TODO: document me
|
|
25
|
+
def self.sleep_retry_interval( time )
|
|
26
|
+
|
|
27
|
+
sleep( time )
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
|
|
24
31
|
# Function to retry code block for x times if exception raises
|
|
25
32
|
# == params
|
|
26
33
|
# options:: Hash of options
|
|
@@ -30,22 +37,21 @@ module MobyUtil
|
|
|
30
37
|
# == returns
|
|
31
38
|
def self.while( options = {}, &block )
|
|
32
39
|
|
|
33
|
-
|
|
40
|
+
# default options
|
|
41
|
+
options.default_values( :tries => 1, :interval => 0, :exception => Exception )
|
|
34
42
|
|
|
35
43
|
attempt = 1
|
|
36
44
|
|
|
37
45
|
begin
|
|
38
46
|
|
|
39
47
|
# yield given block and pass attempt number as parameter
|
|
40
|
-
|
|
48
|
+
yield( attempt )
|
|
41
49
|
|
|
42
50
|
rescue *options[ :exception ]
|
|
43
|
-
|
|
44
|
-
#if ( options[ :tries ] -= 1) > 0 && ![ *options[ :unless ] ].include?( $!.class )
|
|
45
51
|
|
|
46
52
|
if ( attempt < options[ :tries ] ) && ![ *options[ :unless ] ].include?( $!.class )
|
|
47
53
|
|
|
48
|
-
|
|
54
|
+
sleep_retry_interval( options[ :interval ] ) if options[ :interval ] > 0
|
|
49
55
|
|
|
50
56
|
attempt += 1
|
|
51
57
|
|
|
@@ -57,8 +63,7 @@ module MobyUtil
|
|
|
57
63
|
Kernel::raise $!
|
|
58
64
|
|
|
59
65
|
end
|
|
60
|
-
|
|
61
|
-
nil
|
|
66
|
+
|
|
62
67
|
end
|
|
63
68
|
|
|
64
69
|
# Function to retry code block until timeout expires if exception raises
|
|
@@ -70,28 +75,41 @@ module MobyUtil
|
|
|
70
75
|
# == returns
|
|
71
76
|
def self.until( options = {}, &block )
|
|
72
77
|
|
|
73
|
-
|
|
78
|
+
# default options
|
|
79
|
+
options.default_values( :timeout => 0, :interval => 0, :exception => Exception )
|
|
80
|
+
|
|
81
|
+
# store start time
|
|
74
82
|
start_time = Time.now
|
|
75
83
|
|
|
84
|
+
# attempt number
|
|
85
|
+
attempt = 0
|
|
86
|
+
|
|
76
87
|
begin
|
|
77
88
|
|
|
78
|
-
|
|
89
|
+
# execute block
|
|
90
|
+
yield( attempt )
|
|
79
91
|
|
|
80
92
|
rescue *options[ :exception ]
|
|
81
93
|
|
|
82
94
|
if (Time.now - start_time) <= options[ :timeout ] && ![ *options[ :unless ] ].include?( $!.class )
|
|
83
|
-
|
|
95
|
+
|
|
96
|
+
sleep_retry_interval( options[ :interval ] ) if options[ :interval ] > 0
|
|
97
|
+
|
|
98
|
+
attempt += 1
|
|
99
|
+
|
|
84
100
|
retry
|
|
101
|
+
|
|
85
102
|
end
|
|
86
103
|
|
|
87
104
|
# raise exception with correct exception backtrace
|
|
88
105
|
Kernel::raise $!
|
|
89
106
|
|
|
90
|
-
end
|
|
107
|
+
end
|
|
108
|
+
|
|
91
109
|
end
|
|
92
110
|
|
|
93
111
|
# enable hooking for performance measurement & debug logging
|
|
94
|
-
|
|
112
|
+
TDriver::Hooking.hook_methods( self ) if defined?( TDriver::Hooking )
|
|
95
113
|
|
|
96
114
|
end # Retryable
|
|
97
115
|
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
############################################################################
|
|
2
|
+
##
|
|
3
|
+
## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
|
4
|
+
## All rights reserved.
|
|
5
|
+
## Contact: Nokia Corporation (testabilitydriver@nokia.com)
|
|
6
|
+
##
|
|
7
|
+
## This file is part of Testability Driver.
|
|
8
|
+
##
|
|
9
|
+
## If you have questions regarding the use of this file, please contact
|
|
10
|
+
## Nokia at testabilitydriver@nokia.com .
|
|
11
|
+
##
|
|
12
|
+
## This library is free software; you can redistribute it and/or
|
|
13
|
+
## modify it under the terms of the GNU Lesser General Public
|
|
14
|
+
## License version 2.1 as published by the Free Software Foundation
|
|
15
|
+
## and appearing in the file LICENSE.LGPL included in the packaging
|
|
16
|
+
## of this file.
|
|
17
|
+
##
|
|
18
|
+
############################################################################
|
|
19
|
+
|
|
20
|
+
module TDriver
|
|
21
|
+
|
|
22
|
+
class StackableValue
|
|
23
|
+
|
|
24
|
+
# TODO: document me
|
|
25
|
+
def initialize( value, explicit_format = [] )
|
|
26
|
+
|
|
27
|
+
@explicit_format = explicit_format.kind_of?( Array ) ? explicit_format : [ explicit_format ]
|
|
28
|
+
|
|
29
|
+
@stack = []
|
|
30
|
+
|
|
31
|
+
push( value )
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# TODO: document me
|
|
36
|
+
def push( value )
|
|
37
|
+
|
|
38
|
+
unless @explicit_format.empty?
|
|
39
|
+
|
|
40
|
+
found = false
|
|
41
|
+
|
|
42
|
+
# collect verbose type list
|
|
43
|
+
verbose_type_list = @explicit_format.each_with_index.collect{ | type, index |
|
|
44
|
+
|
|
45
|
+
raise TypeError, "invalid argument type #{ type } for check_type. Did you mean #{ type.class }?" unless type.kind_of?( Class )
|
|
46
|
+
|
|
47
|
+
found = true if value.kind_of?( type )
|
|
48
|
+
|
|
49
|
+
# result string, separate types if multiple types given
|
|
50
|
+
"#{ ( ( index > 0 ) ? ( index + 1 < @explicit_format.count ? ", " : " or " ) : "" ) }#{ type.to_s }"
|
|
51
|
+
|
|
52
|
+
}.join
|
|
53
|
+
|
|
54
|
+
raise TypeError, "wrong variable format #{ value.class } for stackable value (expected #{ verbose_type_list })" unless found
|
|
55
|
+
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# add to stack
|
|
59
|
+
@stack << value
|
|
60
|
+
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# TODO: document me
|
|
64
|
+
def pop
|
|
65
|
+
|
|
66
|
+
@stack.pop unless ( @stack.count == 1 )
|
|
67
|
+
|
|
68
|
+
# return last value from stack
|
|
69
|
+
@stack.last
|
|
70
|
+
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# TODO: document me
|
|
74
|
+
def restore
|
|
75
|
+
|
|
76
|
+
@stack = [ @stack.first ]
|
|
77
|
+
|
|
78
|
+
# return first value in stack array
|
|
79
|
+
@stack.last
|
|
80
|
+
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# TODO: document me
|
|
84
|
+
def stacked?
|
|
85
|
+
|
|
86
|
+
# determine if there is values in stack
|
|
87
|
+
@stack.count > 1
|
|
88
|
+
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# TODO: document me
|
|
92
|
+
def to_s
|
|
93
|
+
|
|
94
|
+
# return last value in stack array as string
|
|
95
|
+
@stack.last.to_s
|
|
96
|
+
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# TODO: document me
|
|
100
|
+
def inspect
|
|
101
|
+
|
|
102
|
+
# return inspect of last value in stack array
|
|
103
|
+
@stack.last.inspect
|
|
104
|
+
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# TODO: document me
|
|
108
|
+
def count
|
|
109
|
+
|
|
110
|
+
# return size of the stack array
|
|
111
|
+
@stack.count
|
|
112
|
+
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# TODO: document me
|
|
116
|
+
def first
|
|
117
|
+
|
|
118
|
+
# return first value in stack array
|
|
119
|
+
@stack.first
|
|
120
|
+
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# TODO: document me
|
|
124
|
+
def last
|
|
125
|
+
|
|
126
|
+
# return last value in stack array
|
|
127
|
+
@stack.last
|
|
128
|
+
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# TODO: document me
|
|
132
|
+
def []( value )
|
|
133
|
+
|
|
134
|
+
# return last one if index is too high
|
|
135
|
+
value = -1 if ( value > @stack.count - 1 )
|
|
136
|
+
|
|
137
|
+
@stack[ value ]
|
|
138
|
+
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# TODO: document me
|
|
142
|
+
def []=( position, value )
|
|
143
|
+
|
|
144
|
+
# return last one if index is too high
|
|
145
|
+
value = -1 if ( position > @stack.count - 1 )
|
|
146
|
+
|
|
147
|
+
@stack[ position ] = value
|
|
148
|
+
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# TODO: document me
|
|
152
|
+
def set( value )
|
|
153
|
+
|
|
154
|
+
@stack[ -1 ] = value
|
|
155
|
+
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def ==( value )
|
|
159
|
+
|
|
160
|
+
@stack.last == value
|
|
161
|
+
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# TODO: document me
|
|
165
|
+
def kind_of?( klass )
|
|
166
|
+
|
|
167
|
+
# compary stacked value class
|
|
168
|
+
@stack.last.kind_of?( klass ) or klass == TDriver::StackableValue
|
|
169
|
+
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# create required aliases
|
|
173
|
+
alias_method :size, :count
|
|
174
|
+
|
|
175
|
+
alias_method :<<, :push
|
|
176
|
+
|
|
177
|
+
alias_method :eql?, :==
|
|
178
|
+
|
|
179
|
+
alias_method :value=, :set
|
|
180
|
+
|
|
181
|
+
alias_method :value, :last
|
|
182
|
+
|
|
183
|
+
end # StackableValue
|
|
184
|
+
|
|
185
|
+
end # TDriver
|
|
@@ -52,16 +52,32 @@ class String
|
|
|
52
52
|
# string:: String
|
|
53
53
|
# == returns
|
|
54
54
|
# TrueClass/FalseClass
|
|
55
|
-
def to_boolean
|
|
55
|
+
def to_boolean( *default )
|
|
56
56
|
|
|
57
57
|
if /^(true|false)$/i.match( self.to_s )
|
|
58
58
|
|
|
59
59
|
$1.downcase == 'true'
|
|
60
60
|
|
|
61
61
|
else
|
|
62
|
-
|
|
63
|
-
#default
|
|
64
|
-
|
|
62
|
+
|
|
63
|
+
# pass default value if string didn't contain boolean
|
|
64
|
+
if default.count > 0
|
|
65
|
+
|
|
66
|
+
# retrieve first value from array
|
|
67
|
+
default = default.first
|
|
68
|
+
|
|
69
|
+
# check that argument type is correct
|
|
70
|
+
default.check_type( [ TrueClass, FalseClass ], 'wrong argument type $1 for to_boolean default value argument (expecting $2)')
|
|
71
|
+
|
|
72
|
+
# return default value as result
|
|
73
|
+
default
|
|
74
|
+
|
|
75
|
+
else
|
|
76
|
+
|
|
77
|
+
# raise exception if no default given
|
|
78
|
+
Kernel::raise TypeError, "Unable to convert string \"#{ self }\" to boolean (Expected \"true\" or \"false\")"
|
|
79
|
+
|
|
80
|
+
end
|
|
65
81
|
|
|
66
82
|
end
|
|
67
83
|
|
|
@@ -123,7 +139,7 @@ module MobyUtil
|
|
|
123
139
|
end
|
|
124
140
|
|
|
125
141
|
# enable hooking for performance measurement & debug logging
|
|
126
|
-
|
|
142
|
+
TDriver::Hooking.hook_methods( self ) if defined?( TDriver::Hooking )
|
|
127
143
|
|
|
128
144
|
end # StringHelper
|
|
129
145
|
|
|
@@ -40,7 +40,7 @@ module MobyUtil
|
|
|
40
40
|
# == description
|
|
41
41
|
# Class Method that returns existing connections
|
|
42
42
|
#
|
|
43
|
-
def
|
|
43
|
+
def self.connections()
|
|
44
44
|
return @@_connections
|
|
45
45
|
end
|
|
46
46
|
|
|
@@ -216,6 +216,9 @@ module MobyUtil
|
|
|
216
216
|
|
|
217
217
|
end
|
|
218
218
|
|
|
219
|
+
# enable hoo./base/test_object/factory.rb:king for performance measurement & debug logging
|
|
220
|
+
TDriver::Hooking.hook_methods( self ) if defined?( TDriver::Hooking )
|
|
221
|
+
|
|
219
222
|
end # DBAccess
|
|
220
223
|
|
|
221
224
|
end # MobyUtil
|
|
@@ -17,13 +17,12 @@
|
|
|
17
17
|
##
|
|
18
18
|
############################################################################
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
20
|
# dbaccess related errors
|
|
24
21
|
require File.expand_path( File.join( File.dirname( __FILE__ ), 'error.rb' ) )
|
|
25
22
|
|
|
26
|
-
#
|
|
27
|
-
require File.expand_path( File.join( File.dirname( __FILE__ ), '
|
|
28
|
-
|
|
23
|
+
# database access module implementation
|
|
24
|
+
require File.expand_path( File.join( File.dirname( __FILE__ ), 'access.rb' ) )
|
|
25
|
+
|
|
26
|
+
# database connection module implementation
|
|
27
|
+
require File.expand_path( File.join( File.dirname( __FILE__ ), 'connection.rb' ) )
|
|
29
28
|
|
|
@@ -181,7 +181,7 @@ module MobyUtil
|
|
|
181
181
|
end
|
|
182
182
|
|
|
183
183
|
# enable hooking for performance measurement & debug logging
|
|
184
|
-
|
|
184
|
+
TDriver::Hooking.hook_methods( self ) if defined?( TDriver::Hooking )
|
|
185
185
|
|
|
186
186
|
end # DynamicAttributeFilter
|
|
187
187
|
|
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
############################################################################
|
|
2
|
+
##
|
|
3
|
+
## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
|
4
|
+
## All rights reserved.
|
|
5
|
+
## Contact: Nokia Corporation (testabilitydriver@nokia.com)
|
|
6
|
+
##
|
|
7
|
+
## This file is part of Testability Driver.
|
|
8
|
+
##
|
|
9
|
+
## If you have questions regarding the use of this file, please contact
|
|
10
|
+
## Nokia at testabilitydriver@nokia.com .
|
|
11
|
+
##
|
|
12
|
+
## This library is free software; you can redistribute it and/or
|
|
13
|
+
## modify it under the terms of the GNU Lesser General Public
|
|
14
|
+
## License version 2.1 as published by the Free Software Foundation
|
|
15
|
+
## and appearing in the file LICENSE.LGPL included in the packaging
|
|
16
|
+
## of this file.
|
|
17
|
+
##
|
|
18
|
+
############################################################################
|
|
19
|
+
module TDriver
|
|
20
|
+
|
|
21
|
+
class Hooking
|
|
22
|
+
|
|
23
|
+
# TODO: document me
|
|
24
|
+
class << self
|
|
25
|
+
|
|
26
|
+
@@non_wrappable_methods = [ 'instance' ]
|
|
27
|
+
|
|
28
|
+
# default values
|
|
29
|
+
@@wrapped_methods = {}
|
|
30
|
+
@@wrappee_count = 0
|
|
31
|
+
|
|
32
|
+
@@benchmark = {}
|
|
33
|
+
|
|
34
|
+
@@logger_instance = nil
|
|
35
|
+
|
|
36
|
+
$tdriver_hooking = TDriver::Hooking
|
|
37
|
+
$tdriver_hooking_elapsed_time_stack = []
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
# Function to hook a method
|
|
42
|
+
# == params
|
|
43
|
+
# base:: Class or Module
|
|
44
|
+
# method_name:: Name of the method
|
|
45
|
+
# method_type:: public, private or static
|
|
46
|
+
# == returns
|
|
47
|
+
def hook_method( base, method_name, method_type )
|
|
48
|
+
|
|
49
|
+
# create only one wrapper for each method
|
|
50
|
+
unless @@wrapped_methods.has_key?( "#{ base.name }::#{ method_name }" )
|
|
51
|
+
|
|
52
|
+
# evaluate the generated wrapper source code
|
|
53
|
+
eval("base.#{ base.class.name.downcase }_eval( \"#{ make_wrapper( base, method_name.to_s, method_type.to_s )}\" )") if [ Class, Module ].include?( base.class )
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
nil
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Function to hook static methods for given Class or Module
|
|
62
|
+
# == params
|
|
63
|
+
# base:: Target Class or Module
|
|
64
|
+
# == returns
|
|
65
|
+
def hook_static_methods( _base )
|
|
66
|
+
|
|
67
|
+
if [ Class, Module ].include?( _base.class )
|
|
68
|
+
|
|
69
|
+
_base.singleton_methods( false ).each { | method_name |
|
|
70
|
+
|
|
71
|
+
hook_method( _base, method_name.to_s, "static" ) unless @@non_wrappable_methods.include?( method_name.to_s ) # method_name.to_s for ruby 1.9 compatibility
|
|
72
|
+
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
nil
|
|
78
|
+
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Function to hook instance methods for given Class or Module
|
|
82
|
+
# == params
|
|
83
|
+
# base:: Target Class or Module
|
|
84
|
+
# == returns
|
|
85
|
+
def hook_instance_methods( _base )
|
|
86
|
+
|
|
87
|
+
if [ Class, Module ].include?( _base.class )
|
|
88
|
+
|
|
89
|
+
{
|
|
90
|
+
:public => _base.public_instance_methods( false ),
|
|
91
|
+
:private => _base.private_instance_methods( false ),
|
|
92
|
+
:protected => _base.protected_instance_methods( false )
|
|
93
|
+
|
|
94
|
+
}.each{ | method_type, methods |
|
|
95
|
+
|
|
96
|
+
# method_name.to_s for ruby 1.9 compatibility
|
|
97
|
+
methods.each { | method_name | hook_method( _base, method_name.to_s, method_type.to_s ) unless /__wrappee_\d+/i.match( method_name.to_s ) }
|
|
98
|
+
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
nil
|
|
104
|
+
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Function to retrieve method path (e.g. Module1::Module2::Class1)
|
|
108
|
+
# == params
|
|
109
|
+
# base:: Target Class or Module
|
|
110
|
+
# == returns
|
|
111
|
+
# String:: Method path
|
|
112
|
+
def method_path( _base )
|
|
113
|
+
|
|
114
|
+
[ Class, Module ].include?( _base.class ) ? _base.name : _base.class.name
|
|
115
|
+
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Function to generate unique name for wrappee method
|
|
119
|
+
# == params
|
|
120
|
+
# method_name:: Name of the target method
|
|
121
|
+
# == returns
|
|
122
|
+
# String:: Unique name for wrappee method
|
|
123
|
+
def create_wrappee_name( method_name )
|
|
124
|
+
|
|
125
|
+
wrappee_name = "non_pritanble_method_name" if ( wrappee_name = ( /[a-z0-9_]*/i.match( method_name ) ) ).length == 0
|
|
126
|
+
|
|
127
|
+
"__wrappee_#{ @@wrappee_count }__#{ wrappee_name }"
|
|
128
|
+
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Function for create source code of wrapper for method
|
|
132
|
+
# == params
|
|
133
|
+
# base:: Class or Module
|
|
134
|
+
# method_name:: Name of the method
|
|
135
|
+
# method_type:: public, private or static
|
|
136
|
+
# == returns
|
|
137
|
+
# String:: source code
|
|
138
|
+
def make_wrapper( base, method_name, method_type = nil )
|
|
139
|
+
|
|
140
|
+
# method name with namespace
|
|
141
|
+
base_and_method_name = "#{ base.name }::#{ method_name }"
|
|
142
|
+
|
|
143
|
+
# add method to benchmark table if enabled
|
|
144
|
+
if ENV[ 'TDRIVER_BENCHMARK' ].to_s.downcase == 'true'
|
|
145
|
+
|
|
146
|
+
@@benchmark[ base_and_method_name ] = {
|
|
147
|
+
:time_elapsed => 0,
|
|
148
|
+
:times_called => 0,
|
|
149
|
+
:time_elapsed_total => 0
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# create new name for original method
|
|
155
|
+
original_method_name = create_wrappee_name( method_name )
|
|
156
|
+
|
|
157
|
+
# add method to wrapper methods list
|
|
158
|
+
@@wrapped_methods[ base_and_method_name ] = nil
|
|
159
|
+
|
|
160
|
+
@@wrappee_count += 1
|
|
161
|
+
|
|
162
|
+
case method_type
|
|
163
|
+
|
|
164
|
+
when 'public', 'private', 'static'
|
|
165
|
+
|
|
166
|
+
"#{
|
|
167
|
+
# this is needed if method is static
|
|
168
|
+
"class << self" if method_type == 'static'
|
|
169
|
+
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
# create a copy of original method
|
|
173
|
+
alias_method :#{ original_method_name }, :#{ method_name }
|
|
174
|
+
|
|
175
|
+
#{
|
|
176
|
+
|
|
177
|
+
if method_type == 'static'
|
|
178
|
+
|
|
179
|
+
# undefine original version if static method
|
|
180
|
+
"self.__send__( :undef_method, :#{ method_name } )"
|
|
181
|
+
|
|
182
|
+
else
|
|
183
|
+
|
|
184
|
+
# method visiblity unless method type is static
|
|
185
|
+
"#{ method_type }"
|
|
186
|
+
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
def #{ method_name }( *args, &block )
|
|
192
|
+
|
|
193
|
+
# log method call
|
|
194
|
+
$tdriver_hooking.log( '#{ method_path( base ) }.#{ method_name }', nil )
|
|
195
|
+
|
|
196
|
+
#{
|
|
197
|
+
|
|
198
|
+
if ENV[ 'TDRIVER_BENCHMARK' ].to_s.downcase == 'true'
|
|
199
|
+
|
|
200
|
+
"# store start time for performance measurement
|
|
201
|
+
start_time = Time.now
|
|
202
|
+
|
|
203
|
+
# Time elapsed in sub calls
|
|
204
|
+
$tdriver_hooking_elapsed_time_stack << 0.0
|
|
205
|
+
|
|
206
|
+
begin
|
|
207
|
+
|
|
208
|
+
# call and return result of original method
|
|
209
|
+
__send__(:#{ original_method_name }, *args, &block )
|
|
210
|
+
|
|
211
|
+
rescue
|
|
212
|
+
|
|
213
|
+
raise $!
|
|
214
|
+
|
|
215
|
+
ensure
|
|
216
|
+
|
|
217
|
+
# calculate actual elapsed time, including time elapsed in sub calls
|
|
218
|
+
elapsed_time = Time.now - start_time
|
|
219
|
+
|
|
220
|
+
# elapsed time in sub calls
|
|
221
|
+
elapsed_time_in_subcalls = $tdriver_hooking_elapsed_time_stack.pop || 0
|
|
222
|
+
|
|
223
|
+
# add elapsed time to caller method
|
|
224
|
+
$tdriver_hooking_elapsed_time_stack[ -1 ] += elapsed_time unless $tdriver_hooking_elapsed_time_stack.empty?
|
|
225
|
+
|
|
226
|
+
# store performance results to benchmark hash
|
|
227
|
+
$tdriver_hooking.update_method_benchmark( '#{ base_and_method_name }', elapsed_time_in_subcalls, elapsed_time )
|
|
228
|
+
|
|
229
|
+
end"
|
|
230
|
+
|
|
231
|
+
else
|
|
232
|
+
|
|
233
|
+
"# call original method
|
|
234
|
+
__send__(:#{ original_method_name }, *args, &block )"
|
|
235
|
+
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
private :#{ original_method_name }
|
|
243
|
+
|
|
244
|
+
#{
|
|
245
|
+
|
|
246
|
+
# this is needed if method is static
|
|
247
|
+
"end" if method_type == 'static'
|
|
248
|
+
|
|
249
|
+
}"
|
|
250
|
+
|
|
251
|
+
end # case
|
|
252
|
+
|
|
253
|
+
end # make_wrapper
|
|
254
|
+
|
|
255
|
+
end # self
|
|
256
|
+
|
|
257
|
+
# TODO: document me
|
|
258
|
+
def self.wrappee_count
|
|
259
|
+
|
|
260
|
+
@@wrappee_count
|
|
261
|
+
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# TODO: document me
|
|
265
|
+
def self.wrappee_count=( value )
|
|
266
|
+
|
|
267
|
+
@@wrappee_count = value
|
|
268
|
+
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# TODO: document me
|
|
272
|
+
def self.wrappee_methods
|
|
273
|
+
|
|
274
|
+
@@wrappee_methods
|
|
275
|
+
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# TODO: document me
|
|
279
|
+
def self.wrappee_methods=( value )
|
|
280
|
+
|
|
281
|
+
@@wrappee_methods = value
|
|
282
|
+
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
# TODO: document me
|
|
287
|
+
def self.benchmark
|
|
288
|
+
|
|
289
|
+
@@benchmark
|
|
290
|
+
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# TODO: document me
|
|
294
|
+
def self.benchmark=( value )
|
|
295
|
+
|
|
296
|
+
@@benchmark = value
|
|
297
|
+
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# TODO: document me
|
|
301
|
+
def self.logger_instance
|
|
302
|
+
|
|
303
|
+
@@logger_instance
|
|
304
|
+
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
# Function to set logger instance used by wrapper
|
|
308
|
+
# == params
|
|
309
|
+
# logger_instance:: Instance of TDriver logger
|
|
310
|
+
# == returns
|
|
311
|
+
def self.logger_instance=( logger_instance )
|
|
312
|
+
|
|
313
|
+
@@logger_instance = logger_instance
|
|
314
|
+
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# Function to create logger event - this method is called from wrapper
|
|
318
|
+
# == params
|
|
319
|
+
# text:: Text sent from wrapper
|
|
320
|
+
# arguments:: Not in use
|
|
321
|
+
# == returns
|
|
322
|
+
def self.log( text, *arguments )
|
|
323
|
+
|
|
324
|
+
@@logger_instance.debug( text.to_s ) if @@logger_instance
|
|
325
|
+
|
|
326
|
+
nil
|
|
327
|
+
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# Function to hook all instance and static methods of target Class/Module
|
|
331
|
+
# == params
|
|
332
|
+
# base:: Target Class or Module
|
|
333
|
+
# == returns
|
|
334
|
+
def self.hook_methods( _base )
|
|
335
|
+
|
|
336
|
+
hook_static_methods( _base )
|
|
337
|
+
|
|
338
|
+
hook_instance_methods( _base )
|
|
339
|
+
|
|
340
|
+
nil
|
|
341
|
+
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# Function to update method runtime & calls count for benchmark
|
|
345
|
+
# == params
|
|
346
|
+
# method_name:: Name of the target method
|
|
347
|
+
# time_elapsed_in_subcalls::
|
|
348
|
+
# total_time_elapsed::
|
|
349
|
+
def self.update_method_benchmark( method_name, time_elapsed_in_subcalls, total_time_elapsed )
|
|
350
|
+
|
|
351
|
+
@@benchmark[ method_name ].tap{ | hash |
|
|
352
|
+
|
|
353
|
+
hash[ :time_elapsed ] += total_time_elapsed - time_elapsed_in_subcalls
|
|
354
|
+
hash[ :time_elapsed_total ] += total_time_elapsed
|
|
355
|
+
hash[ :times_called ] += 1
|
|
356
|
+
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def self.print_benchmark( rules = {} )
|
|
362
|
+
|
|
363
|
+
total_run_time = 0
|
|
364
|
+
|
|
365
|
+
# :sort => :total_time || :times_called || :average_time
|
|
366
|
+
|
|
367
|
+
rules = { :sort => :total_time, :order => :ascending, :show_uncalled_methods => true }.merge( rules )
|
|
368
|
+
|
|
369
|
+
puts "%-80s %8s %15s %15s %9s %15s" % [ 'Name:', 'Calls:', 'Time total:', 'W/O subcalls:', '%/run', 'Total/call:' ]
|
|
370
|
+
puts "%-80s %8s %15s %15s %9s %15s" % [ '-' * 80, '-' * 8, '-' * 15, '-' * 15, '-' * 8, '-' * 15 ]
|
|
371
|
+
|
|
372
|
+
table = @@benchmark
|
|
373
|
+
|
|
374
|
+
# calculate average time for method
|
|
375
|
+
table.each{ | key, value |
|
|
376
|
+
|
|
377
|
+
table[ key ][ :average_time ] = ( value[ :times_elapsed_total ] == 0 || value[ :times_called ] == 0 ) ? 0 : value[ :time_elapsed_total ] / value[ :times_called ]
|
|
378
|
+
|
|
379
|
+
total_run_time += value[ :time_elapsed ]
|
|
380
|
+
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
table = table.sort{ | method_a, method_b |
|
|
384
|
+
|
|
385
|
+
case rules[ :sort ]
|
|
386
|
+
|
|
387
|
+
when :name
|
|
388
|
+
method_a[ 0 ] <=> method_b[ 0 ]
|
|
389
|
+
|
|
390
|
+
when :times_called
|
|
391
|
+
method_a[ 1 ][ :times_called ] <=> method_b[ 1 ][ :times_called ]
|
|
392
|
+
|
|
393
|
+
when :total_time
|
|
394
|
+
method_a[ 1 ][ :time_elapsed_total ] <=> method_b[ 1 ][ :time_elapsed_total ]
|
|
395
|
+
|
|
396
|
+
when :total_time_no_subs
|
|
397
|
+
method_a[ 1 ][ :time_elapsed ] <=> method_b[ 1 ][ :time_elapsed ]
|
|
398
|
+
|
|
399
|
+
when :percentage
|
|
400
|
+
|
|
401
|
+
( ( method_a[ 1 ][ :time_elapsed ].to_f / total_run_time.to_f ) * 100 ) <=> ( ( method_b[ 1 ][ :time_elapsed ].to_f / total_run_time.to_f ) * 100 )
|
|
402
|
+
|
|
403
|
+
when :average_time
|
|
404
|
+
method_a[ 1 ][ :average_time ] <=> method_b[ 1 ][ :average_time ]
|
|
405
|
+
|
|
406
|
+
else
|
|
407
|
+
|
|
408
|
+
Kernel::raise ArgumentError.new("Invalid sorting rule, valid rules are :name, :times_called, :total_time, :total_time_no_subs, :percentage or :average_time")
|
|
409
|
+
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
case rules[ :order ]
|
|
415
|
+
|
|
416
|
+
# do nothing
|
|
417
|
+
when :ascending
|
|
418
|
+
|
|
419
|
+
when :descending
|
|
420
|
+
table = table.reverse
|
|
421
|
+
|
|
422
|
+
else
|
|
423
|
+
|
|
424
|
+
Kernel::raise ArgumentError.new("Invalid sort order rule, valid rules are :ascending, :descending")
|
|
425
|
+
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
total_percentage = 0.0
|
|
429
|
+
total_time_elapsed_total = 0.0
|
|
430
|
+
total_average = 0.0
|
|
431
|
+
total_calls = 0
|
|
432
|
+
|
|
433
|
+
table.each{ | method |
|
|
434
|
+
|
|
435
|
+
puts "%-80s %8s %15.8f %15.8f %8.3f%% %15.8f" % [
|
|
436
|
+
method[ 0 ],
|
|
437
|
+
method[ 1 ][ :times_called ],
|
|
438
|
+
method[ 1 ][ :time_elapsed_total ],
|
|
439
|
+
method[ 1 ][ :time_elapsed ],
|
|
440
|
+
|
|
441
|
+
( ( method[ 1 ][ :time_elapsed ].to_f / total_run_time.to_f ) * 100 ),
|
|
442
|
+
|
|
443
|
+
method[ 1 ][ :average_time ]
|
|
444
|
+
] unless !rules[ :show_uncalled_methods ] && method[ 1 ][ :times_called ] == 0
|
|
445
|
+
|
|
446
|
+
total_percentage += ( ( method[ 1 ][ :time_elapsed ].to_f / total_run_time.to_f ) * 100 )
|
|
447
|
+
|
|
448
|
+
total_calls += method[ 1 ][ :times_called ]
|
|
449
|
+
total_time_elapsed_total += method[ 1 ][ :time_elapsed_total ]
|
|
450
|
+
total_average += method[ 1 ][ :average_time ]
|
|
451
|
+
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
puts "%-80s %8s %15s %15s %9s %15s" % [ '-' * 80, '-' * 8, '-' * 15, '-' * 15, '-' * 8, '-' * 15 ]
|
|
455
|
+
|
|
456
|
+
puts "%-80s %8s %15.6f %15.6f %8.3f%% %15.8f" % [ 'Total:', total_calls, total_time_elapsed_total, total_run_time, total_percentage, total_average ]
|
|
457
|
+
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
end # Hooking
|
|
461
|
+
|
|
462
|
+
end # TDriver
|
|
463
|
+
|
|
464
|
+
# deprecated
|
|
465
|
+
module MobyUtil
|
|
466
|
+
|
|
467
|
+
class Hooking
|
|
468
|
+
|
|
469
|
+
def self.instance
|
|
470
|
+
|
|
471
|
+
TDriver::Hooking
|
|
472
|
+
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
end # Hooking
|
|
476
|
+
|
|
477
|
+
end # MobyUtil
|