1 # iExploder - Generates bad HTML files to perform QA for web browsers.
2 # Developed for the Mozilla Foundation.
5 # Copyright (c) 2006 Thomas Stromberg <thomas%stromberg.org>
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.
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:
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.
20 # 2. Altered source versions must be plainly marked as such, and must not be
21 # misrepresented as being the original software.
23 # 3. This notice may not be removed or altered from any source distribution.
28 attr_accessor :test_num, :subtest_num, :lookup_mode, :random_mode, :url
29 attr_accessor :offset, :lines, :stop_num
31 def initialize(max_tags, max_attrs, max_props)
32 @htmlMaxTags = max_tags
33 @htmlMaxAttrs = max_attrs
34 @cssMaxProps = max_props
49 # These if statements are so that mod_ruby doesn't have to reload the files
53 @cssTags = readTagFile('cssproperties.in');
57 @htmlTags = readTagFile('htmltags.in');
60 @htmlAttr = readTagFile('htmlattrs.in');
64 @htmlValues = readTagFile('htmlvalues.in');
68 @cssValues = readTagFile('cssvalues.in');
74 def readTagFile(filename)
76 File.new(filename).readlines.each { |line|
79 # Don't include comments.
80 if (line !~ /^# /) && (line.length > 0)
87 # based on make_up_value, essentially.
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())
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 + ";"
103 return inventValue() + "=" + inventValue()
105 # this my return undefined method + for nil:NilClass
106 when 17 then return inventValue() + "," + inventValue()
109 return "-" + rand(999999).to_s
111 return rand(999999).to_s
116 # based on make_up_value, essentially.
117 def inventCssValue(tag)
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)
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;
137 @mangledTagTotal += 1
140 # 20% chance of closing a tag instead of opening it. This
141 # still counts against @mangledTagTotal, however.
143 out = "</" + tag + ">"
150 # forgot the space between the tag and the attributes
155 attrNum = rand(@htmlMaxAttrs) + 1
158 attr = @htmlAttr[rand(@htmlAttr.length)]
162 # 7.5% of the time we skip the = sign. Don't prefix it
163 # if the attribute ends with a ( however.
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.
180 # end the quote when you are done
185 # 5% chance we skip the space at the end of the name
195 1.upto(rand(@cssMaxProps)+1) {
196 out << @cssTags[rand(@cssTags.length)]
198 # very small chance we let the tag run on.
203 out << inventCssValue(tag)
204 # we almost always put the ; there.
214 # support our local troops!
215 if (@subtest_num > 0) && filterSubTest()
216 if tag =~ /html|body|head/
217 return '<' + tag + '>'
219 return "<x-#@mangledTagTotal>\n"
229 if (@mangledTagTotal >= @offset) && (@mangledTagTotal < (@offset + @lines))
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.
251 if (@offset + @lines) > @htmlMaxTags
252 nextNum = ((@lines * 2 -1)) * @htmlMaxTags
254 nextNum = @subtest_num + 1
261 if (! @test_num) || (@test_num < 1)
264 next_num=nextTestNum()
265 @lines = @subtest_num.div(@htmlMaxTags) + 1
266 @offset = @subtest_num.modulo(@htmlMaxTags)
269 bodyText = mangleTag('html')
270 bodyText << "\n<head>\n"
272 # Only do redirects if lookup=1 has not been specified.
273 if (! @lookup_mode) && (@lines <= @htmlMaxTags) && (@stop_num != @test_num)
276 newpage << "test=" << @test_num.to_s << "&subtest=" << nextSubTestNum().to_s
278 newpage << "test=" << next_num.to_s
282 newpage << "&random=1"
286 newpage << "&stop=" << @stop_num.to_s
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"
294 bodyText << "\t" << mangleTag('meta')
295 bodyText << "\t" << mangleTag('meta')
296 bodyText << "\t" << mangleTag('link')
298 bodyText << "\t<title>[#@test_num] iExploder #{$VERSION} - #{inventValue()}</title>\n"
299 bodyText << "</head>\n\n"
301 # What tags will we be messing with ######################
304 # we already have 5 tags?
305 1.upto(@htmlMaxTags - 5 ) { tagList << @htmlTags[rand(@htmlTags.length)] }
308 bodyText << mangleTag(tag)
309 bodyText << inventValue() + "\n"
311 bodyText << "</body>\n</html>"
319 puts "testing #{max} tags"
320 test = IExploder.new(max, 5, 5)
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"
335 puts "for #{max} tests, you will have #{counter} iterations until #{test.subtest_num}"