/[suikacvs]/markup/html/whatpm/t/CSS-Parser-1.t
Suika

Contents of /markup/html/whatpm/t/CSS-Parser-1.t

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.17 - (hide annotations) (download) (as text)
Fri Feb 8 15:05:56 2008 UTC (17 years, 5 months ago) by wakaba
Branch: MAIN
Changes since 1.16: +17 -6 lines
File MIME type: application/x-troff
++ whatpm/t/ChangeLog	8 Feb 2008 15:05:50 -0000
	* css-2.dat: New test data.

	* CSS-Parser-1.t: |css-2.dat| added.  Support for |@media|.

2008-02-08  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/CSS/ChangeLog	8 Feb 2008 15:05:02 -0000
	* Parser.pm: Support for '@nedia' at-rule.

2008-02-08  Wakaba  <wakaba@suika.fam.cx>

1 wakaba 1.1 #!/usr/bin/perl
2     use strict;
3    
4     use lib qw[/home/wakaba/work/manakai2/lib]; ## TODO: ...
5    
6     use Test;
7    
8     BEGIN { plan tests => 548 }
9    
10     require Whatpm::CSS::Parser;
11     require Message::DOM::Window;
12    
13     require Message::DOM::DOMImplementation;
14     my $dom = Message::DOM::DOMImplementation->new;
15    
16     my $DefaultComputed;
17     my $DefaultComputedText;
18    
19     for my $file_name (map {"t/$_"} qw(
20     css-1.dat
21 wakaba 1.17 css-2.dat
22 wakaba 1.4 css-visual.dat
23 wakaba 1.6 css-generated.dat
24 wakaba 1.9 css-paged.dat
25     css-text.dat
26 wakaba 1.7 css-font.dat
27 wakaba 1.10 css-table.dat
28     css-interactive.dat
29 wakaba 1.1 )) {
30     print "# $file_name\n";
31 wakaba 1.13 open my $file, '<:utf8', $file_name or die "$0: $file_name: $!";
32 wakaba 1.1
33     my $all_test = {document => {}, test => []};
34     my $test;
35     my $mode = 'data';
36     my $doc_id;
37     my $selectors;
38     while (<$file>) {
39     s/\x0D\x0A/\x0A/;
40     if (/^#data$/) {
41     undef $test;
42     $test->{data} = '';
43     push @{$all_test->{test}}, $test;
44     $mode = 'data';
45     } elsif (/#(csstext|cssom)$/) {
46     $test->{$1} = '';
47     $mode = $1;
48     } elsif (/#(computed(?>text)?) (\S+) (.+)$/) {
49     $test->{$1}->{$doc_id = $2}->{$selectors = $3} = '';
50     $mode = $1;
51     } elsif (/^#html (\S+)$/) {
52     undef $test;
53     $test->{format} = 'html';
54     $test->{data} = '';
55     $all_test->{document}->{$1} = $test;
56     $mode = 'data';
57 wakaba 1.2 } elsif (/^#errors$/) {
58     $test->{errors} = [];
59     $mode = 'errors';
60     $test->{data} =~ s/\x0D?\x0A\z//;
61 wakaba 1.4 } elsif (/^#option q$/) {
62     $test->{option}->{parse_mode} = 'q';
63 wakaba 1.1 } elsif (defined $test->{data} and /^$/) {
64     undef $test;
65     } else {
66     if ({data => 1, cssom => 1, csstext => 1}->{$mode}) {
67     $test->{$mode} .= $_;
68     } elsif ($mode eq 'computed' or $mode eq 'computedtext') {
69     $test->{$mode}->{$doc_id}->{$selectors} .= $_;
70 wakaba 1.2 } elsif ($mode eq 'errors') {
71     tr/\x0D\x0A//d;
72     push @{$test->{errors}}, $_;
73 wakaba 1.1 } else {
74     die "Line $.: $_";
75     }
76     }
77     }
78    
79     for my $data (values %{$all_test->{document}}) {
80     if ($data->{format} eq 'html') {
81     my $doc = $dom->create_document;
82     $doc->manakai_is_html (1);
83     $doc->inner_html ($data->{data});
84     $data->{document} = $doc;
85     } else {
86     die "Test data format $data->{format} is not supported";
87     }
88     }
89    
90     for my $test (@{$all_test->{test}}) {
91 wakaba 1.4 my ($p, $css_options) = get_parser ($test->{option}->{parse_mode});
92 wakaba 1.1
93 wakaba 1.2 my @actual_error;
94 wakaba 1.1 $p->{onerror} = sub {
95     my (%opt) = @_;
96 wakaba 1.2 my $uri = ${$opt{uri}};
97     $uri =~ s[^thismessage:/][];
98     push @actual_error, join ';',
99     $uri, $opt{token}->{line}, $opt{token}->{column},
100     $opt{level},
101 wakaba 1.16 $opt{type} . (defined $opt{value} ? ';'.$opt{value} : '');
102 wakaba 1.1 };
103    
104     my $ss = $p->parse_char_string ($test->{data});
105 wakaba 1.2
106     ok ((join "\n", @actual_error), (join "\n", @{$test->{errors} or []}),
107     "#result ($test->{data})");
108 wakaba 1.1
109     if (defined $test->{cssom}) {
110     my $actual = serialize_cssom ($ss);
111     ok $actual, $test->{cssom}, "#cssom ($test->{data})";
112     }
113    
114     if (defined $test->{csstext}) {
115     my $actual = $ss->css_text;
116     ok $actual, $test->{csstext}, "#csstext ($test->{data})";
117     }
118    
119     for my $doc_id (keys %{$test->{computed} or {}}) {
120     for my $selectors (keys %{$test->{computed}->{$doc_id}}) {
121     my ($window, $style) = get_computed_style
122     ($all_test, $doc_id, $selectors, $dom, $css_options, $ss);
123     ## NOTE: $window is the root object, so that we must keep it
124     ## referenced in this block.
125    
126     my $actual = serialize_style ($style, '');
127     my $expected = $DefaultComputed;
128     my $diff = $test->{computed}->{$doc_id}->{$selectors};
129     ($actual, $expected) = apply_diff ($actual, $expected, $diff);
130     ok $actual, $expected,
131     "#computed $doc_id $selectors ($test->{data})";
132     }
133     }
134    
135     for my $doc_id (keys %{$test->{computedtext} or {}}) {
136     for my $selectors (keys %{$test->{computedtext}->{$doc_id}}) {
137     my ($window, $style) = get_computed_style
138     ($all_test, $doc_id, $selectors, $dom, $css_options, $ss);
139     ## NOTE: $window is the root object, so that we must keep it
140     ## referenced in this block.
141    
142     my $actual = $style->css_text;
143     my $expected = $DefaultComputedText;
144     my $diff = $test->{computedtext}->{$doc_id}->{$selectors};
145     ($actual, $expected) = apply_diff ($actual, $expected, $diff);
146     "#computedtext $doc_id $selectors ($test->{data})";
147     ok $actual, $expected,
148     "#computedtext $doc_id $selectors ($test->{data})";
149     }
150     }
151     }
152     }
153    
154     my @longhand;
155     my @shorthand;
156     BEGIN {
157     @longhand = qw/
158     background-attachment background-color background-image
159     background-position-x background-position-y
160     background-repeat border-bottom-color
161     border-bottom-style border-bottom-width border-collapse
162     border-left-color
163     border-left-style border-left-width border-right-color
164     border-right-style border-right-width
165     -manakai-border-spacing-x -manakai-border-spacing-y
166     border-top-color border-top-style border-top-width bottom
167 wakaba 1.15 caption-side clear clip color content counter-increment counter-reset
168 wakaba 1.14 cursor direction display empty-cells float
169 wakaba 1.16 font-family font-size font-size-adjust font-stretch
170     font-style font-variant font-weight height left
171 wakaba 1.1 letter-spacing line-height
172     list-style-image list-style-position list-style-type
173 wakaba 1.16 margin-bottom margin-left margin-right margin-top marker-offset
174     marks max-height max-width min-height min-width opacity -moz-opacity
175 wakaba 1.11 orphans outline-color outline-style outline-width overflow-x overflow-y
176 wakaba 1.1 padding-bottom padding-left padding-right padding-top
177 wakaba 1.16 page page-break-after page-break-before page-break-inside
178     position quotes right size table-layout
179 wakaba 1.1 text-align text-decoration text-indent text-transform
180     top unicode-bidi vertical-align visibility white-space width widows
181     word-spacing z-index
182     /;
183     @shorthand = qw/
184     background background-position
185     border border-color border-style border-width border-spacing
186     border-top border-right border-bottom border-left
187 wakaba 1.11 font list-style margin outline overflow padding
188 wakaba 1.1 /;
189     $DefaultComputedText = q[ border-spacing: 0px;
190     background: transparent none repeat scroll 0% 0%;
191     border: 0px none -manakai-default;
192     border-collapse: separate;
193     bottom: auto;
194     caption-side: top;
195     clear: none;
196 wakaba 1.15 clip: auto;
197 wakaba 1.1 color: -manakai-default;
198 wakaba 1.13 content: normal;
199 wakaba 1.14 counter-increment: none;
200     counter-reset: none;
201 wakaba 1.1 cursor: auto;
202     direction: ltr;
203     display: inline;
204     empty-cells: show;
205     float: none;
206     font-family: -manakai-default;
207     font-size: 16px;
208 wakaba 1.16 font-size-adjust: none;
209     font-stretch: normal;
210 wakaba 1.1 font-style: normal;
211     font-variant: normal;
212     font-weight: 400;
213     height: auto;
214     left: auto;
215     letter-spacing: normal;
216     line-height: normal;
217     list-style-image: none;
218     list-style-position: outside;
219     list-style-type: disc;
220 wakaba 1.5 margin: 0px;
221 wakaba 1.16 marker-offset: auto;
222     marks: none;
223 wakaba 1.1 max-height: none;
224     max-width: none;
225     min-height: 0px;
226     min-width: 0px;
227     opacity: 1;
228     orphans: 2;
229     outline: 0px none invert;
230     overflow: visible;
231 wakaba 1.6 padding: 0px;
232 wakaba 1.16 page: auto;
233 wakaba 1.1 page-break-after: auto;
234     page-break-before: auto;
235     page-break-inside: auto;
236     position: static;
237 wakaba 1.12 quotes: -manakai-default;
238 wakaba 1.1 right: auto;
239 wakaba 1.16 size: auto;
240 wakaba 1.1 table-layout: auto;
241     text-align: begin;
242     text-decoration: none;
243     text-indent: 0px;
244     text-transform: none;
245     top: auto;
246     unicode-bidi: normal;
247     vertical-align: baseline;
248     visibility: visible;
249     white-space: normal;
250     widows: 2;
251     width: auto;
252     word-spacing: normal;
253     z-index: auto;
254     ];
255     $DefaultComputed = $DefaultComputedText;
256     $DefaultComputed =~ s/^ /| /gm;
257     $DefaultComputed =~ s/;$//gm;
258     $DefaultComputed .= q[| -manakai-border-spacing-x: 0px
259     | -manakai-border-spacing-y: 0px
260     | -moz-opacity: 1
261     | background-attachment: scroll
262     | background-color: transparent
263     | background-image: none
264 wakaba 1.8 | background-position: 0% 0%
265 wakaba 1.1 | background-position-x: 0%
266     | background-position-y: 0%
267     | background-repeat: repeat
268     | border-top: 0px none -manakai-default
269     | border-right: 0px none -manakai-default
270     | border-bottom: 0px none -manakai-default
271     | border-left: 0px none -manakai-default
272     | border-bottom-color: -manakai-default
273     | border-bottom-style: none
274     | border-bottom-width: 0px
275     | border-left-color: -manakai-default
276     | border-left-style: none
277     | border-left-width: 0px
278     | border-right-color: -manakai-default
279     | border-right-style: none
280     | border-right-width: 0px
281     | border-top-color: -manakai-default
282     | border-top-style: none
283     | border-top-width: 0px
284     | border-color: -manakai-default
285     | border-style: none
286     | border-width: 0px
287     | float: none
288 wakaba 1.7 | font: 400 16px -manakai-default
289 wakaba 1.6 | list-style: disc none outside
290 wakaba 1.5 | margin-top: 0px
291     | margin-right: 0px
292     | margin-bottom: 0px
293     | margin-left: 0px
294 wakaba 1.1 | outline-color: invert
295     | outline-style: none
296     | outline-width: 0px
297 wakaba 1.11 | overflow-x: visible
298     | overflow-y: visible
299 wakaba 1.6 | padding-bottom: 0px
300     | padding-left: 0px
301     | padding-right: 0px
302     | padding-top: 0px];
303 wakaba 1.1 }
304    
305 wakaba 1.4 sub get_parser ($) {
306     my $parse_mode = shift;
307    
308 wakaba 1.1 my $p = Whatpm::CSS::Parser->new;
309 wakaba 1.4
310     if ($parse_mode eq 'q') {
311     $p->{unitless_px} = 1;
312     $p->{hashless_color} = 1;
313     }
314 wakaba 1.1
315     $p->{prop}->{$_} = 1 for (@longhand, @shorthand);
316     $p->{prop_value}->{display}->{$_} = 1 for qw/
317 wakaba 1.15 block clip inline inline-block inline-table list-item none
318 wakaba 1.1 table table-caption table-cell table-column table-column-group
319     table-header-group table-footer-group table-row table-row-group
320 wakaba 1.16 compact marker
321 wakaba 1.1 /;
322     $p->{prop_value}->{position}->{$_} = 1 for qw/
323     absolute fixed relative static
324     /;
325     $p->{prop_value}->{float}->{$_} = 1 for qw/
326     left right none
327     /;
328     $p->{prop_value}->{clear}->{$_} = 1 for qw/
329     left right none both
330     /;
331     $p->{prop_value}->{direction}->{ltr} = 1;
332     $p->{prop_value}->{direction}->{rtl} = 1;
333 wakaba 1.16 $p->{prop_value}->{marks}->{crop} = 1;
334     $p->{prop_value}->{marks}->{cross} = 1;
335 wakaba 1.1 $p->{prop_value}->{'unicode-bidi'}->{$_} = 1 for qw/
336     normal bidi-override embed
337     /;
338 wakaba 1.11 for my $prop_name (qw/overflow overflow-x overflow-y/) {
339     $p->{prop_value}->{$prop_name}->{$_} = 1 for qw/
340     visible hidden scroll auto -webkit-marquee -moz-hidden-unscrollable
341     /;
342     }
343 wakaba 1.1 $p->{prop_value}->{visibility}->{$_} = 1 for qw/
344     visible hidden collapse
345     /;
346     $p->{prop_value}->{'list-style-type'}->{$_} = 1 for qw/
347     disc circle square decimal decimal-leading-zero
348     lower-roman upper-roman lower-greek lower-latin
349     upper-latin armenian georgian lower-alpha upper-alpha none
350 wakaba 1.16 hebrew cjk-ideographic hiragana katakana hiragana-iroha
351     katakana-iroha
352 wakaba 1.1 /;
353     $p->{prop_value}->{'list-style-position'}->{outside} = 1;
354     $p->{prop_value}->{'list-style-position'}->{inside} = 1;
355     $p->{prop_value}->{'page-break-before'}->{$_} = 1 for qw/
356     auto always avoid left right
357     /;
358     $p->{prop_value}->{'page-break-after'}->{$_} = 1 for qw/
359     auto always avoid left right
360     /;
361     $p->{prop_value}->{'page-break-inside'}->{auto} = 1;
362     $p->{prop_value}->{'page-break-inside'}->{avoid} = 1;
363     $p->{prop_value}->{'background-repeat'}->{$_} = 1 for qw/
364     repeat repeat-x repeat-y no-repeat
365     /;
366     $p->{prop_value}->{'background-attachment'}->{scroll} = 1;
367     $p->{prop_value}->{'background-attachment'}->{fixed} = 1;
368 wakaba 1.16 $p->{prop_value}->{'font-size'}->{$_} = 1 for qw/
369     xx-small x-small small medium large x-large xx-large
370     -manakai-xxx-large -webkit-xxx-large
371     larger smaller
372     /;
373 wakaba 1.1 $p->{prop_value}->{'font-style'}->{normal} = 1;
374     $p->{prop_value}->{'font-style'}->{italic} = 1;
375     $p->{prop_value}->{'font-style'}->{oblique} = 1;
376     $p->{prop_value}->{'font-variant'}->{normal} = 1;
377     $p->{prop_value}->{'font-variant'}->{'small-caps'} = 1;
378 wakaba 1.16 $p->{prop_value}->{'font-stretch'}->{$_} = 1 for
379     qw/normal wider narrower ultra-condensed extra-condensed
380     condensed semi-condensed semi-expanded expanded
381     extra-expanded ultra-expanded/;
382 wakaba 1.1 $p->{prop_value}->{'text-align'}->{$_} = 1 for qw/
383     left right center justify begin end
384     /;
385     $p->{prop_value}->{'text-transform'}->{$_} = 1 for qw/
386     capitalize uppercase lowercase none
387     /;
388     $p->{prop_value}->{'white-space'}->{$_} = 1 for qw/
389     normal pre nowrap pre-line pre-wrap
390     /;
391     $p->{prop_value}->{'text-decoration'}->{$_} = 1 for qw/
392     none blink underline overline line-through
393     /;
394     $p->{prop_value}->{'caption-side'}->{$_} = 1 for qw/
395 wakaba 1.16 top bottom left right
396 wakaba 1.1 /;
397     $p->{prop_value}->{'table-layout'}->{auto} = 1;
398     $p->{prop_value}->{'table-layout'}->{fixed} = 1;
399     $p->{prop_value}->{'border-collapse'}->{collapase} = 1;
400     $p->{prop_value}->{'border-collapse'}->{separate} = 1;
401     $p->{prop_value}->{'empty-cells'}->{show} = 1;
402     $p->{prop_value}->{'empty-cells'}->{hide} = 1;
403     $p->{prop_value}->{cursor}->{$_} = 1 for qw/
404     auto crosshair default pointer move e-resize ne-resize nw-resize n-resize
405     se-resize sw-resize s-resize w-resize text wait help progress
406     /;
407     for my $prop (qw/border-top-style border-left-style
408     border-bottom-style border-right-style outline-style/) {
409     $p->{prop_value}->{$prop}->{$_} = 1 for qw/
410     none hidden dotted dashed solid double groove ridge inset outset
411     /;
412     }
413     for my $prop (qw/color background-color
414     border-bottom-color border-left-color border-right-color
415     border-top-color border-color/) {
416     $p->{prop_value}->{$prop}->{transparent} = 1;
417     $p->{prop_value}->{$prop}->{flavor} = 1;
418     $p->{prop_value}->{$prop}->{'-manakai-default'} = 1;
419     }
420     $p->{prop_value}->{'outline-color'}->{invert} = 1;
421     $p->{prop_value}->{'outline-color'}->{'-manakai-invert-or-currentcolor'} = 1;
422     $p->{pseudo_class}->{$_} = 1 for qw/
423     active checked disabled empty enabled first-child first-of-type
424     focus hover indeterminate last-child last-of-type link only-child
425     only-of-type root target visited
426     lang nth-child nth-last-child nth-of-type nth-last-of-type not
427     -manakai-contains -manakai-current
428     /;
429     $p->{pseudo_element}->{$_} = 1 for qw/
430     after before first-letter first-line
431     /;
432    
433     my $css_options = {
434     prop => $p->{prop},
435     prop_value => $p->{prop_value},
436     pseudo_class => $p->{pseudo_class},
437     pseudo_element => $p->{pseudo_element},
438     };
439    
440     $p->{href} = 'thismessage:/';
441    
442     return ($p, $css_options);
443     } # get_parser
444    
445     sub serialize_cssom ($) {
446     my $ss = shift;
447    
448     if (defined $ss) {
449     if ($ss->isa ('Message::IF::CSSStyleSheet')) {
450     my $v = '';
451     for my $rule (@{$ss->css_rules}) {
452     my $indent = '';
453 wakaba 1.17 $v .= serialize_rule ($rule, $indent);
454 wakaba 1.1 }
455     return $v;
456     } else {
457     return '(' . (ref $ss) . ')';
458     }
459     } else {
460     return '(undef)';
461     }
462     } # serialize_cssom
463 wakaba 1.17
464     sub serialize_rule ($$) {
465     my ($rule, $indent) = @_;
466     my $v = '';
467     if ($rule->type == $rule->STYLE_RULE) {
468     $v .= '| ' . $indent . '<' . $rule->selector_text . ">\n";
469     $v .= serialize_style ($rule->style, $indent . ' ');
470     } elsif ($rule->type == $rule->MEDIA_RULE) {
471     $v .= '| ' . $indent . '@media ' . $rule->media . "\n";
472     $v .= serialize_rule ($_, $indent . ' ') for @{$rule->css_rules};
473     } else {
474     die "Rule type @{[$rule->type]} is not supported";
475     }
476     return $v;
477     } # serialize_rule
478 wakaba 1.1
479     sub get_computed_style ($$$$$$) {
480     my ($all_test, $doc_id, $selectors, $dom, $css_options, $ss) = @_;
481    
482     my $doc = $all_test->{document}->{$doc_id}->{document};
483     unless ($doc) {
484     die "Test document $doc_id is not defined";
485     }
486    
487 wakaba 1.6 my $element = $doc->query_selector ($selectors);
488 wakaba 1.1 unless ($element) {
489     die "Element $selectors not found in document $doc_id";
490     }
491    
492     my $window = Message::DOM::Window->___new ($dom);
493     $window->___set_css_options ($css_options);
494     $window->___set_user_style_sheets ([$ss]);
495     $window->set_document ($doc);
496    
497 wakaba 1.3 my $style = $element->manakai_computed_style;
498 wakaba 1.1 return ($window, $style);
499     } # get_computed_style
500    
501     sub serialize_style ($$) {
502     my ($style, $indent) = @_;
503    
504     ## TODO: check @$style
505    
506     my @v;
507     for (map {get_dom_names ($_)} @shorthand, @longhand) {
508     my $dom = $_->[1];
509     push @v, [$_->[0], $dom, $style->$dom,
510     $style->get_property_priority ($_->[0])];
511     $v[-1]->[3] = ' !' . $v[-1]->[3] if length $v[-1]->[3];
512     }
513     return join '', map {"| $indent$_->[0]: $_->[2]$_->[3]\n"}
514     sort {$a->[0] cmp $b->[0]} grep {length $_->[2]} @v;
515     } # serialize_style
516    
517     sub get_dom_names ($) {
518     my $dom_name = $_[0];
519     if ($_[0] eq 'float') {
520     return ([$_[0] => 'css_float'], [$_[0] => 'style_float']);
521     }
522    
523     $dom_name =~ tr/-/_/;
524     return ([$_[0] => $dom_name]);
525     } # get_dom_names
526    
527     sub apply_diff ($$$) {
528     my ($actual, $expected, $diff) = @_;
529     my @actual = split /[\x0D\x0A]+/, $actual;
530     my @expected = split /[\x0D\x0A]+/, $expected;
531     my @diff = split /[\x0D\x0A]+/, $diff;
532     for (@diff) {
533     if (s/^-//) {
534     push @actual, $_;
535     } elsif (s/^\+//) {
536     push @expected, $_;
537     } else {
538     die "Invalid diff line: $_";
539     }
540     }
541     $actual = join "\n", sort {$a cmp $b} @actual;
542     $expected = join "\n", sort {$a cmp $b} @expected;
543     ($actual, $expected);
544     } # apply_diff

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24