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.
Files changed (116) hide show
  1. data/lib/tdriver-devtools/behaviour/xml/rdoc_behaviour_xml_generator.rb +2 -2
  2. data/lib/tdriver-devtools/tdriver-devtools.rb +1 -1
  3. data/lib/tdriver-devtools/tests/feature_tests/lib/custom_rdoc_generator.rb +3 -3
  4. data/lib/tdriver/base/behaviour/behaviours/object_behaviour_composition.rb +6 -1
  5. data/lib/tdriver/base/behaviour/behaviours/object_behaviour_description.rb +5 -3
  6. data/lib/tdriver/base/behaviour/behaviours/object_composition.rb +1 -1
  7. data/lib/tdriver/base/behaviour/factory.rb +225 -225
  8. data/lib/tdriver/base/errors.rb +1 -1
  9. data/lib/tdriver/base/state_object.rb +227 -179
  10. data/lib/tdriver/base/sut/controller.rb +2 -2
  11. data/lib/tdriver/base/sut/factory.rb +190 -182
  12. data/lib/tdriver/base/sut/generic/behaviours/application.rb +69 -25
  13. data/lib/tdriver/base/sut/generic/behaviours/controller.rb +1 -1
  14. data/lib/tdriver/base/sut/generic/behaviours/find.rb +4 -4
  15. data/lib/tdriver/base/sut/generic/behaviours/flash_behaviour.rb +3 -3
  16. data/lib/tdriver/base/sut/generic/behaviours/sut.rb +350 -165
  17. data/lib/tdriver/base/sut/generic/behaviours/switchbox_behaviour.rb +9 -9
  18. data/lib/tdriver/base/sut/generic/behaviours/verification.rb +191 -103
  19. data/lib/tdriver/base/sut/generic/commands/application.rb +1 -1
  20. data/lib/tdriver/base/sut/generic/commands/key_sequence.rb +1 -1
  21. data/lib/tdriver/base/sut/generic/commands/screen_capture.rb +1 -1
  22. data/lib/tdriver/base/sut/generic/plugin.rb +1 -1
  23. data/lib/tdriver/base/sut/sut.rb +5 -1
  24. data/lib/tdriver/base/test_object/abstract.rb +136 -151
  25. data/lib/tdriver/base/test_object/adapter.rb +293 -82
  26. data/lib/tdriver/base/test_object/behaviours/syncronization.rb +20 -17
  27. data/lib/tdriver/base/test_object/behaviours/test_object.rb +159 -532
  28. data/lib/tdriver/base/test_object/cache.rb +1 -1
  29. data/lib/tdriver/base/test_object/factory.rb +254 -605
  30. data/lib/tdriver/base/test_object/identificator.rb +1 -1
  31. data/lib/tdriver/base/test_object/loader.rb +1 -1
  32. data/lib/tdriver/base/test_object/verification.rb +17 -17
  33. data/lib/tdriver/loader.rb +20 -9
  34. data/lib/tdriver/report/report.rb +5 -0
  35. data/lib/tdriver/report/report_creator.rb +2 -2
  36. data/lib/tdriver/report/report_cucumber_listener.rb +4 -4
  37. data/lib/tdriver/report/report_cucumber_reporter.rb +4 -4
  38. data/lib/tdriver/report/report_execution_statistics.rb +22 -22
  39. data/lib/tdriver/report/report_grouping.rb +2 -2
  40. data/lib/tdriver/report/report_javascript.rb +11 -4
  41. data/lib/tdriver/report/report_test_case_run.rb +2 -2
  42. data/lib/tdriver/report/report_test_run.rb +5 -5
  43. data/lib/tdriver/report/report_test_unit.rb +74 -26
  44. data/lib/tdriver/report/report_writer.rb +70 -13
  45. data/lib/tdriver/tdriver.rb +17 -8
  46. data/lib/tdriver/util/common/array.rb +1 -1
  47. data/lib/tdriver/util/common/crc16.rb +1 -1
  48. data/lib/tdriver/util/common/environment.rb +1 -1
  49. data/lib/tdriver/util/common/file.rb +18 -9
  50. data/lib/tdriver/util/common/gem.rb +1 -1
  51. data/lib/tdriver/util/common/hash.rb +21 -0
  52. data/lib/tdriver/util/common/kernel.rb +1 -1
  53. data/lib/tdriver/util/common/loader.rb +5 -2
  54. data/lib/tdriver/util/common/numeric.rb +54 -3
  55. data/lib/tdriver/util/common/retryable.rb +30 -12
  56. data/lib/tdriver/util/common/stackable.rb +185 -0
  57. data/lib/tdriver/util/common/string.rb +21 -5
  58. data/lib/tdriver/util/{dbaccess/dbaccess.rb → database/access.rb} +4 -1
  59. data/lib/tdriver/util/{dbaccess/dbconnection.rb → database/connection.rb} +3 -0
  60. data/lib/tdriver/util/{dbaccess → database}/error.rb +0 -1
  61. data/lib/tdriver/util/{dbaccess → database}/loader.rb +5 -6
  62. data/lib/tdriver/util/{dynamic_attribute_filter.rb → filters/dynamic_attributes.rb} +1 -1
  63. data/lib/tdriver/util/hooking/hooking.rb +477 -0
  64. data/lib/tdriver/util/loader.rb +35 -29
  65. data/lib/tdriver/util/localisation/error.rb +0 -1
  66. data/lib/tdriver/util/localisation/loader.rb +1 -4
  67. data/lib/tdriver/util/localisation/localisation.rb +30 -27
  68. data/lib/tdriver/util/{common.rb → logger/loader.rb} +2 -4
  69. data/lib/tdriver/util/logger/logger.rb +574 -0
  70. data/lib/tdriver/util/operator_data/loader.rb +4 -3
  71. data/lib/tdriver/util/operator_data/operator_data.rb +5 -5
  72. data/lib/tdriver/util/parameter/parameter.rb +7 -1
  73. data/lib/tdriver/util/parameter/parameter_hash.rb +1 -1
  74. data/lib/tdriver/util/parameter/parameter_template.rb +1 -1
  75. data/lib/tdriver/util/parameter/parameter_user_api.rb +28 -20
  76. data/lib/tdriver/util/parameter/parameter_xml.rb +1 -1
  77. data/lib/tdriver/util/plugin/abstract.rb +1 -1
  78. data/lib/tdriver/util/plugin/service.rb +1 -1
  79. data/lib/tdriver/util/{localisation.rb → recorder/loader.rb} +4 -3
  80. data/lib/tdriver/util/recorder/recorder.rb +66 -0
  81. data/lib/tdriver/util/recorder/scripter.rb +258 -0
  82. data/lib/tdriver/util/{stats.rb → statistics/statistics.rb} +7 -8
  83. data/lib/tdriver/util/user_data/error.rb +0 -1
  84. data/lib/tdriver/util/user_data/loader.rb +1 -2
  85. data/lib/tdriver/util/user_data/user_data.rb +6 -6
  86. data/lib/tdriver/util/video/camera.rb +67 -0
  87. data/lib/tdriver/util/video/camera_linux.rb +139 -0
  88. data/lib/tdriver/util/video/camera_windows.rb +174 -0
  89. data/lib/tdriver/util/video/loader.rb +31 -0
  90. data/lib/tdriver/util/video/video_utils.rb +139 -0
  91. data/lib/tdriver/util/xml/abstraction.rb +56 -5
  92. data/lib/tdriver/util/xml/builder.rb +2 -5
  93. data/lib/tdriver/util/{parameter.rb → xml/comment.rb} +10 -2
  94. data/lib/tdriver/util/xml/loader.rb +32 -22
  95. data/lib/tdriver/util/xml/nil_node.rb +2 -2
  96. data/lib/tdriver/util/xml/parsers/loader.rb +0 -1
  97. data/lib/tdriver/util/xml/parsers/nokogiri/abstraction.rb +18 -44
  98. data/lib/tdriver/util/xml/parsers/nokogiri/attribute.rb +9 -13
  99. data/lib/tdriver/util/xml/parsers/nokogiri/builder.rb +9 -3
  100. data/lib/tdriver/util/xml/parsers/nokogiri/comment.rb +39 -0
  101. data/lib/tdriver/util/xml/parsers/nokogiri/document.rb +6 -11
  102. data/lib/tdriver/util/xml/parsers/nokogiri/element.rb +2 -122
  103. data/lib/tdriver/util/xml/parsers/nokogiri/loader.rb +26 -16
  104. data/lib/tdriver/util/xml/parsers/nokogiri/node.rb +203 -0
  105. data/lib/tdriver/util/xml/parsers/nokogiri/nodeset.rb +1 -2
  106. data/lib/tdriver/util/xml/parsers/nokogiri/text.rb +2 -20
  107. data/lib/tdriver/util/xml/xml.rb +52 -20
  108. data/lib/tdriver/verify/verify.rb +238 -81
  109. data/xml/behaviours/generic.xml +12 -10
  110. metadata +156 -180
  111. data/lib/tdriver/base/test_object/factory_new.rb +0 -202
  112. data/lib/tdriver/util/hooking.rb +0 -434
  113. data/lib/tdriver/util/logger.rb +0 -506
  114. data/lib/tdriver/util/recorder.rb +0 -297
  115. data/lib/tdriver/util/video_utils.rb +0 -384
  116. 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