testability-driver 1.1.1 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/config/sut_parameters.rb +21 -8
  2. data/config/tdriver_custom_error_recovery.rb +83 -0
  3. data/ext/extconf.rb +3 -2
  4. data/ext/native_extensions.c +60 -2
  5. data/lib/tdriver-devtools/behaviour/old/xml/example/flick-example.rb +2 -105
  6. data/lib/tdriver/base/behaviour/factory.rb +154 -89
  7. data/lib/tdriver/base/behaviour/factory_new.rb +409 -0
  8. data/lib/tdriver/base/errors.rb +3 -3
  9. data/lib/tdriver/base/state_object.rb +85 -22
  10. data/lib/tdriver/base/sut/adapter.rb +26 -0
  11. data/lib/tdriver/base/sut/controller.rb +1 -1
  12. data/lib/tdriver/base/sut/generic/behaviours/application.rb +89 -118
  13. data/lib/tdriver/base/sut/generic/behaviours/find.rb +67 -62
  14. data/lib/tdriver/base/sut/generic/behaviours/sut.rb +296 -187
  15. data/lib/tdriver/base/sut/generic/behaviours/switchbox_behaviour.rb +7 -7
  16. data/lib/tdriver/base/sut/generic/commands/application.rb +366 -295
  17. data/lib/tdriver/base/sut/sut.rb +19 -3
  18. data/lib/tdriver/base/test_object/abstract.rb +41 -21
  19. data/lib/tdriver/base/test_object/adapter.rb +62 -9
  20. data/lib/tdriver/base/test_object/behaviours/syncronization.rb +10 -6
  21. data/lib/tdriver/base/test_object/behaviours/test_object.rb +84 -47
  22. data/lib/tdriver/base/test_object/factory.rb +124 -68
  23. data/lib/tdriver/base/test_object/loader.rb +3 -4
  24. data/lib/tdriver/base/test_object/verification.rb +3 -3
  25. data/lib/tdriver/base/test_object/xml/adapter.rb +734 -0
  26. data/lib/tdriver/loader.rb +12 -0
  27. data/lib/tdriver/report/error_recovery/tdriver_error_recovery.rb +3 -2
  28. data/lib/tdriver/report/error_recovery/tdriver_error_recovery_settings.rb +14 -14
  29. data/lib/tdriver/report/report.rb +4 -8
  30. data/lib/tdriver/report/report_api.rb +9 -0
  31. data/lib/tdriver/report/report_crash_file_capture.rb +4 -4
  32. data/lib/tdriver/report/report_creator.rb +57 -35
  33. data/lib/tdriver/report/report_cucumber.rb +1 -1
  34. data/lib/tdriver/report/report_cucumber_listener.rb +5 -158
  35. data/lib/tdriver/report/report_cucumber_reporter.rb +7 -161
  36. data/lib/tdriver/report/report_execution_statistics.rb +4 -4
  37. data/lib/tdriver/report/report_file_capture.rb +5 -5
  38. data/lib/tdriver/report/report_grouping.rb +24 -22
  39. data/lib/tdriver/report/report_junit_xml.rb +5 -5
  40. data/lib/tdriver/report/report_test_case_run.rb +31 -22
  41. data/lib/tdriver/report/report_test_run.rb +107 -104
  42. data/lib/tdriver/report/report_writer.rb +150 -83
  43. data/lib/tdriver/tdriver.rb +147 -103
  44. data/lib/tdriver/util/common/boolean.rb +51 -0
  45. data/lib/tdriver/util/common/crc16.rb +110 -68
  46. data/lib/tdriver/util/common/hash.rb +63 -7
  47. data/lib/tdriver/util/common/kernel.rb +46 -1
  48. data/lib/tdriver/util/common/loader.rb +1 -0
  49. data/lib/tdriver/util/common/object.rb +20 -8
  50. data/lib/tdriver/util/common/string.rb +21 -2
  51. data/lib/tdriver/util/logger/logger.rb +4 -4
  52. data/lib/tdriver/util/parameter/loader.rb +2 -19
  53. data/lib/tdriver/util/parameter/parameter.rb +874 -177
  54. data/lib/tdriver/util/plugin/service.rb +1 -1
  55. data/lib/tdriver/util/recorder/recorder.rb +7 -1
  56. data/lib/tdriver/util/xml/abstraction.rb +13 -1
  57. data/lib/tdriver/util/xml/parsers/nokogiri/abstraction.rb +63 -10
  58. data/lib/tdriver/util/xml/parsers/nokogiri/attribute.rb +8 -2
  59. data/lib/tdriver/util/xml/parsers/nokogiri/document.rb +16 -3
  60. data/lib/tdriver/util/xml/parsers/nokogiri/node.rb +36 -32
  61. data/lib/tdriver/util/xml/parsers/nokogiri/nodeset.rb +19 -22
  62. data/lib/tdriver/util/xml/xml.rb +147 -32
  63. data/lib/tdriver/verify/verify.rb +1112 -289
  64. data/lib/tdriver/version.rb +1 -1
  65. data/xml/templates/generic.xml +14 -2
  66. metadata +51 -24
  67. data/lib/tdriver/util/parameter/parameter_hash.rb +0 -104
  68. data/lib/tdriver/util/parameter/parameter_new.rb +0 -869
  69. data/lib/tdriver/util/parameter/parameter_template.rb +0 -120
  70. data/lib/tdriver/util/parameter/parameter_user_api.rb +0 -116
  71. data/lib/tdriver/util/parameter/parameter_xml.rb +0 -261
@@ -27,10 +27,10 @@ module MobyBase
27
27
 
28
28
  include Singleton
29
29
 
30
- attr_reader :timeout
30
+ attr_reader :timeout, :test_object_adapter
31
31
 
32
32
  # TODO: Document me (TestObjectFactory::initialize)
33
- def initialize
33
+ def initialize( options = {} )
34
34
 
35
35
  # get timeout from parameters, use default value if parameter not found
36
36
  @timeout = $parameters[ :application_synchronization_timeout, "20" ].to_f
@@ -38,6 +38,8 @@ module MobyBase
38
38
  # get timeout retry interval from parameters, use default value if parameter not found
39
39
  @_retry_interval = $parameters[ :application_synchronization_retry_interval, "1" ].to_f
40
40
 
41
+ #@test_object_adapter = TDriver::TestObjectAdapter
42
+
41
43
  end
42
44
 
