|
1 # iExploder - Generates bad HTML files to perform QA for web browsers. |
|
2 # Developed for the Mozilla Foundation. |
|
3 ##################### |
|
4 # |
|
5 # Copyright (c) 2006 Thomas Stromberg <thomas%stromberg.org> |
|
6 # |
|
7 # This software is provided 'as-is', without any express or implied warranty. |
|
8 # In no event will the authors be held liable for any damages arising from the |
|
9 # use of this software. |
|
10 # |
|
11 # Permission is granted to anyone to use this software for any purpose, |
|
12 # including commercial applications, and to alter it and redistribute it |
|
13 # freely, subject to the following restrictions: |
|
14 # |
|
15 # 1. The origin of this software must not be misrepresented; you must not |
|
16 # claim that you wrote the original software. If you use this software in a |
|
17 # product, an acknowledgment in the product documentation would be appreciated |
|
18 # but is not required. |
|
19 # |
|
20 # 2. Altered source versions must be plainly marked as such, and must not be |
|
21 # misrepresented as being the original software. |
|
22 # |
|
23 # 3. This notice may not be removed or altered from any source distribution. |
|
24 |
|
25 $VERSION="1.3.2" |
|
26 |
|
27 class IExploder |
|
28 attr_accessor :test_num, :subtest_num, :lookup_mode, :random_mode, :url |
|
29 attr_accessor :offset, :lines, :stop_num |
|
30 |
|
31 def initialize(max_tags, max_attrs, max_props) |
|
32 @htmlMaxTags = max_tags |
|
33 @htmlMaxAttrs = max_attrs |
|
34 @cssMaxProps = max_props |
|
35 @mangledTagTotal = 0 |
|
36 @stop_num = 0 |
|
37 end |
|
38 |
|
39 def setRandomSeed |
|
40 if @test_num > 0 |
|
41 srand(@test_num) |
|
42 else |
|
43 srand |
|
44 end |
|
45 end |
|
46 |
|
47 |
|
48 def readTagFiles |
|
49 # These if statements are so that mod_ruby doesn't have to reload the files |
|
50 # each time |
|
51 |
|
52 if (! @cssTags) |
|
53 @cssTags = readTagFile('cssproperties.in'); |
|
54 end |
|
55 |
|
56 if (! @htmlTags) |
|
57 @htmlTags = readTagFile('htmltags.in'); |
|
58 end |
|
59 if (! @htmlAttr) |
|
60 @htmlAttr = readTagFile('htmlattrs.in'); |
|
61 end |
|
62 |
|
63 if (! @htmlValues) |
|
64 @htmlValues = readTagFile('htmlvalues.in'); |
|
65 end |
|
66 |
|
67 if (! @cssValues) |
|
68 @cssValues = readTagFile('cssvalues.in'); |
|
69 end |
|
70 |
|
71 end |
|
72 |
|
73 |
|
74 def readTagFile(filename) |
|
75 list = Array.new |
|
76 File.new(filename).readlines.each { |line| |
|
77 line.chop! |
|
78 |
|
79 # Don't include comments. |
|
80 if (line !~ /^# /) && (line.length > 0) |
|
81 list << line |
|
82 end |
|
83 } |
|
84 return list |
|
85 end |
|
86 |
|
87 # based on make_up_value, essentially. |
|
88 def inventValue |
|
89 value = rand(19); |
|
90 case value |
|
91 when 1..3 then return (@htmlValues[rand(@htmlValues.length)]) |
|
92 when 4..5 then return (@htmlValues[rand(@htmlValues.length)] + inventValue()) |
|
93 when 6 then return (@htmlValues[rand(@htmlValues.length)] + "//" + inventValue()) |
|
94 when 7 then return '' |
|
95 # this may return negative argument? |
|
96 when 8..10 then return rand(255).chr * (rand(256)+8) |
|
97 when 11 then return rand(255).chr * (rand(2048)+8) |
|
98 when 12 then return "#" + rand(999999).to_s |
|
99 when 13 then return rand(999999).to_s + "%" |
|
100 when 14..15 then return "&" + rand(999999).to_s + ";" |
|
101 # filters |
|
102 when 16 then |
|
103 return inventValue() + "=" + inventValue() |
|
104 |
|
105 # this my return undefined method + for nil:NilClass |
|
106 when 17 then return inventValue() + "," + inventValue() |
|
107 else |
|
108 if rand(5) > 3 |
|
109 return "-" + rand(999999).to_s |
|
110 else |
|
111 return rand(999999).to_s |
|
112 end |
|
113 end |
|
114 end |
|
115 |
|
116 # based on make_up_value, essentially. |
|
117 def inventCssValue(tag) |
|
118 value = rand(23); |
|
119 case value |
|
120 when 1..10 then return @cssValues[rand(@cssValues.length)] |
|
121 when 11 then return '' |
|
122 when 12 then return rand(255).chr * (rand(8192)+8) |
|
123 when 13 |
|
124 length = rand(1024) + 8 |
|
125 return (rand(255).chr * length) + " " + (rand(255).chr * length) + " " + (rand(255).chr * length) |
|
126 when 14 then return (rand(255).chr * (rand(1024)+3)) + "px" |
|
127 when 15 then return (rand(255).chr * (rand(1024)+3)) + "em" |
|
128 when 16 then return "url(" + inventValue() + ")" |
|
129 when 17..18 then return "#" + rand(999999999).to_s |
|
130 when 19 then return "-" + rand(99999999).to_s |
|
131 else return rand(99999999).to_s; |
|
132 end |
|
133 end |
|
134 |
|
135 |
|
136 def mangleTag(tag) |
|
137 @mangledTagTotal += 1 |
|
138 out = '' |
|
139 |
|
140 # 20% chance of closing a tag instead of opening it. This |
|
141 # still counts against @mangledTagTotal, however. |
|
142 if rand(10) > 8 |
|
143 out = "</" + tag + ">" |
|
144 return out |
|
145 end |
|
146 |
|
147 # we're opening it. |
|
148 out = "<" + tag |
|
149 |
|
150 # forgot the space between the tag and the attributes |
|
151 if rand(15) > 1 |
|
152 out << ' ' |
|
153 end |
|
154 |
|
155 attrNum = rand(@htmlMaxAttrs) + 1 |
|
156 |
|
157 1.upto(attrNum) { |
|
158 attr = @htmlAttr[rand(@htmlAttr.length)] |
|
159 |
|
160 out << attr |
|
161 |
|
162 # 7.5% of the time we skip the = sign. Don't prefix it |
|
163 # if the attribute ends with a ( however. |
|
164 |
|
165 |
|
166 if rand(15) > 1 |
|
167 out << '=' |
|
168 end |
|
169 |
|
170 # sometimes quote it, sometimes not. I doubt the importance |
|
171 # of this test, but mangleme-1.2 added it, and adding more |
|
172 # random-ness never hurt anything but time. I'll do it less often. |
|
173 quote = rand(2) |
|
174 if (quote > 1) |
|
175 out << "\"" |
|
176 end |
|
177 |
|
178 out << inventValue() |
|
179 |
|
180 # end the quote when you are done |
|
181 if (quote > 1) |
|
182 out << "\" " |
|
183 end |
|
184 |
|
185 # 5% chance we skip the space at the end of the name |
|
186 if rand(20) > 1 |
|
187 out << ' ' |
|
188 end |
|
189 |
|
190 } |
|
191 |
|
192 # CSS styles! |
|
193 if rand(4) > 1 |
|
194 out << " style=\"" |
|
195 1.upto(rand(@cssMaxProps)+1) { |
|
196 out << @cssTags[rand(@cssTags.length)] |
|
197 |
|
198 # very small chance we let the tag run on. |
|
199 if rand(50) > 1 |
|
200 out << ": " |
|
201 end |
|
202 |
|
203 out << inventCssValue(tag) |
|
204 # we almost always put the ; there. |
|
205 if rand(50) > 1 |
|
206 out << '; ' |
|
207 end |
|
208 } |
|
209 out << "\"" |
|
210 end |
|
211 |
|
212 out << ">\n" |
|
213 |
|
214 # support our local troops! |
|
215 if (@subtest_num > 0) && filterSubTest() |
|
216 if tag =~ /html|body|head/ |
|
217 return '<' + tag + '>' |
|
218 else |
|
219 return "<x-#@mangledTagTotal>\n" |
|
220 end |
|
221 else |
|
222 return out |
|
223 end |
|
224 end |
|
225 #end |
|
226 |
|
227 def filterSubTest() |
|
228 result = 1 |
|
229 if (@mangledTagTotal >= @offset) && (@mangledTagTotal < (@offset + @lines)) |
|
230 result = nil |
|
231 end |
|
232 return result |
|
233 end |
|
234 |
|
235 def nextTestNum() |
|
236 if random_mode |
|
237 n = rand(99999999) |
|
238 else |
|
239 if @test_num |
|
240 n = @test_num + 1 |
|
241 else |
|
242 n = 1 |
|
243 end |
|
244 end |
|
245 return n |
|
246 end |
|
247 |
|
248 # If we are at line 30 with 8 extra lines, there is no point to try line 31 |
|
249 # with 8 lines as well.. skip back to 1 and bump up the line count. |
|
250 def nextSubTestNum() |
|
251 if (@offset + @lines) > @htmlMaxTags |
|
252 nextNum = ((@lines * 2 -1)) * @htmlMaxTags |
|
253 else |
|
254 nextNum = @subtest_num + 1 |
|
255 end |
|
256 return nextNum |
|
257 end |
|
258 |
|
259 |
|
260 def buildPage |
|
261 if (! @test_num) || (@test_num < 1) |
|
262 @test_num = 1 |
|
263 end |
|
264 next_num=nextTestNum() |
|
265 @lines = @subtest_num.div(@htmlMaxTags) + 1 |
|
266 @offset = @subtest_num.modulo(@htmlMaxTags) |
|
267 |
|
268 # building the HTML |
|
269 bodyText = mangleTag('html') |
|
270 bodyText << "\n<head>\n" |
|
271 |
|
272 # Only do redirects if lookup=1 has not been specified. |
|
273 if (! @lookup_mode) && (@lines <= @htmlMaxTags) && (@stop_num != @test_num) |
|
274 newpage = @url + "?" |
|
275 if @subtest_num > 0 |
|
276 newpage << "test=" << @test_num.to_s << "&subtest=" << nextSubTestNum().to_s |
|
277 else |
|
278 newpage << "test=" << next_num.to_s |
|
279 end |
|
280 |
|
281 if @random_mode |
|
282 newpage << "&random=1" |
|
283 end |
|
284 |
|
285 if @stop_num > 0 |
|
286 newpage << "&stop=" << @stop_num.to_s |
|
287 end |
|
288 |
|
289 bodyText << "\t<META HTTP-EQUIV=\"Refresh\" content=\"0;URL=#{newpage}\">\n" |
|
290 # use both techniques, because you never know how you might be corrupting yourself. |
|
291 bodyText << "\t<script language=\"javascript\">setTimeout('window.location=\"#{newpage}\"', 1000);</script>\n" |
|
292 end |
|
293 |
|
294 bodyText << "\t" << mangleTag('meta') |
|
295 bodyText << "\t" << mangleTag('meta') |
|
296 bodyText << "\t" << mangleTag('link') |
|
297 |
|
298 bodyText << "\t<title>[#@test_num] iExploder #{$VERSION} - #{inventValue()}</title>\n" |
|
299 bodyText << "</head>\n\n" |
|
300 |
|
301 # What tags will we be messing with ###################### |
|
302 tagList = [ 'body'] |
|
303 |
|
304 # we already have 5 tags? |
|
305 1.upto(@htmlMaxTags - 5 ) { tagList << @htmlTags[rand(@htmlTags.length)] } |
|
306 |
|
307 tagList.each { |tag| |
|
308 bodyText << mangleTag(tag) |
|
309 bodyText << inventValue() + "\n" |
|
310 } |
|
311 bodyText << "</body>\n</html>" |
|
312 end |
|
313 end |
|
314 |
|
315 |
|
316 |
|
317 if $0 == __FILE__ |
|
318 max=ARGV[0].to_i |
|
319 puts "testing #{max} tags" |
|
320 test = IExploder.new(max, 5, 5) |
|
321 test.readTagFiles() |
|
322 test.test_num=1 |
|
323 test.subtest_num=1 |
|
324 counter=0 |
|
325 test.lines=0 |
|
326 |
|
327 while test.lines < max |
|
328 test.lines = test.subtest_num.div(max) + 1 |
|
329 test.offset = test.subtest_num.modulo(max) |
|
330 test.subtest_num=test.nextSubTestNum |
|
331 counter = counter + 1 |
|
332 puts "[#{counter}] subtest #{test.subtest_num} is #{test.lines} lines with #{test.offset} offset" |
|
333 end |
|
334 |
|
335 puts "for #{max} tests, you will have #{counter} iterations until #{test.subtest_num}" |
|
336 end |
|
337 |