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 338