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

	* css-1.dat: Basic tests for forward compatible parsing
	are added.

	* CSS-Parser-1.t: Support for |#errors| validation.

++ whatpm/Whatpm/CSS/ChangeLog	22 Jan 2008 12:44:31 -0000
2008-01-22  Wakaba  <wakaba@suika.fam.cx>

	* Parser.pm (new {onerror}): The default error handler (outputting
	to the standard error output) is defined.
	(parse_char_string {get_char}): Set the next number to the
	column number of the last character as the column of the
	end of file pseudo-character.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24