43
45
  # Function to set timeout for TestObjectFactory
@@ -65,6 +67,9 @@ module MobyBase
65
67
  # retrieve sut object
66
68
  sut = rules[ :parent ].instance_variable_get( :@sut )
67
69
 
70
+ # retrieve sut objects test object adapter
71
+ test_object_adapter = sut.instance_variable_get( :@test_object_adapter )
72
+
68
73
  # search parameters for find_objects feature
69
74
  search_parameters = make_object_search_params( rules[ :parent ], rules[ :object_attributes_hash ] )
70
75
 
@@ -90,10 +95,12 @@ module MobyBase
90
95
  :__xy_sorting => directives.has_key?( :__index ),
91
96
 
92
97
  # determine index of test object to be retrieved
93
- :__index => 0
94
-
98
+ :__index => 0,
99
+
100
+ :__retriable_allowed_exceptions => [ MobyBase::TestObjectNotFoundError, MobyBase::MultipleTestObjectsIdentifiedError ]
101
+
95
102
  )
96
-
103
+
97
104
  # identify objects until desired matches found or timeout exceeds
98
105
  MobyUtil::Retryable.until(
99
106
 
@@ -104,15 +111,18 @@ module MobyBase
104
111
  :interval => directives[ :__retry_interval ],
105
112
 
106
113
  # following exceptions are allowed; Retry until timeout exceeds or other exception type is raised
107
- :exception => [ MobyBase::TestObjectNotFoundError, MobyBase::MultipleTestObjectsIdentifiedError ]
108
-
114
+ :exception => directives[ :__retriable_allowed_exceptions ]
115
+
109
116
  ){
110
117
 
111
118
  # refresh sut
112
119
  sut.refresh( directives[ :__refresh_arguments ], search_parameters )
113
120
 
121
+ # in case of test object adapter was change during the refresh (if connected the SUT first time, see possible 'connect_after' hook from sut plugin)
122
+ test_object_adapter = sut.instance_variable_get( :@test_object_adapter )
123
+
114
124
  # retrieve objects from xml
115
- matches, rule = TDriver::TestObjectAdapter.get_objects(
125
+ matches, rule = test_object_adapter.get_objects(
116
126
 
117
127
  rules[ :parent ].xml_data,
118
128
  rules[ :object_attributes_hash ],
@@ -120,14 +130,40 @@ module MobyBase
120
130
 
121
131
  )
122
132
 
133
+ # Temporary prevent users misleading :Text with :text (as they are different)
134
+ if ( rules[ :object_attributes_hash ].has_key?(:Text) )
135
+
136
+ rules[ :object_attributes_hash ][:text] = rules[ :object_attributes_hash ][:Text]
137
+
138
+ end
139
+
140
+ # If retrying and regexp search is turned on then update the rules for text search converting it to a regex
141
+ if (
142
+ matches.empty? and
143
+ $parameters[ sut.id ][:elided_search, 'false'] == 'true' and
144
+ rules[ :object_attributes_hash ].has_key?(:text) and
145
+ rules[ :object_attributes_hash ][ :text ].kind_of? String
146
+ )
147
+ text_string = rules[ :object_attributes_hash ][ :text ]
148
+ if ( $parameters[ sut.id ][:elided_search_with_ellipsis , 'false'] == 'true' )
149
+ ellipsis_char = ".*\xE2\x80\xA6" # unicode \u2026 the ... character \xE2\x80\xA6
150
+ else
151
+ ellipsis_char = ""
152
+ end
153
+ elided_regex = Regexp.new( text_string[0..3] + ellipsis_char )
154
+ rules[ :object_attributes_hash ][ :text ] = elided_regex
155
+ end
156
+
123
157
  # raise exception if no matching object(s) found
124
158
  raise MobyBase::TestObjectNotFoundError, "Cannot find object with rule:\n#{ rules[ :object_attributes_hash ].inspect }" if matches.empty?
125
159
 
126
160
  # raise exception if multiple matches found and only one expected
127
161
  if ( !directives[ :__multiple_objects ] ) && ( matches.count > 1 && !directives[ :__index_given ] )
128
162
 
163
+ message = "Multiple test objects found with rule: #{ rules[ :object_attributes_hash ].inspect }\nMatching objects:\n#{ list_matching_test_objects_as_list( test_object_adapter, matches ) }\n"
164
+
129
165
  # raise exception (with list of paths to all matching objects) if multiple objects flag is false and more than one match found
130
- raise MobyBase::MultipleTestObjectsIdentifiedError, "Multiple test objects found with rule: #{ rules[ :object_attributes_hash ].inspect }\nMatching objects:\n#{ list_matching_test_objects_as_list( matches ) }\n"
166
+ raise MobyBase::MultipleTestObjectsIdentifiedError, message
131
167
 
132
168
  end
133
169
 
@@ -135,9 +171,9 @@ module MobyBase
135
171
  if directives[ :__xy_sorting ] == true
136
172
 
137
173
  # sort elements
138
- TDriver::TestObjectAdapter.sort_elements(
174
+ test_object_adapter.sort_elements(
139
175
  matches,
140
- TDriver::TestObjectAdapter.application_layout_direction( sut )
176
+ test_object_adapter.application_layout_direction( sut )
141
177
  )
142
178
 
143
179
  end
@@ -162,6 +198,12 @@ module MobyBase
162
198
  # TODO: document me
163
199
  def get_test_objects( rules )
164
200
 
201
+ # retrieve sut object
202
+ sut = rules[ :parent ].instance_variable_get( :@sut )
203
+
204
+ # retrieve sut objects test object adapter
205
+ test_object_adapter = sut.instance_variable_get( :@test_object_adapter )
206
+
165
207
  # store rules hash to variable
166
208
  object_attributes_hash = rules[ :object_attributes_hash ].clone
167
209
 
@@ -243,29 +285,29 @@ module MobyBase
243
285
  MobyUtil::DynamicAttributeFilter.instance.add_attributes( object_attributes_hash.keys )
244
286
 
245
287
  child_objects = identify_object( rules ).collect{ | test_object_xml |
288
+
289
+ # in case of test object adapter was change during the refresh (if connected the SUT first time, see possible 'connect_after' hook from sut plugin)
290
+ test_object_adapter = sut.instance_variable_get( :@test_object_adapter )
246
291
 
247
292
  # create parent application test object if none defined in rules; most likely the call is originated from SUT#child, but not by using SUT#application
248
293
  unless identification_directives.has_key?( :__parent_application ) || rules.has_key?( :parent_application )
249
294
 
250
295
  # retrieve application test object xml element
251
- application_test_object_xml = TDriver::TestObjectAdapter.retrieve_parent_application( test_object_xml )
296
+ application_test_object_xml = test_object_adapter.retrieve_parent_application( test_object_xml )
252
297
 
253
298
  unless application_test_object_xml.nil?
254
299
 
255
- # retrieve sut object
256
- sut = rules[ :parent ].instance_variable_get( :@sut )
257
-
258
300
  # retrieve test object id from xml
259
- object_id = TDriver::TestObjectAdapter.test_object_element_attribute( application_test_object_xml, 'id' ){ nil }.to_i
301
+ object_id = test_object_adapter.test_object_element_attribute( application_test_object_xml, 'id', nil ).to_i
260
302
 
261
303
  # retrieve test object name from xml
262
- object_name = TDriver::TestObjectAdapter.test_object_element_attribute( application_test_object_xml, 'name' ){ nil }.to_s
304
+ object_name = test_object_adapter.test_object_element_attribute( application_test_object_xml, 'name', nil ).to_s
263
305
 
264
306
  # retrieve test object type from xml
265
- object_type = TDriver::TestObjectAdapter.test_object_element_attribute( application_test_object_xml, 'type' ){ nil }.to_s
307
+ object_type = test_object_adapter.test_object_element_attribute( application_test_object_xml, 'type', nil ).to_s
266
308
 
267
309
  # calculate object cache hash key
268
- hash_key = TDriver::TestObjectAdapter.test_object_hash( object_id, object_type, object_name )
310
+ hash_key = test_object_adapter.test_object_hash( object_id, object_type, object_name )
269
311
 
270
312
  parent_cache = sut.instance_variable_get( :@child_object_cache )
271
313
 
@@ -281,8 +323,11 @@ module MobyBase
281
323
 
282
324
  :parent => sut,
283
325
  :parent_application => nil,
284
- :xml_object => application_test_object_xml
285
-
326
+ :xml_object => application_test_object_xml,
327
+
328
+ # object identification attributes
329
+ :object_attributes_hash => { :name => object_name, :type => object_type }
330
+
286
331
  )
287
332
 
288
333
  end
@@ -312,7 +357,9 @@ module MobyBase
312
357
  :xml_object => test_object_xml,
313
358
 
314
359
  # object identification attributes
315
- :object_attributes_hash => object_attributes_hash
360
+ :object_attributes_hash => object_attributes_hash,
361
+
362
+ :identification_directives => identification_directives
316
363
 
317
364
  )
318
365
 
@@ -325,32 +372,43 @@ module MobyBase
325
372
 
326
373
  # TODO: document me
327
374
  def make_test_object( rules )
328
-
375
+
329
376
  # get parent object from hash
330
377
  parent = rules[ :parent ]
331
378
 
332
379
  # retrieve sut object
333
380
  sut = parent.instance_variable_get( :@sut )
381
+
382
+ # retrieve sut objects test object adapter
383
+ #test_object_adapter = sut.instance_variable_get( :@sut_controller ).test_object_adapter
384
+ test_object_adapter = sut.instance_variable_get( :@test_object_adapter )
385
+
386
+ # retrieve sut objects test object factory
387
+ #test_object_factory = sut.instance_variable_get( :@sut_controller ).test_object_factory
388
+ test_object_factory = sut.instance_variable_get( :@test_object_factory )
334
389
 
335
390
  # xml object element
336
391
  xml_object = rules[ :xml_object ]
337
392
 
393
+ # additional rules, e.g. :__no_caching
394
+ identification_directives = rules[ :identification_directives ] || {}
395
+
338
396
  # retrieve attributes
339
- #TDriver::TestObjectAdapter.fetch_attributes( xml_object, [ 'id', 'name', 'type', 'env' ], false )
397
+ #@test_object_adapter.fetch_attributes( xml_object, [ 'id', 'name', 'type', 'env' ], false )
340
398
 
341
399
  if xml_object.kind_of?( MobyUtil::XML::Element )
342
400
 
343
401
  # retrieve test object id from xml
344
- object_id = TDriver::TestObjectAdapter.test_object_element_attribute( xml_object, 'id' ){ nil }.to_i
402
+ object_id = test_object_adapter.test_object_element_attribute( xml_object, 'id', nil ).to_i
345
403
 
346
404
  # retrieve test object name from xml
347
- object_name = TDriver::TestObjectAdapter.test_object_element_attribute( xml_object, 'name' ){ nil }.to_s
405
+ object_name = test_object_adapter.test_object_element_attribute( xml_object, 'name', nil ).to_s
348
406
 
349
407
  # retrieve test object type from xml
350
- object_type = TDriver::TestObjectAdapter.test_object_element_attribute( xml_object, 'type' ){ nil }.to_s
408
+ object_type = test_object_adapter.test_object_element_attribute( xml_object, 'type', nil ).to_s
351
409
 
352
410
  # retrieve test object type from xml
353
- env = TDriver::TestObjectAdapter.test_object_element_attribute( xml_object, 'env' ){ $parameters[ sut.id ][ :env ] }.to_s
411
+ env = test_object_adapter.test_object_element_attribute( xml_object, 'env' ){ $parameters[ sut.id ][ :env ] }.to_s
354
412
 
355
413
  else
356
414
 
@@ -366,7 +424,7 @@ module MobyBase
366
424
  end
367
425
 
368
426
  # calculate object cache hash key
369
- hash_key = TDriver::TestObjectAdapter.test_object_hash( object_id, object_type, object_name )
427
+ hash_key = test_object_adapter.test_object_hash( object_id, object_type, object_name )
370
428
 
371
429
  # get reference to parent objects child objects cache
372
430
  parent_cache = rules[ :parent ].instance_variable_get( :@child_object_cache )
@@ -380,45 +438,55 @@ module MobyBase
380
438
  # store xml_object to test object
381
439
  test_object.xml_data = xml_object
382
440
 
441
+ # update test objects creation attributes (either cached object or just newly created child object)
442
+ test_object.instance_variable_get( :@creation_attributes ).merge!( rules[ :object_attributes_hash ] )
443
+
383
444
  else
384
445
 
385
446
  # create test object
386
- test_object = MobyBase::TestObject.new( self, sut, parent, xml_object )
447
+ test_object = MobyBase::TestObject.new(
448
+
449
+ :test_object_factory => test_object_factory, #self,
450
+ :test_object_adapter => test_object_adapter, #@test_object_adapter,
451
+ :creation_attributes => rules[ :object_attributes_hash ],
452
+ :xml_object => xml_object,
453
+ :sut => sut,
454
+
455
+ # set given parent in rules hash as parent object for new child test object
456
+ :parent => parent,
387
457
 
388
- #test_object.instance_variable_set( :@object_behaviours, [] )
458
+ # set given application test object in rules hash as parent application for new child test object
459
+ :parent_application => rules[ :parent_application ]
460
+
461
+ )
462
+
463
+ # temp. variable for object type
464
+ obj_type = object_type.clone
389
465
 
390
466
  # apply all test object related behaviours unless object type is 'application'
391
- object_type << ';*' unless object_type == 'application'
467
+ obj_type << ';*' unless obj_type == 'application'
392
468
 
393
469
  # apply behaviours to test object
394
470
  MobyBase::BehaviourFactory.instance.apply_behaviour!(
395
471
 
396
472
  :object => test_object,
397
- :object_type => [ *object_type.split(';') ],
473
+ :object_type => [ *obj_type.split(';') ],
398
474
  :input_type => [ '*', sut.input.to_s ],
399
475
  :env => [ '*', *env.to_s.split(";") ],
400
476
  :version => [ '*', sut.ui_version.to_s ]
401
477
 
402
478
  )
403
- # create child accessors
404
- TDriver::TestObjectAdapter.create_child_accessors!( xml_object, test_object )
405
-
406
- # set given parent in rules hash as parent object for new child test object
407
- test_object.instance_variable_set( :@parent, parent )
408
479
 
409
- # set given application test object in rules hash as parent application for new child test object
410
- test_object.instance_variable_set( :@parent_application, rules[ :parent_application ] )
480
+ # create child accessors
481
+ test_object_adapter.create_child_accessors!( xml_object, test_object )
411
482
 
412
- # add created test object to parents child objects cache
413
- parent_cache.add_object( test_object )
483
+ # add created test object to parents child objects cache, unless explicitly disabled
484
+ parent_cache.add_object( test_object ) unless identification_directives[ :__no_caching ] == true
414
485
 
415
486
  end
416
487
 
417
488
  # NOTE: Do not remove object_type from object attributes hash_rule due to it is used in find_objects service!
418
489
  #rules[ :object_attributes_hash ].delete( :type )
419
-
420
- # update test objects creation attributes (either cached object or just newly created child object)
421
- test_object.instance_variable_set( :@creation_attributes, rules[ :object_attributes_hash ] )
422
490
 
423
491
  # do not make test object verifications if we are operating on the sut itself (allow run to pass)
424
492
  unless parent.kind_of?( MobyBase::SUT )
@@ -453,8 +521,12 @@ module MobyBase
453
521
 
454
522
  object_search_params = creation_attributes.clone
455
523
 
456
- object_search_params[ :className ] = object_search_params.delete( :type ) if creation_attributes.has_key?( :type )
457
- object_search_params[ :objectName ] = object_search_params.delete( :name ) if creation_attributes.has_key?( :name )
524
+ # see below lines how to do following easier
525
+ #object_search_params[ :className ] = object_search_params.delete( :type ) if creation_attributes.has_key?( :type )
526
+ #object_search_params[ :objectName ] = object_search_params.delete( :name ) if creation_attributes.has_key?( :name )
527
+
528
+ object_search_params.rename_key!( :type, :className ){}
529
+ object_search_params.rename_key!( :name, :objectName ){}
458
530
 
459
531
  object_search_params
460
532
 
@@ -487,31 +559,15 @@ module MobyBase
487
559
  end
488
560
 
489
561
  # TODO: document me
490
- def list_matching_test_objects( matches )
562
+ def list_matching_test_objects_as_list( test_object_adapter, matches )
491
563
 
492
- matches.collect{ | object |
493
-
494
- path = [ object.attribute( 'type' ) ]
564
+ test_object_adapter.list_test_objects_as_string( matches ).each_with_index.collect{ | object, object_index |
495
565
 
496
- while object.attribute( 'type' ) != 'application' do
497
-
498
- # object/objects/object/../..
499
- object = object.parent.parent
500
-
501
- path << object.attribute( 'type' )
502
-
503
- end
504
-
505
- path.reverse.join( '.' )
506
-
507
- }.sort
508
-
509
- end
566
+ "%3s) %s" % [ object_index + 1, object ]
510
567
 
511
- # TODO: document me
512
- def list_matching_test_objects_as_list( matches )
568
+ }.join( "\n" )
513
569
 
514
- list_matching_test_objects( matches ).each_with_index.collect{ | object, object_index | "%3s) %s" % [ object_index + 1, object ] }.join( "\n" )
570
+ #list_matching_test_objects( matches ).each_with_index.collect{ | object, object_index | "%3s) %s" % [ object_index + 1, object ] }.join( "\n" )
515
571
 
516
572
  end
517
573
 
@@ -32,12 +32,11 @@ require File.expand_path( File.join( File.dirname( __FILE__ ), 'cache' ) )
32
32
  # load test object adapter
33
33
  require File.expand_path( File.join( File.dirname( __FILE__ ), 'adapter' ) )
34
34
 
35
+ # load test object adapter
36
+ require File.expand_path( File.join( File.dirname( __FILE__ ), 'xml/adapter' ) )
37
+
35
38
  # load verify ui module
36
39
  require File.expand_path( File.join( File.dirname( __FILE__ ), 'verification' ) )
37
40
 
38
41
  # load test object behaviours
39
42
  MobyUtil::FileHelper.load_modules( File.expand_path( File.join( File.dirname( __FILE__ ), 'behaviours' ) ) )
40
-
41
- # load report api for continous verification reporting purposes
42
- require File.expand_path( File.join( File.dirname( __FILE__ ), '../../report/report_api' ) ) if MobyUtil::Parameter[ :report_attach_continuous_verification_to_reporter, nil ]=='true'
43
-
@@ -104,7 +104,7 @@ module TDriver
104
104
  TDriverReportAPI::tdriver_report_log("<hr />")
105
105
 
106
106
  $logger.enabled = logging_enabled
107
- $logger.log "behaviour", "FAIL;Verification #{verify.message.nil? ? '' : '\"' << verify.message << '\" '}failed:#{e.to_s}.\n#{verify.timeout.nil? ? '' : ' using timeout ' + verify.timeout.to_s}.;#{sut.id.to_s+';sut'};{};verify_always;" << verify.expected.to_s
107
+ $logger.behaviour "FAIL;Verification #{verify.message.nil? ? '' : '\"' << verify.message << '\" '}failed:#{e.to_s}.\n#{verify.timeout.nil? ? '' : ' using timeout ' + verify.timeout.to_s}.;#{sut.id.to_s+';sut'};{};verify_always;" << verify.expected.to_s
108
108
 
109
109
  end
110
110
 
@@ -144,7 +144,7 @@ module TDriver
144
144
 
145
145
  $logger.enabled = logging_enabled
146
146
 
147
- $logger.log "behaviour", "FAIL;Verification #{verify.message.nil? ? '' : '\"' << verify.message << '\" '}failed:#{e.to_s}.\n#{verify.timeout.nil? ? '' : ' using timeout ' + verify.timeout.to_s}.;#{sut.id.to_s+';sut'};{};verify_always;" << verify.expected.to_s
147
+ $logger.behaviour "FAIL;Verification #{verify.message.nil? ? '' : '\"' << verify.message << '\" '}failed:#{e.to_s}.\n#{verify.timeout.nil? ? '' : ' using timeout ' + verify.timeout.to_s}.;#{sut.id.to_s+';sut'};{};verify_always;" << verify.expected.to_s
148
148
 
149
149
  end
150
150
 
@@ -154,7 +154,7 @@ module TDriver
154
154
 
155
155
  $logger.enabled = logging_enabled
156
156
 
157
- $logger.log "behaviour", "FAIL;Verification #{verify.message.nil? ? '' : '\"' << verify.message << '\" '}failed:#{e.to_s}.\n#{verify.timeout.nil? ? '' : ' using timeout ' + verify.timeout.to_s}.;#{sut.id.to_s+';sut'};{};verify_always;" << verify.expected.to_s
157
+ $logger.behaviour "FAIL;Verification #{verify.message.nil? ? '' : '\"' << verify.message << '\" '}failed:#{e.to_s}.\n#{verify.timeout.nil? ? '' : ' using timeout ' + verify.timeout.to_s}.;#{sut.id.to_s+';sut'};{};verify_always;" << verify.expected.to_s
158
158
 
159
159
  @@inside_verify = false
160
160
 
@@ -0,0 +1,734 @@
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
+ module OptimizedXML
23
+
24
+ class TestObjectAdapter
25
+
26
+ # private methods and variables
27
+ class << self
28
+
29
+ private
30
+
31
+ # TODO: document me
32
+ def xpath_attributes( attributes, element_attributes, object_type )
33
+
34
+ # collect attributes
35
+ attributes = attributes.collect{ | key, values |
36
+
37
+ # allow multiple values options for attribute, e.g. object which 'text' attribute value is either '1' or '2'
38
+ ( values.kind_of?( Array ) ? values : [ values ] ).collect{ | value |
39
+
40
+ # concatenate string if it contains single and double quotes, otherwise return as is
41
+ value = xpath_literal_string( value )
42
+
43
+ prefix_key = "@#{ key }"
44
+
45
+ if @partial_match_allowed.include?( [ object_type, key ] )
46
+
47
+ # regexp support is needed also here
48
+
49
+ prefix_value = "[contains(.,#{ value })]"
50
+ attribute_value = "contains(.,#{ value })"
51
+
52
+ else
53
+
54
+ if value.kind_of?( Regexp )
55
+
56
+ prefix_value = "regexp_compare(#{ prefix_key },'#{ value.source }',#{ value.options })"
57
+ attribute_value = "regexp_compare(.,'#{ value.source }',#{ value.options })"
58
+
59
+ prefix_key = ""
60
+
61
+ else
62
+
63
+ #prefix_value = "=#{ value }"
64
+ #attribute_value = "text()=#{ value }"
65
+
66
+ prefix_value = "=#{ value }"
67
+ attribute_value = ".=#{ value }"
68
+
69
+ end
70
+
71
+ end
72
+
73
+ # construct xpath
74
+ #"(#{ element_attributes ? "#{ prefix_key }#{ prefix_value } or " : "" }attr[translate(@name,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='#{ key }' and #{ attribute_value }])"
75
+ "(#{ element_attributes ? "#{ prefix_key }#{ prefix_value } or " : "" }attr[translate(@name,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='#{ key }' and #{ attribute_value }])"
76
+
77
+ }.join( ' or ' ) # join attribute alternative values
78
+
79
+ }.join( ' and ' ) # join all required attributes
80
+
81
+ #p attributes
82
+
83
+ # return created xpath or nil if no attributes given
84
+ if attributes.empty?
85
+
86
+ # no attributes given
87
+ nil
88
+
89
+ else
90
+
91
+ # return result
92
+ attributes
93
+
94
+ end
95
+
96
+ end # xpath_attributes
97
+
98
+ # TODO: document me
99
+ def initialize_class
100
+
101
+ # special cases: allow partial match when value of type and attribute name matches
102
+ @partial_match_allowed = [ 'list_item', 'text' ], [ 'application', 'fullname' ]
103
+
104
+ end
105
+
106
+ end # static
107
+
108
+ # TODO: document me
109
+ def self.regexp_compare( nodeset, source, options )
110
+
111
+ # rebuild defined regexp, used while matching element content
112
+ regexp_object = Regexp.new( source.to_s, options.to_i )
113
+
114
+ # collect all nodes matching with regexp
115
+ nodeset.find_all{ | node | node.content =~ regexp_object }
116
+
117
+ end
118
+
119
+ # TODO: document me
120
+ def self.xpath_to_object( rules, find_all_children )
121
+
122
+ # convert hash keys to downcased string
123
+ rules = Hash[
124
+
125
+ rules.collect{ | key, value |
126
+
127
+ case value
128
+
129
+ # pass the value as is if type of regexp or array; array is used in localisation cases e.g. [ 'one', 'yksi', 'uno' ] # etc
130
+ when Regexp, Array
131
+
132
+ [ key.to_s.downcase, value ]
133
+
134
+ else
135
+
136
+ [ key.to_s.downcase, value.to_s ]
137
+
138
+ end
139
+
140
+ }
141
+
142
+ ]
143
+
144
+ # xpath container array
145
+ test_object_xpath_array = []
146
+
147
+ # store and remove object element attributes from hash
148
+ object_element_attributes = rules.delete_keys!( 'name', 'type', 'parent', 'id' )
149
+
150
+ # children method may request test objects of any type
151
+ if object_element_attributes[ 'type' ] == '*'
152
+
153
+ # test object with any name, type, parent and id is allowed
154
+ test_object_xpath_array << '@*'
155
+
156
+ else
157
+
158
+ # required attributes
159
+ test_object_xpath_array << xpath_attributes( object_element_attributes, true, object_element_attributes[ 'type' ] )
160
+
161
+ end
162
+
163
+ # additional attributes, eg. :text, :x, :y etc.
164
+ test_object_xpath_array << xpath_attributes( rules, false, object_element_attributes[ 'type' ] )
165
+
166
+ # join required and additional attribute strings
167
+ test_object_xpath_string = test_object_xpath_array.compact.join( ' and ' )
168
+
169
+ # return any child element under current node or only immediate child element
170
+ #find_all_children ? "*//obj[#{ test_object_xpath_string }]" : "obj[1]/obj[#{ test_object_xpath_string }]"
171
+
172
+ find_all_children ? ".//obj[#{ test_object_xpath_string }]" : "obj[#{ test_object_xpath_string }]"
173
+
174
+ #"*//obj[#{ test_object_xpath_string }]"
175
+
176
+ end
177
+
178
+ # TODO: document me
179
+ def self.xpath_literal_string( string )
180
+
181
+ return string if string.kind_of?( Regexp )
182
+
183
+ # make sure that argument is type of string
184
+ string = string.to_s
185
+
186
+ # does not contain no single quotes
187
+ if not string.include?("'")
188
+
189
+ result = "'#{ string }'"
190
+
191
+ # does not contain no double quotes
192
+ elsif not string.include?('"')
193
+
194
+ result = "\"#{ string }\""
195
+
196
+ # contains single and double quotes
197
+ else
198
+
199
+ # open new item
200
+ result = ["'"]
201
+
202
+ # iterate through each character
203
+ string.each_char{ | char |
204
+
205
+ case char
206
+
207
+ # encapsulate single quotes with double quotes
208
+ when "'"
209
+
210
+ # close current item
211
+ result.last << char
212
+
213
+ # add encapsulated single quote
214
+ result << "\"'\""
215
+
216
+ # open new item
217
+ result << char
218
+
219
+ else
220
+
221
+ # any other character will appended as is
222
+ result.last << char
223
+
224
+ end
225
+
226
+ }
227
+
228
+ # close last sentence
229
+ result.last << "'"
230
+
231
+ # create concat clause for xpath
232
+ result = "concat(#{ result.join(',') })"
233
+
234
+ end
235
+
236
+ result
237
+
238
+ end
239
+
240
+ # TODO: document me
241
+ def self.get_objects( source_data, rules, find_all_children )
242
+
243
+ rule = xpath_to_object( rules, find_all_children )
244
+
245
+ [
246
+
247
+ # perform xpath to source xml data
248
+ source_data.xpath( rule, self ),
249
+
250
+ # return also created xpath
251
+ rule
252
+
253
+ ]
254
+
255
+ end
256
+
257
+ # TODO: document me
258
+ def self.test_object_hash( object_id, object_type, object_name )
259
+
260
+ # calculate test object hash
261
+ ( ( ( 17 * 37 + object_id ) * 37 + object_type.hash ) * 37 + object_name.hash )
262
+
263
+ end
264
+
265
+ # Sort XML nodeset of test objects with layout direction
266
+ def self.sort_elements( nodeset, layout_direction = 'LeftToRight' )
267
+
268
+ # cache for x_absolute and y_absolute values; reduces dramatically number of xpath calls
269
+ cache = {}
270
+
271
+ # xpath pattern to be used for x_absolute attribute value
272
+ x_absolute_pattern = './attr[@name="x_absolute"]/text()'
273
+
274
+ # xpath pattern to be used for x_absolute attribute value
275
+ y_absolute_pattern = './attr[@name="y_absolute"]/text()'
276
+
277
+ # collect only nodes that has x_absolute and y_absolute attributes
278
+ nodeset.collect!{ | node |
279
+
280
+ # retrieve x_absolute attribute
281
+ x_absolute = node.at_xpath( x_absolute_pattern )
282
+
283
+ # retrieve y_absolute attribute
284
+ y_absolute = node.at_xpath( y_absolute_pattern )
285
+
286
+ # return unmodified nodeset if both attributes was not found
287
+ if x_absolute.nil? || y_absolute.nil?
288
+
289
+ #warn("Warning: Unable to sort object set due to object type of #{ node.attribute( 'type' ).inspect } does not have \"x_absolute\" or \"y_absolute\" attribute")
290
+
291
+ return nodeset
292
+
293
+ else
294
+
295
+ # store attributes to cache for further processing
296
+ cache[ node ] = [ x_absolute.content.to_i, y_absolute.content.to_i ]
297
+
298
+ # return node as result
299
+ node
300
+
301
+ end
302
+
303
+ }.compact!.sort!{ | element_a, element_b |
304
+
305
+ # retrieve element a's attributes x and y
306
+ element_a_x, element_a_y = cache[ element_a ]
307
+
308
+ # retrieve element b's attributes x and y
309
+ element_b_x, element_b_y = cache[ element_b ]
310
+
311
+ case layout_direction
312
+
313
+ when 'LeftToRight'
314
+
315
+ # compare elements
316
+ ( element_a_y == element_b_y ) ? ( element_a_x <=> element_b_x ) : ( element_a_y <=> element_b_y )
317
+
318
+ when 'RightToLeft'
319
+
320
+ # compare elements
321
+ ( element_a_y == element_b_y ) ? ( element_b_x <=> element_a_x ) : ( element_a_y <=> element_b_y )
322
+
323
+ else
324
+
325
+ # raise exception if layout direction it not supported
326
+ Kernel::raise ArgumentError, "Unsupported layout direction #{ layout_direction.inspect }"
327
+
328
+ end
329
+
330
+ }
331
+
332
+ end
333
+
334
+ def self.parent_test_object_element( test_object )
335
+
336
+ # retrieve parent of current xml element; obj/..
337
+ test_object.xml_data.parent #.parent
338
+
339
+ end
340
+
341
+ # TODO: document me
342
+ def self.test_object_element_attributes( source_data )
343
+
344
+ Hash[
345
+ source_data.attributes.collect{ | key, value |
346
+ [ key.to_s, value.to_s ]
347
+ }
348
+ ]
349
+
350
+ end
351
+
352
+ # TODO: document me
353
+ def self.test_object_element_attribute( source_data, attribute_name, *default, &block )
354
+
355
+ result = source_data.attribute( attribute_name )
356
+
357
+ # if no attribute found call optional code block or raise exception
358
+ unless result
359
+
360
+ if block_given?
361
+
362
+ # pass return value of block as result
363
+ yield( attribute_name )
364
+
365
+ else
366
+
367
+ # raise exception if no default value given
368
+ if default.empty?
369
+
370
+ # raise exception if no such attribute found
371
+ Kernel::raise MobyBase::AttributeNotFoundError, "Could not find test object element attribute #{ attribute_name.inspect }"
372
+
373
+ else
374
+
375
+ # pass default value as result
376
+ default.first
377
+
378
+ end
379
+
380
+ end
381
+
382
+ else
383
+
384
+ result
385
+
386
+ end
387
+
388
+ end
389
+
390
+ # TODO: document me
391
+ def self.test_object_attribute( source_data, attribute_name, *default, &block )
392
+
393
+ # TODO: consider using at_xpath and adding text() to query string; however "downside" is that if multiple matches found only first value will be returned as result
394
+
395
+ # retrieve attribute(s) from xml
396
+ nodeset = source_data.xpath(
397
+
398
+ "attr[translate(@name,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='#{ attribute_name.downcase }']"
399
+
400
+ )
401
+
402
+ # if no attributes found call optional code block or raise exception
403
+ if nodeset.empty?
404
+
405
+ if block_given?
406
+
407
+ # pass return value of block as result
408
+ yield( attribute_name )
409
+
410
+ else
411
+
412
+ # raise exception if no default value given
413
+ if default.empty?
414
+
415
+ # raise exception if no such attribute found
416
+ Kernel::raise MobyBase::AttributeNotFoundError, "Could not find attribute #{ attribute_name.inspect }" # for test object of type #{ type.to_s }"
417
+
418
+ else
419
+
420
+ # pass default value as result
421
+ default.first
422
+
423
+ end
424
+
425
+ end # block_given?
426
+
427
+ else # not nodeset.empty?
428
+
429
+ # attribute(s) found
430
+ # Need to disable this for now
431
+ # Kernel::raise MobyBase::MultipleAttributesFoundError.new( "Multiple attributes found with name '%s'" % name ) if nodeset.count > 1
432
+
433
+ # return found attribute
434
+ nodeset.first.content
435
+
436
+ end
437
+
438
+ end
439
+
440
+ # TODO: document me
441
+ def self.test_object_attributes( source_data, inclusive_filter = [] )
442
+
443
+ # convert all keys to lowercase
444
+ inclusive_filter.collect!{ | key | key.to_s.downcase } unless inclusive_filter.empty?
445
+
446
+ # return hash of test object attributes
447
+ Hash[
448
+
449
+ # iterate each attribute and collect name and value
450
+ source_data.xpath( 'attr' ).collect{ | value |
451
+
452
+ # retrieve attribute name
453
+ name = value.attribute('name').to_s
454
+
455
+ # collect attribute elements name and content
456
+ unless inclusive_filter.empty?
457
+
458
+ [ name, value.content ] if inclusive_filter.include?( name.downcase )
459
+
460
+ else
461
+
462
+ # pass the attribute pair - no filtering done
463
+ [ name, value.content ]
464
+
465
+ end
466
+
467
+ }
468
+
469
+ ]
470
+
471
+ end
472
+
473
+ # TODO: document me
474
+ def self.application_layout_direction( sut )
475
+
476
+ # temporary fix until testobject will be associated to parent application object
477
+ unless MobyUtil::DynamicAttributeFilter.instance.has_attribute?( 'layoutDirection' )
478
+
479
+ # temporary fix: add 'layoutDirection' to dynamic attributes filtering whitelist...
480
+ MobyUtil::DynamicAttributeFilter.instance.add_attribute( 'layoutDirection' )
481
+
482
+ # temporary fix: ... and refresh sut to retrieve updated xml data
483
+ sut.refresh
484
+
485
+ end
486
+
487
+ # TODO: parent application test object should be passed to get_test_objects; TestObjectAdapter#test_object_attribute( @app.xml_data, 'layoutDirection')
488
+ #( sut.xml_data.at_xpath('*//obj[@type="application"]/attr[@name="layoutDirection"]/text()').content || 'LeftToRight' ).to_s
489
+
490
+ ( sut.xml_data.at_xpath('.//obj[@type="application"]/attr[@name="layoutDirection"]/text()').content || 'LeftToRight' ).to_s
491
+
492
+ end
493
+
494
+ # TODO: document me
495
+ def self.create_child_accessors!( source_data, test_object )
496
+
497
+ # iterate through each child object type attribute and create accessor method
498
+ source_data.xpath( 'obj/@type' ).each{ | object_type |
499
+
500
+ # skip if object type value is nil or empty due to child accessor cannot be created
501
+ next if object_type.nil? || object_type.to_s.empty?
502
+
503
+ # convert attribute node value to string
504
+ object_type = object_type.content
505
+
506
+ # skip if child accessor is already created
507
+ next if test_object.respond_to?( object_type )
508
+
509
+ begin
510
+
511
+ # create child accessor method to test object unless already exists
512
+ test_object.instance_eval(
513
+
514
+ "def #{ object_type }( rules = {} ); raise TypeError,'parameter <rules> should be hash' unless rules.kind_of?( Hash ); rules[ :type ]=:#{ object_type }; child( rules ); end;"
515
+
516
+ ) unless object_type.empty?
517
+
518
+ # in case if object type has some invalid characters, e.g. type is "ns:object"
519
+ rescue SyntaxError
520
+
521
+ warn "warning: unable to create accessor to child test object whose type is #{ object_type.inspect }"
522
+
523
+ end
524
+
525
+ }
526
+
527
+ end
528
+
529
+ # TODO: document me
530
+ def self.state_object_xml( source_data, id )
531
+
532
+ # collect each object from source xml
533
+ objects = source_data.xpath( 'tasInfo/obj' ).collect{ | element | element.to_s }.join
534
+
535
+ # return xml root element
536
+ MobyUtil::XML.parse_string(
537
+ "<sut name='sut' type='sut' id='#{ id }'>#{ objects }</sut>"
538
+ ).root
539
+
540
+ end
541
+
542
+ def self.retrieve_parent_application( xml_source )
543
+
544
+ xml_source_iterator = xml_source.clone
545
+
546
+ while xml_source_iterator.kind_of?( MobyUtil::XML::Element )
547
+
548
+ if ( test_object_element_attribute( xml_source_iterator, 'type' ) == 'application' )
549
+
550
+ return xml_source_iterator
551
+
552
+ end
553
+
554
+ if xml_source_iterator.kind_of?( MobyUtil::XML::Element )
555
+
556
+ xml_source_iterator = xml_source_iterator.parent
557
+
558
+ else
559
+
560
+ # not found from xml tree
561
+ break
562
+
563
+ end
564
+
565
+ end
566
+
567
+ #warn("warning: unable to retrieve parent application")
568
+
569
+ raise MobyBase::TestObjectNotFoundError, "Unable to retrieve parent application"
570
+
571
+ # return application object or nil if no parent found
572
+ # Does is make sense to return nil - shouldn't all test objects belong to an application? Maybe throw exception if application not found
573
+
574
+ #nil
575
+
576
+ #return @sut.child( :type => 'application' ) rescue nil
577
+
578
+ end
579
+
580
+ # TODO: document me
581
+ def self.get_xml_element_for_test_object( test_object )
582
+
583
+ # retrieve nodeset from sut xml_data
584
+ nodeset = test_object.instance_variable_get( :@sut ).xml_data.xpath( test_object.instance_variable_get( :@x_path ) )
585
+
586
+ # raise exception if no test objects found
587
+ Kernel::raise MobyBase::TestObjectNotFoundError if nodeset.empty?
588
+
589
+ # return first test object from the nodeset
590
+ nodeset.first
591
+
592
+ end
593
+
594
+ # TODO: document me
595
+ def self.get_test_object_identifiers( xml_source, test_object = nil )
596
+
597
+ # retrieve parent xpath if test_object given
598
+ parent_xpath = test_object ? test_object.instance_variable_get( :@parent ).x_path : ""
599
+
600
+ # retrieve type attribute
601
+ type = xml_source.attribute( 'type' )
602
+
603
+ # retrieve id attribute
604
+ id = xml_source.attribute( 'id' )
605
+
606
+ # retrieve env attribute
607
+ env = xml_source.attribute( 'env' )
608
+
609
+ # retrieve test object element attributes and return array containting xpath to test object, name, type and id elements
610
+ [
611
+ # x_path to test object
612
+ #test_object ? "#{ parent_xpath }/*//obj[@type='#{ type }' and @id='#{ id }']" : nil,
613
+
614
+ test_object ? "#{ parent_xpath }/.//obj[@type='#{ type }' and @id='#{ id }']" : nil,
615
+
616
+ # test object name
617
+ xml_source.attribute( 'name' ),
618
+
619
+ # test object type
620
+ type,
621
+
622
+ # test object id
623
+ id,
624
+
625
+ env
626
+
627
+ ]
628
+
629
+ end
630
+
631
+ # TODO: document me
632
+ def self.hash_to_element_attributes( hash )
633
+
634
+ hash.collect{ | key, value |
635
+
636
+ "#{ key.to_s }=\"#{ value.to_s }\""
637
+
638
+ }.join(' ')
639
+
640
+ end
641
+
642
+ # TODO: document me
643
+ def self.merge_application_elements( xml_string )
644
+
645
+ # parse the ui state xml
646
+ document_root = MobyUtil::XML.parse_string( xml_string ).root
647
+
648
+ # retrieve application objects as nodeset
649
+ nodeset = document_root.xpath('/tasMessage/tasInfo/obj')
650
+
651
+ # check if multiple application objects found
652
+ if nodeset.count > 1
653
+
654
+ # new header, apply original element attributes
655
+ new_xml = "<tasMessage #{ hash_to_element_attributes( document_root.attributes ) }><tasInfo #{ hash_to_element_attributes( nodeset.first.parent.attributes ) }>"
656
+
657
+ # flag defining that is application element already created
658
+ application_element_set = false
659
+
660
+ # collect environment values
661
+ environments = document_root.xpath('/tasMessage/tasInfo/obj[@type="application"]/@env').collect{ | attribute | attribute.to_s }
662
+
663
+ # iterate through each object found in xml
664
+ nodeset.each{ | object |
665
+
666
+ # only one application element
667
+ unless application_element_set
668
+
669
+ #application_objects << object
670
+
671
+ # retrieve object attributes
672
+ attributes = object.attributes
673
+
674
+ # merge env to attributes hash
675
+ attributes['env'] = environments.join(';')
676
+
677
+ # add application object xml element to new xml string
678
+ new_xml << "<obj #{ hash_to_element_attributes( attributes ) }>"
679
+
680
+ # application element is now set, no need to do it again
681
+ application_element_set = true
682
+
683
+ end
684
+
685
+ # append all found elements
686
+ object.xpath('./*').each{ | object | new_xml << object.to_s }
687
+
688
+ }
689
+
690
+ # multiple applications found, return merged application xml
691
+ new_xml << "</obj></tasInfo></tasMessage>"
692
+
693
+ else
694
+
695
+ # only one application found, return data as is
696
+ xml_string
697
+
698
+ end
699
+
700
+ end
701
+
702
+ # TODO: document me
703
+ def self.list_test_objects_as_string( source_data )
704
+
705
+ source_data.collect{ | object |
706
+
707
+ path = [ object.attribute( 'type' ) ]
708
+
709
+ while object.attribute( 'type' ) != 'application' do
710
+
711
+ # object/objects/object/../..
712
+ object = object.parent
713
+
714
+ path << object.attribute( 'type' )
715
+
716
+ end
717
+
718
+ path.reverse.join( '.' )
719
+
720
+ }.sort
721
+
722
+ end
723
+
724
+ # enable hooking for performance measurement & debug logging
725
+ TDriver::Hooking.hook_methods( self ) if defined?( TDriver::Hooking )
726
+
727
+ # initialize TDriver::TestObjectAdapter
728
+ initialize_class
729
+
730
+ end # TestObjectAdapter
731
+
732
+ end # OptimizedXML
733
+
734
+ end # TDriver