/[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.18 - (hide annotations) (download) (as text)
Sun Feb 10 07:34:10 2008 UTC (17 years, 5 months ago) by wakaba
Branch: MAIN
Changes since 1.17: +8 -2 lines
File MIME type: application/x-troff
++ whatpm/t/ChangeLog	10 Feb 2008 07:34:02 -0000
2008-02-10  Wakaba  <wakaba@suika.fam.cx>

	* css-3.dat: New test file.

	* CSS-Parser-1.t: |css-3.dat| is added.  '-moz-pre-wrap' added.
	Typo for 'collapse' value fixed.  Support for '@namespace'
	serialization.

	* css-1.dat: New test data for selectors parsing.

	* css-font.dat: Error locations for 'u' errors are corrected.

	* css-table.dat: Test data for 'border-collapse' are added.

	* css-text.dat: Test data for '-moz-pre-wrap' are added.

	* css-visual.dat: Test data for 'background-position' are added.

++ whatpm/Whatpm/Charset/ChangeLog	10 Feb 2008 07:31:32 -0000
2008-02-10  Wakaba  <wakaba@suika.fam.cx>

	* CharDet.pm (detect): Catch an error in detection function.

++ whatpm/Whatpm/CSS/ChangeLog	10 Feb 2008 07:30:34 -0000
2008-02-10  Wakaba  <wakaba@suika.fam.cx>

	* Parser.pm: |attr(prefix|localname)| did not work.  Support
	for uppercase namespace prefixes (namespace prefixes were
	not normalized into lowercase when they were looked up.).  Report
	the property name token when an unknown property error is raised.
	The '-moz-pre-wrap' value is supported for 'white-space' property.
	('background-position'): 'center left' and 'center right'
	were not supported.

	* SelectorsParser.pm: Report the namespace prefix token when
	an undeclared prefix error is raised.  S_TOKEN in
	COMBINATOR_STATE was not handled correctly.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24