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
|
@@ -1,297 +0,0 @@
|
|
|
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
|
-
# Class for recording scripts from qt applications.
|
|
21
|
-
# Complete test script recording not supported.
|
|
22
|
-
# Application must be running when recording is started and
|
|
23
|
-
# must not be closed as a during the recording.
|
|
24
|
-
|
|
25
|
-
module MobyUtil
|
|
26
|
-
|
|
27
|
-
class Recorder
|
|
28
|
-
|
|
29
|
-
#TODO detect app start for later versions...
|
|
30
|
-
def self.start_rec( app )
|
|
31
|
-
|
|
32
|
-
#Kernel::raise ArgumentError.new("Application must be defined.") unless app
|
|
33
|
-
app.check_type( MobyBase::TestObject, "Wrong argument type $1 for application object (expected $2)" )
|
|
34
|
-
|
|
35
|
-
app.start_recording
|
|
36
|
-
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# Prints the recorded events as an tdriver script fragment.
|
|
40
|
-
def self.print_script( sut, app, object_identificators = ['text','icontext','label'] )
|
|
41
|
-
|
|
42
|
-
# verify that sut type is type of MobyBase::SUT
|
|
43
|
-
#Kernel::raise ArgumentError.new("Sut must be defined.") unless sut
|
|
44
|
-
sut.check_type( MobyBase::SUT, "Wrong argument type $1 for SUT (expected $2)" )
|
|
45
|
-
|
|
46
|
-
#Kernel::raise ArgumentError.new("Application must be defined.") unless app
|
|
47
|
-
app.check_type( MobyBase::TestObject, "Wrong argument type $1 for application object (expected $2)" )
|
|
48
|
-
|
|
49
|
-
#Kernel::raise ArgumentError.new("Object identificators must be set, use defaults if not sure what the use.") unless object_identificators
|
|
50
|
-
object_identificators.check_type( Array, "Wrong argument type $1 for object identificators (expected $2)" )
|
|
51
|
-
|
|
52
|
-
xml_source = app.print_recordings
|
|
53
|
-
|
|
54
|
-
app.stop_recording
|
|
55
|
-
|
|
56
|
-
Scripter.new( sut.id, object_identificators ).write_fragment( MobyBase::StateObject.new( xml_source ), app.name )
|
|
57
|
-
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
end # Recorder
|
|
62
|
-
|
|
63
|
-
class Scripter
|
|
64
|
-
|
|
65
|
-
def initialize(sut_id, object_identificators)
|
|
66
|
-
|
|
67
|
-
@_object_identificators = object_identificators
|
|
68
|
-
|
|
69
|
-
@_tap_max_time = Parameter[sut_id][:record_tap_time_treshold].to_i
|
|
70
|
-
@_tap_min_distance = Parameter[sut_id][:record_move_treshold].to_i
|
|
71
|
-
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def write_fragment(xml_as_object, app_name)
|
|
75
|
-
|
|
76
|
-
script = "# Insert the script fragment below into your test \n"
|
|
77
|
-
script << "# Add verification points if needed. \n \n"
|
|
78
|
-
script << "# For testing the script! Do not include in your own test scripts. \n"
|
|
79
|
-
script << "@app = sut.application(:name =>'" << app_name << "') \n"
|
|
80
|
-
script << "# To test the script make sure the application is in the same state as it was when recording was started. \n\n"
|
|
81
|
-
script << "#################################### \n"
|
|
82
|
-
script << "# Begin recorded script \n"
|
|
83
|
-
script << "#################################### \n \n"
|
|
84
|
-
|
|
85
|
-
event_list = xml_as_object.events
|
|
86
|
-
event_count = event_list.attribute('eventCount').to_i
|
|
87
|
-
|
|
88
|
-
mouse_down = false
|
|
89
|
-
points = Array.new
|
|
90
|
-
active_target = nil
|
|
91
|
-
scripting = false;
|
|
92
|
-
mouse_status = 0
|
|
93
|
-
previous_time = nil
|
|
94
|
-
event = nil
|
|
95
|
-
|
|
96
|
-
for i in 0...event_count
|
|
97
|
-
|
|
98
|
-
event = event_list.event(:id => i.to_s)
|
|
99
|
-
type = event.name
|
|
100
|
-
|
|
101
|
-
previous_time = event.attribute('timeStamp').to_i unless previous_time
|
|
102
|
-
|
|
103
|
-
if type == 'MouseButtonPress'
|
|
104
|
-
active_target = get_target_details(event.child(:id => i.to_s))
|
|
105
|
-
scripting = true
|
|
106
|
-
mouse_status = 1
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
duration = get_duration(previous_time, event.attribute('timeStamp').to_i)
|
|
110
|
-
|
|
111
|
-
point = {'x' => event.attribute('windowX'), 'y' => event.attribute('windowY'), 'interval' => duration}
|
|
112
|
-
points.push(point) if scripting
|
|
113
|
-
|
|
114
|
-
previous_time = event.attribute('timeStamp').to_i
|
|
115
|
-
|
|
116
|
-
if type == 'MouseButtonRelease' and scripting
|
|
117
|
-
|
|
118
|
-
#mouse status based on the previous (if target changed no press)
|
|
119
|
-
mouse_status = 3 if mouse_status == 1
|
|
120
|
-
mouse_status = 2 if mouse_status == 0
|
|
121
|
-
script << generate_command(active_target, points, mouse_status) << "\n"
|
|
122
|
-
points.clear
|
|
123
|
-
active_target = nil
|
|
124
|
-
scripting = false
|
|
125
|
-
|
|
126
|
-
end
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
if scripting and event
|
|
130
|
-
|
|
131
|
-
script << generate_command(active_target, points, mouse_status) << "\n"
|
|
132
|
-
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
script << "\n"
|
|
136
|
-
script << "#################################### \n"
|
|
137
|
-
script << "# End recorded script \n"
|
|
138
|
-
script << "#################################### \n"
|
|
139
|
-
script
|
|
140
|
-
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
private
|
|
144
|
-
|
|
145
|
-
def add_command( mouse_status, active_target, points, duration )
|
|
146
|
-
|
|
147
|
-
fragment << fragment
|
|
148
|
-
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def get_target_details(test_object)
|
|
152
|
-
|
|
153
|
-
target = test_object.type
|
|
154
|
-
params = Array.new
|
|
155
|
-
|
|
156
|
-
params.push(":name=>'#{ test_object.name }'")
|
|
157
|
-
|
|
158
|
-
if test_object.name.empty?
|
|
159
|
-
|
|
160
|
-
params.clear
|
|
161
|
-
|
|
162
|
-
@_object_identificators.each do |attribute|
|
|
163
|
-
|
|
164
|
-
begin
|
|
165
|
-
|
|
166
|
-
value = test_object.attribute(attribute)
|
|
167
|
-
params.push(":#{ attribute } => '#{ value }'") unless value == ""
|
|
168
|
-
|
|
169
|
-
rescue MobyBase::AttributeNotFoundError
|
|
170
|
-
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
if params.size > 0
|
|
178
|
-
|
|
179
|
-
target << "( "
|
|
180
|
-
|
|
181
|
-
until params.size == 0
|
|
182
|
-
|
|
183
|
-
target << params.pop
|
|
184
|
-
target << ", " if params.size > 0
|
|
185
|
-
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
target << " )"
|
|
189
|
-
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
target
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
# mouse_status:
|
|
196
|
-
# 0 = no press or release
|
|
197
|
-
# 1 = press, no release
|
|
198
|
-
# 2 = release, no press
|
|
199
|
-
# 3 = press and release
|
|
200
|
-
def generate_command(target_details, points, mouse_status)
|
|
201
|
-
|
|
202
|
-
command = "@app."
|
|
203
|
-
|
|
204
|
-
if valid_gesture?(points)
|
|
205
|
-
|
|
206
|
-
command << target_details << ".gesture_points(\n\t[\n"
|
|
207
|
-
duration = 0
|
|
208
|
-
|
|
209
|
-
for i in 0...points.size
|
|
210
|
-
|
|
211
|
-
value = points[ i ]
|
|
212
|
-
command << "\t\t{'x' => " << value[ "x" ].to_s << ", 'y' => " << value[ "y" ].to_s << ", 'interval' => " << value[ "interval" ].to_s << " }"
|
|
213
|
-
command << ", \n" unless i == points.size - 1
|
|
214
|
-
duration = duration + value[ 'interval' ]
|
|
215
|
-
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
command << "\n\t], \n\t" << duration.to_s << ", \n\t"
|
|
219
|
-
|
|
220
|
-
case mouse_status
|
|
221
|
-
when 0
|
|
222
|
-
command << "{ :press => false, :release => false }"
|
|
223
|
-
when 1
|
|
224
|
-
command << "{ :press => true, :release => false }"
|
|
225
|
-
when 2
|
|
226
|
-
command << "{ :press => false, :release => true }"
|
|
227
|
-
when 3
|
|
228
|
-
command << "{ :press => true, :release => true }"
|
|
229
|
-
end
|
|
230
|
-
|
|
231
|
-
command << "\n)"
|
|
232
|
-
|
|
233
|
-
command
|
|
234
|
-
|
|
235
|
-
elsif mouse_status > 0
|
|
236
|
-
|
|
237
|
-
duration = 0
|
|
238
|
-
|
|
239
|
-
points.each{|value| duration = duration + value['interval']}
|
|
240
|
-
|
|
241
|
-
if mouse_status == 1
|
|
242
|
-
|
|
243
|
-
command << target_details << ".tap_down"
|
|
244
|
-
|
|
245
|
-
elsif duration < @_tap_max_time
|
|
246
|
-
|
|
247
|
-
command << target_details << ".tap"
|
|
248
|
-
|
|
249
|
-
else
|
|
250
|
-
|
|
251
|
-
command << target_details << ".long_tap( " << duration.to_s << " )"
|
|
252
|
-
|
|
253
|
-
end
|
|
254
|
-
|
|
255
|
-
end
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
def valid_gesture?(points)
|
|
259
|
-
|
|
260
|
-
return false if points.size < 2
|
|
261
|
-
|
|
262
|
-
min_x = -1
|
|
263
|
-
max_x = -1
|
|
264
|
-
min_y = -1
|
|
265
|
-
max_y = -1
|
|
266
|
-
|
|
267
|
-
for i in 0...points.size
|
|
268
|
-
|
|
269
|
-
value = points[i]
|
|
270
|
-
|
|
271
|
-
x = value['x'].to_i
|
|
272
|
-
y = value['y'].to_i
|
|
273
|
-
|
|
274
|
-
min_x = x if x < min_x or min_x == -1
|
|
275
|
-
max_x = x if x > max_x or max_x == -1
|
|
276
|
-
min_y = y if y < min_y or min_y == -1
|
|
277
|
-
max_y = y if y > max_y or max_y == -1
|
|
278
|
-
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
return false if (max_x - min_x).abs < @_tap_min_distance and (max_y - min_y).abs < @_tap_min_distance
|
|
282
|
-
|
|
283
|
-
true
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
def get_duration(start_time, end_time)
|
|
288
|
-
duration_millis = end_time - start_time
|
|
289
|
-
#we want this in second
|
|
290
|
-
duration_millis = duration_millis.to_f
|
|
291
|
-
duration_secs = duration_millis / 1000
|
|
292
|
-
duration_secs
|
|
293
|
-
end
|
|
294
|
-
|
|
295
|
-
end # Scripter
|
|
296
|
-
|
|
297
|
-
end
|
|
@@ -1,384 +0,0 @@
|
|
|
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
|
-
|
|
21
|
-
module MobyUtil
|
|
22
|
-
|
|
23
|
-
# Base class and interface for camera recorders in TDriver
|
|
24
|
-
class TDriverCam
|
|
25
|
-
|
|
26
|
-
# Automatically select the right kind of camera implementation for the detected platform.
|
|
27
|
-
def self.new_cam( *args )
|
|
28
|
-
|
|
29
|
-
if EnvironmentHelper.windows?
|
|
30
|
-
|
|
31
|
-
return TDriverWinCam.new( *args )
|
|
32
|
-
|
|
33
|
-
elsif EnvironmentHelper.linux?
|
|
34
|
-
|
|
35
|
-
return TDriverLinuxCam.new( *args )
|
|
36
|
-
|
|
37
|
-
else
|
|
38
|
-
raise RuntimeError.new("Unidentified platform type, unable to select platform specific camera. Supported platform types: Linux, Windows.")
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def initialize
|
|
44
|
-
raise RuntimeError.new("TDriverCam abstract class")
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def start_recording
|
|
48
|
-
raise RuntimeError.new("TDriverCam abstract class")
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def stop_recording
|
|
52
|
-
raise RuntimeError.new("TDriverCam abstract class")
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
end # TDriverCam
|
|
56
|
-
|
|
57
|
-
# Windows DirectShow webcam implementation
|
|
58
|
-
class TDriverWinCam < TDriverCam
|
|
59
|
-
|
|
60
|
-
@_device = nil
|
|
61
|
-
@_video_file = nil
|
|
62
|
-
@_recording = false
|
|
63
|
-
@_rec_options = nil
|
|
64
|
-
@_owcc_startex = nil
|
|
65
|
-
@_owcc_stop = nil
|
|
66
|
-
STARTUP_TIMEOUT = 60
|
|
67
|
-
DEFAULT_OPTIONS = { :device => nil, :width => 640, :height => 480, :fps => 30 }
|
|
68
|
-
|
|
69
|
-
# Creates a new recroding object witdh the given recording options
|
|
70
|
-
# === params
|
|
71
|
-
# video_file: String, path and name of file where the recorded video is stored
|
|
72
|
-
# user_options: (optional) Hash, keys :fps, :width, :height can be used to overwrite defaults
|
|
73
|
-
def initialize( video_file, user_options = {} )
|
|
74
|
-
|
|
75
|
-
require 'Win32API'
|
|
76
|
-
|
|
77
|
-
begin
|
|
78
|
-
|
|
79
|
-
@_owcc_startex = Win32API.new( 'TDriverWebCamControl', 'OscarWebCamControlStartEx', [ 'P', 'N', 'N', 'N', 'L', 'L', 'L', 'I', 'I' ], 'L' )
|
|
80
|
-
@_owcc_stop = Win32API.new( 'TDriverWebCamControl', 'OscarWebCamControlStop', [ 'L' ], 'V' )
|
|
81
|
-
|
|
82
|
-
rescue Exception => e
|
|
83
|
-
|
|
84
|
-
begin
|
|
85
|
-
|
|
86
|
-
#OSCARWEBCAMCONTROL_API long OscarWebCamControlStartEx(char* captureFile, double compQuality, DWORD dwBitsPerSec, long lFramesPerSec, long lCapWidth, long lCapHeight, bool bFlipV, bool bFlipH)
|
|
87
|
-
@_owcc_startex = Win32API.new( 'OscarWebCamControl', 'OscarWebCamControlStartEx', [ 'P', 'N', 'N', 'N', 'L', 'L', 'L', 'I', 'I' ], 'L' )
|
|
88
|
-
#OSCARWEBCAMCONTROL_API void OscarWebCamControlStop(long pTargetMediaControl)
|
|
89
|
-
@_owcc_stop = Win32API.new( 'OscarWebCamControl', 'OscarWebCamControlStop', [ 'L' ], 'V' )
|
|
90
|
-
|
|
91
|
-
rescue Exception => ee
|
|
92
|
-
raise RuntimeError.new( "Failed to connect to video recording DLL file (TDriverWebCamControl.dll or OscarWebCamControl.dll). Details:\n" + ee.message )
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
if user_options.has_key? :device
|
|
98
|
-
puts "WARNING: TDriverWinCam does not support the :device option. This setting is ignored."
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
@_control_id = nil
|
|
102
|
-
@_video_file = video_file
|
|
103
|
-
@_rec_options = DEFAULT_OPTIONS.merge user_options
|
|
104
|
-
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
# Starts recording based on options given during initialization
|
|
108
|
-
# === raises
|
|
109
|
-
# RuntimeError: No filename has been defined or recording initialization failed due to timeout.
|
|
110
|
-
def start_recording
|
|
111
|
-
|
|
112
|
-
raise RuntimeError.new("No video file defined, unable to start recording.") unless !@_video_file.nil?
|
|
113
|
-
|
|
114
|
-
if File.exists?( @_video_file )
|
|
115
|
-
begin
|
|
116
|
-
File.delete( @_video_file )
|
|
117
|
-
rescue
|
|
118
|
-
# no reaction to failed file ops, unless recording fails
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
@_control_id = @_owcc_startex.call( @_video_file, 0, 0, 0, @_rec_options[ :fps ].to_i, @_rec_options[ :width ].to_i, @_rec_options[ :height ].to_i, 0, 0 )
|
|
123
|
-
|
|
124
|
-
if @_control_id == 0
|
|
125
|
-
Kernel::raise RuntimeError.new( "Failed to start video recording.\nFile: " + @_video_file + "\nFPS: " + @_rec_options[ :fps ].to_s + "\nWidth: " + @_rec_options[ :width ].to_s + "\nHeight: " + @_rec_options[ :height ].to_s )
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
file_timed_out = false
|
|
129
|
-
file_timeout = Time.now + STARTUP_TIMEOUT
|
|
130
|
-
|
|
131
|
-
while File.size?( @_video_file ).nil? && !file_timed_out do
|
|
132
|
-
#wait for recording to start, ie. filesize > 0
|
|
133
|
-
sleep 1
|
|
134
|
-
# force refresh file size
|
|
135
|
-
begin
|
|
136
|
-
if File.exists?( @_video_file )
|
|
137
|
-
File.open( @_video_file, 'r' ) do
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
rescue
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
if Time.now > file_timeout
|
|
144
|
-
file_timed_out = true
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
if file_timed_out
|
|
149
|
-
# make sure recording is not initializing, clean up any failed file
|
|
150
|
-
begin
|
|
151
|
-
@_owcc_stop.call( @_control_id )
|
|
152
|
-
rescue
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
if File.exists?( @_video_file )
|
|
156
|
-
begin
|
|
157
|
-
File.delete( @_video_file )
|
|
158
|
-
rescue
|
|
159
|
-
end
|
|
160
|
-
end
|
|
161
|
-
raise RuntimeError.new( "Failed to start recording. Timeout: #{STARTUP_TIMEOUT} File: \"#{@_video_file}\" " )
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
@_recording = true
|
|
165
|
-
|
|
166
|
-
return nil
|
|
167
|
-
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
# Stops ongoing recording
|
|
171
|
-
def stop_recording
|
|
172
|
-
if @_recording
|
|
173
|
-
@_recording = false
|
|
174
|
-
@_owcc_stop.call( @_control_id )
|
|
175
|
-
end
|
|
176
|
-
return nil
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
end #TDriverWinCam
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
# Linux streamer webcam implementation
|
|
183
|
-
# Requires that the streamer application is installed
|
|
184
|
-
class TDriverLinuxCam < TDriverCam
|
|
185
|
-
|
|
186
|
-
@_device = nil
|
|
187
|
-
@_video_file = nil
|
|
188
|
-
@_recording = false
|
|
189
|
-
@_rec_options = nil
|
|
190
|
-
@_owcc_startex = nil
|
|
191
|
-
@_owcc_stop = nil
|
|
192
|
-
STARTUP_TIMEOUT = 60
|
|
193
|
-
DEFAULT_OPTIONS = { :device => '/dev/video0', :width => 320, :height => 240, :fps => 5 }
|
|
194
|
-
|
|
195
|
-
# Creates a new recroding object witdh the given recording options
|
|
196
|
-
# === params
|
|
197
|
-
# video_file: String, path and name of file where the recorded video is stored
|
|
198
|
-
# user_options: (optional) Hash, keys :fps, :width, :height can be used to overwrite defaults
|
|
199
|
-
def initialize( video_file, user_options = {} )
|
|
200
|
-
|
|
201
|
-
@_control_id = nil
|
|
202
|
-
@_video_file = video_file
|
|
203
|
-
@_rec_options = DEFAULT_OPTIONS.merge user_options
|
|
204
|
-
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
# Starts recording based on options given during initialization
|
|
208
|
-
# === raises
|
|
209
|
-
# RuntimeError: No filename has been defined or recording initialization failed due to timeout.
|
|
210
|
-
def start_recording
|
|
211
|
-
|
|
212
|
-
raise RuntimeError.new( "No video file defined, unable to start recording." ) unless !@_video_file.nil?
|
|
213
|
-
|
|
214
|
-
if File.exists?( @_video_file )
|
|
215
|
-
begin
|
|
216
|
-
File.delete( @_video_file )
|
|
217
|
-
rescue
|
|
218
|
-
# no reaction to failed file ops, unless recording fails
|
|
219
|
-
end
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
rec_command = 'streamer -q -c ' + @_rec_options[ :device ].to_s + ' -f rgb24 -r ' + @_rec_options[ :fps ].to_s + ' -t 99:00:00 -o ' + @_video_file.to_s + ' -s ' + @_rec_options[ :width ].to_s + 'x' + @_rec_options[ :height ].to_s
|
|
223
|
-
|
|
224
|
-
@_streamer_pid = fork do
|
|
225
|
-
begin
|
|
226
|
-
Kernel::exec( rec_command )
|
|
227
|
-
rescue Exception => e
|
|
228
|
-
raise RuntimeError.new( "An error was encountered while launching streamer:\n" << e.inspect )
|
|
229
|
-
end
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
file_timed_out = false
|
|
233
|
-
file_timeout = Time.now + STARTUP_TIMEOUT
|
|
234
|
-
|
|
235
|
-
while File.size?( @_video_file ).nil? && !file_timed_out do
|
|
236
|
-
#wait for recording to start, ie. filesize > 0
|
|
237
|
-
sleep 0.1
|
|
238
|
-
|
|
239
|
-
if Time.now > file_timeout
|
|
240
|
-
file_timed_out = true
|
|
241
|
-
end
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
if file_timed_out
|
|
245
|
-
# make sure recording is not initializing, clean up any failed file
|
|
246
|
-
begin
|
|
247
|
-
Process.kill( 9, @_streamer_pid.to_i )
|
|
248
|
-
rescue
|
|
249
|
-
end
|
|
250
|
-
|
|
251
|
-
if File.exists?( @_video_file )
|
|
252
|
-
begin
|
|
253
|
-
File.delete( @_video_file )
|
|
254
|
-
rescue
|
|
255
|
-
end
|
|
256
|
-
end
|
|
257
|
-
raise RuntimeError.new( "Failed to start recording. Timeout: #{STARTUP_TIMEOUT} second(s). File: \"#{@_video_file}\" " )
|
|
258
|
-
end
|
|
259
|
-
|
|
260
|
-
@_recording = true
|
|
261
|
-
|
|
262
|
-
return nil
|
|
263
|
-
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
# Stops ongoing recording
|
|
267
|
-
def stop_recording
|
|
268
|
-
if @_recording
|
|
269
|
-
@_recording = false
|
|
270
|
-
Process.kill( 9, @_streamer_pid.to_i )
|
|
271
|
-
end
|
|
272
|
-
return nil
|
|
273
|
-
end
|
|
274
|
-
|
|
275
|
-
end #TDriverLinuxCam
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
# Checks if the target video contains enough activity to be considered active or static.
|
|
279
|
-
#
|
|
280
|
-
# === params
|
|
281
|
-
# in_target_video: String, Name and path of video file to analyze
|
|
282
|
-
# in_fps: (optional) Numeric, frames to be analyzed per second
|
|
283
|
-
# in_image_treshold: (optional) Numeric, minimum change between two frames for them to be considered different
|
|
284
|
-
# in_video_treshold: (optional) Numeric, Minimum percentage of frames with changes for the video to be considered alive.
|
|
285
|
-
# in_verbose: (optional) Boolean, True for verbose output including target video statistics
|
|
286
|
-
def self.video_alive?( in_target_video, in_fps = 1, in_image_treshold = 4, in_video_treshold = 35, in_verbose = false )
|
|
287
|
-
|
|
288
|
-
puts "Arguments fps: " << in_fps.inspect << " frame: " << in_image_treshold.inspect << " video: " << in_video_treshold.inspect if in_verbose
|
|
289
|
-
in_change = in_image_treshold / 100.0
|
|
290
|
-
|
|
291
|
-
alive_temp_folder = "temp_target_alive"
|
|
292
|
-
|
|
293
|
-
require 'rmagick'
|
|
294
|
-
|
|
295
|
-
raise ArgumentError.new( "The FPS argument must be an Interger or a Float, it was a #{ in_fps.class }." ) unless in_fps.kind_of? Numeric
|
|
296
|
-
raise ArgumentError.new( "The frame treshold argument must be an Interger or a Float, it was a #{ in_image_treshold.class }." ) unless in_image_treshold.kind_of? Numeric
|
|
297
|
-
raise ArgumentError.new( "The video treshold argument must be an Interger or a Float, it was a #{ in_video_treshold.class }." ) unless in_video_treshold.kind_of? Numeric
|
|
298
|
-
|
|
299
|
-
ts = Time.now if in_verbose
|
|
300
|
-
|
|
301
|
-
begin
|
|
302
|
-
FileUtils.remove_dir alive_temp_folder
|
|
303
|
-
rescue
|
|
304
|
-
# failed to remove dir, do nothing
|
|
305
|
-
end
|
|
306
|
-
|
|
307
|
-
begin
|
|
308
|
-
FileUtils.mkdir_p alive_temp_folder
|
|
309
|
-
rescue
|
|
310
|
-
|
|
311
|
-
end
|
|
312
|
-
|
|
313
|
-
begin
|
|
314
|
-
File.delete 'video_split.log' if File.exist? 'video_split.log'
|
|
315
|
-
rescue
|
|
316
|
-
end
|
|
317
|
-
|
|
318
|
-
if in_verbose
|
|
319
|
-
system('ffmpeg.exe -v 0 -i '+in_target_video.to_s+' -y -f image2 -r '+in_fps.to_s+' '+alive_temp_folder+'/frame-%05d.png')
|
|
320
|
-
else
|
|
321
|
-
system('ffmpeg.exe 2>video_split.log -v 0 -i '+in_target_video.to_s+' -y -f image2 -r '+in_fps.to_s+' '+alive_temp_folder+'/frame-%05d.png')
|
|
322
|
-
end
|
|
323
|
-
|
|
324
|
-
puts "Video processing duration: " << (Time.now - ts).to_s if in_verbose
|
|
325
|
-
|
|
326
|
-
t_start = Time.now
|
|
327
|
-
|
|
328
|
-
im_files = Dir.glob( alive_temp_folder + '/frame-*.png' )
|
|
329
|
-
|
|
330
|
-
raise RuntimeError.new( "No video frames found for analysis." ) if im_files.size == 0
|
|
331
|
-
|
|
332
|
-
d_max = 0.0
|
|
333
|
-
d_min = 1.0
|
|
334
|
-
|
|
335
|
-
d_sum = 0.0
|
|
336
|
-
|
|
337
|
-
dif_count = 0
|
|
338
|
-
|
|
339
|
-
pre_obj = Magick::ImageList.new(im_files[0])
|
|
340
|
-
|
|
341
|
-
(im_files.size-1).times do | im_index |
|
|
342
|
-
|
|
343
|
-
im_file = Magick::ImageList.new(im_files[ im_index ])
|
|
344
|
-
pre_file = pre_obj
|
|
345
|
-
|
|
346
|
-
dif = pre_file.compare_channel(im_file, Magick::RootMeanSquaredErrorMetric)[1]
|
|
347
|
-
if in_verbose
|
|
348
|
-
d_min = dif unless dif >= d_min
|
|
349
|
-
d_max = dif unless dif <= d_max
|
|
350
|
-
d_sum += dif
|
|
351
|
-
end
|
|
352
|
-
dif_count += 1 if dif > in_change
|
|
353
|
-
puts "Processing image: " << im_file.to_s << " I: " << (im_index+1).to_s << " C: " << dif.to_s if in_verbose
|
|
354
|
-
|
|
355
|
-
pre_obj = im_file
|
|
356
|
-
|
|
357
|
-
end
|
|
358
|
-
|
|
359
|
-
if in_verbose
|
|
360
|
-
puts "Max difference: " << d_max.to_s << "\nMin difference: " << d_min.to_s << "\n"
|
|
361
|
-
puts "Mean difference: " << (d_sum/im_files.size).to_s unless im_files.size == 0
|
|
362
|
-
puts "Count of images exceeding difference tolerance: " << dif_count.to_s
|
|
363
|
-
|
|
364
|
-
puts "Fraction of images exceeding difference tolerance: " << (dif_count.to_f/im_files.size).to_s unless im_files.size == 0
|
|
365
|
-
puts "Analysis duration: " << (Time.now - t_start).to_s
|
|
366
|
-
puts "Total duration: " << (Time.now - ts).to_s
|
|
367
|
-
end
|
|
368
|
-
|
|
369
|
-
begin
|
|
370
|
-
FileUtils.remove_dir alive_temp_folder
|
|
371
|
-
rescue
|
|
372
|
-
end
|
|
373
|
-
|
|
374
|
-
begin
|
|
375
|
-
File.delete 'video_split.log' if File.exist? 'video_split.log'
|
|
376
|
-
rescue
|
|
377
|
-
end
|
|
378
|
-
|
|
379
|
-
# Check if enough frames had changes
|
|
380
|
-
return (dif_count.to_f/im_files.size)*100 >= in_video_treshold
|
|
381
|
-
|
|
382
|
-
end
|
|
383
|
-
|
|
384
|
-
end
|