File Coverage

blib/lib/OPM/Validate.pm
Criterion Covered Total %
statement 128 180 71.1
branch 15 24 62.5
condition 13 15 86.6
subroutine 7 7 100.0
pod 1 1 100.0
total 164 227 72.2


line stmt bran cond sub pod time code
1             package OPM::Validate;
2              
3             # ABSTRACT: Validate .opm files
4              
5 2     2   949 use v5.20;
  2         10  
6              
7 2     2   8 use strict;
  2         3  
  2         40  
8 2     2   9 use warnings;
  2         4  
  2         1736  
9              
10             our $VERSION = '1.11'; # VERSION
11              
12             my %boundaries = (
13             # tag min max
14             CVS => [ 0, 1],
15             Name => [ 1, 1],
16             URL => [ 1, 1],
17             Vendor => [ 1, 1],
18             Version => [ 1, 1],
19             BuildDate => [ 0, 1],
20             BuildHost => [ 0, 1],
21             );
22              
23             sub validate {
24 20     20 1 16975 my ($class, $content, $is_sopm) = @_;
25              
26 20         195 $content =~ s{
27            
28             }{}xmsg;
29              
30 20         47 my ($check,$grammar) = _grammar( $content, $is_sopm );
31 20         503 my $match = $content =~ $grammar;
32 20 100       123 die 'Invalid .opm file' if !$match;
33              
34 16         46 for my $key ( keys %boundaries ) {
35 112         138 $check->( $key );
36             }
37             }
38              
39             sub _grammar {
40 20     20   38 my ($content, $is_sopm) = @_;
41              
42 20   100     66 $is_sopm //= 0;
43              
44 20         29 my %s;
45             my %pos;
46              
47             my $check = sub {
48 1050     1050   1488 my ($name, $max, $min) = @_;
49              
50 1050 100 66     1836 $max //= (exists $boundaries{$name} ? $boundaries{$name}->[-1] : 100_000);
51 1050 100 100     2165 $min //= (exists $boundaries{$name} ? $boundaries{$name}->[0] : 0);
52              
53 1050         1952 my ($first,$second) = split /\./, $name;
54              
55 1050   100     2223 my $check = $s{$name} // 0;
56 1050 100 100     2197 if ( $second && ref $s{$first} ) {
57 652   50     1039 $check = $s{$first}->{$second} // 0;
58             }
59              
60 1050         1063 my $context = '';
61 1050         1104 my $pos = $pos{$name};
62 1050 100       1260 if ( $pos ) {
63 82         111 my $start_length = 30;
64 82         101 my $start = $pos - $start_length;
65 82 50       120 if ( $start < 0 ) {
66 0         0 $start_length = $pos;
67 0         0 $start = 0;
68             }
69              
70 82         84 my $end_length = 30;
71 82 50       134 if ( $pos + $end_length > length $content ) {
72 0         0 $end_length = ( length $content ) - $pos;
73             }
74              
75 82         366 $context = sprintf "\n%s <-- --> %s",
76             ( substr $content, $start, $start_length ),
77             ( substr $content, $pos, $end_length );
78             }
79              
80 1050 50       9331 if ( $check > $max ) {
    50          
81 0         0 die sprintf 'Too many "%s" elements. Max %s element(s) allowed.%s', $name, $max, $context;
82             }
83             elsif ( $check < $min ) {
84 0         0 die sprintf 'Too few "%s" elements. Min %s element(s) required.%s', $name, $min, $context;
85             }
86 20         101 };
87              
88             my $grammar = qr{
89             \A(?&PACKAGE)\z
90              
91             (?(DEFINE)
92             (?
93             \s* <\?xml \s* version="1.0" \s* encoding="[^"]+?" \s* \?> \s+
94             \s* <(?otrs|otobo)_package \s+ version="[12].[0-9]+">
95             (?:\s*(?&PACKAGE_TAGS))+
96             \s* \s*
97             )
98              
99             (?
100 8         27 (?:.*?(?{$pos{CVS} = pos(); $s{CVS}++; $check->('CVS')})) |
  8         13  
  8         15  
101 20         52 (?:.*?(?{$pos{Name} = pos(); ++$s{Name}; $check->('Name')})) |
  20         31  
  20         58  
102 20         37 (?:.*?(?{++$s{Version}; $check->('Version')})) |
  20         30  
103 18         40 (?:.*?(?{++$s{Vendor}; $check->('Vendor')})) |
  18         25  
104 18         30 (?:.*?(?{++$s{URL}; $check->('URL')})) |
  18         22  
105 14         22 (?:.*?(?{++$s{License}; $check->('License')})) |
  14         17  
106 12         28 (?:.*?(?{$s{BuildDate}++; $check->('BuildDate')})) |
  12         19  
107 12         24 (?:.*?(?{$s{BuildHost}++; $check->('BuildHost')})) |
  12         14  
108             (?:.*?) |
109             (?&FRAMEWORK) |
110             (?&DESCRIPTION) |
111             (?&INTRO) |
112             (?&CODE) |
113             (?&PACKAGEMERGE) |
114             (?&FILELIST) |
115             (?&PREREQ) |
116             (?&DATABASE) |
117             (?&CHANGELOG)
118             )
119              
120             (?
121 26         365 .*?
122             (*COMMIT)
123             )
124              
125             (?
126 4         8 (?:Maximum="[0-9\.]+"(?{++$s{'Framework.Max'}; $check->('Framework.Max',1)})) |
  4         6  
127 4         8 (?:Minimum="[0-9\.]+"(?{++$s{'Framework.Min'}; $check->('Framework.Min',1)}))
  4         5  
128             )
129              
130             (?
131             .*?
132             (*COMMIT)
133             )
134              
135             (?
136 40         59 (?:Lang="(?[a-zA-Z]+)"(?{++$s{'Description.Lang'}; $check->('Description.Lang',1)})) |
  40         52  
137 0         0 (?:Format="[a-zA-Z]+"(?{++$s{'Description.Format'}; $check->('Description.Format',1)})) |
  0         0  
138 0         0 (?:Translatable="[01]"(?{++$s{'Description.Translatable'}; $check->('Description.Translatable',1)}))
  0         0  
139             )
140              
141             (?
142 16         35
143             ( \s+ (?&CHANGELOG_ATTR))+>.*?
144 16         40 (?{$check->('ChangeLog.Date', 1, 1); $check->('ChangeLog.Version', 1, 1); })
  16         20  
145             (*COMMIT)
146             )
147              
148             (?
149 16         26 (?:Date="[^"]+"(?{++$s{'ChangeLog.Date'}; $check->('ChangeLog.Date',1,1)})) |
  16         19  
150 16         29 (?:Version="[0-9\.]+"(?{++$s{'ChangeLog.Version'}; $check->('ChangeLog.Version',1,1)}))
  16         19  
151             )
152              
153             (?
154             Install|Upgrade|Reinstall|Uninstall) (?{delete $s{Intro};})
155             ( \s+ (?&INTRO_ATTR))+>.*?
156 6         13 (?{$check->('Intro.Type', 1, 1);})
157             (*COMMIT)
158             )
159              
160             (?
161 6         11 (?:Type="(?i:Post|Pre)"(?{++$s{Intro}->{Type}; $check->('Intro.Type',1,1)})) |
  6         12  
162 2         4 (?:Title="[^"]+"(?{++$s{Intro}->{Title}; $check->('Intro.Title',1)})) |
  2         11  
163 0         0 (?:Format="[^"]+"(?{++$s{Intro}->{Format}; $check->('Intro.Format',1)})) |
  0         0  
164 2         7 (?:Lang="[A-Za-z]+"(?{++$s{Intro}->{Lang}; $check->('Intro.Lang',1)})) |
  2         5  
165 0         0 (?:Translatable="[01]"(?{++$s{Intro}->{Translatable}; $check->('Intro.Translatable',1)})) |
  0         0  
166 2 0   2   835 (?:Version="[0-9\.]+"(?{++$s{Intro}->{Version}; $check->('Intro.Version',$+{intro_type} eq 'Upgrade' ? 1 : 0)}))
  2         666  
  2         3614  
  0         0  
  0         0  
167             )
168            
169             (?
170             Install|Upgrade|Reinstall|Uninstall) (?{delete $s{Code};})
171             ( \s+ (?&CODE_ATTR))+>.*?
172 0         0 (?{$check->('Code.Type', 1, 1);})
173             (*COMMIT)
174             )
175              
176             (?
177 0         0 (?:Type="(?i:Post|Pre)"(?{++$s{Code}->{Type}; $check->('Code.Type',1,1)})) |
  0         0  
178 0 0       0 (?:Version="[^"]+"(?{++$s{Code}->{Version}; $check->('Code.Version',$+{code_type} eq 'Upgrade' ? 1 : 0)}))
  0         0  
179             )
180              
181             (?
182             <(?Module|Package)Required (?{delete $s{Prereq};})
183             (\s+ (?&VERSION))?>.+?
184 10         20 (?{$check->('Prereq.Version', 1); delete $s{Prereq}})
185             (*COMMIT)
186             )
187              
188             (?
189 10         24 (?:Version="[^"]+"(?{++$s{Prereq}->{Version}; $check->('Prereq.Version', 1)}))
  10         13  
190             )
191              
192             (?
193            
194             (\s+ (?&PACKAGEMERGEATTR))+>.*?
195 2         5 (?{$check->('Merge.Name', 1, 1);})
196             (*COMMIT)
197             )
198              
199             (?
200 2         4 (?:TargetVersion="[0-9\.]+"(?{++$s{'Merge.Version'}; $check->('Merge.Version', 1)})) |
  2         4  
201 2         10 (?:Name="[^"]+"(?{++$s{'Merge.Name'};}))
202             )
203              
204              
205             (?
206             (\s*(?(?{$is_sopm})(?&FILESOPM)|(?&FILEOPM)))+\s*
207             (*COMMIT)
208             )
209              
210             (?
211            
212 19         53 ( \s+ (?&FILE_ATTR))+ \s* />(?{$check->('File.' . $_ , 1, 1) for qw/Location Permission/;})
213             (*COMMIT)
214             )
215              
216             (?
217            
218             ( \s+ (?&FILE_ATTR))+ \s* > \s* [A-Za-z0-9\+/\s]+ (*COMMIT) ={0,3} \s*?
219 70         211 (?{$check->('File.' . $_ , 1, 1) for qw/Location Permission Encode/;})
220             (*COMMIT)
221             )
222              
223             (?
224 89         536 (?:Location="[^"]+"(?{++$s{File}->{Location};})) |
225 70         763 (?:Encode="[^"]+"(?{++$s{File}->{Encode};})) |
226 89         583 (?:Permission="[0-7]+"(?{++$s{File}->{Permission};}))
227             )
228              
229             (?
230             Install|Upgrade|Reinstall|Uninstall) (?{delete $s{Database}})
231             ( \s+ (?&DATABASE_ATTR)){0,3}>
232             (\s* (?&DATABASE_TAGS) )+ \s*
233            
234             (*COMMIT)
235             )
236              
237             (?
238 11         31 (?:Type="(?i:Post|Pre)"(?{++$s{Database}->{Type}; $check->('Database.Type',1)})) |
  11         17  
239 0         0 (?:Version="[^"]+"(?{++$s{Database}->{Version}; $check->('Database.Version', 0)}))
  0         0  
240             )
241              
242             (?
243             (?&TABLE_CREATE) |
244             (?&TABLE_DROP) |
245             (?&TABLE_ALTER) |
246             (?&INSERT)
247             )
248              
249             (?
250            
251             (?: \s+ (?&TABLE_ATTR))+ \s*>
252             (?: \s* (?&TABLE_CREATE_TAGS) )+ \s*
253            
254             (*COMMIT)
255             )
256              
257             (?
258             (?&COLUMN) |
259             (?&FOREIGN_KEY) |
260             (?&INDEX) |
261             (?&UNIQUE)
262             )
263              
264             (?
265            
266             ( \s+ (?&TABLE_ATTR))+>
267             (\s* (?&TABLE_ALTER_TAGS) )+ \s*
268            
269             (*COMMIT)
270             )
271              
272             (?
273             (?&COLUMN_ADD) |
274             (?&COLUMN_DROP) |
275             (?&COLUMN_CHANGE) |
276             (?&FOREIGN_KEY_CREATE) |
277             (?&FOREIGN_KEY_DROP) |
278             (?&INDEX_CREATE) |
279             (?&INDEX_DROP) |
280             (?&UNIQUE_CREATE) |
281             (?&UNIQUE_DROP)
282             )
283              
284             (?
285            
286             ( \s+ (?&TABLE_ATTR))+ \s* (?:/>|>\s*)
287             (*COMMIT)
288             )
289              
290             (?
291 32         63 (?:Name="([^"]+)"(?{$pos{'Table.Name'} = pos(); ++$s{Table}->{Name}; $check->('Table.Name',1,1)})) |
  32         62  
  32         47  
