oats 0.0.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 (181) hide show
  1. data/.gitignore +6 -0
  2. data/Gemfile +15 -0
  3. data/README.txt +165 -0
  4. data/Rakefile +2 -0
  5. data/bin/agent +204 -0
  6. data/bin/oats +10 -0
  7. data/bin/occ +29 -0
  8. data/bin/results_cleanup +6 -0
  9. data/doc/COPYING +55 -0
  10. data/doc/LICENSE +55 -0
  11. data/doc/OATS_Framework.doc +0 -0
  12. data/doc/classes/ApplicationLogs.html +239 -0
  13. data/doc/classes/Campaign.html +843 -0
  14. data/doc/classes/CommandlineOptions.html +131 -0
  15. data/doc/classes/Driver.html +182 -0
  16. data/doc/classes/Hash.html +137 -0
  17. data/doc/classes/Ide.html +194 -0
  18. data/doc/classes/MapSelenium.html +197 -0
  19. data/doc/classes/Net.html +107 -0
  20. data/doc/classes/Oats/OatsFilterError.html +119 -0
  21. data/doc/classes/Oats.html +998 -0
  22. data/doc/classes/OatsAssertError.html +119 -0
  23. data/doc/classes/OatsBadInput.html +119 -0
  24. data/doc/classes/OatsData.html +290 -0
  25. data/doc/classes/OatsError.html +117 -0
  26. data/doc/classes/OatsExit.html +117 -0
  27. data/doc/classes/OatsLock.html +254 -0
  28. data/doc/classes/OatsMain.html +182 -0
  29. data/doc/classes/OatsMysqlError.html +113 -0
  30. data/doc/classes/OatsMysqlMissingInput.html +113 -0
  31. data/doc/classes/OatsReportError.html +113 -0
  32. data/doc/classes/OatsSetupError.html +119 -0
  33. data/doc/classes/OatsTestError.html +119 -0
  34. data/doc/classes/OatsTestExit.html +119 -0
  35. data/doc/classes/OatsTestLocateError.html +120 -0
  36. data/doc/classes/OatsVerifyError.html +119 -0
  37. data/doc/classes/Ragent.html +397 -0
  38. data/doc/classes/Rclient.html +236 -0
  39. data/doc/classes/Report.html +368 -0
  40. data/doc/classes/Reports.html +244 -0
  41. data/doc/classes/RestApi.html +333 -0
  42. data/doc/classes/RhttpClient.html +236 -0
  43. data/doc/classes/Rimap.html +170 -0
  44. data/doc/classes/Rmysql.html +176 -0
  45. data/doc/classes/Roptions.html +131 -0
  46. data/doc/classes/Rselenium.html +233 -0
  47. data/doc/classes/Rssh.html +138 -0
  48. data/doc/classes/Runnable.html +174 -0
  49. data/doc/classes/SFTriggers.html +206 -0
  50. data/doc/classes/Selenium/Client/Driver.html +211 -0
  51. data/doc/classes/Selenium/Client.html +107 -0
  52. data/doc/classes/Selenium.html +107 -0
  53. data/doc/classes/SystemCapture.html +304 -0
  54. data/doc/classes/TestCase.html +418 -0
  55. data/doc/classes/TestData.html +235 -0
  56. data/doc/classes/TestList.html +264 -0
  57. data/doc/classes/Tools.html +244 -0
  58. data/doc/classes/Util.html +201 -0
  59. data/doc/classes/Variation.html +206 -0
  60. data/doc/fr_class_index.html +92 -0
  61. data/doc/fr_method_index.html +465 -0
  62. data/doc/index.html +23 -0
  63. data/doc/oats_httpd.conf +32 -0
  64. data/doc/oats_user.yml +25 -0
  65. data/doc/rdoc-style.css +208 -0
  66. data/lib/deep_merge/.gitignore +2 -0
  67. data/lib/deep_merge/core.rb +195 -0
  68. data/lib/deep_merge/deep_merge.rb +1 -0
  69. data/lib/deep_merge/deep_merge_hash.rb +28 -0
  70. data/lib/deep_merge/rails_compat.rb +27 -0
  71. data/lib/oats/application_logs.rb +163 -0
  72. data/lib/oats/build_id.rb +58 -0
  73. data/lib/oats/commandline_options.rb +128 -0
  74. data/lib/oats/diff.rb +278 -0
  75. data/lib/oats/driver.rb +492 -0
  76. data/lib/oats/ide.rb +227 -0
  77. data/lib/oats/keywords.rb +67 -0
  78. data/lib/oats/log4r_init.rb +14 -0
  79. data/lib/oats/mysql.rb +97 -0
  80. data/lib/oats/oats.rb +637 -0
  81. data/lib/oats/oats_data.rb +400 -0
  82. data/lib/oats/oats_exceptions.rb +25 -0
  83. data/lib/oats/oats_lock.rb +261 -0
  84. data/lib/oats/oats_selenium_api.rb +639 -0
  85. data/lib/oats/oselenium.rb +189 -0
  86. data/lib/oats/ossh.rb +36 -0
  87. data/lib/oats/patches_for_eventmachine_12.10.rb +66 -0
  88. data/lib/oats/ragent.rb +321 -0
  89. data/lib/oats/rclient.rb +42 -0
  90. data/lib/oats/report.rb +207 -0
  91. data/lib/oats/roptions.rb +88 -0
  92. data/lib/oats/test_case.rb +510 -0
  93. data/lib/oats/test_data.rb +98 -0
  94. data/lib/oats/test_list.rb +141 -0
  95. data/lib/oats/unixdiff.rb +75 -0
  96. data/lib/oats/util.rb +125 -0
  97. data/lib/oats/version.rb +3 -0
  98. data/lib/oats.rb +36 -0
  99. data/nbproject/configs/agent.properties +0 -0
  100. data/nbproject/configs/gr.properties +0 -0
  101. data/nbproject/project.properties +10 -0
  102. data/nbproject/project.xml +17 -0
  103. data/oats.gemspec +42 -0
  104. data/oats_ini.yml +258 -0
  105. data/oats_tests/Gemfile +18 -0
  106. data/oats_tests/aut_ini.yml +30 -0
  107. data/oats_tests/bin/oats +8 -0
  108. data/oats_tests/environments/qa.yml +4 -0
  109. data/oats_tests/environments/qa_chrome.yml +4 -0
  110. data/oats_tests/examples/core/coreExamples.yml +8 -0
  111. data/oats_tests/examples/core/expectedException.rb +39 -0
  112. data/oats_tests/examples/core/ok_verify.rb +2 -0
  113. data/oats_tests/examples/core/ok_verify.rb_ok/out/myfile.txt +1 -0
  114. data/oats_tests/examples/core/ok_verify.rb_ok/out/myfile2.txt +1 -0
  115. data/oats_tests/examples/core/ok_verify.rb_ok/rats_test.log +2 -0
  116. data/oats_tests/examples/core/unexpectedException.rb +30 -0
  117. data/oats_tests/examples/examples.yml +13 -0
  118. data/oats_tests/examples/gui/guiExamples.yml +7 -0
  119. data/oats_tests/examples/gui/seleniumGoogle.rb +10 -0
  120. data/oats_tests/examples/gui/webdriverGoogle.rb +9 -0
  121. data/oats_tests/examples/keywords/SampleXlList-1.xls +0 -0
  122. data/oats_tests/examples/keywords/SampleXlList-2.xls +0 -0
  123. data/oats_tests/examples/keywords/SampleXlLists.xls +0 -0
  124. data/oats_tests/examples/keywords/keywordsDriver.rb +1 -0
  125. data/oats_tests/examples/keywords/keywordsExamples.yml +8 -0
  126. data/oats_tests/examples/keywords/keywordsTC1.yml +5 -0
  127. data/oats_tests/examples/keywords/keywordsTestlist.yml +16 -0
  128. data/oats_tests/examples/needsWork/addTestDynamically.rb +4 -0
  129. data/oats_tests/examples/needsWork/emailVerify.rb +34 -0
  130. data/oats_tests/examples/needsWork/testSql/rtest.sql +6 -0
  131. data/oats_tests/examples/needsWork/testSql/rtest.yml +11 -0
  132. data/oats_tests/examples/occTest/occTest.rb +13 -0
  133. data/oats_tests/examples/occTest/occTest_1.rb +1 -0
  134. data/oats_tests/examples/occTest/occTest_1_1.rb +1 -0
  135. data/oats_tests/examples/occTest/occTest_1_2.rb +1 -0
  136. data/oats_tests/examples/occTest/occTest_1_3.rb +1 -0
  137. data/oats_tests/examples/occTest/occTest_1_4.rb +1 -0
  138. data/oats_tests/examples/occTest/occTest_2.rb +1 -0
  139. data/oats_tests/examples/occTest/occTest_2_1.rb +1 -0
  140. data/oats_tests/examples/occTest/occTest_2_2.rb +1 -0
  141. data/oats_tests/examples/occTest/occTest_2_3.rb +1 -0
  142. data/oats_tests/examples/occTest/occTest_2_4.rb +1 -0
  143. data/oats_tests/examples/occTest/occTest_3.rb +1 -0
  144. data/oats_tests/examples/occTest/occTest_3_1.rb +1 -0
  145. data/oats_tests/examples/occTest/occTest_3_2.rb +1 -0
  146. data/oats_tests/examples/occTest/occTest_3_3.rb +1 -0
  147. data/oats_tests/examples/occTest/occTest_3_4.rb +1 -0
  148. data/oats_tests/examples/occTest/occTest_4.rb +1 -0
  149. data/oats_tests/examples/occTest/occTestlist.yml +9 -0
  150. data/oats_tests/examples/occTest/occTestlist_1.yml +9 -0
  151. data/oats_tests/examples/occTest/occTestlist_2.yml +9 -0
  152. data/oats_tests/examples/occTest/occTestlist_3.yml +9 -0
  153. data/oats_tests/examples/occTest/variation1.yml +4 -0
  154. data/oats_tests/examples/occTest/variation2.yml +4 -0
  155. data/oats_tests/examples/testFileLocationUnitTests/extn_driver.rb +4 -0
  156. data/oats_tests/examples/testFileLocationUnitTests/folder/oats.yml +3 -0
  157. data/oats_tests/examples/testFileLocationUnitTests/folder/t1.rb +2 -0
  158. data/oats_tests/examples/testFileLocationUnitTests/folder1/t1.yml +2 -0
  159. data/oats_tests/examples/testFileLocationUnitTests/folder1/t1_1.yml +3 -0
  160. data/oats_tests/examples/testFileLocationUnitTests/folder2/oats.yml +3 -0
  161. data/oats_tests/examples/testFileLocationUnitTests/folder2/t1.rb +2 -0
  162. data/oats_tests/examples/testFileLocationUnitTests/folder2/t1.yml +2 -0
  163. data/oats_tests/examples/testFileLocationUnitTests/no_yaml.rb +3 -0
  164. data/oats_tests/examples/testFileLocationUnitTests/post_yaml.rb +1 -0
  165. data/oats_tests/examples/testFileLocationUnitTests/t1.rb +4 -0
  166. data/oats_tests/examples/testFileLocationUnitTests/t1.yml +2 -0
  167. data/oats_tests/examples/testFileLocationUnitTests/t1_1.yml +3 -0
  168. data/oats_tests/examples/testFileLocationUnitTests/testDir/oats.yml +3 -0
  169. data/oats_tests/examples/testFileLocationUnitTests/testDir/t1.rb +2 -0
  170. data/oats_tests/examples/testFileLocationUnitTests/testDir/t1.yml +2 -0
  171. data/oats_tests/examples/testFileLocationUnitTests/testDir2/t1.rb +2 -0
  172. data/oats_tests/examples/testFileLocationUnitTests/testDir2/t1.yml +2 -0
  173. data/oats_tests/examples/testFileLocationUnitTests/unitTestList.yml +36 -0
  174. data/oats_tests/examples/testFileLocationUnitTests/yml_driver.rb +2 -0
  175. data/oats_tests/lib/business.rb +28 -0
  176. data/oats_tests/lib/sample_xl_lists.rb +37 -0
  177. data/test/common_test_unit_setup.rb +21 -0
  178. data/test/test_basic.rb +16 -0
  179. data/test/test_selenium.rb +16 -0
  180. data/test/test_xl_lists.rb +16 -0
  181. metadata +291 -0
