testability-driver 1.1.1 → 1.2.1

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 (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