292 0         0 (?:Type="(?i:Post|Pre)"(?{++$s{Table}->{Type}; $check->('Table.Type',0)})) |
  0         0  
293 20 50       43 (?:Version="[^"]+"(?{++$s{Table}->{Version}; $check->('Table.Version',$+{database_type} eq 'Upgrade' ? 1 : 0)}))
  20         130  
294             )
295              
296             (?
297            
298             ( \s+ (?&COLUMN_ATTR))+ \s* (?:/>|>\s*)
299             (*COMMIT)
300             )
301              
302             (?
303            
304             ( \s+ (?&COLUMN_ATTR))+ \s* (?:/>|>\s*)
305             (*COMMIT)
306             )
307              
308             (?
309 83         193 (?:Name="[^"]+"(?{++$s{Column}->{Name}; $check->('Column.Name',1,1);})) |
  83         104  
310 6         16 (?:AutoIncrement="(?:true|false)"(?{++$s{Column}->{AutoIncrement}; $check->('Column.AutoIncrement',1)})) |
  6         11  
311 83         163 (?:Required="(?:true|false)"(?{++$s{Column}->{Required}; $check->('Column.Required',1)})) |
  83         112  
312 6         17 (?:PrimaryKey="(?:true|false)"(?{++$s{Column}->{PrimaryKey}; $check->('Column.PrimaryKey',1)})) |
  6         9  
313 83         174 (?:\bType="[A-Za-z]+"(?{++$s{Column}->{Type}; $check->('Column.Type',1)})) |
  83         99  
314 34         71 (?:Size="\d+"(?{++$s{Column}->{Size}; $check->('Column.Size',1)})) |
  34         44  
315 0         0 (?:Default="[^"]+"(?{++$s{Column}->{Default}; $check->('Column.Default',1)}))
  0         0  
316             )
317              
318             (?
319            
320             ( \s+ (?&COLUMN_CHANGE_ATTR))+ \s* (?:/>|>\s*)
321             (*COMMIT)
322             )
323              
324             (?
325 2         8 (?:NameOld=".*?"(?{++$s{Column}->{NameOld}; $check->('Column.NameOld',1)})) |
  2         3  
326 2         7 (?:NameNew=".*?"(?{++$s{Column}->{NameNew}; $check->('Column.NameNew',1)})) |
  2         4  
327 0         0 (?:AutoIncrement="(?:true|false)"(?{++$s{Column}->{AutoIncrement}; $check->('Column.AutoIncrement',1)})) |
  0         0  
328 2         8 (?:Required="(?:true|false)"(?{++$s{Column}->{Required}; $check->('Column.Required',1)})) |
  2         5  
329 0         0 (?:PrimaryKey="(?:true|false)"(?{++$s{Column}->{PrimaryKey}; $check->('Column.PrimaryKey',1)})) |
  0         0  
330 2         7 (?:Type=".*?"(?{++$s{Column}->{Type}; $check->('Column.Type',1)})) |
  2         4  
331 2         11 (?:Size="\d+"(?{++$s{Column}->{Size}; $check->('Column.Size',1)})) |
  2         6  
332 0         0 (?:Default=".*?"(?{++$s{Column}->{Default}; $check->('Column.Default',1)}))
  0         0  
333             )
334              
335             (?
336            
337             ( \s+ (?&COLUMN_DROP_ATTR))+ (?:/>|>\s*)
338             (*COMMIT)
339             )
340              
341             (?
342 0         0 (?:Name=".*?"(?{++$s{Column}->{Name}; $check->('Column.Name',1,1)}))
  0         0  
343             )
344              
345             (?
346 0         0
  0         0  
347             ( \s+ (?&INSERT_ATTR))+>
348             (\s+ (?&INSERT_DATA) )+ \s*
349            
350             (*COMMIT)
351             )
352              
353             (?
354 0         0 (?:Table=".*?"(?{++$s{'Insert.Table'}; $check->('Insert.Table',1,1)})) |
  0         0  
355 0         0 (?:Type=".*?"(?{++$s{'Insert.Type'}; $check->('Insert.Type',1)})) |
  0         0  
356 0         0 (?:Version=".*?"(?{++$s{'Insert.Version'}; $check->('Insert.Version',1)}))
  0         0  
357             )
358              
359             (?
360 0         0
  0         0  
361             ( \s+ (?&INSERT_DATA_ATTR))+>
362             .*?
363            
364             )
365              
366             (?
367 0         0 (?:Key=".*?"(?{++$s{'Data.Key'}; $check->('Data.Key',1,1)})) |
  0         0  
368 0         0 (?:Translatable="[01]"(?{++$s{'Data.Translatable'}; $check->('Data.Translatable',1)})) |
  0         0  
369 0         0 (?:Type=".*?"(?{++$s{'Data.Type'}; $check->('Data.Type',1)}))
  0         0  
370             )
371              
372             (?
373            
374             (\s+ (?&INDEX_COLUMN) )+ \s*
375            
376             )
377              
378             (?
379            
380             ( \s+ (?&NAME_ATTR))? \s* (?:/>|>\s*)
381             )
382              
383             (?
384            
385             ( \s+ (?&NAME_ATTR))? \s* (?:/>|>\s*)
386             )
387              
388             (?
389            
390             ( \s+ (?&NAME_ATTR))? \s* (?:/>|>\s*)
391             )
392              
393             (?
394            
395             (\s+ (?&UNIQUE_COLUMN) )+ \s*
396            
397             )
398              
399             (?
400            
401             ( \s+ (?&NAME_ATTR))? \s* (?:/>|>\s*)
402             )
403              
404             (?
405            
406             ( \s+ (?&NAME_ATTR))? \s* (?:/>|>\s*)
407             )
408              
409             (?
410             |>\s*)
411             )
412              
413             (?
414 0         0 (?:Name=".*?"(?{++$s{'Generic.Name'}; $check->('Generic.Name',1,1)}))
  0         0  
415             )
416            
417             (?
418            
419             (\s+ (?&REFERENCE) )+ \s*
420            
421             )
422              
423             (?
424            
425             ( \s+ (?&NAME_ATTR))? \s* (?:/>|>\s*)
426             )
427              
428             (?
429             |>\s*(?:(?&REFERENCE)\s*)*)
430             )
431              
432             (?
433 10         20 (?:ForeignTable=".*?"(?{++$s{'ForeignTable'}; $check->('ForeignTable',1,1)}))
  10         17  
434             )
435              
436             (?
437 19         169
438             ( \s+ (?&REFERENCE_ATTR) )+ \s*
439             (?:\s*/>|>\s*)
440             )
441              
442             (?
443 19         36 (?:Local=".*?"(?{++$s{'Reference.Local'}; $check->('Reference.Local',1,1)})) |
  19         24  
444 19         35 (?:Foreign=".*?"(?{++$s{'Reference.Foreign'}; $check->('Reference.Foreign',1,1)}))
  19         27  
445             )
446             )
447 20         147 }xms;
448              
449 20         53 return $check, $grammar;
450             }
451              
452             1;
453              
454             __END__