| # iExploder - Generates bad HTML files to perform QA for web browsers. |
| # Developed for the Mozilla Foundation. |
| ##################### |
| # |
| # Copyright (c) 2006 Thomas Stromberg <thomas%stromberg.org> |
| # |
| # This software is provided 'as-is', without any express or implied warranty. |
| # In no event will the authors be held liable for any damages arising from the |
| # use of this software. |
| # |
| # Permission is granted to anyone to use this software for any purpose, |
| # including commercial applications, and to alter it and redistribute it |
| # freely, subject to the following restrictions: |
| # |
| # 1. The origin of this software must not be misrepresented; you must not |
| # claim that you wrote the original software. If you use this software in a |
| # product, an acknowledgment in the product documentation would be appreciated |
| # but is not required. |
| # |
| # 2. Altered source versions must be plainly marked as such, and must not be |
| # misrepresented as being the original software. |
| # |
| # 3. This notice may not be removed or altered from any source distribution. |
| |
| $VERSION="1.3.2" |
| |
| class IExploder |
| attr_accessor :test_num, :subtest_num, :lookup_mode, :random_mode, :url |
| attr_accessor :offset, :lines, :stop_num |
| |
| def initialize(max_tags, max_attrs, max_props) |
| @htmlMaxTags = max_tags |
| @htmlMaxAttrs = max_attrs |
| @cssMaxProps = max_props |
| @mangledTagTotal = 0 |
| @stop_num = 0 |
| end |
| |
| def setRandomSeed |
| if @test_num > 0 |
| srand(@test_num) |
| else |
| srand |
| end |
| end |
| |
| |
| def readTagFiles |
| # These if statements are so that mod_ruby doesn't have to reload the files |
| # each time |
| |
| if (! @cssTags) |
| @cssTags = readTagFile('cssproperties.in'); |
| end |
| |
| if (! @htmlTags) |
| @htmlTags = readTagFile('htmltags.in'); |
| end |
| if (! @htmlAttr) |
| @htmlAttr = readTagFile('htmlattrs.in'); |
| end |
| |
| if (! @htmlValues) |
| @htmlValues = readTagFile('htmlvalues.in'); |
| end |
| |
| if (! @cssValues) |
| @cssValues = readTagFile('cssvalues.in'); |
| end |
| |
| end |
| |
| |
| def readTagFile(filename) |
| list = Array.new |
| File.new(filename).readlines.each { |line| |
| line.chop! |
| |
| # Don't include comments. |
| if (line !~ /^# /) && (line.length > 0) |
| list << line |
| end |
| } |
| return list |
| end |
| |
| # based on make_up_value, essentially. |
| def inventValue |
| value = rand(19); |
| case value |
| when 1..3 then return (@htmlValues[rand(@htmlValues.length)]) |
| when 4..5 then return (@htmlValues[rand(@htmlValues.length)] + inventValue()) |
| when 6 then return (@htmlValues[rand(@htmlValues.length)] + "//" + inventValue()) |
| when 7 then return '' |
| # this may return negative argument? |
| when 8..10 then return rand(255).chr * (rand(256)+8) |
| when 11 then return rand(255).chr * (rand(2048)+8) |
| when 12 then return "#" + rand(999999).to_s |
| when 13 then return rand(999999).to_s + "%" |
| when 14..15 then return "&" + rand(999999).to_s + ";" |
| # filters |
| when 16 then |
| return inventValue() + "=" + inventValue() |
| |
| # this my return undefined method + for nil:NilClass |
| when 17 then return inventValue() + "," + inventValue() |
| else |
| if rand(5) > 3 |
| return "-" + rand(999999).to_s |
| else |
| return rand(999999).to_s |
| end |
| end |
| end |
| |
| # based on make_up_value, essentially. |
| def inventCssValue(tag) |
| value = rand(23); |
| case value |
| when 1..10 then return @cssValues[rand(@cssValues.length)] |
| when 11 then return '' |
| when 12 then return rand(255).chr * (rand(8192)+8) |
| when 13 |
| length = rand(1024) + 8 |
| return (rand(255).chr * length) + " " + (rand(255).chr * length) + " " + (rand(255).chr * length) |
| when 14 then return (rand(255).chr * (rand(1024)+3)) + "px" |
| when 15 then return (rand(255).chr * (rand(1024)+3)) + "em" |
| when 16 then return "url(" + inventValue() + ")" |
| when 17..18 then return "#" + rand(999999999).to_s |
| when 19 then return "-" + rand(99999999).to_s |
| else return rand(99999999).to_s; |
| end |
| end |
| |
| |
| def mangleTag(tag) |
| @mangledTagTotal += 1 |
| out = '' |
| |
| # 20% chance of closing a tag instead of opening it. This |
| # still counts against @mangledTagTotal, however. |
| if rand(10) > 8 |
| out = "</" + tag + ">" |
| return out |
| end |
| |
| # we're opening it. |
| out = "<" + tag |
| |
| # forgot the space between the tag and the attributes |
| if rand(15) > 1 |
| out << ' ' |
| end |
| |
| attrNum = rand(@htmlMaxAttrs) + 1 |
| |
| 1.upto(attrNum) { |
| attr = @htmlAttr[rand(@htmlAttr.length)] |
| |
| out << attr |
| |
| # 7.5% of the time we skip the = sign. Don't prefix it |
| # if the attribute ends with a ( however. |
| |
| |
| if rand(15) > 1 |
| out << '=' |
| end |
| |
| # sometimes quote it, sometimes not. I doubt the importance |
| # of this test, but mangleme-1.2 added it, and adding more |
| # random-ness never hurt anything but time. I'll do it less often. |
| quote = rand(2) |
| if (quote > 1) |
| out << "\"" |
| end |
| |
| out << inventValue() |
| |
| # end the quote when you are done |
| if (quote > 1) |
| out << "\" " |
| end |
| |
| # 5% chance we skip the space at the end of the name |
| if rand(20) > 1 |
| out << ' ' |
| end |
| |
| } |
| |
| # CSS styles! |
| if rand(4) > 1 |
| out << " style=\"" |
| 1.upto(rand(@cssMaxProps)+1) { |
| out << @cssTags[rand(@cssTags.length)] |
| |
| # very small chance we let the tag run on. |
| if rand(50) > 1 |
| out << ": " |
| end |
| |
| out << inventCssValue(tag) |
| # we almost always put the ; there. |
| if rand(50) > 1 |
| out << '; ' |
| end |
| } |
| out << "\"" |
| end |
| |
| out << ">\n" |
| |
| # support our local troops! |
| if (@subtest_num > 0) && filterSubTest() |
| if tag =~ /html|body|head/ |
| return '<' + tag + '>' |
| else |
| return "<x-#@mangledTagTotal>\n" |
| end |
| else |
| return out |
| end |
| end |
| #end |
| |
| def filterSubTest() |
| result = 1 |
| if (@mangledTagTotal >= @offset) && (@mangledTagTotal < (@offset + @lines)) |
| result = nil |
| end |
| return result |
| end |
| |
| def nextTestNum() |
| if random_mode |
| n = rand(99999999) |
| else |
| if @test_num |
| n = @test_num + 1 |
| else |
| n = 1 |
| end |
| end |
| return n |
| end |
| |
| # If we are at line 30 with 8 extra lines, there is no point to try line 31 |
| # with 8 lines as well.. skip back to 1 and bump up the line count. |
| def nextSubTestNum() |
| if (@offset + @lines) > @htmlMaxTags |
| nextNum = ((@lines * 2 -1)) * @htmlMaxTags |
| else |
| nextNum = @subtest_num + 1 |
| end |
| return nextNum |
| end |
| |
| |
| def buildPage |
| if (! @test_num) || (@test_num < 1) |
| @test_num = 1 |
| end |
| next_num=nextTestNum() |
| @lines = @subtest_num.div(@htmlMaxTags) + 1 |
| @offset = @subtest_num.modulo(@htmlMaxTags) |
| |
| # building the HTML |
| bodyText = mangleTag('html') |
| bodyText << "\n<head>\n" |
| |
| # Only do redirects if lookup=1 has not been specified. |
| if (! @lookup_mode) && (@lines <= @htmlMaxTags) && (@stop_num != @test_num) |
| newpage = @url + "?" |
| if @subtest_num > 0 |
| newpage << "test=" << @test_num.to_s << "&subtest=" << nextSubTestNum().to_s |
| else |
| newpage << "test=" << next_num.to_s |
| end |
| |
| if @random_mode |
| newpage << "&random=1" |
| end |
| |
| if @stop_num > 0 |
| newpage << "&stop=" << @stop_num.to_s |
| end |
| |
| bodyText << "\t<META HTTP-EQUIV=\"Refresh\" content=\"0;URL=#{newpage}\">\n" |
| # use both techniques, because you never know how you might be corrupting yourself. |
| bodyText << "\t<script language=\"javascript\">setTimeout('window.location=\"#{newpage}\"', 1000);</script>\n" |
| end |
| |
| bodyText << "\t" << mangleTag('meta') |
| bodyText << "\t" << mangleTag('meta') |
| bodyText << "\t" << mangleTag('link') |
| |
| bodyText << "\t<title>[#@test_num] iExploder #{$VERSION} - #{inventValue()}</title>\n" |
| bodyText << "</head>\n\n" |
| |
| # What tags will we be messing with ###################### |
| tagList = [ 'body'] |
| |
| # we already have 5 tags? |
| 1.upto(@htmlMaxTags - 5 ) { tagList << @htmlTags[rand(@htmlTags.length)] } |
| |
| tagList.each { |tag| |
| bodyText << mangleTag(tag) |
| bodyText << inventValue() + "\n" |
| } |
| bodyText << "</body>\n</html>" |
| end |
| end |
| |
| |
| |
| if $0 == __FILE__ |
| max=ARGV[0].to_i |
| puts "testing #{max} tags" |
| test = IExploder.new(max, 5, 5) |
| test.readTagFiles() |
| test.test_num=1 |
| test.subtest_num=1 |
| counter=0 |
| test.lines=0 |
| |
| while test.lines < max |
| test.lines = test.subtest_num.div(max) + 1 |
| test.offset = test.subtest_num.modulo(max) |
| test.subtest_num=test.nextSubTestNum |
| counter = counter + 1 |
| puts "[#{counter}] subtest #{test.subtest_num} is #{test.lines} lines with #{test.offset} offset" |
| end |
| |
| puts "for #{max} tests, you will have #{counter} iterations until #{test.subtest_num}" |
| end |
| |