@@ -0,0 +1,400 @@
1
+ #require 'yaml' # http://www.ruby-doc.org/core/classes/YAML.html
2
+ module Oats
3
+
4
+ # Keeps history of YAML files loaded into the global $oats.
5
+ # if omit is true add the yaml name into the result directory hierarchy
6
+ class OatsDataLoadHistoryItem
7
+ attr_accessor :file, :omit, :in_result_dir
8
+ def initialize(file, omit = false)
9
+ @file = file
10
+ @omit = omit
11
+ @in_result_dir = true
12
+ end
13
+ end
14
+
15
+ class OatsData
16
+ @@oats_def_file = nil
17
+ @@resolve_path = []
18
+ # Resolves ENV[ entries in a oats_data
19
+ def OatsData.resolve(oats_data = nil)
20
+ oats_data = $oats unless oats_data
21
+ changed = false
22
+ oats_data.each do |key, val|
23
+ if val.instance_of?(String)
24
+ begin
25
+ next unless /Oats\.|ENV\[/ =~ val # skip if no need for change
26
+ new_val = eval(val)
27
+ next if new_val.instance_of?(String) and /Oats\.|ENV\[/ =~ new_val # skip if change did not help
28
+ # new_key = key.sub(/\s*\(wait4definition\)\s*/,'')
29
+ # next unless new_key == key or new_val # skip if wait4defs in effect
30
+ # oats_data[new_key] = new_val
31
+ oats_data[key] = if new_val == 'previous_oats_value'
32
+ oc = @@oats_copy
33
+ @@resolve_path.each { |k| oc = oc[k] }
34
+ oc[key]
35
+ else
36
+ new_val
37
+ end
38
+ changed = true
39
+ # oats_data.delete(key) unless new_key == key
40
+ rescue Exception =>e
41
+ $log.error "While evaluating Oats.data #{key}: #{val}"
42
+ raise e
43
+ end
44
+ elsif val.instance_of?(Array)
45
+ val.each_with_index do | item, index |
46
+ if item.instance_of?(String)
47
+ begin
48
+ next unless /Oats\.|ENV\[/ =~ item # skip if no need for change
49
+ new_val = eval(item)
50
+ next if /Oats\.|ENV\[/ =~ new_val # skip if change did not help
51
+ val[index] = if new_val == 'previous_oats_value'
52
+ oc = @@oats_copy
53
+ @@resolve_path.each { |k| oc = oc[k] }
54
+ oc[key]
55
+ else
56
+ new_val
57
+ end
58
+ changed = true
59
+ rescue Exception =>exc
60
+ $log.error "While evaluating [#{item}] for [#{index}]th entry in Rat.data #{key}: #{val}"
61
+ raise exc
62
+ end
63
+ elsif item.instance_of?(Hash)
64
+ begin
65
+ @@resolve_path.push(key)
66
+ res_return = resolve(item)
67
+ ensure
68
+ @@resolve_path.pop
69
+ end
70
+ changed = res_return || changed
71
+ end
72
+ end
73
+ elsif val.instance_of?(Hash)
74
+ begin
75
+ @@resolve_path.push(key)
76
+ res_return = resolve(val)
77
+ ensure
78
+ @@resolve_path.pop
79
+ end
80
+ changed = res_return || changed
81
+ end
82
+ end
83
+ while changed
84
+ changed = resolve(oats_data)
85
+ end
86
+ changed
87
+ end
88
+
89
+ # If no input, use default YAML file, overridden by user's HOME or OATS_INI
90
+ # Specified oats_file becomes the overriding file.
91
+ # If specified, oats_default data is used instead of the default YAML contents.
92
+ # Returns loaded data in $oats
93
+ @@define_always = nil
94
+ def OatsData.load(oats_file = ENV['OATS_INI'] , oats_default = nil)
95
+ @@define_always = nil
96
+ @@oats_def_file ||= ENV['OATS_HOME'] + '/oats_ini.yml'
97
+
98
+ if oats_file
99
+ raise(OatsError, "Can not locate: #{oats_file}") unless File.exist?(oats_file)
100
+ else
101
+ oats_file = ENV['HOME'] ? File.join( ENV['HOME'] , 'oats_user.yml') : nil
102
+ oats_file = nil unless oats_file and File.exist?(oats_file)
103
+ end
104
+ if oats_file
105
+ begin
106
+ oats_data = YAML.load_file(oats_file)
107
+ rescue
108
+ raise(OatsError, "While loading [#{oats_file}] " + $!)
109
+ end
110
+ end
111
+
112
+ if oats_default # make a deep copy
113
+ oats_default = Marshal.load(Marshal.dump(oats_default))
114
+ else # Only the first time, when reading the _ini files
115
+ begin
116
+ oats_default = YAML.load_file(@@oats_def_file)
117
+ rescue
118
+ raise(OatsError,"Error loading [#{@@oats_def_file}]: " + $!)
119
+ end
120
+ $oats = oats_default # So that resolve can resolve Oats.data calls
121
+ begin
122
+ OatsData.resolve(oats_default)
123
+ rescue
124
+ $log.error "While resolving variables in: " + @@oats_def_file
125
+ # raise(OatsError, $!.to_s)
126
+ raise $!
127
+ end
128
+ # Use this hash to persist internally used oats_data
129
+ oats_default['_'] = {}
130
+ oats_default['_']['load_history'] = [ OatsDataLoadHistoryItem.new(@@oats_def_file) ]
131
+ $oats = oats_default # $oats now has data resolved and has load_history
132
+ # For some reason OCC/Ubuntu needed the environment but not Mac
133
+ aut_dir_test = ENV['OATS_TESTS'] || oats_data['execution']['dir_tests'] || oats_default['execution']['dir_tests']
134
+ if aut_dir_test
135
+ aut_ini = aut_dir_test + '/aut_ini.yml'
136
+ oats_default['include_yaml'] ||= aut_ini if File.exists?(aut_ini)
137
+ end
138
+ OatsData.include_yaml_file(oats_default['include_yaml'], @@oats_def_file) if oats_default['include_yaml']
139
+ oats_default = $oats
140
+ end
141
+
142
+ if oats_data
143
+ incl_yamls = oats_data['include_yaml']
144
+ if incl_yamls and not oats_data['include_yaml_later']
145
+ oats_data['include_yaml'] = nil
146
+ OatsData.include_yaml_file(incl_yamls, oats_file)
147
+ end
148
+ begin
149
+ @@oats_copy = Marshal.load(Marshal.dump($oats))
150
+ merged = OatsData.merge($oats,oats_data)
151
+ rescue OatsError
152
+ $log.error "While merging: " + oats_file
153
+ raise(OatsError, $!.to_s)
154
+ rescue
155
+ $log.error "While merging: " + oats_file
156
+ raise
157
+ end
158
+ merged['_']['load_history'] << OatsDataLoadHistoryItem.new(oats_file)
159
+ $oats = merged
160
+ if incl_yamls and oats_data['include_yaml_later']
161
+ merged['include_yaml'] = nil
162
+ OatsData.include_yaml_file(incl_yamls, oats_file)
163
+ end
164
+ begin
165
+ OatsData.resolve(merged)
166
+ rescue
167
+ $log.error "While resolving variables in: " + oats_file
168
+ raise $!
169
+ end
170
+ result = $oats
171
+ else
172
+ $log.warn("Could not find oats-user.yml via OATS_INI or HOME definition. Using the system default.")
173
+ result = oats_default
174
+ end
175
+ return result
176
+ end
177
+
178
+ @@include_hist = []
179
+ # Handles include_yaml files. Calls OatsData.overlay to modify $oats. Returns nothing.
180
+ def OatsData.include_yaml_file(incl_yamls, oats_file = nil)
181
+ return unless incl_yamls
182
+ if incl_yamls
183
+ incl_yamls = [incl_yamls] if incl_yamls.instance_of?(String)
184
+ incl_yamls.each do |incl_yaml|
185
+ begin
186
+ incl_yaml_file = TestData.locate(incl_yaml, File.dirname(oats_file))
187
+ Oats.assert incl_yaml_file, "Can not locate file: #{incl_yaml}"
188
+
189
+ hist = Oats.data['_']['load_history'].collect{|i| i.file} + @@include_hist
190
+ Oats.assert ! hist.include?(incl_yaml_file), "Attempt to re-include #{incl_yaml_file} into #{hist.inspect}"
191
+ begin
192
+ @@include_hist.push(incl_yaml_file)
193
+ OatsData.overlay(incl_yaml_file)
194
+ ensure
195
+ Oats.assert_equal incl_yaml_file, @@include_hist.pop
196
+ end
197
+ rescue
198
+ msg = "While including [#{incl_yaml}]"
199
+ msg += " from: " + oats_file if oats_file
200
+ # raise(OatsError, msg + $!)
201
+ $log.error msg
202
+ raise $!
203
+ end
204
+ end
205
+ return
206
+ end
207
+ end
208
+
209
+ # Returns Oats.data history files in an array.
210
+ def OatsData.history(omit=false)
211
+ if omit
212
+ Oats.data['_']['load_history'].select{|i| i.omit and i.in_result_dir}.collect{|i| i.file}
213
+ else
214
+ Oats.data['_']['load_history'].collect{|i| i.file}
215
+ end
216
+ end
217
+
218
+ # Overrides config_ini hash tree with custom_ini.
219
+ # At each level keep only the items specified by the include_list array
220
+ def OatsData.merge(config_ini, custom_ini)
221
+ merged_config = config_ini
222
+ raise(OatsError, "Original YAML is not a hash: " + config_ini.inspect) unless config_ini.class == Hash
223
+ return config_ini unless custom_ini # If input YAML is empty
224
+ raise(OatsError, "Override YAML is not a hash: " + custom_ini.inspect) unless custom_ini.class == Hash
225
+ include_array = []
226
+ include_list_exists = false
227
+ @@define_always = custom_ini['define_always'] if custom_ini.include?('define_always')
228
+ custom_ini.each do |key, val|
229
+ if config_ini.has_key?(key)
230
+ old_val = config_ini[key]
231
+ unless old_val.nil? or val.nil?
232
+ if (old_val.class != val.class)
233
+ if val.instance_of?(FalseClass) # If new value is different and false set it to nil
234
+ val = nil
235
+ elsif not ( (val.instance_of?(TrueClass) or val.instance_of?(FalseClass)) and
236
+ (old_val.instance_of?(TrueClass) or old_val.instance_of?(FalseClass)) )
237
+ $log.error "Entry [#{key}] was previously set to [#{old_val}] with type [" + old_val.class.to_s + "]"
238
+ $log.error " now being set to [#{val}] with type [" + val.class.to_s + "]"
239
+ $log.error " entry [#{old_val}] of the original YAML entry is part of: " + config_ini.inspect
240
+ $log.error " entry [#{val}] of over-ride YAML entry is part of: " + custom_ini.inspect
241
+ raise(OatsError, "Attempt to override OATS data with a different type.")
242
+ end
243
+ end
244
+ end
245
+ else
246
+ if key == "include_list"
247
+ raise(OatsError, "The include_list " + val.inspect + " is not an Array") unless val.class == Array
248
+ include_array = val
249
+ include_list_exists = true
250
+ else
251
+ add_key = key.sub(/\s*\(define\)\s*/,'')
252
+ if add_key == key and ! @@define_always
253
+ raise(OatsError, "Override YAML key [#{key}] is not defined in the master YAML: " + @@oats_def_file)
254
+ else
255
+ if merged_config.has_key?(add_key)
256
+ key = add_key
257
+ old_val = config_ini[key]
258
+ else
259
+ merged_config[add_key] = val
260
+ merged_config.delete(key) unless add_key == key
261
+ next
262
+ end
263
+ end
264
+ end
265
+ end
266
+ case val
267
+ when Hash then merged_config[key] = merge( old_val, val) # Deep copy for Hashes
268
+ when Array then # Only shallow copy for Arrays
269
+ new_arr = []
270
+ val.each { |e| new_arr << e }
271
+ merged_config[key] = new_arr
272
+ when String then merged_config[key] = val.dup
273
+ else
274
+ unless (val.nil? and (old_val.class == Hash) ) || key == "include_list"
275
+ merged_config[key] = val
276
+ end
277
+ end
278
+ end
279
+ if include_list_exists
280
+ return nil if include_array.nil?
281
+ merged_config.delete_if { |key,value| not include_array.include?(key) }
282
+ end
283
+ merged_config
284
+ end
285
+ # Overlays $oats with contents of oats_file. Performs no compatibility checking.
286
+ def OatsData.overlay(oats_file)
287
+ if oats_file.instance_of?(Hash)
288
+ oats_overlay = oats_file
289
+ oats_file = oats_overlay.keys.first + '_' + oats_overlay.values.first.keys.first
290
+ else
291
+ if oats_file
292
+ raise(OatsError, "Can not locate [#{oats_file}]") unless File.exist?(oats_file)
293
+ else
294
+ raise(OatsError, "Must specify a oats_file")
295
+ end
296
+ begin
297
+ oats_overlay = YAML.load_file(oats_file)
298
+ rescue
299
+ raise(OatsError, "While loading [#{oats_file}] " + $!)
300
+ end
301
+ end
302
+ incl_yamls = oats_overlay['include_yaml']
303
+ if incl_yamls and not oats_overlay['include_yaml_later']
304
+ oats_overlay['include_yaml'] = nil
305
+ OatsData.include_yaml_file(incl_yamls, oats_file)
306
+ end
307
+ # Clone it, don't change the original
308
+ oats_new = Marshal.load(Marshal.dump($oats))
309
+ begin
310
+ OatsData.overlay_data(oats_overlay, oats_new)
311
+ rescue OatsError
312
+ $log.error "While overlaying: " + oats_file
313
+ raise(OatsError, $!.to_s)
314
+ rescue Exception
315
+ $log.error "While overlaying: " + oats_file
316
+ raise
317
+ end
318
+ oats_new['_']['load_history'] << OatsDataLoadHistoryItem.new(oats_file)
319
+
320
+ $oats = oats_new
321
+ if incl_yamls and oats_overlay['include_yaml_later']
322
+ oats_overlay['include_yaml'] = nil
323
+ OatsData.include_yaml_file(incl_yamls, oats_file)
324
+ end
325
+ begin
326
+ OatsData.resolve(oats_new)
327
+ rescue
328
+ $log.error "While resolving variables in: " + oats_file
329
+ raise $!
330
+ end
331
+ oats_new = $oats
332
+ return oats_new
333
+ end
334
+
335
+ # Recurse thru matching hash only, replace rest by overlay
336
+ def OatsData.overlay_data(overlay, oats_data)
337
+ overlay.each do |key, val|
338
+ default_key = key.sub(/\s*\((default|define)\)\s*/,'')
339
+ if default_key == key
340
+ default_key = nil
341
+ else
342
+ key = default_key
343
+ end
344
+ if val.instance_of?(Hash)
345
+ if oats_data[key].instance_of?(Hash)
346
+ OatsData.overlay_data(val, oats_data[key])
347
+ else
348
+ oats_data[key] = val if oats_data[key] == nil
349
+ OatsData.overlay_data(val, oats_data[key])
350
+ end
351
+ else
352
+ if default_key
353
+ oats_data[key] = val unless oats_data[key]
354
+ else
355
+ oats_data[key] = val
356
+ end
357
+ end
358
+ end
359
+ end
360
+
361
+ # Regenerates input file based on YAML. Assumes input is absolute path and exists.
362
+ # Return generated file path or nil if nothing is generated.
363
+ def OatsData.regenerate_file(file_in)
364
+ file_in_root = file_in.sub(/(.*)\..*$/,'\1')
365
+ file_in_extn = File.extname(file_in)
366
+ yaml_in = file_in_root + '.yml'
367
+ return nil unless File.exist?(yaml_in)
368
+ file_gen = file_in_root + '.gen' + file_in_extn
369
+ yml_out = file_in_root + '.gen.yml'
370
+ $log.debug "Regenerating [#{file_in}] into [#{file_gen}] based on [#{yaml_in}]"
371
+ file_contents = IO.read(file_in)
372
+ test_data = YAML.load_file(yaml_in)
373
+ if OatsData.map_file_contents(Oats.data, test_data, file_contents)
374
+ File.open( file_gen, 'w' ) { |out| out.print file_contents }
375
+ File.open( yml_out, 'w' ) { |out| YAML.dump( test_data, out ) }
376
+ return file_gen
377
+ else
378
+ return nil
379
+ end
380
+ end
381
+
382
+ # Helper method to replace HTML file contents based on oats and test data mappings.
383
+ # Returns true if substitutions are made
384
+ def OatsData.map_file_contents(oats_data, test_data, file_contents)
385
+ return false unless test_data
386
+ changed = false
387
+ test_data.each do |key,val|
388
+ repVal = oats_data[key]
389
+ next if val.nil? or repVal.nil?
390
+ if val.class == Hash
391
+ changed = changed or map_file_contents( repVal, val, file_contents)
392
+ else
393
+ changed = changed or file_contents.sub!(val, repVal)
394
+ test_data[key] = repVal
395
+ end
396
+ end
397
+ end
398
+
399
+ end
400
+ end
@@ -0,0 +1,25 @@
1
+ # Defines general exception classes used in OATS.
2
+
3
+ # Not an error, but a request to exit gracefully from deep within OATS code.
4
+ class OatsExit < StandardError ; end
5
+
6
+ # Unclassified exceptions used by OATS framework
7
+ class OatsError < StandardError ; end
8
+
9
+ # Errors related to Oats Setup
10
+ class OatsSetupError < OatsError ; end
11
+
12
+ # Commandline or related oats-user.yml value errors
13
+ class OatsBadInput < OatsError ; end
14
+
15
+ # Assertion of failure of an individual OATS test
16
+ class OatsTestError < OatsError ; end
17
+
18
+ # Raised by Oats.assert_* methods
19
+ class OatsAssertError < OatsTestError ; end
20
+
21
+ # Assertion of failure during verification of test output
22
+ class OatsVerifyError < OatsTestError ; end
23
+
24
+ # Not an error, but a way to escape a OatsTest from the middle of it.
25
+ class OatsTestExit < OatsTestError ; end
@@ -0,0 +1,261 @@
1
+ # Manages a lock file indicating a OATS session is in process
2
+ require 'win32ole' if ENV['OS'] == 'Windows_NT'
3
+ module Oats
4
+
5
+ module OatsLock
6
+ @@file_handle = nil
7
+ @@is_locked = false
8
+
9
+ # Returns true if able to set the lock.
10
+ def OatsLock.set(verbose = nil)
11
+ if OatsLock.locked?(true)
12
+ return false
13
+ else
14
+ @@file_handle = File.open(in_progress_file, 'w')
15
+ my_pid = Process.pid.to_s
16
+ @@file_handle.puts my_pid
17
+ if ENV['OS'] != 'Windows_NT' or ENV['TEMP'] =~ /^\/cygdrive/
18
+ # Leave file handle open for windows to detect and kill associated java, etc.
19
+ # processes using file handles.
20
+ @@file_handle.close
21
+ @@file_handle = nil
22
+ end
23
+ @@is_locked = true
24
+ return true
25
+ end
26
+ end
27
+
28
+ # Returns the locked state after the last check
29
+ # verify: true will verify the true state of the lock
30
+ def OatsLock.locked?(verify = nil)
31
+ return @@is_locked unless verify
32
+ @@is_locked = false
33
+ if ENV['OS'] != 'Windows_NT' or ENV['TEMP'] =~ /^\/cygdrive/
34
+ if File.exist?(in_progress_file)
35
+ pids = IO.readlines(in_progress_file)
36
+ ruby_pid = pids.shift
37
+ return @@is_locked unless ruby_pid
38
+ ps_line = `ps -p #{ruby_pid} `
39
+ if ps_line =~ /bin\/ruby/
40
+ @@is_locked = true
41
+ $log.error "Another oats session is possibly in progress:"
42
+ $log.error ">> #{ps_line}"
43
+ $log.error "Please kill locking processes or remove #{in_progress_file}."
44
+ else
45
+ pids.each { |pid| OatsLock.kill_pid(pid.chomp) }
46
+ FileUtils.rm(in_progress_file)
47
+ end
48
+ end
49
+ else
50
+ begin
51
+ FileUtils.rm(in_progress_file)
52
+ rescue Errno::ENOENT # No such File or Directory
53
+ rescue Errno::EACCES # unlink Permission denied
54
+ @@is_locked = true
55
+ return @@is_locked if verify == :handles_are_cleared
56
+ # Attempt to kill all dangling processes that prevent removal of the lock
57
+ proc_array = nil
58
+ hstring = lock_file
59
+ ok_to_kill = /(java)|(mysql)||(chromedriver)|(firefox)||(chrome)|(iexplore)\.exe/
60
+ pid, proc_name, handle_string, line = nil
61
+ matches = IO.popen("handle #{hstring}").readlines
62
+ oats_is_alive = false
63
+ matches.each do |lvar|
64
+ line = lvar.chomp
65
+ proc_array = parse_windows_handle_process_line(line)
66
+ pid, proc_name, handle_string = proc_array
67
+ next unless pid
68
+ if proc_name =~ /ruby/
69
+ # if pid = $$.to_s
70
+ # @@is_locked = false
71
+ # return false
72
+ # end
73
+ oats_is_alive = line
74
+ $log.error "Another oats session is possibly in progress:"
75
+ $log.error ">> #{line}"
76
+ $log.error "Please kill locking processes and remove this file if the oats session is defunct."
77
+ break
78
+ end
79
+ end
80
+ @@is_locked = oats_is_alive
81
+ unless oats_is_alive
82
+ matches.each do |lvar|
83
+ line = lvar.chomp
84
+ pid, proc_name, handle_string = parse_windows_handle_process_line(line)
85
+ next unless pid
86
+ raise "Handle error for [#{hstring}] Please notify OATS administrator." unless handle_string =~ /#{hstring}/
87
+ $log.warn "Likely locking process: [#{line}]"
88
+ if proc_name =~ ok_to_kill
89
+ $log.warn "Will attempt to kill [#{proc_name}] with PID #{pid}"
90
+ signal = 'KILL'
91
+ killed = Process.kill(signal,pid.to_i)
92
+ if RUBY_VERSION =~ /^1.9/
93
+ if killed.empty?
94
+ killed = 0
95
+ else
96
+ killed = 1
97
+ end
98
+ end
99
+ if killed == 0
100
+ $log.warn "Failed to kill the process"
101
+ else
102
+ $log.warn "Successfully killed [#{proc_name}]"
103
+ end
104
+ else
105
+ $log.warn "Oats is configured not to auto-kill process [#{proc_name}]"
106
+ end
107
+ end
108
+ sleep 2 # Need time to clear the process handles
109
+ @@is_locked = OatsLock.locked?(:handles_are_cleared) # Still locked?
110
+ end
111
+ @@is_locked = proc_array if @@is_locked and proc_array
112
+ end
113
+ end
114
+ return @@is_locked
115
+ end
116
+
117
+ def OatsLock.kill_pid(pid,info_line=nil)
118
+ signal = 'KILL'
119
+ no_such_process = false
120
+ begin
121
+ killed = Process.kill(signal,pid.to_i)
122
+ rescue Errno::ESRCH # OK if the process is gone
123
+ no_such_process = true
124
+ end
125
+ if RUBY_VERSION =~ /^1.9/
126
+ if killed.empty?
127
+ killed = 0
128
+ else
129
+ killed = 1
130
+ end
131
+ end
132
+ if no_such_process
133
+ # $log.debug "No such process #{info_line||pid}"
134
+ elsif killed == 0
135
+ $log.warn "Failed to kill [#{info_line||pid}]"
136
+ else
137
+ $log.warn "Successfully killed [#{info_line||pid}]"
138
+ end
139
+ end
140
+
141
+ def OatsLock.find_matching_processes(proc_names)
142
+ matched = []
143
+ if ENV['OS'] == 'Windows_NT'
144
+ processes = WIN32OLE.connect("winmgmts://").ExecQuery("select * from win32_process")
145
+ # for process in processes do
146
+ # for property in process.Properties_ do
147
+ # puts property.Name
148
+ # end
149
+ # break
150
+ # end
151
+ processes.each do |process|
152
+ if process.Commandline =~ proc_names
153
+ matched.push [process.ProcessId,process.Name,nil, process.CommandLine]
154
+ end
155
+ end
156
+ else
157
+ pscom = ENV['OS'] == 'Linux' ? 'ps lxww' : 'ps -ef'
158
+ `#{pscom}`.split("\n").each do |lvar|
159
+ line = lvar.chomp
160
+ case ENV['OS']
161
+ when 'Darwin' # ps -ef output
162
+ pid = line[5..11]
163
+ next if pid.to_i == 0
164
+ ppid = line[12..16]
165
+ proc_name = line[50..-1]
166
+ when 'Linux' # ps ww output
167
+ pid = line[7..12]
168
+ next if pid.to_i == 0
169
+ ppid = line[13..18]
170
+ proc_name = line[69..-1]
171
+ else
172
+ raise OatError, "Do not know how to parse ps output from #{ENV['OS']}"
173
+ end
174
+ next unless pid
175
+ matched.push [pid.strip, proc_name.strip, ppid.strip, line.strip] if proc_name =~ proc_names
176
+ end
177
+ end
178
+ return matched
179
+ end
180
+
181
+ def OatsLock.kill_webdriver_browsers
182
+ match = "ruby.*oats/lib/oats_main.rb"
183
+ # Not tested on agents on Windows_NT
184
+ if $oats_execution['agent']
185
+ nickname = $oats_execution['agent']['execution:occ:agent_nickname']
186
+ port = $oats_execution['agent']['execution:occ:agent_port']
187
+ match += " -p #{port} -n #{nickname}"
188
+ end
189
+
190
+ # Kill all selenium automation chrome jobs on MacOS. Assumes MacOS is for development only, not OCC.
191
+ # Will cause problems if multiple agents are run on MacOS
192
+ if ENV['OS'] == 'Darwin'
193
+ chrome_automation_procs = OatsLock.find_matching_processes(/ Chrome .* --dom-automation/)
194
+ chrome_automation_procs.each do |pid,proc_name,ppid|
195
+ OatsLock.kill_pid pid
196
+ end
197
+ end
198
+
199
+ oats_procs = OatsLock.find_matching_processes(/#{match}\z/)
200
+ chromedriver_procs = OatsLock.find_matching_processes(/IEXPLORE.EXE\" -noframemerging|(chromedriver|firefox(-bin|\.exe\") -no-remote)/)
201
+ webdriver_procs = OatsLock.find_matching_processes(/webdriver/)
202
+ oats_procs.each do |opid,oproc_name,oppid|
203
+ chromedriver_procs.each do |cpid,cproc_name,cppid|
204
+ if cppid == opid
205
+ webdriver_procs.each do |wpid,wproc_name,wppid|
206
+ OatsLock.kill_pid wpid if wppid == cpid
207
+ end
208
+ OatsLock.kill_pid cpid
209
+ end
210
+ end
211
+ end
212
+ # If parent ruby dies, ppid reverts to "1"
213
+ (chromedriver_procs + webdriver_procs).each do |pid,proc_name,ppid|
214
+ OatsLock.kill_pid pid if ppid == "1" and proc_name !~ /defunct/
215
+ end
216
+ end
217
+
218
+ # Removes lock
219
+ def OatsLock.reset
220
+ if @@file_handle # Only for Windows
221
+ @@file_handle.close
222
+ @@file_handle = nil
223
+ @@is_locked = true
224
+ else # Doesn't return status properly for non-windows, just resets the lock
225
+ if $oats_execution['agent'].nil? and ENV['OS'] != 'Windows_NT' and File.exist?(in_progress_file)
226
+ pids = IO.readlines(in_progress_file)
227
+ current_pid = pids.shift
228
+ pids.each { |pid| OatsLock.kill_pid(pid.chomp) } # Legacy firefox
229
+ end
230
+ @@is_locked = false
231
+ end
232
+ FileUtils.rm_f in_progress_file
233
+ return @@is_locked
234
+ end
235
+
236
+ class << self
237
+ private
238
+ def lock_file
239
+ file = 'oats_in_progress.lock'
240
+ if $oats_execution['agent'] && $oats_execution['agent']['execution:occ:agent_nickname']
241
+ file = $oats_execution['agent']['execution:occ:agent_nickname'] + '_' + file
242
+ end
243
+ return file
244
+ end
245
+
246
+ def in_progress_file
247
+ return ENV['HOME'] + '/' + lock_file
248
+ end
249
+
250
+ def parse_windows_handle_process_line(line)
251
+ line =~ /(.*) pid:(.*) .*: (.*)/
252
+ return nil unless $1
253
+ proc_name = $1
254
+ pid = $2
255
+ handle_string = $3
256
+ return pid.strip, proc_name.strip, handle_string.strip
257
+ end
258
+ end
259
+
260
+ end
261
+ end