/[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.7 - (show annotations) (download) (as text)
Sat Jan 26 04:50:05 2008 UTC (18 years, 6 months ago) by wakaba
Branch: MAIN
Changes since 1.6: +2 -1 lines
File MIME type: application/x-troff
++ whatpm/t/ChangeLog	26 Jan 2008 04:49:57 -0000
	* css-font.dat: New test file.

	* CSS-Parser-1.dat: |css-font.dat| is added.

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

++ whatpm/Whatpm/CSS/ChangeLog	26 Jan 2008 04:49:36 -0000
	* Parser.pm (font serialize_shorthand): Implemented.
	($generic_font_keywords): Typo fixed.

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

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 css-visual.dat
22 css-generated.dat
23 css-font.dat
24 )) {
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 } elsif (/^#errors$/) {
53 $test->{errors} = [];
54 $mode = 'errors';
55 $test->{data} =~ s/\x0D?\x0A\z//;
56 } elsif (/^#option q$/) {
57 $test->{option}->{parse_mode} = 'q';
58 } 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 } elsif ($mode eq 'errors') {
66 tr/\x0D\x0A//d;
67 push @{$test->{errors}}, $_;
68 } 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 my ($p, $css_options) = get_parser ($test->{option}->{parse_mode});
87
88 my @actual_error;
89 $p->{onerror} = sub {
90 my (%opt) = @_;
91 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 };
98
99 my $ss = $p->parse_char_string ($test->{data});
100
101 ok ((join "\n", @actual_error), (join "\n", @{$test->{errors} or []}),
102 "#result ($test->{data})");
103
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 margin: 0px;
208 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 padding: 0px;
217 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 | background-position-x: 0%
247 | background-position-y: 0%
248 | background-repeat: repeat
249 | border-top: 0px none -manakai-default
250 | border-right: 0px none -manakai-default
251 | border-bottom: 0px none -manakai-default
252 | border-left: 0px none -manakai-default
253 | border-bottom-color: -manakai-default
254 | border-bottom-style: none
255 | border-bottom-width: 0px
256 | border-left-color: -manakai-default
257 | border-left-style: none
258 | border-left-width: 0px
259 | border-right-color: -manakai-default
260 | border-right-style: none
261 | border-right-width: 0px
262 | border-top-color: -manakai-default
263 | border-top-style: none
264 | border-top-width: 0px
265 | border-color: -manakai-default
266 | border-style: none
267 | border-width: 0px
268 | float: none
269 | font: 400 16px -manakai-default
270 | list-style: disc none outside
271 | margin-top: 0px
272 | margin-right: 0px
273 | margin-bottom: 0px
274 | margin-left: 0px
275 | outline-color: invert
276 | outline-style: none
277 | outline-width: 0px
278 | padding-bottom: 0px
279 | padding-left: 0px
280 | padding-right: 0px
281 | padding-top: 0px];
282 }
283
284 sub get_parser ($) {
285 my $parse_mode = shift;
286
287 my $p = Whatpm::CSS::Parser->new;
288
289 if ($parse_mode eq 'q') {
290 $p->{unitless_px} = 1;
291 $p->{hashless_color} = 1;
292 }
293
294 $p->{prop}->{$_} = 1 for (@longhand, @shorthand);
295 $p->{prop_value}->{display}->{$_} = 1 for qw/
296 block inline inline-block inline-table list-item none
297 table table-caption table-cell table-column table-column-group
298 table-header-group table-footer-group table-row table-row-group
299 /;
300 $p->{prop_value}->{position}->{$_} = 1 for qw/
301 absolute fixed relative static
302 /;
303 $p->{prop_value}->{float}->{$_} = 1 for qw/
304 left right none
305 /;
306 $p->{prop_value}->{clear}->{$_} = 1 for qw/
307 left right none both
308 /;
309 $p->{prop_value}->{direction}->{ltr} = 1;
310 $p->{prop_value}->{direction}->{rtl} = 1;
311 $p->{prop_value}->{'unicode-bidi'}->{$_} = 1 for qw/
312 normal bidi-override embed
313 /;
314 $p->{prop_value}->{overflow}->{$_} = 1 for qw/
315 visible hidden scroll auto
316 /;
317 $p->{prop_value}->{visibility}->{$_} = 1 for qw/
318 visible hidden collapse
319 /;
320 $p->{prop_value}->{'list-style-type'}->{$_} = 1 for qw/
321 disc circle square decimal decimal-leading-zero
322 lower-roman upper-roman lower-greek lower-latin
323 upper-latin armenian georgian lower-alpha upper-alpha none
324 /;
325 $p->{prop_value}->{'list-style-position'}->{outside} = 1;
326 $p->{prop_value}->{'list-style-position'}->{inside} = 1;
327 $p->{prop_value}->{'page-break-before'}->{$_} = 1 for qw/
328 auto always avoid left right
329 /;
330 $p->{prop_value}->{'page-break-after'}->{$_} = 1 for qw/
331 auto always avoid left right
332 /;
333 $p->{prop_value}->{'page-break-inside'}->{auto} = 1;
334 $p->{prop_value}->{'page-break-inside'}->{avoid} = 1;
335 $p->{prop_value}->{'background-repeat'}->{$_} = 1 for qw/
336 repeat repeat-x repeat-y no-repeat
337 /;
338 $p->{prop_value}->{'background-attachment'}->{scroll} = 1;
339 $p->{prop_value}->{'background-attachment'}->{fixed} = 1;
340 $p->{prop_value}->{'font-style'}->{normal} = 1;
341 $p->{prop_value}->{'font-style'}->{italic} = 1;
342 $p->{prop_value}->{'font-style'}->{oblique} = 1;
343 $p->{prop_value}->{'font-variant'}->{normal} = 1;
344 $p->{prop_value}->{'font-variant'}->{'small-caps'} = 1;
345 $p->{prop_value}->{'text-align'}->{$_} = 1 for qw/
346 left right center justify begin end
347 /;
348 $p->{prop_value}->{'text-transform'}->{$_} = 1 for qw/
349 capitalize uppercase lowercase none
350 /;
351 $p->{prop_value}->{'white-space'}->{$_} = 1 for qw/
352 normal pre nowrap pre-line pre-wrap
353 /;
354 $p->{prop_value}->{'text-decoration'}->{$_} = 1 for qw/
355 none blink underline overline line-through
356 /;
357 $p->{prop_value}->{'caption-side'}->{$_} = 1 for qw/
358 top bottom
359 /;
360 $p->{prop_value}->{'table-layout'}->{auto} = 1;
361 $p->{prop_value}->{'table-layout'}->{fixed} = 1;
362 $p->{prop_value}->{'border-collapse'}->{collapase} = 1;
363 $p->{prop_value}->{'border-collapse'}->{separate} = 1;
364 $p->{prop_value}->{'empty-cells'}->{show} = 1;
365 $p->{prop_value}->{'empty-cells'}->{hide} = 1;
366 $p->{prop_value}->{cursor}->{$_} = 1 for qw/
367 auto crosshair default pointer move e-resize ne-resize nw-resize n-resize
368 se-resize sw-resize s-resize w-resize text wait help progress
369 /;
370 for my $prop (qw/border-top-style border-left-style
371 border-bottom-style border-right-style outline-style/) {
372 $p->{prop_value}->{$prop}->{$_} = 1 for qw/
373 none hidden dotted dashed solid double groove ridge inset outset
374 /;
375 }
376 for my $prop (qw/color background-color
377 border-bottom-color border-left-color border-right-color
378 border-top-color border-color/) {
379 $p->{prop_value}->{$prop}->{transparent} = 1;
380 $p->{prop_value}->{$prop}->{flavor} = 1;
381 $p->{prop_value}->{$prop}->{'-manakai-default'} = 1;
382 }
383 $p->{prop_value}->{'outline-color'}->{invert} = 1;
384 $p->{prop_value}->{'outline-color'}->{'-manakai-invert-or-currentcolor'} = 1;
385 $p->{pseudo_class}->{$_} = 1 for qw/
386 active checked disabled empty enabled first-child first-of-type
387 focus hover indeterminate last-child last-of-type link only-child
388 only-of-type root target visited
389 lang nth-child nth-last-child nth-of-type nth-last-of-type not
390 -manakai-contains -manakai-current
391 /;
392 $p->{pseudo_element}->{$_} = 1 for qw/
393 after before first-letter first-line
394 /;
395
396 my $css_options = {
397 prop => $p->{prop},
398 prop_value => $p->{prop_value},
399 pseudo_class => $p->{pseudo_class},
400 pseudo_element => $p->{pseudo_element},
401 };
402
403 $p->{href} = 'thismessage:/';
404
405 return ($p, $css_options);
406 } # get_parser
407
408 sub serialize_cssom ($) {
409 my $ss = shift;
410
411 if (defined $ss) {
412 if ($ss->isa ('Message::IF::CSSStyleSheet')) {
413 my $v = '';
414 for my $rule (@{$ss->css_rules}) {
415 my $indent = '';
416 if ($rule->type == $rule->STYLE_RULE) {
417 $v .= '| ' . $indent . '<' . $rule->selector_text . ">\n";
418 $v .= serialize_style ($rule->style, $indent . ' ');
419 } else {
420 die "Rule type @{[$rule->type]} is not supported";
421 }
422 }
423 return $v;
424 } else {
425 return '(' . (ref $ss) . ')';
426 }
427 } else {
428 return '(undef)';
429 }
430 } # serialize_cssom
431
432 sub get_computed_style ($$$$$$) {
433 my ($all_test, $doc_id, $selectors, $dom, $css_options, $ss) = @_;
434
435 my $doc = $all_test->{document}->{$doc_id}->{document};
436 unless ($doc) {
437 die "Test document $doc_id is not defined";
438 }
439
440 my $element = $doc->query_selector ($selectors);
441 unless ($element) {
442 die "Element $selectors not found in document $doc_id";
443 }
444
445 my $window = Message::DOM::Window->___new ($dom);
446 $window->___set_css_options ($css_options);
447 $window->___set_user_style_sheets ([$ss]);
448 $window->set_document ($doc);
449
450 my $style = $element->manakai_computed_style;
451 return ($window, $style);
452 } # get_computed_style
453
454 sub serialize_style ($$) {
455 my ($style, $indent) = @_;
456
457 ## TODO: check @$style
458
459 my @v;
460 for (map {get_dom_names ($_)} @shorthand, @longhand) {
461 my $dom = $_->[1];
462 push @v, [$_->[0], $dom, $style->$dom,
463 $style->get_property_priority ($_->[0])];
464 $v[-1]->[3] = ' !' . $v[-1]->[3] if length $v[-1]->[3];
465 }
466 return join '', map {"| $indent$_->[0]: $_->[2]$_->[3]\n"}
467 sort {$a->[0] cmp $b->[0]} grep {length $_->[2]} @v;
468 } # serialize_style
469
470 sub get_dom_names ($) {
471 my $dom_name = $_[0];
472 if ($_[0] eq 'float') {
473 return ([$_[0] => 'css_float'], [$_[0] => 'style_float']);
474 }
475
476 $dom_name =~ tr/-/_/;
477 return ([$_[0] => $dom_name]);
478 } # get_dom_names
479
480 sub apply_diff ($$$) {
481 my ($actual, $expected, $diff) = @_;
482 my @actual = split /[\x0D\x0A]+/, $actual;
483 my @expected = split /[\x0D\x0A]+/, $expected;
484 my @diff = split /[\x0D\x0A]+/, $diff;
485 for (@diff) {
486 if (s/^-//) {
487 push @actual, $_;
488 } elsif (s/^\+//) {
489 push @expected, $_;
490 } else {
491 die "Invalid diff line: $_";
492 }
493 }
494 $actual = join "\n", sort {$a cmp $b} @actual;
495 $expected = join "\n", sort {$a cmp $b} @expected;
496 ($actual, $expected);
497 } # apply_diff

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24