/[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.14 - (hide annotations) (download) (as text)
Tue Jan 29 22:15:01 2008 UTC (17 years, 5 months ago) by wakaba
Branch: MAIN
Changes since 1.13: +4 -1 lines
File MIME type: application/x-troff
++ whatpm/t/ChangeLog	29 Jan 2008 22:14:49 -0000
2008-01-30  Wakaba  <wakaba@suika.fam.cx>

	* css-generated.dat: New test data for 'counter-increment', 'content',
	and 'counter-reset' are added.

	* CSS-Parser-1.t: 'counter-reset' and 'counter-increment'
	are added.

++ whatpm/Whatpm/CSS/ChangeLog	29 Jan 2008 22:13:54 -0000
2008-01-30  Wakaba  <wakaba@suika.fam.cx>

	* Parser.pm ($default_serializer): New 'ADDCOUNTER'
	and 'SETCOUNTER' types supported.
	('content'): 'counter()' and 'counters()' are supported iff
	the property 'counter-reset' is supported.
	('counter-reset', 'counter-increment'): Implemented.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24