/[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.8 - (hide annotations) (download) (as text)
Sat Jan 26 11:18:40 2008 UTC (17 years, 5 months ago) by wakaba
Branch: MAIN
Changes since 1.7: +1 -0 lines
File MIME type: application/x-troff
++ whatpm/t/ChangeLog	26 Jan 2008 11:18:34 -0000
	* CSS-Parser-1.t: 'background-position' was missing
	from the list of default values.

	* css-visual.dat: New test data for 'background'
	and 'background-position' are added.

2008-01-26  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/CSS/ChangeLog	26 Jan 2008 11:17:46 -0000
	* Parser.pm ('background' serialize_multiple, 'background-position'
	serialize_shorthand): Reimplemented taking 'inherit'
	and 'important' into account.
	('background' parse): Support for '+'.  Correct initial value
	for 'background-position-y' was not set in some cases.
	Wrong value was set to 'background-position-x' in some case.
	Did not return by some syntax errors.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24