oats 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/Gemfile +15 -0
- data/README.txt +165 -0
- data/Rakefile +2 -0
- data/bin/agent +204 -0
- data/bin/oats +10 -0
- data/bin/occ +29 -0
- data/bin/results_cleanup +6 -0
- data/doc/COPYING +55 -0
- data/doc/LICENSE +55 -0
- data/doc/OATS_Framework.doc +0 -0
- data/doc/classes/ApplicationLogs.html +239 -0
- data/doc/classes/Campaign.html +843 -0
- data/doc/classes/CommandlineOptions.html +131 -0
- data/doc/classes/Driver.html +182 -0
- data/doc/classes/Hash.html +137 -0
- data/doc/classes/Ide.html +194 -0
- data/doc/classes/MapSelenium.html +197 -0
- data/doc/classes/Net.html +107 -0
- data/doc/classes/Oats/OatsFilterError.html +119 -0
- data/doc/classes/Oats.html +998 -0
- data/doc/classes/OatsAssertError.html +119 -0
- data/doc/classes/OatsBadInput.html +119 -0
- data/doc/classes/OatsData.html +290 -0
- data/doc/classes/OatsError.html +117 -0
- data/doc/classes/OatsExit.html +117 -0
- data/doc/classes/OatsLock.html +254 -0
- data/doc/classes/OatsMain.html +182 -0
- data/doc/classes/OatsMysqlError.html +113 -0
- data/doc/classes/OatsMysqlMissingInput.html +113 -0
- data/doc/classes/OatsReportError.html +113 -0
- data/doc/classes/OatsSetupError.html +119 -0
- data/doc/classes/OatsTestError.html +119 -0
- data/doc/classes/OatsTestExit.html +119 -0
- data/doc/classes/OatsTestLocateError.html +120 -0
- data/doc/classes/OatsVerifyError.html +119 -0
- data/doc/classes/Ragent.html +397 -0
- data/doc/classes/Rclient.html +236 -0
- data/doc/classes/Report.html +368 -0
- data/doc/classes/Reports.html +244 -0
- data/doc/classes/RestApi.html +333 -0
- data/doc/classes/RhttpClient.html +236 -0
- data/doc/classes/Rimap.html +170 -0
- data/doc/classes/Rmysql.html +176 -0
- data/doc/classes/Roptions.html +131 -0
- data/doc/classes/Rselenium.html +233 -0
- data/doc/classes/Rssh.html +138 -0
- data/doc/classes/Runnable.html +174 -0
- data/doc/classes/SFTriggers.html +206 -0
- data/doc/classes/Selenium/Client/Driver.html +211 -0
- data/doc/classes/Selenium/Client.html +107 -0
- data/doc/classes/Selenium.html +107 -0
- data/doc/classes/SystemCapture.html +304 -0
- data/doc/classes/TestCase.html +418 -0
- data/doc/classes/TestData.html +235 -0
- data/doc/classes/TestList.html +264 -0
- data/doc/classes/Tools.html +244 -0
- data/doc/classes/Util.html +201 -0
- data/doc/classes/Variation.html +206 -0
- data/doc/fr_class_index.html +92 -0
- data/doc/fr_method_index.html +465 -0
- data/doc/index.html +23 -0
- data/doc/oats_httpd.conf +32 -0
- data/doc/oats_user.yml +25 -0
- data/doc/rdoc-style.css +208 -0
- data/lib/deep_merge/.gitignore +2 -0
- data/lib/deep_merge/core.rb +195 -0
- data/lib/deep_merge/deep_merge.rb +1 -0
- data/lib/deep_merge/deep_merge_hash.rb +28 -0
- data/lib/deep_merge/rails_compat.rb +27 -0
- data/lib/oats/application_logs.rb +163 -0
- data/lib/oats/build_id.rb +58 -0
- data/lib/oats/commandline_options.rb +128 -0
- data/lib/oats/diff.rb +278 -0
- data/lib/oats/driver.rb +492 -0
- data/lib/oats/ide.rb +227 -0
- data/lib/oats/keywords.rb +67 -0
- data/lib/oats/log4r_init.rb +14 -0
- data/lib/oats/mysql.rb +97 -0
- data/lib/oats/oats.rb +637 -0
- data/lib/oats/oats_data.rb +400 -0
- data/lib/oats/oats_exceptions.rb +25 -0
- data/lib/oats/oats_lock.rb +261 -0
- data/lib/oats/oats_selenium_api.rb +639 -0
- data/lib/oats/oselenium.rb +189 -0
- data/lib/oats/ossh.rb +36 -0
- data/lib/oats/patches_for_eventmachine_12.10.rb +66 -0
- data/lib/oats/ragent.rb +321 -0
- data/lib/oats/rclient.rb +42 -0
- data/lib/oats/report.rb +207 -0
- data/lib/oats/roptions.rb +88 -0
- data/lib/oats/test_case.rb +510 -0
- data/lib/oats/test_data.rb +98 -0
- data/lib/oats/test_list.rb +141 -0
- data/lib/oats/unixdiff.rb +75 -0
- data/lib/oats/util.rb +125 -0
- data/lib/oats/version.rb +3 -0
- data/lib/oats.rb +36 -0
- data/nbproject/configs/agent.properties +0 -0
- data/nbproject/configs/gr.properties +0 -0
- data/nbproject/project.properties +10 -0
- data/nbproject/project.xml +17 -0
- data/oats.gemspec +42 -0
- data/oats_ini.yml +258 -0
- data/oats_tests/Gemfile +18 -0
- data/oats_tests/aut_ini.yml +30 -0
- data/oats_tests/bin/oats +8 -0
- data/oats_tests/environments/qa.yml +4 -0
- data/oats_tests/environments/qa_chrome.yml +4 -0
- data/oats_tests/examples/core/coreExamples.yml +8 -0
- data/oats_tests/examples/core/expectedException.rb +39 -0
- data/oats_tests/examples/core/ok_verify.rb +2 -0
- data/oats_tests/examples/core/ok_verify.rb_ok/out/myfile.txt +1 -0
- data/oats_tests/examples/core/ok_verify.rb_ok/out/myfile2.txt +1 -0
- data/oats_tests/examples/core/ok_verify.rb_ok/rats_test.log +2 -0
- data/oats_tests/examples/core/unexpectedException.rb +30 -0
- data/oats_tests/examples/examples.yml +13 -0
- data/oats_tests/examples/gui/guiExamples.yml +7 -0
- data/oats_tests/examples/gui/seleniumGoogle.rb +10 -0
- data/oats_tests/examples/gui/webdriverGoogle.rb +9 -0
- data/oats_tests/examples/keywords/SampleXlList-1.xls +0 -0
- data/oats_tests/examples/keywords/SampleXlList-2.xls +0 -0
- data/oats_tests/examples/keywords/SampleXlLists.xls +0 -0
- data/oats_tests/examples/keywords/keywordsDriver.rb +1 -0
- data/oats_tests/examples/keywords/keywordsExamples.yml +8 -0
- data/oats_tests/examples/keywords/keywordsTC1.yml +5 -0
- data/oats_tests/examples/keywords/keywordsTestlist.yml +16 -0
- data/oats_tests/examples/needsWork/addTestDynamically.rb +4 -0
- data/oats_tests/examples/needsWork/emailVerify.rb +34 -0
- data/oats_tests/examples/needsWork/testSql/rtest.sql +6 -0
- data/oats_tests/examples/needsWork/testSql/rtest.yml +11 -0
- data/oats_tests/examples/occTest/occTest.rb +13 -0
- data/oats_tests/examples/occTest/occTest_1.rb +1 -0
- data/oats_tests/examples/occTest/occTest_1_1.rb +1 -0
- data/oats_tests/examples/occTest/occTest_1_2.rb +1 -0
- data/oats_tests/examples/occTest/occTest_1_3.rb +1 -0
- data/oats_tests/examples/occTest/occTest_1_4.rb +1 -0
- data/oats_tests/examples/occTest/occTest_2.rb +1 -0
- data/oats_tests/examples/occTest/occTest_2_1.rb +1 -0
- data/oats_tests/examples/occTest/occTest_2_2.rb +1 -0
- data/oats_tests/examples/occTest/occTest_2_3.rb +1 -0
- data/oats_tests/examples/occTest/occTest_2_4.rb +1 -0
- data/oats_tests/examples/occTest/occTest_3.rb +1 -0
- data/oats_tests/examples/occTest/occTest_3_1.rb +1 -0
- data/oats_tests/examples/occTest/occTest_3_2.rb +1 -0
- data/oats_tests/examples/occTest/occTest_3_3.rb +1 -0
- data/oats_tests/examples/occTest/occTest_3_4.rb +1 -0
- data/oats_tests/examples/occTest/occTest_4.rb +1 -0
- data/oats_tests/examples/occTest/occTestlist.yml +9 -0
- data/oats_tests/examples/occTest/occTestlist_1.yml +9 -0
- data/oats_tests/examples/occTest/occTestlist_2.yml +9 -0
- data/oats_tests/examples/occTest/occTestlist_3.yml +9 -0
- data/oats_tests/examples/occTest/variation1.yml +4 -0
- data/oats_tests/examples/occTest/variation2.yml +4 -0
- data/oats_tests/examples/testFileLocationUnitTests/extn_driver.rb +4 -0
- data/oats_tests/examples/testFileLocationUnitTests/folder/oats.yml +3 -0
- data/oats_tests/examples/testFileLocationUnitTests/folder/t1.rb +2 -0
- data/oats_tests/examples/testFileLocationUnitTests/folder1/t1.yml +2 -0
- data/oats_tests/examples/testFileLocationUnitTests/folder1/t1_1.yml +3 -0
- data/oats_tests/examples/testFileLocationUnitTests/folder2/oats.yml +3 -0
- data/oats_tests/examples/testFileLocationUnitTests/folder2/t1.rb +2 -0
- data/oats_tests/examples/testFileLocationUnitTests/folder2/t1.yml +2 -0
- data/oats_tests/examples/testFileLocationUnitTests/no_yaml.rb +3 -0
- data/oats_tests/examples/testFileLocationUnitTests/post_yaml.rb +1 -0
- data/oats_tests/examples/testFileLocationUnitTests/t1.rb +4 -0
- data/oats_tests/examples/testFileLocationUnitTests/t1.yml +2 -0
- data/oats_tests/examples/testFileLocationUnitTests/t1_1.yml +3 -0
- data/oats_tests/examples/testFileLocationUnitTests/testDir/oats.yml +3 -0
- data/oats_tests/examples/testFileLocationUnitTests/testDir/t1.rb +2 -0
- data/oats_tests/examples/testFileLocationUnitTests/testDir/t1.yml +2 -0
- data/oats_tests/examples/testFileLocationUnitTests/testDir2/t1.rb +2 -0
- data/oats_tests/examples/testFileLocationUnitTests/testDir2/t1.yml +2 -0
- data/oats_tests/examples/testFileLocationUnitTests/unitTestList.yml +36 -0
- data/oats_tests/examples/testFileLocationUnitTests/yml_driver.rb +2 -0
- data/oats_tests/lib/business.rb +28 -0
- data/oats_tests/lib/sample_xl_lists.rb +37 -0
- data/test/common_test_unit_setup.rb +21 -0
- data/test/test_basic.rb +16 -0
- data/test/test_selenium.rb +16 -0
- data/test/test_xl_lists.rb +16 -0
- metadata +291 -0
data/doc/rdoc-style.css
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
|
2
|
+
body {
|
3
|
+
font-family: Verdana,Arial,Helvetica,sans-serif;
|
4
|
+
font-size: 90%;
|
5
|
+
margin: 0;
|
6
|
+
margin-left: 40px;
|
7
|
+
padding: 0;
|
8
|
+
background: white;
|
9
|
+
}
|
10
|
+
|
11
|
+
h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }
|
12
|
+
h1 { font-size: 150%; }
|
13
|
+
h2,h3,h4 { margin-top: 1em; }
|
14
|
+
|
15
|
+
a { background: #eef; color: #039; text-decoration: none; }
|
16
|
+
a:hover { background: #039; color: #eef; }
|
17
|
+
|
18
|
+
/* Override the base stylesheet's Anchor inside a table cell */
|
19
|
+
td > a {
|
20
|
+
background: transparent;
|
21
|
+
color: #039;
|
22
|
+
text-decoration: none;
|
23
|
+
}
|
24
|
+
|
25
|
+
/* and inside a section title */
|
26
|
+
.section-title > a {
|
27
|
+
background: transparent;
|
28
|
+
color: #eee;
|
29
|
+
text-decoration: none;
|
30
|
+
}
|
31
|
+
|
32
|
+
/* === Structural elements =================================== */
|
33
|
+
|
34
|
+
div#index {
|
35
|
+
margin: 0;
|
36
|
+
margin-left: -40px;
|
37
|
+
padding: 0;
|
38
|
+
font-size: 90%;
|
39
|
+
}
|
40
|
+
|
41
|
+
|
42
|
+
div#index a {
|
43
|
+
margin-left: 0.7em;
|
44
|
+
}
|
45
|
+
|
46
|
+
div#index .section-bar {
|
47
|
+
margin-left: 0px;
|
48
|
+
padding-left: 0.7em;
|
49
|
+
background: #ccc;
|
50
|
+
font-size: small;
|
51
|
+
}
|
52
|
+
|
53
|
+
|
54
|
+
div#classHeader, div#fileHeader {
|
55
|
+
width: auto;
|
56
|
+
color: white;
|
57
|
+
padding: 0.5em 1.5em 0.5em 1.5em;
|
58
|
+
margin: 0;
|
59
|
+
margin-left: -40px;
|
60
|
+
border-bottom: 3px solid #006;
|
61
|
+
}
|
62
|
+
|
63
|
+
div#classHeader a, div#fileHeader a {
|
64
|
+
background: inherit;
|
65
|
+
color: white;
|
66
|
+
}
|
67
|
+
|
68
|
+
div#classHeader td, div#fileHeader td {
|
69
|
+
background: inherit;
|
70
|
+
color: white;
|
71
|
+
}
|
72
|
+
|
73
|
+
|
74
|
+
div#fileHeader {
|
75
|
+
background: #057;
|
76
|
+
}
|
77
|
+
|
78
|
+
div#classHeader {
|
79
|
+
background: #048;
|
80
|
+
}
|
81
|
+
|
82
|
+
|
83
|
+
.class-name-in-header {
|
84
|
+
font-size: 180%;
|
85
|
+
font-weight: bold;
|
86
|
+
}
|
87
|
+
|
88
|
+
|
89
|
+
div#bodyContent {
|
90
|
+
padding: 0 1.5em 0 1.5em;
|
91
|
+
}
|
92
|
+
|
93
|
+
div#description {
|
94
|
+
padding: 0.5em 1.5em;
|
95
|
+
background: #efefef;
|
96
|
+
border: 1px dotted #999;
|
97
|
+
}
|
98
|
+
|
99
|
+
div#description h1,h2,h3,h4,h5,h6 {
|
100
|
+
color: #125;;
|
101
|
+
background: transparent;
|
102
|
+
}
|
103
|
+
|
104
|
+
div#validator-badges {
|
105
|
+
text-align: center;
|
106
|
+
}
|
107
|
+
div#validator-badges img { border: 0; }
|
108
|
+
|
109
|
+
div#copyright {
|
110
|
+
color: #333;
|
111
|
+
background: #efefef;
|
112
|
+
font: 0.75em sans-serif;
|
113
|
+
margin-top: 5em;
|
114
|
+
margin-bottom: 0;
|
115
|
+
padding: 0.5em 2em;
|
116
|
+
}
|
117
|
+
|
118
|
+
|
119
|
+
/* === Classes =================================== */
|
120
|
+
|
121
|
+
table.header-table {
|
122
|
+
color: white;
|
123
|
+
font-size: small;
|
124
|
+
}
|
125
|
+
|
126
|
+
.type-note {
|
127
|
+
font-size: small;
|
128
|
+
color: #DEDEDE;
|
129
|
+
}
|
130
|
+
|
131
|
+
.xxsection-bar {
|
132
|
+
background: #eee;
|
133
|
+
color: #333;
|
134
|
+
padding: 3px;
|
135
|
+
}
|
136
|
+
|
137
|
+
.section-bar {
|
138
|
+
color: #333;
|
139
|
+
border-bottom: 1px solid #999;
|
140
|
+
margin-left: -20px;
|
141
|
+
}
|
142
|
+
|
143
|
+
|
144
|
+
.section-title {
|
145
|
+
background: #79a;
|
146
|
+
color: #eee;
|
147
|
+
padding: 3px;
|
148
|
+
margin-top: 2em;
|
149
|
+
margin-left: -30px;
|
150
|
+
border: 1px solid #999;
|
151
|
+
}
|
152
|
+
|
153
|
+
.top-aligned-row { vertical-align: top }
|
154
|
+
.bottom-aligned-row { vertical-align: bottom }
|
155
|
+
|
156
|
+
/* --- Context section classes ----------------------- */
|
157
|
+
|
158
|
+
.context-row { }
|
159
|
+
.context-item-name { font-family: monospace; font-weight: bold; color: black; }
|
160
|
+
.context-item-value { font-size: small; color: #448; }
|
161
|
+
.context-item-desc { color: #333; padding-left: 2em; }
|
162
|
+
|
163
|
+
/* --- Method classes -------------------------- */
|
164
|
+
.method-detail {
|
165
|
+
background: #efefef;
|
166
|
+
padding: 0;
|
167
|
+
margin-top: 0.5em;
|
168
|
+
margin-bottom: 1em;
|
169
|
+
border: 1px dotted #ccc;
|
170
|
+
}
|
171
|
+
.method-heading {
|
172
|
+
color: black;
|
173
|
+
background: #ccc;
|
174
|
+
border-bottom: 1px solid #666;
|
175
|
+
padding: 0.2em 0.5em 0 0.5em;
|
176
|
+
}
|
177
|
+
.method-signature { color: black; background: inherit; }
|
178
|
+
.method-name { font-weight: bold; }
|
179
|
+
.method-args { font-style: italic; }
|
180
|
+
.method-description { padding: 0 0.5em 0 0.5em; }
|
181
|
+
|
182
|
+
/* --- Source code sections -------------------- */
|
183
|
+
|
184
|
+
a.source-toggle { font-size: 90%; }
|
185
|
+
div.method-source-code {
|
186
|
+
background: #262626;
|
187
|
+
color: #ffdead;
|
188
|
+
margin: 1em;
|
189
|
+
padding: 0.5em;
|
190
|
+
border: 1px dashed #999;
|
191
|
+
overflow: hidden;
|
192
|
+
}
|
193
|
+
|
194
|
+
div.method-source-code pre { color: #ffdead; overflow: hidden; }
|
195
|
+
|
196
|
+
/* --- Ruby keyword styles --------------------- */
|
197
|
+
|
198
|
+
.standalone-code { background: #221111; color: #ffdead; overflow: hidden; }
|
199
|
+
|
200
|
+
.ruby-constant { color: #7fffd4; background: transparent; }
|
201
|
+
.ruby-keyword { color: #00ffff; background: transparent; }
|
202
|
+
.ruby-ivar { color: #eedd82; background: transparent; }
|
203
|
+
.ruby-operator { color: #00ffee; background: transparent; }
|
204
|
+
.ruby-identifier { color: #ffdead; background: transparent; }
|
205
|
+
.ruby-node { color: #ffa07a; background: transparent; }
|
206
|
+
.ruby-comment { color: #b22222; font-weight: bold; background: transparent; }
|
207
|
+
.ruby-regexp { color: #ffa07a; background: transparent; }
|
208
|
+
.ruby-value { color: #7fffd4; background: transparent; }
|
@@ -0,0 +1,195 @@
|
|
1
|
+
module DeepMerge
|
2
|
+
|
3
|
+
class InvalidParameter < StandardError; end
|
4
|
+
|
5
|
+
# DEFAULT_FIELD_KNOCKOUT_PREFIX = '--'
|
6
|
+
|
7
|
+
# Deep Merge core documentation.
|
8
|
+
# deep_merge! method permits merging of arbitrary child elements. The two top level
|
9
|
+
# elements must be hashes. These hashes can contain unlimited (to stack limit) levels
|
10
|
+
# of child elements. These child elements to not have to be of the same types.
|
11
|
+
# Where child elements are of the same type, deep_merge will attempt to merge them together.
|
12
|
+
# Where child elements are not of the same type, deep_merge will skip or optionally overwrite
|
13
|
+
# the destination element with the contents of the source element at that level.
|
14
|
+
# So if you have two hashes like this:
|
15
|
+
# source = {:x => [1,2,3], :y => 2}
|
16
|
+
# dest = {:x => [4,5,'6'], :y => [7,8,9]}
|
17
|
+
# dest.deep_merge!(source)
|
18
|
+
# Results: {:x => [1,2,3,4,5,'6'], :y => 2}
|
19
|
+
# By default, "deep_merge!" will overwrite any unmergeables and merge everything else.
|
20
|
+
# To avoid this, use "deep_merge" (no bang/exclamation mark)
|
21
|
+
#
|
22
|
+
# Options:
|
23
|
+
# Options are specified in the last parameter passed, which should be in hash format:
|
24
|
+
# hash.deep_merge!({:x => [1,2]}, {:knockout_prefix => '--'})
|
25
|
+
# :preserve_unmergeables DEFAULT: false
|
26
|
+
# Set to true to skip any unmergeable elements from source
|
27
|
+
# :overwrite_arrays DEFAULT: false
|
28
|
+
# Set to true to not merge but overwrite arrays
|
29
|
+
# :knockout_prefix DEFAULT: nil
|
30
|
+
# Set to string value to signify prefix which deletes elements from existing element
|
31
|
+
# :sort_merged_arrays DEFAULT: false
|
32
|
+
# Set to true to sort all arrays that are merged together
|
33
|
+
# :unpack_arrays DEFAULT: nil
|
34
|
+
# Set to string value to run "Array::join" then "String::split" against all arrays
|
35
|
+
# :merge_debug DEFAULT: false
|
36
|
+
# Set to true to get console output of merge process for debugging
|
37
|
+
#
|
38
|
+
# Selected Options Details:
|
39
|
+
# :knockout_prefix => The purpose of this is to provide a way to remove elements
|
40
|
+
# from existing Hash by specifying them in a special way in incoming hash
|
41
|
+
# source = {:x => ['--1', '2']}
|
42
|
+
# dest = {:x => ['1', '3']}
|
43
|
+
# dest.ko_deep_merge!(source)
|
44
|
+
# Results: {:x => ['2','3']}
|
45
|
+
# Additionally, if the knockout_prefix is passed alone as a string, it will cause
|
46
|
+
# the entire element to be removed:
|
47
|
+
# source = {:x => '--'}
|
48
|
+
# dest = {:x => [1,2,3]}
|
49
|
+
# dest.ko_deep_merge!(source)
|
50
|
+
# Results: {:x => ""}
|
51
|
+
# :unpack_arrays => The purpose of this is to permit compound elements to be passed
|
52
|
+
# in as strings and to be converted into discrete array elements
|
53
|
+
# irsource = {:x => ['1,2,3', '4']}
|
54
|
+
# dest = {:x => ['5','6','7,8']}
|
55
|
+
# dest.deep_merge!(source, {:unpack_arrays => ','})
|
56
|
+
# Results: {:x => ['1','2','3','4','5','6','7','8'}
|
57
|
+
# Why: If receiving data from an HTML form, this makes it easy for a checkbox
|
58
|
+
# to pass multiple values from within a single HTML element
|
59
|
+
#
|
60
|
+
# There are many tests for this library - and you can learn more about the features
|
61
|
+
# and usages of deep_merge! by just browsing the test examples
|
62
|
+
def self.deep_merge!(source, dest, options = {})
|
63
|
+
# turn on this line for stdout debugging text
|
64
|
+
merge_debug = options[:merge_debug] || false
|
65
|
+
overwrite_unmergeable = !options[:preserve_unmergeables]
|
66
|
+
knockout_prefix = options[:knockout_prefix] || nil
|
67
|
+
raise InvalidParameter, "knockout_prefix cannot be an empty string in deep_merge!" if knockout_prefix == ""
|
68
|
+
raise InvalidParameter, "overwrite_unmergeable must be true if knockout_prefix is specified in deep_merge!" if knockout_prefix && !overwrite_unmergeable
|
69
|
+
# if present: we will split and join arrays on this char before merging
|
70
|
+
array_split_char = options[:unpack_arrays] || false
|
71
|
+
# Don't merge but overwrite arrays
|
72
|
+
overwrite_arrays = options[:overwrite_arrays] || false
|
73
|
+
# request that we sort together any arrays when they are merged
|
74
|
+
sort_merged_arrays = options[:sort_merged_arrays] || false
|
75
|
+
di = options[:debug_indent] || ''
|
76
|
+
# do nothing if source is nil
|
77
|
+
return dest if source.nil? || (source.respond_to?(:blank?) && source.blank?)
|
78
|
+
# if dest doesn't exist, then simply copy source to it
|
79
|
+
if !(dest) && overwrite_unmergeable
|
80
|
+
dest = source; return dest
|
81
|
+
end
|
82
|
+
|
83
|
+
puts "#{di}Source class: #{source.class.inspect} :: Dest class: #{dest.class.inspect}" if merge_debug
|
84
|
+
if source.kind_of?(Hash)
|
85
|
+
puts "#{di}Hashes: #{source.inspect} :: #{dest.inspect}" if merge_debug
|
86
|
+
source.each do |src_key, src_value|
|
87
|
+
if dest.kind_of?(Hash)
|
88
|
+
puts "#{di} looping: #{src_key.inspect} => #{src_value.inspect} :: #{dest.inspect}" if merge_debug
|
89
|
+
if dest[src_key]
|
90
|
+
puts "#{di} ==>merging: #{src_key.inspect} => #{src_value.inspect} :: #{dest[src_key].inspect}" if merge_debug
|
91
|
+
dest[src_key] = deep_merge!(src_value, dest[src_key], options.merge(:debug_indent => di + ' '))
|
92
|
+
else # dest[src_key] doesn't exist so we want to create and overwrite it (but we do this via deep_merge!)
|
93
|
+
puts "#{di} ==>merging over: #{src_key.inspect} => #{src_value.inspect}" if merge_debug
|
94
|
+
# note: we rescue here b/c some classes respond to "dup" but don't implement it (Numeric, TrueClass, FalseClass, NilClass among maybe others)
|
95
|
+
begin
|
96
|
+
src_dup = src_value.dup # we dup src_value if possible because we're going to merge into it (since dest is empty)
|
97
|
+
rescue TypeError
|
98
|
+
src_dup = src_value
|
99
|
+
end
|
100
|
+
dest[src_key] = deep_merge!(src_value, src_dup, options.merge(:debug_indent => di + ' '))
|
101
|
+
end
|
102
|
+
else # dest isn't a hash, so we overwrite it completely (if permitted)
|
103
|
+
if overwrite_unmergeable
|
104
|
+
puts "#{di} overwriting dest: #{src_key.inspect} => #{src_value.inspect} -over-> #{dest.inspect}" if merge_debug
|
105
|
+
dest = overwrite_unmergeables(source, dest, options)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
elsif source.kind_of?(Array)
|
110
|
+
puts "#{di}Arrays: #{source.inspect} :: #{dest.inspect}" if merge_debug
|
111
|
+
# if we are instructed, join/split any source arrays before processing
|
112
|
+
if array_split_char
|
113
|
+
puts "#{di} split/join on source: #{source.inspect}" if merge_debug
|
114
|
+
source = source.join(array_split_char).split(array_split_char)
|
115
|
+
if dest.kind_of?(Array)
|
116
|
+
dest = dest.join(array_split_char).split(array_split_char)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
# if there's a naked knockout_prefix in source, that means we are to truncate dest
|
120
|
+
if source.index(knockout_prefix)
|
121
|
+
dest = clear_or_nil(dest); source.delete(knockout_prefix)
|
122
|
+
end
|
123
|
+
if dest.kind_of?(Array) and ! overwrite_arrays
|
124
|
+
if knockout_prefix
|
125
|
+
print "#{di} knocking out: " if merge_debug
|
126
|
+
# remove knockout prefix items from both source and dest
|
127
|
+
source.delete_if do |ko_item|
|
128
|
+
retval = false
|
129
|
+
item = ko_item.respond_to?(:gsub) ? ko_item.gsub(%r{^#{knockout_prefix}}, "") : ko_item
|
130
|
+
if item != ko_item
|
131
|
+
print "#{ko_item} - " if merge_debug
|
132
|
+
dest.delete(item)
|
133
|
+
dest.delete(ko_item)
|
134
|
+
retval = true
|
135
|
+
end
|
136
|
+
retval
|
137
|
+
end
|
138
|
+
puts if merge_debug
|
139
|
+
end
|
140
|
+
puts "#{di} merging arrays: #{source.inspect} :: #{dest.inspect}" if merge_debug
|
141
|
+
dest = dest | source
|
142
|
+
dest.sort! if sort_merged_arrays
|
143
|
+
elsif overwrite_unmergeable or overwrite_arrays
|
144
|
+
puts "#{di} overwriting dest: #{source.inspect} -over-> #{dest.inspect}" if merge_debug
|
145
|
+
dest = overwrite_unmergeables(source, dest, options)
|
146
|
+
end
|
147
|
+
else # src_hash is not an array or hash, so we'll have to overwrite dest
|
148
|
+
puts "#{di}Others: #{source.inspect} :: #{dest.inspect}" if merge_debug
|
149
|
+
dest = overwrite_unmergeables(source, dest, options)
|
150
|
+
end
|
151
|
+
puts "#{di}Returning #{dest.inspect}" if merge_debug
|
152
|
+
dest
|
153
|
+
end # deep_merge!
|
154
|
+
|
155
|
+
# allows deep_merge! to uniformly handle overwriting of unmergeable entities
|
156
|
+
def self.overwrite_unmergeables(source, dest, options)
|
157
|
+
merge_debug = options[:merge_debug] || false
|
158
|
+
overwrite_unmergeable = !options[:preserve_unmergeables]
|
159
|
+
knockout_prefix = options[:knockout_prefix] || false
|
160
|
+
di = options[:debug_indent] || ''
|
161
|
+
if knockout_prefix && overwrite_unmergeable
|
162
|
+
if source.kind_of?(String) # remove knockout string from source before overwriting dest
|
163
|
+
if options[:overwrite_arrays] # Don't knock out strings for OATs
|
164
|
+
src_tmp = source
|
165
|
+
else
|
166
|
+
src_tmp = source.gsub(%r{^#{knockout_prefix}},"")
|
167
|
+
end
|
168
|
+
elsif source.kind_of?(Array) # remove all knockout elements before overwriting dest
|
169
|
+
src_tmp = source.delete_if {|ko_item| ko_item.kind_of?(String) && ko_item.match(%r{^#{knockout_prefix}}) }
|
170
|
+
else
|
171
|
+
src_tmp = source
|
172
|
+
end
|
173
|
+
if src_tmp == source # if we didn't find a knockout_prefix then we just overwrite dest
|
174
|
+
puts "#{di}#{src_tmp.inspect} -over-> #{dest.inspect}" if merge_debug
|
175
|
+
dest = src_tmp
|
176
|
+
else # if we do find a knockout_prefix, then we just delete dest
|
177
|
+
puts "#{di}\"\" -over-> #{dest.inspect}" if merge_debug
|
178
|
+
dest = ""
|
179
|
+
end
|
180
|
+
elsif overwrite_unmergeable
|
181
|
+
dest = source
|
182
|
+
end
|
183
|
+
dest
|
184
|
+
end
|
185
|
+
|
186
|
+
def self.clear_or_nil(obj)
|
187
|
+
if obj.respond_to?(:clear)
|
188
|
+
obj.clear
|
189
|
+
else
|
190
|
+
obj = nil
|
191
|
+
end
|
192
|
+
obj
|
193
|
+
end
|
194
|
+
|
195
|
+
end # module DeepMerge
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'deep_merge/deep_merge_hash'
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'deep_merge/core'
|
2
|
+
|
3
|
+
module DeepMerge
|
4
|
+
module DeepMergeHash
|
5
|
+
# ko_hash_merge! will merge and knockout elements prefixed with DEFAULT_FIELD_KNOCKOUT_PREFIX
|
6
|
+
def ko_deep_merge!(source, options = {})
|
7
|
+
default_opts = {:knockout_prefix => "--", :preserve_unmergeables => false}
|
8
|
+
DeepMerge::deep_merge!(source, self, default_opts.merge(options))
|
9
|
+
end
|
10
|
+
|
11
|
+
# deep_merge! will merge and overwrite any unmergeables in destination hash
|
12
|
+
def deep_merge!(source, options = {})
|
13
|
+
default_opts = {:preserve_unmergeables => false}
|
14
|
+
DeepMerge::deep_merge!(source, self, default_opts.merge(options))
|
15
|
+
end
|
16
|
+
|
17
|
+
# deep_merge will merge and skip any unmergeables in destination hash
|
18
|
+
def deep_merge(source, options = {})
|
19
|
+
default_opts = {:preserve_unmergeables => true}
|
20
|
+
DeepMerge::deep_merge!(source, self, default_opts.merge(options))
|
21
|
+
end
|
22
|
+
|
23
|
+
end # DeepMergeHashExt
|
24
|
+
end
|
25
|
+
|
26
|
+
class Hash
|
27
|
+
include DeepMerge::DeepMergeHash
|
28
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'deep_merge/core'
|
2
|
+
|
3
|
+
module DeepMerge
|
4
|
+
module RailsCompat
|
5
|
+
# ko_hash_merge! will merge and knockout elements prefixed with DEFAULT_FIELD_KNOCKOUT_PREFIX
|
6
|
+
def ko_deeper_merge!(source, options = {})
|
7
|
+
default_opts = {:knockout_prefix => "--", :preserve_unmergeables => false}
|
8
|
+
DeepMerge::deep_merge!(source, self, default_opts.merge(options))
|
9
|
+
end
|
10
|
+
|
11
|
+
# deep_merge! will merge and overwrite any unmergeables in destination hash
|
12
|
+
def deeper_merge!(source, options = {})
|
13
|
+
default_opts = {:preserve_unmergeables => false}
|
14
|
+
DeepMerge::deep_merge!(source, self, default_opts.merge(options))
|
15
|
+
end
|
16
|
+
|
17
|
+
# deep_merge will merge and skip any unmergeables in destination hash
|
18
|
+
def deeper_merge(source, options = {})
|
19
|
+
default_opts = {:preserve_unmergeables => true}
|
20
|
+
DeepMerge::deep_merge!(source, self, default_opts.merge(options))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Hash
|
26
|
+
include ::DeepMerge::RailsCompat
|
27
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'oats/ossh'
|
3
|
+
# Returns all the matching IPs from the listed logs
|
4
|
+
|
5
|
+
class ApplicationLogs
|
6
|
+
|
7
|
+
@@app_logs_error_getter = {} # webHostName => ApplicationLogs objects
|
8
|
+
@@ip = nil
|
9
|
+
@@plink_cmd_params = {} # webHostName => Extra parameters to connect or ''
|
10
|
+
PLINK_TIMEOUT = 5
|
11
|
+
|
12
|
+
# Call returns only errors occurring after the previous call.
|
13
|
+
def ApplicationLogs.new_errors(initial = false)
|
14
|
+
logs = $oats['env']['web']['logs']
|
15
|
+
logs = nil if logs.nil? or logs.empty?
|
16
|
+
return [] unless logs
|
17
|
+
host = $oats['env']['web']['host']
|
18
|
+
if @@app_logs_error_getter[host]
|
19
|
+
return [] if initial
|
20
|
+
else
|
21
|
+
@@app_logs_error_getter[host] = ApplicationLogs.new(host,logs)
|
22
|
+
end
|
23
|
+
return @@app_logs_error_getter[host].new_errors
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(host,logs)
|
27
|
+
ApplicationLogs.set_ip unless @@ip
|
28
|
+
@logs = logs
|
29
|
+
@log_error_count = []
|
30
|
+
@command = "plink #{host} -l loguser sudo grep -i #{@@ip} " + @logs.join(' ')
|
31
|
+
ApplicationLogs.set_plink_cmd_params
|
32
|
+
@command_issue = @command
|
33
|
+
end
|
34
|
+
|
35
|
+
def new_errors
|
36
|
+
$log.debug "Interrogating the application logs: #{@command_issue}"
|
37
|
+
errors = IO.popen(@command).readlines
|
38
|
+
total_errors = 0
|
39
|
+
@log_error_count.each {|i| total_errors += i}
|
40
|
+
new_error = []
|
41
|
+
return new_error if total_errors == errors.length
|
42
|
+
i = 0
|
43
|
+
new_error_count = []
|
44
|
+
new_error_count[0] = 0
|
45
|
+
errors.each do |line|
|
46
|
+
prefix = Regexp.new('^'+@logs[i]+':')
|
47
|
+
unless prefix =~ line
|
48
|
+
@log_error_count[i] = new_error_count[i]
|
49
|
+
i += 1
|
50
|
+
break if i == @logs.length
|
51
|
+
new_error_count[i] = 0
|
52
|
+
redo
|
53
|
+
end
|
54
|
+
new_error_count[i] += 1
|
55
|
+
new_error << line if new_error_count[i] > (@log_error_count[i] ? @log_error_count[i] : 0)
|
56
|
+
end
|
57
|
+
@log_error_count[i] = new_error_count[i]
|
58
|
+
return new_error
|
59
|
+
end
|
60
|
+
|
61
|
+
# Tails logs continuously.
|
62
|
+
def ApplicationLogs.tail_errors
|
63
|
+
tail_ip = $oats['execution']['tail_logs_ip']
|
64
|
+
logs = $oats['env']['web']['logs']
|
65
|
+
logs = nil if logs.nil? or logs.empty?
|
66
|
+
host = $oats['env']['web']['host']
|
67
|
+
if tail_ip
|
68
|
+
raise(OatsBadInput, "The execution:tail_logs_ip is set to [#{tail_ip}] but env:web:logs is empty.") unless logs
|
69
|
+
else
|
70
|
+
return
|
71
|
+
end
|
72
|
+
if tail_ip and tail_ip.instance_of?(String)
|
73
|
+
if /^\d\d\.\d\d*\.\d\d*\.\d\d*$/ =~ tail_ip
|
74
|
+
@@ip = tail_ip
|
75
|
+
else
|
76
|
+
raise(OatsBadInput, "Input for execution:tail_logs_ip [#{tail_ip}] is not in proper IP format.")
|
77
|
+
end
|
78
|
+
else
|
79
|
+
ApplicationLogs.set_ip unless @@ip
|
80
|
+
end
|
81
|
+
ApplicationLogs.set_plink_cmd_params(true)
|
82
|
+
command = "plink #{host} -l loguser sudo tail -f " + logs.join(' ')
|
83
|
+
command_issue = command
|
84
|
+
puts "Filtering for [#{@@ip}] after executing command: #{command}"
|
85
|
+
IO.popen(command_issue) do |io|
|
86
|
+
while io.gets do
|
87
|
+
puts $_ if Regexp.new(@@ip) =~ $_ or /^==>/ =~ $_
|
88
|
+
end
|
89
|
+
end
|
90
|
+
exit 0 # Should never get here. User has to kill the process to quit
|
91
|
+
end
|
92
|
+
|
93
|
+
def ApplicationLogs.set_plink_cmd_params(from_tail = nil)
|
94
|
+
host = $oats['env']['web']['host']
|
95
|
+
return if @@plink_cmd_params[host]
|
96
|
+
# Ossh does not support this option anymore. Must go in via Paegant
|
97
|
+
# Clean-up the code below later.
|
98
|
+
@@plink_cmd_params = ''
|
99
|
+
error = 0
|
100
|
+
error_msg = nil
|
101
|
+
error_msg2 = nil
|
102
|
+
correct_response = false
|
103
|
+
cmd_issue = "plink #{host} -l loguser whoami 2>&1"
|
104
|
+
cmd_display = cmd_issue
|
105
|
+
$log.debug "Issuing: " + cmd_issue
|
106
|
+
$log.debug "Please start Paegant with loguser key if there is no response in {PLINK_TIMEOUT} seconds."
|
107
|
+
begin
|
108
|
+
timeout(PLINK_TIMEOUT) do
|
109
|
+
IO.popen(cmd_issue) do |ios|
|
110
|
+
while ios.gets(':') do
|
111
|
+
if /s password:/ =~ $_
|
112
|
+
error = 1
|
113
|
+
error_msg = "Received Plink authentication challenge from "
|
114
|
+
error_msg2 = "If you have not started Paegant already, please do so."
|
115
|
+
elsif /s password:/ =~ $_
|
116
|
+
error = 2
|
117
|
+
error_msg = "Received Plink authentication challenge from "
|
118
|
+
error_msg2 = "If you have not defined an entry for [#{host} on Putty already, please do so."
|
119
|
+
elsif /^Password:/ =~ $_
|
120
|
+
error = 3
|
121
|
+
error_msg = "Received 'sudo root' authentication challenge from "
|
122
|
+
error_msg2 = "Sudo password cash has timed out. Please type the following on a shell commandline: #{cmd_display}"
|
123
|
+
elsif /^Unable to open connection:/ =~ $_
|
124
|
+
error = 4
|
125
|
+
error_msg = "Could not connect to "
|
126
|
+
error_msg2 = "You need to unset error log interrogation to run this test in this environment."
|
127
|
+
# elsif /^root/ =~ $_
|
128
|
+
elsif /^loguser/ =~ $_
|
129
|
+
correct_response = true
|
130
|
+
end
|
131
|
+
$log.error $_ unless $_.nil? or correct_response
|
132
|
+
ios.gets
|
133
|
+
$log.error $_ unless $_.nil? or correct_response
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
rescue Timeout::Error
|
138
|
+
$log.error "plink command timed out after [#{PLINK_TIMEOUT}] seconds."
|
139
|
+
end
|
140
|
+
if error == 0 and correct_response
|
141
|
+
$log.info "Received proper response from Plink."
|
142
|
+
return
|
143
|
+
else
|
144
|
+
if error_msg
|
145
|
+
if error_msg2
|
146
|
+
$log.error error_msg + "[#{host}]"
|
147
|
+
else
|
148
|
+
raise(OatsSetupError,error_msg)
|
149
|
+
end
|
150
|
+
raise(OatsSetupError,error_msg2)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def ApplicationLogs.set_ip
|
156
|
+
IO.popen('ipconfig') do |io|
|
157
|
+
while io.gets do
|
158
|
+
@@ip = $_.chomp.sub(/.*IP Address.*: (\d.*\d).*/,'\1') if /IP Address.*: / =~ $_
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# Should be overridden by the user if desired
|
2
|
+
module Oats
|
3
|
+
|
4
|
+
module BuildId
|
5
|
+
def BuildId.generate
|
6
|
+
return
|
7
|
+
dir_results = $oats['execution']['dir_results']
|
8
|
+
env_name = $oats['env']['name']
|
9
|
+
return unless env_name and $oats['execution']['build_version']
|
10
|
+
TestList.current.variations.last.env_name = env_name
|
11
|
+
run_info_file = File.join(dir_results,'run_info_' + env_name + '.txt')
|
12
|
+
unless File.exist?(run_info_file)
|
13
|
+
File.open(run_info_file, 'w') do |f|
|
14
|
+
YAML.dump($oats,f)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
if Oats.context['build_version'] # Collected build data previously
|
18
|
+
return if Oats.context['build_version']['execution'] == $oats['execution']['build_version'] # no change
|
19
|
+
Oats.context['build_version']['execution'] = $oats['execution']['build_version'] # Use the latest, input from test list
|
20
|
+
return
|
21
|
+
end
|
22
|
+
# First time collecting build data
|
23
|
+
Oats.context['build_version'] = { 'execution' => $oats['execution']['build_version'] }
|
24
|
+
all_build_info = ''
|
25
|
+
build_id_file = File.join(dir_results,'buildID_' + env_name + '.txt')
|
26
|
+
|
27
|
+
for host in $oats['execution']['build_versions'] do
|
28
|
+
web_host = $oats['env'][host] && $oats['env'][host]['host']
|
29
|
+
next unless web_host
|
30
|
+
urls = $oats['env'][host]['buildID_url']
|
31
|
+
next unless urls
|
32
|
+
urls = [ urls ] unless urls.instance_of? Array
|
33
|
+
versions = ''
|
34
|
+
urls.each do |url|
|
35
|
+
begin
|
36
|
+
if RUBY_VERSION =~ /^1.9/
|
37
|
+
resp = Net::HTTP.new(web_host, 80).get(url) # 1.9 doesn't like the second parameter
|
38
|
+
else
|
39
|
+
resp = Net::HTTP.new(web_host, 80).get(url, nil )
|
40
|
+
end
|
41
|
+
build_info = resp.body if resp.code == '200'
|
42
|
+
rescue
|
43
|
+
$log.error $!.to_s
|
44
|
+
$log.error "Occurred after issuing get request to [http://#{web_host}#{url}]"
|
45
|
+
build_info = nil
|
46
|
+
end
|
47
|
+
if build_info
|
48
|
+
versions += ' ' unless versions == ''
|
49
|
+
versions += build_info[6..(build_info.index("\n")-1)]
|
50
|
+
all_build_info += "--- #{host} #{url} --- \n" + build_info + "\n"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
Oats.context['build_version'][host] = versions
|
54
|
+
end if $oats['execution']['build_versions']
|
55
|
+
File.open(build_id_file, 'w') { |f| f.puts all_build_info } unless all_build_info == ''
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|