Reviewed by Maciej.
[WebKit-https.git] / WebKitTools / iExploder / htdocs / iexploder.rb
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