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,189 @@
1
+ require 'oats/oats_selenium_api'
2
+
3
+ # Advertised OATS methods to be used from tests
4
+ # Selenium object accessor created on demand by Oat.browser
5
+
6
+ def selenium
7
+ $selenium || Oats.browser
8
+ end
9
+
10
+ module Oats
11
+
12
+ class Oselenium
13
+ @@browsers = []
14
+ attr_reader :browser, :site, :login_base_url, :user
15
+ @@server_started_by_oats = false
16
+
17
+ def Oselenium.browsers
18
+ @@browsers
19
+ end
20
+
21
+ def Oselenium.browser(*args)
22
+ if args.include?(true)
23
+ new_browser = true
24
+ args.delete true
25
+ end
26
+ if not args.empty?
27
+ if not new_browser and Oselenium.browsers.last
28
+ return Oselenium.browsers.last.login(*args)
29
+ else
30
+ return Oselenium.new(*args).browser
31
+ end
32
+ elsif Oselenium.browsers.last
33
+ return Oselenium.browsers.last
34
+ else
35
+ return Oselenium.new.browser
36
+ end
37
+ end
38
+
39
+ def initialize(*args)
40
+
41
+ browser_type = $oats['selenium']['browser_type']
42
+ # is_webdriver = ! $oats['selenium']['rcdriver']
43
+
44
+ unless $oats_global['download_dir']
45
+ if browser_type == 'firefox'
46
+ $oats_global['download_dir'] = $oats['execution']['dir_results'] + '/downloads'
47
+ elsif Oats.data("selenium.default_downloads")
48
+ $oats_global['download_dir'] = File.join(ENV[ ENV['OS'] == 'Windows_NT' ? 'USERPROFILE' : 'HOME'], 'Downloads')
49
+ $oats_global['download_dir'].gsub!('\\','/') if ENV['OS'] == 'Windows_NT'
50
+ end
51
+ end
52
+
53
+ download_dir = $oats_global['download_dir'].gsub('/','\\\\') if $oats_global['download_dir'] and
54
+ ENV['OS'] == 'Windows_NT'
55
+ download_dir ||= $oats_global['download_dir']
56
+ case browser_type
57
+ when 'firefox'
58
+ profile = $oats['selenium']['firefox_profile']
59
+ $oats_global["browser_path"] ||= ENV[profile] if profile
60
+ # if is_webdriver
61
+ profile = Selenium::WebDriver::Firefox::Profile.from_name profile if profile
62
+ profile = Selenium::WebDriver::Firefox::Profile.new unless profile
63
+ profile['browser.download.dir'] = download_dir
64
+ profile["browser.helperApps.neverAsk.saveToDisk"] = "text/plain, application/vnd.ms-excel, application/pdf, text/csv"
65
+ profile['browser.download.folderList'] = 2
66
+ $oats['selenium']['firefox_profile_set'].each { |method,value| profile.send method+'=', value }
67
+
68
+ when 'chrome'
69
+ profile = Selenium::WebDriver::Chrome::Profile.new
70
+ profile['download.prompt_for_download'] = false
71
+ profile['download.default_directory'] = download_dir
72
+ unless $oats_global['browser_path']
73
+ vpath = File.join($oats['_']['vendor'], ENV['OS'], 'chromedriver' + (ENV['OS'] == 'Windows_NT' ? '.exe' : '') )
74
+ $oats_global['browser_path'] = vpath if File.exist?(vpath)
75
+ end
76
+ end
77
+ FileUtils.rm_f Dir.glob(File.join($oats_global['download_dir'],'*')) if $oats_global['download_dir']
78
+ Oats.info "Browser type: #{browser_type.inspect}, profile: #{profile.inspect}, path: #{$oats_global["browser_path"].inspect}"
79
+ $oats_info['browser'] = $oats['selenium']['browser_type'].sub(/ .*/,'')
80
+ remote_webdriver = $oats['selenium']['remote_webdriver']
81
+ driver_type = $oats['selenium']['browser_type']
82
+ if remote_webdriver
83
+ remote_webdriver = remote_webdriver[browser_type]
84
+ if remote_webdriver
85
+ driver_type = 'remote'
86
+ @remote_webdriver_is_active = true
87
+ end
88
+ end
89
+ opts = [ driver_type.to_sym ]
90
+ opts_hash ={}
91
+ opts_hash[:profile] = profile if profile and driver_type != 'remote'
92
+ if driver_type == 'remote'
93
+ opts_hash[:url] = "http://"+remote_webdriver+'/wd/hub'
94
+ opts_hash[:desired_capabilities] = $oats['selenium']['browser_type'].to_sym
95
+ end
96
+ case browser_type
97
+ when /firefox/
98
+ Selenium::WebDriver::Firefox.path = $oats_global["browser_path"]
99
+ when /chrome/
100
+ Selenium::WebDriver::Chrome.driver_path = $oats_global['browser_path']
101
+ end if $oats_global['browser_path']
102
+ browser_options = Oats.data "selenium.options.#{browser_type}"
103
+ browser_options.each_pair { |name, val| opts_hash[name.to_sym] = val } if browser_options
104
+ opts.push(opts_hash) unless opts_hash.empty?
105
+ # OatsLock.record_firefox_processes do
106
+ @browser = Selenium::WebDriver.for(*opts)
107
+ # end
108
+ @browser.osel = self
109
+ $selenium = @browser
110
+ @@browsers << @browser
111
+ login(*args)
112
+ end
113
+
114
+ # Stub method/interface for actions to take after instantiating the browser
115
+ def login(*args) #
116
+ selenium.open(args.first)
117
+ end
118
+
119
+ # True if selenium is running in remote_webdriver mode
120
+ def remote_webdriver?
121
+ @remote_webdriver_is_active
122
+ end
123
+
124
+ # Fixes the path to Windows if running on windows remote webdriver
125
+ def Oselenium.remote_webdriver_map_file_path(file)
126
+ if $selenium and $selenium.osel.remote_webdriver?
127
+ file.sub!(ENV['OATS_HOME'], $oats['selenium']['remote_webdriver']['oats_dir'])
128
+ file_os = $oats['selenium']['remote_webdriver']['os']
129
+ else
130
+ file_os = ENV['OS']
131
+ end
132
+ file_os == 'Windows_NT' ? file.gsub('/','\\') : file
133
+ end
134
+
135
+ def Oselenium.reset
136
+ Oselenium.close
137
+ rescue Timeout::Error
138
+ ## Sometimes doesn't die gracefully. It is OK, will be killed below.
139
+ ensure
140
+ @@browsers = []
141
+ $selenium = nil
142
+ OatsLock.kill_webdriver_browsers
143
+ end
144
+
145
+ def Oselenium.close
146
+ while browser = Oselenium.browsers.pop
147
+ if browser.osel.site
148
+ browser.oats_debug "Closing browser session from #{browser.osel.site}"
149
+ else
150
+ browser.oats_debug "Closing browser session."
151
+ end
152
+ browser.quit
153
+ end
154
+ $selenium = nil
155
+ end
156
+
157
+ def Oselenium.pause_browser
158
+ return if Oselenium.browsers.empty? or ! TestData.pause_after_error
159
+ seconds = 999999
160
+ pause_val = $oats['selenium']['pause_on_exit']
161
+ if not pause_val.integer? or
162
+ pause_val <= 0
163
+ seconds = nil
164
+ elsif pause_val == 1
165
+ seconds = nil unless TestData.current_test.status == 1
166
+ elsif pause_val > 0
167
+ seconds = pause_val
168
+ end
169
+ if seconds
170
+ $stderr.puts "Paused because selenium:pause_on_exit is set to [#{pause_val}]"
171
+ $stderr.puts "PLEASE HIT <ENTER> TO CONTINUE!"
172
+ begin
173
+ # timeout(seconds) { loop { browser.get_title ; sleep 1 } }
174
+ timeout(seconds) { STDIN.readline }
175
+ rescue Timeout::Error
176
+ end
177
+ end
178
+ TestData.pause_after_error = false
179
+ end
180
+
181
+ def Oselenium.port
182
+ # if $oats_execution['agent']
183
+ # '4'+$oats_execution['agent']['execution:occ:agent_port'].to_s
184
+ # else
185
+ $oats['selenium']['port']
186
+ # end
187
+ end
188
+ end
189
+ end
data/lib/oats/ossh.rb ADDED
@@ -0,0 +1,36 @@
1
+ #require 'util'
2
+ class OatsOsshMissingInput < OatsTestError ; end
3
+
4
+ module Oats
5
+
6
+ # Implement Oats.rssh and Oats.rput functionality. See Oats.rssh documentation.
7
+ module Ossh
8
+ def Ossh.run(cmd_file, dir = nil , host = nil, username = nil, rput = nil)
9
+ username ||= Oats.data['ssh']['username']
10
+ host ||= Oats.data['ssh']['host']
11
+ raise(OatsOsshMissingInput, "Ossh plink requires a host.") unless host
12
+ if username == 'root'
13
+ cmd = "plink #{host} -l #{Oats.data['ssh']['root_sudo_username']} "
14
+ cmd_file = 'sudo -u root ' + cmd_file
15
+ else
16
+ cmd = "plink #{host} -l #{username} "
17
+ end
18
+ if rput
19
+ source_cmd = 'echo'
20
+ if File.exist?(cmd_file)
21
+ cmd_file.gsub! /\//, '\\'
22
+ source_cmd = 'type'
23
+ end
24
+ cmd = "#{source_cmd} #{cmd_file} | #{cmd}\"/home/levent.atasoy/bin/oats_put_file.sh #{dir}\""
25
+ else
26
+ if dir
27
+ cmd += "\"cd #{dir} ; #{cmd_file} 2>&1\""
28
+ else
29
+ cmd += "\"#{cmd_file} 2>&1\""
30
+ end
31
+ end
32
+ $log.info cmd
33
+ `#{cmd}`
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,66 @@
1
+ module EventMachine
2
+
3
+ def self.event_callback conn_binding, opcode, data # :nodoc:
4
+ #
5
+ # Changed 27Dec07: Eliminated the hookable error handling.
6
+ # No one was using it, and it degraded performance significantly.
7
+ # It's in original_event_callback, which is dead code.
8
+ #
9
+ # Changed 25Jul08: Added a partial solution to the problem of exceptions
10
+ # raised in user-written event-handlers. If such exceptions are not caught,
11
+ # we must cause the reactor to stop, and then re-raise the exception.
12
+ # Otherwise, the reactor doesn't stop and it's left on the call stack.
13
+ # This is partial because we only added it to #unbind, where it's critical
14
+ # (to keep unbind handlers from being re-entered when a stopping reactor
15
+ # runs down open connections). It should go on the other calls to user
16
+ # code, but the performance impact may be too large.
17
+ #
18
+ if opcode == ConnectionUnbound
19
+ if c = @conns.delete( conn_binding )
20
+ begin
21
+ c.unbind
22
+ rescue
23
+ @wrapped_exception = $!
24
+ stop
25
+ end
26
+ elsif c = @acceptors.delete( conn_binding )
27
+ # no-op
28
+ else
29
+ # raise ConnectionNotBound, "recieved ConnectionUnbound for an unknown signature: #{conn_binding}"
30
+ end
31
+ elsif opcode == ConnectionAccepted
32
+ accep,args,blk = @acceptors[conn_binding]
33
+ raise NoHandlerForAcceptedConnection unless accep
34
+ c = accep.new data, *args
35
+ @conns[data] = c
36
+ blk and blk.call(c)
37
+ c # (needed?)
38
+ elsif opcode == ConnectionCompleted
39
+ c = @conns[conn_binding]
40
+ if c
41
+ c.connection_completed
42
+ # else
43
+ # raise ConnectionNotBound, "received ConnectionCompleted for unknown signature: #{conn_binding}"
44
+ end
45
+ ##
46
+ # The remaining code is a fallback for the pure ruby and java reactors.
47
+ # In the C++ reactor, these events are handled in the C event_callback() in rubymain.cpp
48
+ elsif opcode == TimerFired
49
+ t = @timers.delete( data )
50
+ return if t == false # timer cancelled
51
+ t or raise UnknownTimerFired, "timer data: #{data}"
52
+ t.call
53
+ elsif opcode == ConnectionData
54
+ c = @conns[conn_binding] or raise ConnectionNotBound, "received data #{data} for unknown signature: #{conn_binding}"
55
+ c.receive_data data
56
+ elsif opcode == LoopbreakSignalled
57
+ run_deferred_callbacks
58
+ elsif opcode == ConnectionNotifyReadable
59
+ c = @conns[conn_binding] or raise ConnectionNotBound
60
+ c.notify_readable
61
+ elsif opcode == ConnectionNotifyWritable
62
+ c = @conns[conn_binding] or raise ConnectionNotBound
63
+ c.notify_writable
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,321 @@
1
+ #require 'eventmachine' # http://eventmachine.rubyforge.org/EventMachine.html#M000486
2
+ #require 'patches_for_eventmachine_12.10'
3
+ require 'em-http-request' # https://github.com/igrigorik/em-http-request/wiki/Issuing-Requests
4
+ require 'json'
5
+
6
+ module Oats
7
+
8
+ class Ragent < EventMachine::Connection
9
+ attr_accessor :job_count, :jid, :occ_reintroduction_wait_time
10
+ attr_reader :request
11
+ @@logger = nil
12
+ @@oats_info_snapshot = {}
13
+ @@job_count = 1
14
+ @@occ_default = nil
15
+ @@logger = Log4r::Logger.new('A')
16
+ include EM::P::ObjectProtocol
17
+
18
+
19
+ # Non-nil if in the state of requesting for the next job
20
+ def Ragent.in_next_job
21
+ @@in_next_job
22
+ end
23
+ def Ragent.in_next_job=(v)
24
+ @@in_next_job = v
25
+ end
26
+ def Ragent.is_busy=(jid)
27
+ @@is_busy = jid
28
+ end
29
+ # Current/Last jobid worked on, or false
30
+ def Ragent.is_busy
31
+ @@is_busy
32
+ end
33
+
34
+ # Contains YAML OCC entries if oats is started in agent mode
35
+ def Ragent.occ
36
+ @@occ_default
37
+ end
38
+
39
+ def Ragent.start(occ_def)
40
+ @@occ_default = occ_def # This should not change during agent execution
41
+ @@logger.add('console')
42
+ 3.times do |count| # In case of unexpected exceptions
43
+ begin
44
+ @@occ_reintroduction_wait_time = nil
45
+ @@is_busy = false # If agent is started from scratch assume previous one is gone
46
+ @@in_next_job = false
47
+ @@logger.info "====================================================================================="
48
+ mach_port = ENV['HOSTNAME'] + ':' + $oats['execution']['occ']['port'].to_s
49
+ @@logger.info "Started OATS Server execution-#{count} on #{mach_port} at #{Time.now} "
50
+ EventMachine::run do
51
+ EventMachine::start_server @@occ_default['agent_host'], @@occ_default['agent_port'].to_i, Ragent
52
+ EventMachine.next_tick do
53
+ Ragent.server_logger nil,'Initiating contact with OCC '
54
+ Ragent.start_next_job unless Ragent.is_busy
55
+ end
56
+ end
57
+ break # Shutdown requested
58
+ rescue Exception => exc
59
+ @@logger.error exc
60
+ end
61
+ end
62
+ end
63
+
64
+ # def Ragent.force_close_connection
65
+ # end
66
+ def Ragent.job_count=(val)
67
+ @@job_count = val
68
+ end
69
+ def Ragent.job_count
70
+ @@job_count
71
+ end
72
+
73
+ def Ragent.snapshot_oats_info(oats_info)
74
+ @@oats_info_snapshot = oats_info
75
+ end
76
+
77
+ # Generates summary data into the input oats_info
78
+ def regen_results_summary!(oats_info)
79
+ return Report.results(oats_info['test_files'],true)
80
+ rescue
81
+ server_logger $!.inspect + "\n" + $!.backtrace.join("\n ")
82
+ false
83
+ end
84
+
85
+ # Results_status: Early, Partial, Current, Archived, Missing, Error
86
+ def get_oats_info
87
+ if @request[:jobid] == Ragent.is_busy # working on it now
88
+ if @request[:jobid] == @@oats_info_snapshot['jobid'] # first test started processing
89
+ if regen_results_summary!(@@oats_info_snapshot) # summary succeeded
90
+ oats_info = Marshal.load(Marshal.dump(@@oats_info_snapshot))
91
+ oats_info['results_status'] = 'Partial'
92
+ else
93
+ server_logger "[ERROR] Can not regen_results_summary from the snapshot"
94
+ oats_info = {'results_status' => 'Error'}
95
+ end
96
+ elsif @@oats_info_snapshot['jobid']
97
+ msg = "ERROR: Unexpected condition. Request jobid: #{@request[:jobid]} does not match stored job id: #{@@oats_info_snapshot['jobid']}"
98
+ server_logger msg
99
+ oats_info = { 'jobid' => @request[:jobid], 'results_status' => 'Error', 'error_message' => msg }
100
+ else
101
+ oats_info = Marshal.load(Marshal.dump(Oats.context))
102
+ oats_info['results_status'] = 'Early'
103
+ oats_info['jobid'] = Ragent.is_busy
104
+ end
105
+ else # Search for request on disk in archive or results
106
+ res_dir = File.join(Oats.result_archive_dir, @request[:jobid].to_s)
107
+ results_file = File.join( res_dir,'results.dump')
108
+ if File.readable?(results_file) # Archived ones should have summary
109
+ oats_info = Report.oats_info_retrieve(results_file)
110
+ oats_info['results_status'] = 'Archived'
111
+ else # May have to regenerate the summary, in case test had died
112
+ results_file = File.join( $oats['execution']['dir_results'], 'results.dump')
113
+ if File.readable?(results_file)
114
+ oats_info = Report.oats_info_retrieve(results_file)
115
+ if regen_results_summary!(oats_info) and @request[:jobid] == oats_info['jobid']
116
+ oats_info['results_status'] = 'Current'
117
+ else
118
+ oats_info = {} unless oats_info.instance_of?(Hash)
119
+ oats_info['debug_message'] = "request_jobid: #{@request[:jobid]}, current oats_info jobid: #{oats_info['jobid']}"
120
+ oats_info['results_status'] = 'Missing'
121
+ end
122
+ else
123
+ oats_info = { 'results_status' => 'Missing' ,
124
+ 'debug_message' => "No readable: #{results_file}" }
125
+ end
126
+ end
127
+ end
128
+ # Convert object to hash
129
+ oats_info['test_files'] = oats_info['test_files'].testlist_hash if oats_info['test_files']
130
+ return oats_info
131
+ rescue
132
+ server_logger $!.inspect + "\n" + $!.backtrace.join("\n ")
133
+ end
134
+
135
+ def receive_object(request)
136
+ @request = request
137
+ password = @request.delete(:password)
138
+ server_logger "Received " + @request.inspect
139
+ @request[:password] = password
140
+ response = {}
141
+ case @request[:command]
142
+
143
+ when 'status'
144
+ EventMachine.next_tick { run_next_job } unless Ragent.is_busy
145
+
146
+ when 'start'
147
+ if Ragent.is_busy
148
+ server_logger "Not getting next job again because Ragent.is_busy: #{Ragent.is_busy}"
149
+ else
150
+ EventMachine.next_tick { run_next_job }
151
+ end
152
+
153
+ when 'results'
154
+ begin
155
+ response[:oats_info] = get_oats_info
156
+ rescue
157
+ server_logger $!.inspect + "\n" + $!.backtrace.join("\n ")
158
+ end
159
+
160
+ when 'run' # only called from oats client, not from OCC
161
+ EventMachine.defer( proc {
162
+ Driver.start(@request[:jobid], @request[:args])
163
+ } ) unless Ragent.is_busy
164
+
165
+ when 'stop' # any further test execution for this jobid
166
+ Oats.context['stop_oats'] = @request[:id] if @request[:stop_jobs].include?(Oats.context['jobid'])
167
+
168
+ when 'shutdown'
169
+ else
170
+ response[:unknown_command] = true
171
+ server_logger "Unknown command #{@request[:command]}"
172
+ end
173
+ response[:is_busy] = Ragent.is_busy
174
+ stop_oats = Oats.context && Oats.context['stop_oats']
175
+ response[:is_signal_oats_to_stop] = stop_oats if stop_oats
176
+ server_logger "Sending " + response.inspect
177
+ response[:password] = password
178
+ send_object(response)
179
+ close_connection_after_writing
180
+ rescue
181
+ server_logger $!.inspect + "\n" + $!.backtrace.join("\n ")
182
+ end
183
+
184
+ def unbind
185
+ if @request[:command] == 'shutdown'
186
+ server_logger "Shutting down the server."
187
+ EventMachine::stop_event_loop
188
+ end
189
+ end
190
+
191
+ def run_next_job(prev_jobid = nil)
192
+ return unless @request[:occ_host] # Bad input or invoked via client, not occ
193
+ occ = @@occ_default.clone
194
+ occ['server_host'] = @request[:occ_host]
195
+ occ['server_port'] = @request[:occ_port]
196
+ Ragent.start_next_job(occ, self, prev_jobid)
197
+ end
198
+
199
+ def self.start_next_job(occ = @@occ_default, ra = nil, prev_jobid = nil)
200
+ if Ragent.in_next_job or Ragent.is_busy
201
+ msg = if Ragent.in_next_job
202
+ if Ragent.in_next_job == nil.object_id
203
+ "already getting the initial job."
204
+ else
205
+ "already getting job for #{Ragent.in_next_job}]"
206
+ end
207
+ else
208
+ "became busy with #{Ragent.is_busy}"
209
+ end
210
+ Ragent.server_logger ra, "Not requesting new job since #{msg}"
211
+ return false
212
+ end
213
+ Ragent.in_next_job = ra.object_id
214
+ # Double check that this agent has the busy lock
215
+ if not Ragent.in_next_job or Ragent.in_next_job != ra.object_id
216
+ Ragent.server_logger(ra, "Not requesting new job since " +
217
+ "now another Ragent #{ra.object_id} is requesting next job besides current #{Ragent.in_next_job}")
218
+ return false
219
+ end
220
+ query = {
221
+ 'nickname' => occ['agent_nickname'],
222
+ 'machine' => occ['agent_host'],
223
+ 'port' => occ['agent_port'] }
224
+ query['jobid'] = prev_jobid if prev_jobid
225
+ query['repo'] = ENV['OATS_CODE_VERSION'].to_s if ENV['OATS_CODE_VERSION'] and ENV['OATS_CODE_VERSION'] != ''
226
+ query['logfile'] = File.basename(ENV['OATS_AGENT_LOGFILE']||'agent.log')
227
+ Ragent.server_logger ra, "Getting next OCC job: " + query.inspect
228
+ query['password='] = ra.request[:password] if ra and ra.request[:password]
229
+ # Default inactivity_timeout of 10 is not enough when OCC is restarting too
230
+ # many agents. Agent gives up in 10secs but OCC hands over the job in 20secs.
231
+ # As a result OCC thinks job is received but agent has never seen the job.
232
+ connection_options = { :connect_timeout => 60,:inactivity_timeout => 60}
233
+ http_req = EventMachine::HttpRequest.new('http://' + occ['server_host'] + ":#{occ['server_port']}",connection_options)
234
+ http = http_req.get :path => '/jobs/nxt', :query => query
235
+ http.errback { self.no_response(occ,ra, prev_jobid) }
236
+ http.callback do
237
+ status = http.response_header.status
238
+ if status == 200
239
+ data =http.response
240
+ nxt_job = JSON.parse(data) if data
241
+ if nxt_job['jid']
242
+ Ragent.is_busy = nxt_job['jid']
243
+ Ragent.in_next_job = false
244
+ if ra
245
+ ra.jid = nxt_job['jid']
246
+ ra.occ_reintroduction_wait_time = nil # Reset wait time to default if heard from OCC
247
+ end
248
+ Ragent.server_logger ra, "Job-#{Ragent.job_count} #{nxt_job.inspect}"
249
+ EventMachine.defer do
250
+ begin
251
+ opts= {'execution:environments' => [nxt_job['env']],
252
+ 'execution:test_files' => [nxt_job['list']] }
253
+ opts['_:options'] = nxt_job['options'].split(',') if nxt_job['options'] and nxt_job['options'] != ''
254
+ Ragent.snapshot_oats_info({})
255
+ Driver.start(nxt_job['jid'],opts)
256
+ ensure
257
+ Ragent.is_busy = false
258
+ end
259
+ Ragent.job_count += 1
260
+ if ra
261
+ ra.run_next_job(nxt_job['jid'] )
262
+ else
263
+ Ragent.start_next_job(occ,ra,nxt_job['jid'])
264
+ end
265
+ end
266
+ else
267
+ Ragent.in_next_job = false
268
+ Ragent.server_logger ra, "No more pending jobs at OCC. Pausing processing.\n"
269
+ Ragent.job_count = 1
270
+ Ragent.server_logger ra, "***********************************************************\n"
271
+ end
272
+ else
273
+ self.no_response(occ,ra, prev_jobid)
274
+ end
275
+ end
276
+ rescue RuntimeError => e.message
277
+ Ragent.in_next_job = false
278
+ if e.message == 'eventmachine not initialized: evma_connect_to_server'
279
+ Ragent.server_logger ra, "Shutting down..."
280
+ else
281
+ Ragent.server_logger ra, $!.inspect + "\n" + $!.backtrace.join("\n ")
282
+ end
283
+ end
284
+
285
+ def self.no_response(occv,ra, prev_jobid)
286
+ Ragent.in_next_job = false
287
+ Ragent.server_logger ra, "OCC did not respond."
288
+ wait = ra ? ra.occ_reintroduction_wait_time : @@occ_reintroduction_wait_time
289
+ wait ||= $oats['execution']['occ']['timeout_waiting_for_occ']
290
+ # Keep retrying to introduce, doubling the intervals
291
+ self.server_logger ra, "Will retry in #{wait} seconds."
292
+ EM.add_timer(wait) do
293
+ Ragent.start_next_job(occv,ra, prev_jobid) unless Ragent.is_busy # by now
294
+ end
295
+ wait *= (1.5 + rand(101)/100.0)
296
+ wait = wait.round
297
+ if ra
298
+ ra.occ_reintroduction_wait_time = wait
299
+ else
300
+ @@occ_reintroduction_wait_time = wait
301
+ end
302
+ end
303
+
304
+ def server_logger(arg)
305
+ Ragent.server_logger self, arg
306
+ end
307
+
308
+ def Ragent.server_logger(ra, arg)
309
+ if ra
310
+ req = ra.request
311
+ jid = ra.jid
312
+ rt = ":R#{(req and req[:id]) ? req[:id] : ra.object_id}"
313
+ rt += "#{" J:#{jid}" if jid }"
314
+ end
315
+ @@logger.info "[RS#{rt}] #{arg}"
316
+ rescue
317
+ @@logger.error $!.inspect + "\n" + $!.backtrace.join("\n ")
318
+ end
319
+
320
+ end
321
+ end
@@ -0,0 +1,42 @@
1
+ module Oats
2
+
3
+ class Rclient < EventMachine::Connection
4
+ attr_reader :response
5
+ include EM::P::ObjectProtocol
6
+ # def serializer
7
+ # YAML
8
+ # end
9
+ #
10
+ def initialize(host, request)
11
+ @host = host
12
+ @request = request
13
+ end
14
+
15
+ def receive_object(response)
16
+ @response = response
17
+ password = response.delete(:password)
18
+ length = 500
19
+ resp = response.inspect
20
+ resp = resp[0..length] + ' ..... ' + resp[-length..-1] if resp.size > 2*length
21
+ client_logger "Received " + resp + " from"
22
+ response[:password] = password
23
+ client_logger "Command was not recognized" if response[:unknown_command]
24
+ end
25
+
26
+ def client_logger(arg)
27
+ $log.info arg + " #{@request[:id]}@#{@host} at " + Time.now.strftime("%y-%m-%d %H:%M:%S")
28
+ end
29
+
30
+ def post_init
31
+ password = @request.delete(:password)
32
+ client_logger "Sending " + @request.inspect + " to"
33
+ @request[:password] = password
34
+ send_object(@request)
35
+ end
36
+ def unbind
37
+ client_logger "Did not hear from " unless @response
38
+ EventMachine::stop_event_loop
39
+ end
40
+ end
41
+
42
+ end