/[suikacvs]/webroot/www/webidl2tests/bin/wttjs.pl
Suika

Contents of /webroot/www/webidl2tests/bin/wttjs.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.16 - (show annotations) (download)
Sat Oct 11 08:05:37 2008 UTC (15 years, 7 months ago) by wakaba
Branch: MAIN
CVS Tags: HEAD
Changes since 1.15: +4 -1 lines
File MIME type: text/plain
++ wtt/bin/ChangeLog	11 Oct 2008 08:05:35 -0000
	* wttjs.pl: Noted that Whatpm::WebIDL has no more dependency.

2008-10-11  Wakaba  <wakaba@suika.fam.cx>

1 #!/usr/bin/perl
2 use strict;
3
4 use Getopt::Long;
5 use File::Path qw/mkpath/;
6
7 my $input;
8 my $instances;
9 my $instances_input;
10 my $test_dir_name = 'test';
11 my $testset_id;
12 my $result_list_url = q<http://suika.fam.cx/gate/test-results/list/>;
13
14 require JSON;
15 use Pod::Usage;
16
17 GetOptions (
18 'help' => sub {
19 pod2usage (-exitval => 0, -verbose => 2);
20 },
21 'idl-file-name=s' => sub {
22 if ($_[1] =~ /\b([A-Za-z0-9_-]+)(?:\.[A-Za-z0-9_-]+)?$/) {
23 $testset_id = $1 unless defined $testset_id;
24 }
25 open my $file, '<:utf8', $_[1] or die "$0: $_[1]: $!";
26 local $/ = undef;
27 $input = '';
28 $input .= $_ while <$file>;
29 },
30 'instances-file-name=s' => sub {
31 open my $file, '<:utf8', $_[1] or die "$0: $_[1]: $!";
32 local $/ = undef;
33 $instances_input = '';
34 $instances_input .= $_ while <$file>;
35
36 $instances = JSON::jsonToObj ($instances_input);
37 },
38 'result-list-url=s' => \$result_list_url,
39 'test-dir-name=s' => \$test_dir_name,
40 'testset-id=s' => \$testset_id,
41 ) or pod2usage (-exitval => 1, -verbose => 1);
42 pod2usage (-exitval => 1, -verbose => 1,
43 -msg => "Required argument --testset-id is not specified.\n")
44 unless defined $testset_id;
45 pod2usage (-exitval => 1, -verbose => 1,
46 -msg => "Required argument --idl-file-name is not specified.\n")
47 unless defined $input;
48
49 my $testset_dir_name = $test_dir_name . '/' . $testset_id . '/';
50 mkpath $testset_dir_name;
51
52 my $all_tests = [];
53
54 my $crash_info = $instances->{_crash} || {};
55 delete $instances->{_crash} if $instances;
56
57 require Whatpm::WebIDL;
58 my $p = Whatpm::WebIDL::Parser->new;
59 my $defs = $p->parse_char_string ($input);
60
61 ## The transitive closure of the "inherited by" relationship, except
62 ## for the trivial I->I relationship.
63 my $inherited_by = {};
64 my @interface = @{$defs->child_nodes};
65 while (@interface) {
66 my $interface = shift @interface;
67 if ($interface->isa ('Whatpm::WebIDL::Interface')) {
68 my @inherits = map {[split /::/, $_]->[-1]} @{$interface->inheritances};
69 $inherited_by->{$_}->{$interface->node_name} = 1 for @inherits;
70 } elsif ($interface->isa ('Whatpm::WebIDL::Module')) {
71 unshift @interface, @{$interface->child_nodes};
72 }
73 }
74 while (1) {
75 ## NOTE: This is not so good with respect to the computation
76 ## complexity, but it should not affect the performance so much,
77 ## since the number of interfaces is not so many in general.
78 my $changed;
79 for my $a (keys %$inherited_by) {
80 for my $b (keys %{$inherited_by->{$a}}) {
81 for my $c (keys %{$inherited_by->{$b} or {}}) {
82 unless ($inherited_by->{$a}->{$c}) {
83 $inherited_by->{$a}->{$c} = 1;
84 $changed = 1;
85 }
86 }
87 }
88 }
89 last unless $changed;
90 }
91
92 @interface = @{$defs->child_nodes};
93 while (@interface) {
94 my $interface = shift @interface;
95 if ($interface->isa ('Whatpm::WebIDL::Interface')) {
96 next if $interface->is_forward_declaration;
97
98 my $interface_name = $interface->node_name;
99 my $interface_id = generate_id ($interface_name, 1);
100
101 my $all_instances = [];
102 for my $i ($interface_name,
103 keys %{$inherited_by->{$interface_name} or {}}) {
104 push @$all_instances, map {[$i, $_]} @{$instances->{$i} or []};
105 }
106
107 ## Interface object
108 my $has_interface_object;
109 unless ($interface->has_extended_attribute ('NoInterfaceObject')) {
110 $has_interface_object = 1;
111
112 generate_test
113 ($interface_id . '-interface-object-has-property',
114 qq{var global = wttGetGlobal ();\n} .
115 qq{wttAssertTrue ('$interface_name' in global, '0');\n},
116 label => qq{$interface_name (interface object)});
117
118 generate_test
119 ($interface_id . '-interface-object-dont-delete',
120 qq{var global = wttGetGlobal ();\n} .
121 qq{wttAssertDontDelete (global, '$interface_name', '1');\n},
122 depends => [$interface_id . '-interface-object-has-property'],
123 label => qq{$interface_name {DontDelete}});
124
125 generate_test
126 ($interface_id . '-interface-object-dont-enum',
127 qq{var global = wttGetGlobal ();\n} .
128 qq{wttAssertDontEnum (global, '$interface_name', '1');\n},
129 depends => [$interface_id . '-interface-object-has-property'],
130 label => qq{$interface_name {DontEnum}});
131
132 generate_test
133 ($interface_id . '-interface-object-prototype',
134 qq{var global = wttGetGlobal ();\n} .
135 qq{wttAssertEquals (global.$interface_name.__proto__,
136 Object.prototype,
137 'same-as-object-prototype');\n},
138 depends => ['__proto__',
139 $interface_id . '-interface-object-has-property'],
140 label => qq{$interface_name.[[Prototype]]});
141
142 ## MUST have [[Construct]] - can't be tested (though we can test
143 ## its existence by invoking |new|, but it might invoke an
144 ## exception and there is no reliable way to distinguish the
145 ## exception from the failure to invoke [[Construct]] because of
146 ## lack of it (WebIDL allows any possible implementation of
147 ## [[Construct]], which implies any exception may be thrown)).
148
149 ## Interface prototype object
150 generate_test
151 ($interface_id . '-interface-prototype-object-has-property',
152 qq{wttAssertTrue ('prototype' in $interface_name, '0');\n},
153 depends => [$interface_id . '-interface-object-has-property'],
154 label => qq{$interface_name.prototype});
155
156 generate_test
157 ($interface_id . '-interface-prototype-object-dont-delete',
158 qq{wttAssertDontDelete ($interface_name, 'prototype', '1');\n},
159 depends => [$interface_id .
160 '-interface-prototype-object-has-property'],
161 label => qq{$interface_name.prototype {DontDelete}});
162
163 generate_test
164 ($interface_id . '-interface-prototype-object-read-only',
165 qq{wttAssertReadOnly ($interface_name, 'prototype', '1');\n},
166 depends => [$interface_id .
167 '-interface-prototype-object-has-property'],
168 label => qq{$interface_name.prototype {ReadOnly}});
169
170 ## Interface object's [[HasInstance]]
171 generate_test
172 ($interface_id . '-interface-object-has-instance-non-object',
173 qq{wttAssertFalse (null instanceof $interface_name, 'null');\n} .
174 qq{wttAssertFalse (undefined instanceof $interface_name,
175 'undefined');\n} .
176 qq{wttAssertFalse (0 instanceof $interface_name, 'number');\n} .
177 qq{wttAssertFalse ("" instanceof $interface_name, 'string');\n},
178 depends => [$interface_id .'-interface-object-has-property'],
179 label => qq{non_object instanceof $interface_name});
180 ## NOTE: WebIDL's algorithm, step 1 cases
181
182 generate_test
183 ($interface_id .
184 '-interface-object-has-instance-host-object-' . $_->[1]->{id},
185 qq{var v = wttGetInstance ('$_->[0]', '$_->[1]->{id}');\n} .
186 qq{wttAssertTrue (v instanceof $interface_name, '1');\n},
187 depends => [$interface_id . '-interface-object-has-property'],
188 label => qq{${interface_name}_instance ($_->[1]->{id}) instanceof } .
189 $interface_name)
190 for @$all_instances;
191 ## NOTE: WebIDL's algorithm, step 5 cases
192
193 generate_test
194 ($interface_id . '-interface-object-has-instance-prototype-null',
195 qq{function V () {};\n} .
196 qq{V.prototype = null;\n} .
197 qq{var v = new V ();\n} .
198 qq{wttAssertFalse (v instanceof $interface_name, 'null');\n},
199 depends => [$interface_id . '-interface-object-has-property'],
200 label => qq{{[[Prototype]]: null} instanceof $interface_name});
201 ## NOTE: WebIDL's algorithm, step 7 cases
202
203 generate_test
204 ($interface_id . '-interface-object-has-instance-prototype-object',
205 qq{function V () {};\n} .
206 qq{V.prototype = $interface_name.prototype;\n} .
207 qq{var v = new V ();\n} .
208 qq{wttAssertTrue (v instanceof $interface_name, 'level-0');\n} .
209 qq{function W () {};\n} .
210 qq{W.prototype = v;\n} .
211 qq{var w = new W ();\n} .
212 qq{wttAssertTrue (w instanceof $interface_name, 'level-1');\n},
213 depends => [$interface_id . '-interface-object-has-property',
214 $interface_id .
215 '-interface-prototype-object-has-property'],
216 label => qq{Object instanceof $interface_name});
217 ## NOTE: WebIDL's algorithm, step 8 cases
218
219 generate_test
220 ($interface_id .
221 '-interface-object-has-instance-prototype-differeent-object',
222 qq{wttAssertFalse ({} instanceof $interface_name, 'object');\n} .
223 qq{wttAssertFalse ((new Date ()) instanceof $interface_name,
224 'date');\n} .
225 qq{wttAssertFalse ([] instanceof $interface_name, 'array');\n},
226 depends => [$interface_id . '-interface-object-has-property'],
227 label => qq{builtin instanceof $interface_name});
228 ## NOTE: WebIDL's algorithm, step 9 -> step 7 cases
229
230 if ($interface->has_extended_attribute ('Constructor')) {
231 ## Interface prototype object's constructor
232 generate_test
233 ($interface_id .
234 '-interface-prototype-object-constructor-has-property',
235 qq{wttAssertTrue ('constructor' in $interface_name.prototype,
236 '0');\n},
237 depends => [$interface_id .
238 '-interface-prototype-object-has-property'],
239 label => qq{$interface_name.prototype.constructor});
240
241 generate_test
242 ($interface_id .
243 '-interface-prototype-object-constructor-dont-enum',
244 qq{wttAssertDontEnum ($interface_name.prototype, 'constructor',
245 '0');\n},
246 depends => [$interface_id .
247 '-interface-prototype-object-constructor-has-property'],
248 label => qq{$interface_name.prototype.constructor {DontEnum}});
249 }
250
251 ## TODO: "However, it MUST be an object that provides access to
252 ## the properties corresponding to the operations and constants
253 ## defined on the interfaces from which this interface
254 ## inherits. Changes made to the interface prototype objects of
255 ## superinterfaces MUST be reflected through this object, as
256 ## with normal prototype-based single inheritance in
257 ## ECMAScript. If more than one superinterface has a given
258 ## property, it is implementation specific which one is
259 ## accessed. "
260
261 ## ToString
262 if ($interface->has_extended_attribute ('Stringifies')) {
263 generate_test
264 ($interface_id .
265 '-interface-prototype-object-to-string-has-property',
266 qq{wttAssertTrue ('toString' in $interface_name.prototype, '1');\n},
267 depends => [$interface_id .
268 '-interface-prototype-object-has-property'],
269 label => qq{$interface_name.prototype.toString});
270
271 generate_test
272 ($interface_id . '-interface-prototype-object-to-string-type',
273 qq{wttAssertEquals (typeof ($interface_name.prototype.toString),
274 'function', 'function');\n},
275 depends => [$interface_id .
276 '-interface-prototype-object-to-string-has-property'],
277 label => qq{typeof ($interface_name.prototype.toString) === Function});
278 ## NOTE: There is no reliable way to distinguish a Function
279 ## object from some host object that behaves as if it were a
280 ## Function object, afaict.
281 }
282 }
283
284 for (@{$interface->get_extended_attribute_nodes ('NamedConstructor')}) {
285 my $name = $_->value;
286 my $id = generate_id ($name, 1);
287
288 generate_test
289 ($interface_id . '-constructor-' . $id . '-has-property',
290 qq{var global = wttGetGlobal ();\n} .
291 qq{wttAssertTrue ('$name' in global, '0');\n},
292 label => qq{$name (Constructor)});
293
294 generate_test
295 ($interface_id . '-constructor-' . $id . '-dont-delete',
296 qq{var global = wttGetGlobal ();\n} .
297 qq{wttAssertDontDelete (global, '$name', '1');\n},
298 depends => [$interface_id . '-constructor-' . $id . '-has-property'],
299 label => qq{$name {DontDelete}});
300
301 generate_test
302 ($interface_id . '-constructor-' . $id . '-dont-enum',
303 qq{var global = wttGetGlobal ();\n} .
304 qq{wttAssertDontEnum (global, '$name', '1');\n},
305 depends => [$interface_id . '-constructor-' . $id . '-has-property'],
306 label => qq{$name {DontEnum}});
307
308 ## [[Construct]] must return an object implementing the
309 ## interface or throw an exception - don't check for now (see
310 ## note above).
311 }
312
313 my %has_method;
314 for my $def (@{$interface->child_nodes}) {
315 if ($def->isa ('Whatpm::WebIDL::Const')) {
316 my $const_name = $def->node_name;
317 my $const_value = $def->value_text;
318 my $const_id = generate_id ($const_name, 2);
319
320 if ($has_interface_object) {
321 for my $aaa (['interface-object', $interface_name, []],
322 ['interface-prototype-object',
323 $interface_name.'.prototype',
324 [$interface_id .
325 '-interface-prototype-object-has-property']]) {
326 generate_test
327 ($interface_id . '-'.$aaa->[0].'-const-' . $const_id .
328 '-has-property',
329 qq{wttAssertTrue ('$const_name' in $aaa->[1], '1');\n},
330 depends => $aaa->[2],
331 label => qq{$aaa->[1].$const_name});
332
333 generate_test
334 ($interface_id . '-'.$aaa->[0].'-const-' . $const_id .
335 '-dont-delete',
336 qq{wttAssertDontDelete ($aaa->[1], '$const_name', '1');\n},
337 depends => [$interface_id . '-'.$aaa->[0].'-const-'.
338 $const_id .'-has-property'],
339 label => qq{$aaa->[1].$const_name {DontDelete}});
340
341 generate_test
342 ($interface_id . '-'.$aaa->[0].'-const-' . $const_id .
343 '-read-only',
344 qq{wttAssertReadOnly ($aaa->[1], '$const_name', '1');\n},
345 depends => [$interface_id . '-'.$aaa->[0].'-const-'.
346 $const_id .'-has-property'],
347 label => qq{$aaa->[1].$const_name {ReadOnly}});
348
349 generate_test
350 ($interface_id . '-'.$aaa->[0].'-const-' . $const_id . '-value',
351 qq{wttAssertEquals ($aaa->[1].$const_name, $const_value,
352 '1');\n},
353 depends => [$interface_id . '-'.$aaa->[0].'-const-'.
354 $const_id .'-has-property'],
355 label => qq{$aaa->[1].$const_name value});
356 }
357 }
358 } elsif ($def->isa ('Whatpm::WebIDL::Operation')) {
359 my $method_name = $def->node_name;
360 my $method_id = generate_id ($method_name);
361
362 next if $has_method{$method_name};
363 $has_method{$method_name} = 1;
364
365 generate_test
366 ($interface_id . '-interface-prototype-object-method-' . $method_id .
367 '-has-property',
368 qq{wttAssertTrue ('$method_name' in $interface_name.prototype,
369 '1');\n},
370 depends => [$interface_id .
371 '-interface-prototype-object-has-property'],
372 label => qq{$interface_name.prototype.$method_name});
373
374 generate_test
375 ($interface_id . '-interface-prototype-object-method-' . $method_id .
376 '-dont-enum',
377 qq{wttAssertDontEnum ($interface_name.prototype, '$method_name',
378 '1');\n},
379 depends => [$interface_id . '-interface-prototype-object-method-'.
380 $method_id .'-has-property'],
381 label => qq{$interface_name.prototype.$method_name {DontEnum}});
382
383 ## TODO: If there is multiple definitions for the same
384 ## identifier, test whether a TypeError is thrown in case
385 ## arguments are less than the minimum number of arguments for
386 ## the operation.
387 } elsif ($def->isa ('Whatpm::WebIDL::Attribute')) {
388 my $attr_name = $def->node_name;
389 my $attr_id = generate_id ($attr_name);
390
391 for my $i (@$all_instances) {
392 generate_test
393 ($interface_id . '-instance-' . $i->[1]->{id} .
394 '-attr-' . $attr_id . '-has-property',
395 qq{var v = wttGetInstance ('$i->[0]', '$i->[1]->{id}');\n} .
396 qq{wttAssertTrue ('$attr_name' in v, '1');\n},
397 label => qq{${interface_name}_instance ($i->[1]->{id}).$attr_name});
398
399 generate_test
400 ($interface_id . '-instance-' . $i->[1]->{id} .
401 '-attr-' . $attr_id . '-dont-delete',
402 qq{var v = wttGetInstance ('$i->[0]', '$i->[1]->{id}');\n} .
403 qq{wttAssertDontDelete (v, '$attr_name', '1');\n},
404 depends => [$interface_id . '-instance-' . $i->[1]->{id} .
405 '-attr-' . $attr_id . '-has-property'],
406 label => qq{${interface_name}_instance ($i->[1]->{id}).$attr_name {DontDelete}});
407
408 if ($def->readonly and
409 not $def->has_extended_attribute ('PutForwards')) {
410 generate_test
411 ($interface_id . '-instance-' . $i->[1]->{id} .
412 '-attr-' . $attr_id . '-read-only',
413 qq{var v = wttGetInstance ('$i->[0]', '$i->[1]->{id}');\n} .
414 qq{wttAssertReadOnly (v, '$attr_name', '1');\n},
415 depends => [$interface_id . '-instance-' . $i->[1]->{id} .
416 '-attr-' . $attr_id . '-has-property'],
417 label => qq{${interface_name}_instance ($i->[1]->{id}).$attr_name {ReadOnly}});
418 }
419
420 ## TODO: "Changes made to the interface prototype objects of
421 ## interfaces implemented by the host object MUST be
422 ## reflected through this object."
423 }
424 }
425
426 ## TODO: IndexGetter/NameGetter/IndexSetter/NameSetter
427 }
428 } elsif ($interface->isa ('Whatpm::WebIDL::Exception')) {
429 my $interface_name = $interface->node_name;
430 my $interface_id = generate_id ($interface_name, 1);
431
432 my $all_instances = [];
433 push @$all_instances,
434 map {[$interface_name, $_]} @{$instances->{$interface_name} or []};
435
436 ## Exception interface object
437 my $has_interface_object;
438 unless ($interface->has_extended_attribute ('NoInterfaceObject')) {
439 $has_interface_object = 1;
440
441 generate_test
442 ($interface_id . '-interface-object-has-property',
443 qq{var global = wttGetGlobal ();\n} .
444 qq{wttAssertTrue ('$interface_name' in global, '0');\n},
445 label => qq{$interface_name (exception interface object)});
446
447 generate_test
448 ($interface_id . '-interface-object-dont-delete',
449 qq{var global = wttGetGlobal ();\n} .
450 qq{wttAssertDontDelete (global, '$interface_name', '1');\n},
451 depends => [$interface_id . '-interface-object-has-property'],
452 label => qq{$interface_name {DontDelete}});
453
454 generate_test
455 ($interface_id . '-interface-object-dont-enum',
456 qq{var global = wttGetGlobal ();\n} .
457 qq{wttAssertDontEnum (global, '$interface_name', '1');\n},
458 depends => [$interface_id . '-interface-object-has-property'],
459 label => qq{$interface_name {DontEnum}});
460
461 generate_test
462 ($interface_id . '-interface-object-prototype',
463 qq{var global = wttGetGlobal ();\n} .
464 qq{wttAssertEquals (global.$interface_name.__proto__,
465 Object.prototype,
466 'same-as-object-prototype');\n},
467 depends => ['__proto__',
468 $interface_id . '-interface-object-has-property'],
469 label => qq{$interface_name.[[Prototype]]});
470
471 ## Exception interface prototype object
472 generate_test
473 ($interface_id . '-interface-prototype-object-has-property',
474 qq{wttAssertTrue ('prototype' in $interface_name, '0');\n},
475 depends => [$interface_id . '-interface-object-has-property'],
476 label => qq{$interface_name.prototype});
477
478 generate_test
479 ($interface_id . '-interface-prototype-object-dont-delete',
480 qq{wttAssertDontDelete ($interface_name, 'prototype', '1');\n},
481 depends => [$interface_id .
482 '-interface-prototype-object-has-property'],
483 label => qq{$interface_name.prototype {DontDelete}});
484
485 generate_test
486 ($interface_id . '-interface-prototype-object-read-only',
487 qq{wttAssertReadOnly ($interface_name, 'prototype', '1');\n},
488 depends => [$interface_id .
489 '-interface-prototype-object-has-property'],
490 label => qq{$interface_name.prototype {ReadOnly}});
491
492 ## Exception interface object's [[HasInstance]]
493 generate_test
494 ($interface_id . '-interface-object-has-instance-non-object',
495 qq{wttAssertFalse (null instanceof $interface_name, 'null');\n} .
496 qq{wttAssertFalse (undefined instanceof $interface_name,
497 'undefined');\n} .
498 qq{wttAssertFalse (0 instanceof $interface_name, 'number');\n} .
499 qq{wttAssertFalse ("" instanceof $interface_name, 'string');\n},
500 depends => [$interface_id .'-interface-object-has-property'],
501 label => qq{non_object instanceof $interface_name});
502 ## NOTE: WebIDL's algorithm, step 1 cases
503
504 generate_test
505 ($interface_id .
506 '-interface-object-has-instance-host-object-' . $_->[1]->{id},
507 qq{var v = wttGetInstance ('$_->[0]', '$_->[1]->{id}');\n} .
508 qq{wttAssertTrue (v instanceof $interface_name, '1');\n},
509 depends => [$interface_id . '-interface-object-has-property'],
510 label => qq{${interface_name}_instance ($_->[1]->{id}) instanceof } .
511 $interface_name)
512 for @$all_instances;
513 ## NOTE: WebIDL's algorithm, step 2 cases
514
515 generate_test
516 ($interface_id . '-interface-object-has-instance-prototype-null',
517 qq{function V () {};\n} .
518 qq{V.prototype = null;\n} .
519 qq{var v = new V ();\n} .
520 qq{wttAssertFalse (v instanceof $interface_name, 'null');\n},
521 depends => [$interface_id . '-interface-object-has-property'],
522 label => qq{{[[Prototype]]: null} instanceof $interface_name});
523 ## NOTE: WebIDL's algorithm, step 3 cases
524
525 generate_test
526 ($interface_id . '-interface-object-has-instance-prototype-object',
527 qq{function V () {};\n} .
528 qq{V.prototype = $interface_name.prototype;\n} .
529 qq{var v = new V ();\n} .
530 qq{wttAssertFalse (v instanceof $interface_name, 'level-0');\n} .
531 qq{function W () {};\n} .
532 qq{W.prototype = v;\n} .
533 qq{var w = new W ();\n} .
534 qq{wttAssertTrue (w instanceof $interface_name, 'level-1');\n},
535 depends => [$interface_id . '-interface-object-has-property',
536 $interface_id .
537 '-interface-prototype-object-has-property'],
538 label => qq{Object instanceof $interface_name});
539 ## NOTE: WebIDL's algorithm, step 3 cases
540
541 generate_test
542 ($interface_id .
543 '-interface-object-has-instance-prototype-differeent-object',
544 qq{wttAssertFalse ({} instanceof $interface_name, 'object');\n} .
545 qq{wttAssertFalse ((new Date ()) instanceof $interface_name,
546 'date');\n} .
547 qq{wttAssertFalse ([] instanceof $interface_name, 'array');\n},
548 depends => [$interface_id . '-interface-object-has-property'],
549 label => qq{builtin instanceof $interface_name});
550 ## NOTE: WebIDL's algorithm, step 3 cases
551 }
552
553 if ($has_interface_object) {
554 my $mod = $interface->parent_node;
555 if ($mod and $mod->isa ('Whatpm::WebIDL::Module')) {
556 my $has_consts;
557 for (@{$mod->get_extended_attribute_nodes ('ExceptionConsts')}) {
558 if ($_->value eq $interface_name) {
559 $has_consts = 1;
560 last;
561 }
562 }
563
564 if ($has_consts) {
565 for my $def (@{$mod->child_nodes}) {
566 if ($def->isa ('Whatpm::WebIDL::Const')) {
567 my $const_name = $def->node_name;
568 my $const_value = $def->value_text;
569 my $const_id = generate_id ($const_name, 2);
570
571 for my $aaa (['interface-object', $interface_name, []],
572 ['interface-prototype-object',
573 $interface_name.'.prototype',
574 [$interface_id .
575 '-interface-prototype-object-has-property']]) {
576 generate_test
577 ($interface_id . '-'.$aaa->[0].'-const-' . $const_id .
578 '-has-property',
579 qq{wttAssertTrue ('$const_name' in $aaa->[1], '1');\n},
580 depends => $aaa->[2],
581 label => qq{$aaa->[1].$const_name});
582
583 generate_test
584 ($interface_id . '-'.$aaa->[0].'-const-' . $const_id .
585 '-dont-delete',
586 qq{wttAssertDontDelete ($aaa->[1], '$const_name', '1');\n},
587 depends => [$interface_id . '-'.$aaa->[0].'-const-'.
588 $const_id .'-has-property'],
589 label => qq{$aaa->[1].$const_name {DontDelete}});
590
591 generate_test
592 ($interface_id . '-'.$aaa->[0].'-const-' . $const_id .
593 '-read-only',
594 qq{wttAssertReadOnly ($aaa->[1], '$const_name', '1');\n},
595 depends => [$interface_id . '-'.$aaa->[0].'-const-'.
596 $const_id .'-has-property'],
597 label => qq{$aaa->[1].$const_name {ReadOnly}});
598
599 generate_test
600 ($interface_id . '-'.$aaa->[0].'-const-' . $const_id .
601 '-value',
602 qq{wttAssertEquals ($aaa->[1].$const_name, $const_value,
603 '1');\n},
604 depends => [$interface_id . '-'.$aaa->[0].'-const-'.
605 $const_id .'-has-property'],
606 label => qq{$aaa->[1].$const_name value});
607 }
608 }
609 }
610 }
611 }
612 }
613
614 for my $def (@{$interface->child_nodes}) {
615 if ($def->isa ('Whatpm::WebIDL::ExceptionMember')) {
616 my $attr_name = $def->node_name;
617 my $attr_id = generate_id ($attr_name);
618
619 for my $i (@$all_instances) {
620 generate_test
621 ($interface_id . '-instance-' . $i->[1]->{id} .
622 '-attr-' . $attr_id . '-has-property',
623 qq{var v = wttGetInstance ('$i->[0]', '$i->[1]->{id}');\n} .
624 qq{wttAssertTrue ('$attr_name' in v, '1');\n},
625 label => qq{${interface_name}_instance ($i->[1]->{id}).$attr_name});
626
627 generate_test
628 ($interface_id . '-instance-' . $i->[1]->{id} .
629 '-attr-' . $attr_id . '-dont-delete',
630 qq{var v = wttGetInstance ('$i->[0]', '$i->[1]->{id}');\n} .
631 qq{wttAssertDontDelete (v, '$attr_name', '1');\n},
632 depends => [$interface_id . '-instance-' . $i->[1]->{id} .
633 '-attr-' . $attr_id . '-has-property'],
634 label => qq{${interface_name}_instance ($i->[1]->{id}).$attr_name {DontDelete}});
635 }
636 }
637 }
638
639 for my $i (@$all_instances) {
640 generate_test
641 ($interface_id . '-instance-' . $i->[1]->{id} . '-prototype',
642 qq{var v = wttGetInstance ('$i->[0]', '$i->[1]->{id}');\n} .
643 qq{wttAssertEquals (v.__proto__, $interface_name.prototype,
644 'same-as-interface-prototype');\n},
645 depends => ['__proto__',
646 $interface_id .
647 '-interface-prototype-object-has-property'],
648 label => qq{${interface_name}_instance ($i->[1]->{id}).[[Prototype]]});
649 }
650 } elsif ($interface->isa ('Whatpm::WebIDL::Module')) {
651 unshift @interface, @{$interface->child_nodes};
652 }
653 }
654
655 generate_support_files ();
656
657 sub htescape ($) {
658 my $s = shift;
659 $s =~ s/&/&amp;/g;
660 $s =~ s/</&lt;/g;
661 $s =~ s/"/&quot;/g;
662 return $s;
663 } # htescape
664
665 sub generate_id ($$) {
666 my $s = shift;
667 if ($_[0] == 2) {
668 $s =~ tr/A-Z_/a-z-/;
669 return $s;
670 } else {
671 $s =~ s/([A-Z]+)$/-@{[lc $1]}/;
672 $s =~ s/([A-Z]+)([A-Z])/-@{[lc $1]}-@{[lc $2]}/g;
673 $s =~ s/([A-Z])/-@{[lc $1]}/g;
674 $s =~ s/^-// if $_[0];
675 }
676 return $s;
677 } # generate_id
678
679 sub generate_test ($$;%) {
680 my ($test_id, $test_code, %opt) = @_;
681
682 my $test_file_name = $testset_dir_name . $test_id . '.html';
683 push @$all_tests, {depends => $opt{depends},
684 id => $test_id,
685 fileName => $test_id . '.html',
686 label => $opt{label}};
687
688 open my $test_file, '>:utf8', $test_file_name
689 or die "$0: $test_file_name: $!";
690 print $test_file q[<!DOCTYPE HTML><title>];
691 print $test_file htescape ($test_id);
692 print $test_file qq[</title><script src=wtt.js></script>\n];
693 print $test_file qq[<p id=result class=FAIL>FAIL (noscript)</p>\n\n];
694 print $test_file qq[<script>
695 var globalId = '$test_id';
696 wttSetStatus ('FAIL', 'script')
697
698 try {
699
700 wttCheckCrash (globalId);
701
702 $test_code
703
704 wttOk ();
705
706 } catch (e) {
707 if (e instanceof WttFail) {
708 //
709 } else if (e instanceof WttSkip) {
710 wttSetStatus ('SKIPPED', e.message);
711 } else {
712 throw e;
713 }
714 }
715
716 </script>];
717 } # generate_test
718
719 sub generate_support_files () {
720 {
721 my $path = $testset_dir_name . '.htaccess';
722 open my $file, '>:utf8', $path or die "$0: $path: $!";
723 print $file qq[AddType text/html .html\n];
724 print $file qq[AddCharset utf-8 .html\n];
725 print $file qq[AddType text/javascript .js\n];
726 print $file qq[AddCharset utf-8 .js\n];
727 }
728
729 {
730 my $path = $testset_dir_name . 'all.html';
731 open my $file, '>:utf8', $path or die "$0: $path: $!";
732 print $file qq[<!DOCTYPE HTML>
733 <title>@{[htescape ($testset_id)]}</title>
734 <body>
735 <p id=status>Not executed, since scripting is not enabled.</p>
736 <p><span id=passed>0</span> passed,
737 <span id=failed>0</span> failed,
738 <span id=skipped>0</span> skipped.</p>
739
740 <form method=post accept-charset=utf-8></form>
741
742 <p><a href="@{[htescape ($result_list_url.$testset_id)]}/all">See
743 result of other browsers</a>
744
745 <p>Failed tests:
746 <ul id=failed-list></ul>
747
748 <p>Skipped tests:
749 <ul id=skipped-list></ul>
750
751 <iframe style="border-width: 0; width: 0; height: 0"></iframe>
752 <script>
753 var tests = @{[JSON::objToJson ($all_tests)]};
754 var testsLength = tests.length;
755 </script>
756 <script>
757 document.getElementById ('status').firstChild.data = 'Executing...';
758
759 var iframe = document.getElementsByTagName ('iframe')[0];
760 var form = document.forms[0];
761
762 var testResults = {};
763 var passedTestsNumber = 0;
764 var failedTestsNumber = 0;
765 var skippedTestsNumber = 0;
766 var currentTest;
767
768 if (document.all && !window.opera) {
769 iframe.onreadystatechange = function () {
770 if (this.readyState == 'complete') {
771 getTestResult ();
772 while (true) {
773 if (nextTest ()) break;
774 }
775 }
776 }
777 } else {
778 iframe.onload = function () {
779 getTestResult ();
780 while (true) {
781 if (nextTest ()) break;
782 }
783 }
784 }
785
786 while (true) {
787 if (nextTest ()) break;
788 }
789
790 function getTestResult () {
791 if (!currentTest) return;
792
793 var r = iframe.contentWindow.document.getElementById ('result');
794 if (r.className == 'PASS') {
795 document.getElementById ('passed').firstChild.data = ++passedTestsNumber;
796 testResults[currentTest.id] = true;
797 } else {
798 var idPrefix = r.className == 'SKIPPED' ? 'skipped' : 'failed';
799 document.getElementById (idPrefix).firstChild.data
800 = (r.className == 'SKIPPED' ?
801 ++skippedTestsNumber : ++failedTestsNumber);
802 var li = document.createElement ('li');
803 li.innerHTML = '<a>xxxx</a>: <span>xxxx</span>';
804 li.firstChild.href = currentTest.fileName;
805 li.firstChild.title = currentTest.id;
806 li.firstChild.firstChild.data = currentTest.label || currentTest.id;
807 li.lastChild.firstChild.data = r.firstChild.data;
808 document.getElementById (idPrefix + '-list').appendChild (li);
809 }
810
811 var i = document.createElement ('p');
812 i.innerHTML = '<input name=test-name type=hidden>' +
813 '<input name=test-label type=hidden>' +
814 '<input name=test-class type=hidden>' +
815 '<input name=test-result type=hidden>';
816 i.childNodes[0].value = currentTest.fileName;
817 i.childNodes[1].value = currentTest.label || '';
818 i.childNodes[2].value = r.className;
819 i.childNodes[3].value = r.firstChild.data;
820 form.appendChild (i);
821 } // getTestResult
822
823 function nextTest () {
824 if (tests.length > 0) {
825 document.getElementById ('status').firstChild.data
826 = (testsLength - tests.length + 1) + ' of ' + testsLength;
827
828 var nextTest = tests.shift ();
829
830 var skipTest = false;
831 if (nextTest.depends) {
832 for (var i in nextTest.depends) {
833 var dTestId = nextTest.depends[i];
834 if (!testResults[dTestId]) {
835 document.getElementById ('skipped').firstChild.data
836 = ++skippedTestsNumber;
837 var li = document.createElement ('li');
838 li.innerHTML = '<a>xxxx</a>: skipped due to failure of <a>yy</a>';
839 li.firstChild.href = nextTest.fileName;
840 li.firstChild.title = nextTest.id;
841 li.firstChild.firstChild.data = nextTest.label || nextTest.id;
842 li.lastChild.firstChild.data = dTestId;
843 document.getElementById ('skipped-list').appendChild (li);
844
845 var i = document.createElement ('p');
846 i.innerHTML = '<input name=test-name type=hidden>' +
847 '<input name=test-label type=hidden>' +
848 '<input name=test-class type=hidden>' +
849 '<input name=test-result type=hidden>';
850 i.childNodes[0].value = nextTest.fileName;
851 i.childNodes[1].value = nextTest.label || '';
852 i.childNodes[2].value = 'SKIPPED';
853 i.childNodes[3].value = 'skipped (' + dTestId + ')';
854 form.appendChild (i);
855
856 skipTest = true;
857 break;
858 }
859 }
860 }
861 if (skipTest) {
862 return false;
863 }
864
865 currentTest = nextTest;
866 iframe.src = nextTest.fileName;
867 } else {
868 iframe.onreadystatechange = null;
869 iframe.onload = null;
870 document.getElementById ('status').firstChild.data = 'Done';
871
872 // Submission form
873 form.action = '$result_list_url$testset_id';
874
875 var i = document.createElement ('p');
876 i.innerHTML = '<input name=env-name type=hidden value="">' +
877 '<input type=submit value="Submit this result">';
878 i.firstChild.value = navigator.userAgent;
879 form.appendChild (i);
880 }
881
882 return true;
883 } // nextTest
884 </script>
885 ];
886 }
887
888 {
889 my $path = $testset_dir_name . 'wtt.js';
890 open my $file, '>:utf8', $path or die "$0: $path: $!";
891 print $file q[
892 function WttFail () {
893 //
894 } // WttFail
895
896 function WttSkip (message) {
897 this.message = message;
898 } // WttSkip
899
900 function wttGetGlobal () {
901 return window;
902 } // wttGetGlobal
903
904 function wttAssertTrue (condition, localId) {
905 if (!condition) {
906 wttSetStatus ('FAIL', localId + ' false (true expected)');
907 throw new WttFail ();
908 }
909
910 // condition is true.
911 } // wttAssertTrue
912
913
914 function wttAssertFalse (condition, localId) {
915 if (condition) {
916 wttSetStatus ('FAIL', localId + ' true (false expected)');
917 throw new WttFail ();
918 }
919
920 // condition is true.
921 } // wttAssertFalse
922
923 function wttAssertEquals (actual, expected, localId) {
924 if (actual !== expected) {
925 wttSetStatus ('FAIL', localId + ' got ' + dumpValue (actual) +
926 ' where ' + dumpValue (expected) + ' is expected');
927 throw new WttFail ();
928 }
929
930 // actual === expected
931 } // wttAssertEquals
932
933 function wttAssertDontEnum (object, propName, localId) {
934 for (var n in object) {
935 if (n === propName) {
936 wttSetStatus ('FAIL', localId + ' (DontEnum expected)');
937 throw new WttFail ();
938 }
939 }
940
941 // object[propName] is {DontEnum}.
942 } // wttAssertDontEnum
943
944 function wttAssertDontDelete (object, propName, localId) {
945 var propValue = object[propName];
946
947 if (!delete object[propName]) {
948 wttSetStatus ('FAIL', localId + ' (delete returns true)');
949 throw new WttFail ();
950 // According to ECMA 262, [[Delete]] returns false if DontDelete is set.
951 }
952
953 if (!(propName in object)) {
954 wttSetStatus ('FAIL', localId + ' (delete does delete the property)');
955 throw new WttFail ();
956 }
957
958 if (object[propName] !== propValue) {
959 wttSetStatus ('FAIL', localId + ' (delete change the value to ' +
960 dumpValue (object[propName]) + ' where ' +
961 dumpValue (propValue) + ' is expected)');
962 throw new WttFail ();
963
964 /* The WebIDL specification does not change semantics of the
965 [[Delete]] internal method from the ECMA 262 3rd edition.
966 In ECMA 262, [[Delete]] does not do anything except for returning
967 a |false| value in case the {DontDelete} attribute is set to the
968 attribute. Therefore, the property value must not be changed
969 before and after the |delete| operation, which just invokes the
970 [[Delete]] method.
971 */
972 }
973 } // wttAssertDontDelete
974
975 function wttAssertReadOnly (object, propName, localId) {
976 /*
977 Note that this function returns a wrong result when the [[Put]]
978 or [[CanPut]] method of /object/ is replaced by another steps
979 from those defined in ECMA 262. According to WebIDL spec,
980 objects conforming to that specification does not modify those
981 methods unless explicitly defined (e.g. for objects with [NamedSetter]).
982 */
983
984 var propValue = object[propName];
985
986 try {
987 object[propName] = 'abcdefg';
988 } catch (e) {
989 /*
990 According to the [[Put]] algorithms of ECMA 262 and WebIDL, assigning
991 a value to a read-only property should not raise an exception.
992 However, since testing the behavior of [[Put]] is not the purpose of
993 this test, we catch any exception thrown by the assignment.
994 Note that it might also catch any exception thrown by non-standard
995 setter extension, if any.
996 */
997 }
998 if (object[propName] === 'abcdefg') {
999 wttSetStatus ('FAIL', localId + ' (value changed)');
1000 throw new WttFail ();
1001 }
1002
1003 if (object[propName] !== propValue) {
1004 wttSetStatus ('FAIL', localId + ' (putting changes value from ' +
1005 dumpValue (propValue) + ' to ' +
1006 dumpValue (object[propName]) + ')');
1007 throw new WttFail ();
1008 }
1009
1010 // The property seems read only.
1011 } // wttAssertReadOnly
1012
1013 function wttOk () {
1014 wttSetStatus ('PASS');
1015 } // wttOk
1016
1017 function wttSetStatus (s, t) {
1018 var result = document.getElementById ('result');
1019 result.firstChild.data
1020 = s + ' (' + globalId + (t != null ? '-' + t : '') + ')';
1021 result.className = s;
1022 } // wttSetStatus
1023
1024 function dumpValue (v) {
1025 return '"' + v + '", type ' + typeof (v);
1026 } // dumpValue
1027 ];
1028
1029 print $file qq[
1030
1031 var wttInstanceInfo = $instances_input;
1032 function wttGetInstance (interface, id) {
1033 var giCodes = wttInstanceInfo[interface];
1034 if (!giCodes) {
1035 wttSetStatus ('FAIL',
1036 'broken testcase - no code for ' + interface + ' ' + id);
1037 throw new WttFail ();
1038 }
1039
1040 for (var i in giCodes) {
1041 if (giCodes[i].id == id) {
1042 var v;
1043 var message;
1044 try {
1045 v = eval (giCodes[i].code);
1046 } catch (e) {
1047 v = null;
1048 message = '' + e;
1049 }
1050 if (!v) {
1051 throw new WttSkip ('cannot obtain instance by ' + id +
1052 (message ? ' (' + message + ')' : ''));
1053 }
1054 return v;
1055 }
1056 }
1057
1058 if (!giCodes) {
1059 wttSetStatus ('FAIL',
1060 'broken testcase - no code for ' + interface + ' ' + id);
1061 throw new WttFail ();
1062 }
1063 } // wttGetInstance
1064
1065 var crashInfo = @{[JSON::objToJson ($crash_info)]};
1066 function wttCheckCrash (testId) {
1067 var entry = crashInfo[testId];
1068 if (!entry) return;
1069
1070 var ua = navigator.userAgent;
1071 for (var i = 0; i < entry.length; i++) {
1072 var reg = new RegExp (entry[i]);
1073 if (ua.match (reg)) {
1074 wttSetStatus ('FAIL',
1075 'Skipped because it is known that this test case would crash the browser in use');
1076 throw new WttFail ();
1077 }
1078 }
1079 } // wttCheckCrash
1080
1081 ] if defined $instances_input;
1082 }
1083 } # generate_support_files
1084
1085 __END__
1086
1087 =head1 NAME
1088
1089 wttjs.pl - WebIDL ECMAScript Binding Test Suite Generator
1090
1091 =head1 SYNOPSIS
1092
1093 $ perl wttjs.pl \
1094 --idl-file-name input.idl \
1095 --instances-file-name input.json \
1096 --test-dir-name output-dir/ \
1097 --testset-id testname
1098
1099 $ perl wttjs.pl --help
1100
1101 =head1 DESCRIPTION
1102
1103 The script C<wttjs.pl> generates a set of test cases for the
1104 conformance of a Web browser's DOM implementation, with regard to a
1105 set of interfaces described by a WebIDL fragment.
1106
1107 For more information, see the readme document
1108 L<http://suika.fam.cx/www/webidl2tests/readme>.
1109
1110 =head1 ARGUMENTS
1111
1112 This script accepts command-line arguments in the
1113 L<Getopt::Long|Getopt::Long> style. Any argument can be specified at
1114 most once.
1115
1116 =over 4
1117
1118 =item C<--help>
1119
1120 Show the help on the command-line arguments and exit the script.
1121
1122 =item C<--idl-file-name I<file-name.idl>> (B<REQUIRED>)
1123
1124 The name or path of the file that contains the IDL fragment that
1125 defines a set of interfaces and exceptions to be tested against.
1126
1127 If this argument is not specified, if the specified file is not found,
1128 or if the file cannot be read due to some I/O error, the script would
1129 exit with an error message.
1130
1131 The file must be encoded in UTF-8 (Perl's C<utf-8> encoding).
1132 Otherwise, the result test cases might be broken.
1133
1134 The file must contain a syntactically valid IDL fragment. Any failure
1135 to conform to the WebIDL syntax would be reported to the standard
1136 error output. Such errors are handled by the CSS-like
1137 forward-compatible parsing rule as implemented by the
1138 L<Whatpm::WebIDL|Whatpm::WebIDL> parser.
1139
1140 Though the content of the file don't have to be a conforming IDL
1141 fragment, the result test cases might be broken if it is not. It is
1142 encouraged to check the conformance of the input IDL fragment by a
1143 conformance checker, e.g. WebHACC
1144 L<http://suika.fam.cx/gate/2007/html/cc/>.
1145
1146 =item C<--instances-file-name I<file-name.json>> (Default: No instance generation)
1147
1148 The name or path of the file that contains supplement information on
1149 interfaces and exceptions defined in the IDL fragment.
1150
1151 If the specified file is not found, or if the file cannot be read due
1152 to some I/O error, the script would exit with an error message.
1153
1154 The file must be encoded in UTF-8 (Perl's C<utf-8> encoding).
1155 Otherwise, the file might be considered as broken, or the result test
1156 cases might be broken.
1157
1158 The file must contain a JSON representation of a data structure
1159 described in the readme document. If the content is not valid as
1160 JSON, then the script would exit with an error message. If the
1161 content does not encode the data structure specified in the readme
1162 document, the script might exit with a Perl script execution error.
1163 Even when no Perl error stops the script, the result test cases might
1164 be broken.
1165
1166 If this argument is not specified, then it is assumed that no
1167 additional information is available.
1168
1169 =item C<--test-dir-name I<path-to-dir/>> (Default: C<./tests/>)
1170
1171 The name or path of the directory, in which the directory for the test
1172 files is created.
1173
1174 The default value that is used when this argument is not specified is
1175 C<./tests/>.
1176
1177 All files generated by this script is put into the directory
1178 C<I<test-dir-name>/I<testset-id>/>, where I<test-dir-name> is the
1179 value specified by the C<--test-dir-name> argument and I<testset-id>
1180 is the value specified by the C<--testset-id> argument. If there is
1181 no such a directory, then it is created by the script.
1182
1183 =item C<--testset-id I<id>> (B<REQUIRED>)
1184
1185 The identifier of the test suite.
1186
1187 If this argument is not specified, the script would exit with an error
1188 message.
1189
1190 Though any value can be specified as identifier, it should be a string
1191 consist of characters C<a>..C<z>, C<0>..C<9>, and C<-> only, with no
1192 leading C<-> character, for filesystem safety and compatibility with
1193 the test result summary script (see the readme document).
1194
1195 =back
1196
1197 =head1 DEPENDENCY
1198
1199 This script, in addition to Perl 5.8.* or later, requires the
1200 following modules:
1201
1202 =over 4
1203
1204 =item L<JSON|JSON>
1205
1206 A JSON parser and serializer, which is available from CPAN.
1207
1208 To install the L<JSON|JSON> module from the CPAN, type:
1209
1210 # perl -MCPAN -eshell
1211 cpan> install JSON
1212
1213 =item L<Whatpm::WebIDL|Whatpm::WebIDL>
1214
1215 A WebIDL parser and object model implementation, which is part of the
1216 Whatpm package L<http://suika.fam.cx/www/markup/html/whatpm/readme>.
1217
1218 Note that L<Whatpm::WebIDL|Whatpm::WebIDL> does not depend on any
1219 other module.
1220
1221 =back
1222
1223 =head1 SEE ALSO
1224
1225 Readme L<http://suika.fam.cx/www/webidl2tests/readme>.
1226
1227 Web IDL specification, revision 1.96 (3 September 2008 Editor's Draft)
1228 L<http://dev.w3.org/cvsweb/~checkout~/2006/webapi/WebIDL/Overview.html?rev=1.96&content-type=text/html;%20charset=utf-8>.
1229
1230 L<Whatpm::WebIDL|Whatpm::WebIDL>, which is used to parse IDL
1231 fragments.
1232
1233 =head1 AUTHOR
1234
1235 Wakaba <w@suika.fam.cx>
1236
1237 =head1 LICENSE
1238
1239 Copyright 2008 Wakaba <w@suika.fam.cx>
1240
1241 This library is free software; you can redistribute it and/or modify
1242 it under the same terms as Perl itself.
1243
1244 =cut
1245
1246 ## $Date: 2008/10/11 08:00:26 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24