1#!/usr/bin/perl -T
2
3use lib '.'; use lib 't';
4use SATest; sa_t_init("mkrules");
5use Test::More tests => 97;
6use File::Copy;
7use File::Path;
8
9# ---------------------------------------------------------------------------
10print " script runs, even with nothing to do\n\n";
11
12my $tdir = "$workdir/mkrules_t";
13
14mkpath ([$tdir, "$tdir/rulesrc", "$tdir/rules"]);
15
16write_file("$tdir/MANIFEST", [ ]);
17write_file("$tdir/MANIFEST.SKIP", [ "foo2\n" ]);
18write_file("$tdir/rules/active.list", [ "" ]);
19
20ok (mkrun ("--src $tdir/rulesrc --out $tdir/rules --manifest $tdir/MANIFEST --manifestskip $tdir/MANIFEST.SKIP --active $tdir/rules/active.list", \&patterns_run_cb));
21ok ok_all_patterns();
22save_tdir();
23
24# ---------------------------------------------------------------------------
25print " promotion of an active rule\n\n";
26
27%patterns = (
28  '72_active.cf: WARNING: not listed in manifest file' => manif_found,
29  "body GOOD /foo/"   => rule_line_1,
30  "describe GOOD desc_found"  => rule_line_2,
31);
32%anti_patterns = (
33  "describe T_GOOD desc_found"  => rule_line_2,
34);
35
36mkpath ([ "$tdir/rulesrc/sandbox/foo", "$tdir/rules" ]);
37
38write_file("$tdir/MANIFEST", [ ]);
39write_file("$tdir/MANIFEST.SKIP", [ "foo2\n" ]);
40write_file("$tdir/rules/active.list", [ "GOOD\n" ]);
41write_file("$tdir/rulesrc/sandbox/foo/20_foo.cf", [
42    "body GOOD /foo/\n",
43    "describe GOOD desc_found\n"
44]);
45
46ok (mkrun ("--src $tdir/rulesrc --out $tdir/rules --manifest $tdir/MANIFEST --manifestskip $tdir/MANIFEST.SKIP --active $tdir/rules/active.list 2>&1", \&patterns_run_cb));
47checkfile("$tdir/rules/72_active.cf", \&patterns_run_cb);
48checkfile("$tdir/rules/70_sandbox.cf", \&patterns_run_cb);
49ok ok_all_patterns();
50save_tdir();
51
52# ---------------------------------------------------------------------------
53print " non-promotion of an inactive rule\n\n";
54
55%patterns = (
56  '70_sandbox.cf: WARNING: not listed in manifest file' => manif_found,
57  "body T_GOOD /foo/"   => rule_line_1,
58  "describe T_GOOD desc_found"  => rule_line_2,
59);
60%anti_patterns = (
61  "describe GOOD desc_found"  => rule_line_2,
62);
63
64mkpath ([ "$tdir/rulesrc/sandbox/foo", "$tdir/rules" ]);
65
66write_file("$tdir/MANIFEST", [ ]);
67write_file("$tdir/MANIFEST.SKIP", [ "foo2\n" ]);
68write_file("$tdir/rules/active.list", [ "NOT_GOOD\n" ]);
69write_file("$tdir/rulesrc/sandbox/foo/20_foo.cf", [
70    "body GOOD /foo/\n",
71    "describe GOOD desc_found\n"
72]);
73
74ok (mkrun ("--src $tdir/rulesrc --out $tdir/rules --manifest $tdir/MANIFEST --manifestskip $tdir/MANIFEST.SKIP --active $tdir/rules/active.list 2>&1", \&patterns_run_cb));
75checkfile("$tdir/rules/72_active.cf", \&patterns_run_cb);
76checkfile("$tdir/rules/70_sandbox.cf", \&patterns_run_cb);
77ok ok_all_patterns();
78save_tdir();
79
80# ---------------------------------------------------------------------------
81print " non-promotion of an inactive rule with score set\n\n";
82
83%patterns = (
84  '70_sandbox.cf: WARNING: not listed in manifest file' => manif_found,
85  "body T_GOOD /foo/"   => rule_line_1,
86  "describe T_GOOD desc_found"  => rule_line_2,
87  "#score T_GOOD 4.0"  => score_good,
88);
89%anti_patterns = (
90  "describe GOOD desc_found"  => rule_line_2,
91  "score GOOD 4.0"  => 'score',
92);
93
94mkpath ([ "$tdir/rulesrc/sandbox/foo", "$tdir/rules" ]);
95
96write_file("$tdir/MANIFEST", [ ]);
97write_file("$tdir/MANIFEST.SKIP", [ "foo2\n" ]);
98write_file("$tdir/rules/active.list", [ "NOT_GOOD\n" ]);
99write_file("$tdir/rulesrc/sandbox/foo/20_foo.cf", [
100    "body GOOD /foo/\n",
101    "score GOOD 4.0\n",
102    "describe GOOD desc_found\n"
103]);
104
105ok (mkrun ("--src $tdir/rulesrc --out $tdir/rules --manifest $tdir/MANIFEST --manifestskip $tdir/MANIFEST.SKIP --active $tdir/rules/active.list 2>&1", \&patterns_run_cb));
106checkfile("$tdir/rules/72_active.cf", \&patterns_run_cb);
107checkfile("$tdir/rules/70_sandbox.cf", \&patterns_run_cb);
108ok ok_all_patterns();
109save_tdir();
110
111# ---------------------------------------------------------------------------
112print " non-promotion of a broken rule\n\n";
113
114%patterns = (
115  '70_sandbox.cf: WARNING: not listed in manifest file' => manif_found,
116  'LINT FAILED' => lint_failed,
117);
118%anti_patterns = (
119  "body GOOD"   => rule_line_1,
120  "describe GOOD desc_found"  => rule_line_2,
121);
122
123mkpath ([ "$tdir/rulesrc/sandbox/foo", "$tdir/rules" ]);
124
125write_file("$tdir/MANIFEST", [ ]);
126write_file("$tdir/MANIFEST.SKIP", [ "foo2\n" ]);
127write_file("$tdir/rules/active.list", [ "GOOD\n" ]);
128write_file("$tdir/rulesrc/sandbox/foo/20_foo.cf", [
129    "body GOOD /***\n",
130    "describe GOOD desc_found\n"
131]);
132
133ok (mkrun ("--src $tdir/rulesrc --out $tdir/rules --manifest $tdir/MANIFEST --manifestskip $tdir/MANIFEST.SKIP --active $tdir/rules/active.list 2>&1", \&patterns_run_cb));
134checkfile("$tdir/rules/70_sandbox.cf", \&patterns_run_cb);
135ok (-f "$tdir/rules/72_active.cf");
136ok (-s "$tdir/rules/72_active.cf" == 0);
137ok ok_all_patterns();
138save_tdir();
139
140# ---------------------------------------------------------------------------
141print " promotion of an active meta rule\n\n";
142
143%patterns = (
144  '70_sandbox.cf: WARNING: not listed in manifest file' => manif_found,
145  '20_foo.cf: 1 active rules, 1 other' => 'foundrule',
146  "body __GOOD /foo/"   => rule_line_1,
147  "meta GOOD (__GOOD)"   => rule_line_1a,
148  "describe GOOD desc_found"  => rule_line_2,
149);
150%anti_patterns = (
151  "describe T_GOOD desc_found"  => rule_line_2,
152);
153
154mkpath ([ "$tdir/rulesrc/sandbox/foo", "$tdir/rules" ]);
155
156write_file("$tdir/MANIFEST", [ ]);
157write_file("$tdir/MANIFEST.SKIP", [ "foo2\n" ]);
158write_file("$tdir/rules/active.list", [ "GOOD\n" ]);
159write_file("$tdir/rulesrc/sandbox/foo/20_foo.cf", [
160    "body __GOOD /foo/\n",
161    "meta GOOD (__GOOD)\n",
162    "describe GOOD desc_found\n"
163]);
164
165ok (mkrun ("--src $tdir/rulesrc --out $tdir/rules --manifest $tdir/MANIFEST --manifestskip $tdir/MANIFEST.SKIP --active $tdir/rules/active.list 2>&1", \&patterns_run_cb));
166checkfile("$tdir/rules/72_active.cf", \&patterns_run_cb);
167checkfile("$tdir/rules/70_sandbox.cf", \&patterns_run_cb);
168ok ok_all_patterns();
169save_tdir();
170
171# ---------------------------------------------------------------------------
172print " inactive meta rule\n\n";
173
174%patterns = (
175  '70_sandbox.cf: WARNING: not listed in manifest file' => manif_found,
176  '20_foo.cf: 0 active rules, 2 other' => 'foundrule',
177  "body __GOOD /foo/"   => rule_line_1,
178  "meta T_GOOD (__GOOD)"   => rule_line_1a,
179  "describe T_GOOD desc_found"  => rule_line_2,
180);
181%anti_patterns = (
182  "describe GOOD desc_found"  => rule_line_2,
183);
184
185mkpath ([ "$tdir/rulesrc/sandbox/foo", "$tdir/rules" ]);
186
187write_file("$tdir/MANIFEST", [ ]);
188write_file("$tdir/MANIFEST.SKIP", [ "foo2\n" ]);
189write_file("$tdir/rules/active.list", [ "NOT_GOOD\n" ]);
190write_file("$tdir/rulesrc/sandbox/foo/20_foo.cf", [
191    "body __GOOD /foo/\n",
192    "meta GOOD (__GOOD)\n",
193    "describe GOOD desc_found\n"
194]);
195
196ok (mkrun ("--src $tdir/rulesrc --out $tdir/rules --manifest $tdir/MANIFEST --manifestskip $tdir/MANIFEST.SKIP --active $tdir/rules/active.list 2>&1", \&patterns_run_cb));
197checkfile("$tdir/rules/72_active.cf", \&patterns_run_cb);
198checkfile("$tdir/rules/70_sandbox.cf", \&patterns_run_cb);
199ok ok_all_patterns();
200save_tdir();
201
202# ---------------------------------------------------------------------------
203print " active plugin in sandbox\n\n";
204
205%patterns = (
206  '70_sandbox.cf: WARNING: not listed in manifest file' => manif_found,
207  "loadplugin Good plugin.pm" => loadplugin_found,
208  "body GOOD eval:check_foo()"   => rule_line_1,
209  "describe GOOD desc_found"  => rule_line_2,
210  "ifplugin Good" => if1,
211  "endif" => endif_found,
212);
213%anti_patterns = (
214  "describe T_GOOD desc_found"  => rule_line_2,
215);
216
217mkpath ([ "$tdir/rulesrc/sandbox/foo", "$tdir/rules" ]);
218
219write_file("$tdir/MANIFEST", [ "rulesrc/sandbox/foo/20_foo.cf\n", "rulesrc/sandbox/foo/plugin.pm\n" ]);
220write_file("$tdir/MANIFEST.SKIP", [ "foo2\n" ]);
221write_file("$tdir/rules/active.list", [ "GOOD\n" ]);
222write_file("$tdir/rulesrc/sandbox/foo/20_foo.cf", [
223    "loadplugin Good plugin.pm\n",
224    "ifplugin Good\n",
225    "body GOOD eval:check_foo()\n",
226    "describe GOOD desc_found\n",
227    "endif\n",
228]);
229write_file("$tdir/rulesrc/sandbox/foo/plugin.pm", [
230    'package Good;',
231    'use Mail::SpamAssassin::Plugin; our @ISA = qw(Mail::SpamAssassin::Plugin);',
232    'sub new { my ($class, $m) = @_; $class = ref($class) || $class;',
233    'my $self = bless $class->SUPER::new($m), $class;',
234    '$self->register_eval_rule("check_foo"); return $self; }',
235    'sub check_foo { my ($self, $pms) = @_; return 1; }',
236]);
237
238ok (mkrun ("--src $tdir/rulesrc --out $tdir/rules --manifest $tdir/MANIFEST --manifestskip $tdir/MANIFEST.SKIP --active $tdir/rules/active.list 2>&1", \&patterns_run_cb));
239# checkfile("$tdir/rules/72_active.cf", \&patterns_run_cb);
240checkfile("$tdir/rules/70_sandbox.cf", \&patterns_run_cb);
241ok (-f "$tdir/rules/plugin.pm");
242ok ok_all_patterns();
243save_tdir();
244
245# ---------------------------------------------------------------------------
246print " inactive plugin\n\n";
247
248%patterns = (
249  '70_sandbox.cf: WARNING: not listed in manifest file' => manif_found,
250  # "WARNING: GOOD: renamed as T_GOOD due to missing T_ prefix" => warning_seen,
251  "loadplugin Good plugin.pm" => loadplugin_found,
252  "body T_GOOD eval:check_foo()" => rule_line_1,
253  "describe T_GOOD desc_found" => rule_line_2,
254  "ifplugin Good" => if1,
255  "endif" => endif_found,
256);
257%anti_patterns = (
258  "describe GOOD desc_found"  => rule_line_2,
259);
260
261mkpath ([ "$tdir/rulesrc/sandbox/foo", "$tdir/rules" ]);
262
263write_file("$tdir/MANIFEST", [ "rulesrc/sandbox/foo/20_foo.cf\n", "rulesrc/sandbox/foo/plugin.pm\n" ]);
264write_file("$tdir/MANIFEST.SKIP", [ "foo2\n" ]);
265write_file("$tdir/rules/active.list", [ "NOT_GOOD\n" ]);
266write_file("$tdir/rulesrc/sandbox/foo/20_foo.cf", [
267    "loadplugin Good plugin.pm\n",
268    "ifplugin Good\n",
269    "body GOOD eval:check_foo()\n",
270    "describe GOOD desc_found\n",
271    "endif\n",
272]);
273write_file("$tdir/rulesrc/sandbox/foo/plugin.pm", [
274    'package Good;',
275    'use Mail::SpamAssassin::Plugin; our @ISA = qw(Mail::SpamAssassin::Plugin);',
276    'sub new { my ($class, $m) = @_; $class = ref($class) || $class;',
277    'my $self = bless $class->SUPER::new($m), $class;',
278    '$self->register_eval_rule("check_foo"); return $self; }',
279    'sub check_foo { my ($self, $pms) = @_; return 1; }',
280]);
281
282ok (mkrun ("--src $tdir/rulesrc --out $tdir/rules --manifest $tdir/MANIFEST --manifestskip $tdir/MANIFEST.SKIP --active $tdir/rules/active.list 2>&1", \&patterns_run_cb));
283# checkfile("$tdir/rules/72_active.cf", \&patterns_run_cb);
284checkfile("$tdir/rules/70_sandbox.cf", \&patterns_run_cb);
285ok (-f "$tdir/rules/plugin.pm");
286ok ok_all_patterns();
287save_tdir();
288
289
290# ---------------------------------------------------------------------------
291print " active plugin, but the .pm file is AWOL\n\n";
292
293%patterns = (
294  "body GOOD eval:check_foo()"   => rule_line_1,
295  "describe GOOD desc_found"  => rule_line_2,
296  "ifplugin Good" => if1,
297  "endif" => endif_found,
298  "rulesrc/sandbox/foo/20_foo.cf: WARNING: plugin code file '$workdir/mkrules_t/rulesrc/sandbox/foo/plugin.pm' not found, line ignored: loadplugin Good plugin.pm" => plugin_not_found,
299);
300%anti_patterns = (
301  "describe T_GOOD desc_found"  => rule_line_2,
302);
303
304rmtree([ $tdir ]); mkpath ([ "$tdir/rulesrc/sandbox/foo", "$tdir/rules" ]);
305
306write_file("$tdir/MANIFEST", [ "rulesrc/sandbox/foo/20_foo.cf\n", "rulesrc/sandbox/foo/plugin.pm\n" ]);
307write_file("$tdir/MANIFEST.SKIP", [ "foo2\n" ]);
308write_file("$tdir/rules/active.list", [ "GOOD\n" ]);
309write_file("$tdir/rulesrc/sandbox/foo/20_foo.cf", [
310    "loadplugin Good plugin.pm\n",
311    "ifplugin Good\n",
312    "body GOOD eval:check_foo()\n",
313    "describe GOOD desc_found\n",
314    "endif\n",
315]);
316
317ok (mkrun ("--src $tdir/rulesrc --out $tdir/rules --manifest $tdir/MANIFEST --manifestskip $tdir/MANIFEST.SKIP --active $tdir/rules/active.list 2>&1", \&patterns_run_cb));
318checkfile("$tdir/rules/72_active.cf", \&patterns_run_cb);
319# checkfile("$tdir/rules/70_sandbox.cf", \&patterns_run_cb);
320ok (!-f "$tdir/rules/plugin.pm");
321ok ok_all_patterns();
322save_tdir();
323
324# ---------------------------------------------------------------------------
325print " active plugin, but the .pm file is not in MANIFEST\n\n";
326
327%patterns = (
328  "body GOOD eval:check_foo()"   => rule_line_1,
329  "describe GOOD desc_found"  => rule_line_2,
330  "ifplugin Good" => if1,
331  "endif" => endif_found,
332  "tryplugin Good plugin.pm" => 'tryplugin',
333  "$workdir/mkrules_t/rulesrc/sandbox/foo/20_foo.cf: WARNING: '$workdir/mkrules_t/rules/plugin.pm' not listed in manifest file, making 'tryplugin': loadplugin Good plugin.pm" => not_found_in_manifest_warning
334);
335%anti_patterns = (
336);
337
338rmtree([ $tdir ]); mkpath ([ "$tdir/rulesrc/sandbox/foo", "$tdir/rules" ]);
339
340write_file("$tdir/MANIFEST", [ "rulesrc/sandbox/foo/20_foo.cf\n" ]);
341write_file("$tdir/MANIFEST.SKIP", [ ]);
342write_file("$tdir/rules/active.list", [ "GOOD\n" ]);
343write_file("$tdir/rulesrc/sandbox/foo/20_foo.cf", [
344    "loadplugin Good plugin.pm\n",
345    "ifplugin Good\n",
346    "body GOOD eval:check_foo()\n",
347    "describe GOOD desc_found\n",
348    "endif\n",
349]);
350write_file("$tdir/rulesrc/sandbox/foo/plugin.pm", [
351    'package Good;',
352    'use Mail::SpamAssassin::Plugin; our @ISA = qw(Mail::SpamAssassin::Plugin);',
353    'sub new { my ($class, $m) = @_; $class = ref($class) || $class;',
354    'my $self = bless $class->SUPER::new($m), $class;',
355    '$self->register_eval_rule("check_foo"); return $self; }',
356    'sub check_foo { my ($self, $pms) = @_; return 1; }',
357]);
358
359ok (mkrun ("--src $tdir/rulesrc --out $tdir/rules --manifest $tdir/MANIFEST --manifestskip $tdir/MANIFEST.SKIP --active $tdir/rules/active.list 2>&1", \&patterns_run_cb));
360# checkfile("$tdir/rules/72_active.cf", \&patterns_run_cb);
361checkfile("$tdir/rules/70_sandbox.cf", \&patterns_run_cb);
362ok (-f "$tdir/rules/plugin.pm");
363ok ok_all_patterns();
364save_tdir();
365
366# ---------------------------------------------------------------------------
367print "meta rule depends on unpromoted subrule in lexically-earlier file\n\n";
368# (see mail from Sidney of Oct 16 2006, rules HS_INDEX_PARAM and HS_PHARMA_1)
369
370%patterns = (
371  "header T_GOOD_SUB"   => rule_line_1,
372  "header T_BAD_SUB"   => rule_line_2,
373  "meta GOOD (T_GOOD_SUB && !T_BAD_SUB)" => meta_found
374);
375%anti_patterns = (
376);
377
378rmtree([ $tdir ]); mkpath ([ "$tdir/rulesrc/sandbox/foo", "$tdir/rules" ]);
379
380write_file("$tdir/MANIFEST", [ "rules/72_active.cf\n" ]);
381write_file("$tdir/MANIFEST.SKIP", [ ]);
382write_file("$tdir/rules/active.list", [ "GOOD\n" ]);
383write_file("$tdir/rulesrc/sandbox/foo/20_aaa.cf", [
384    "meta GOOD (GOOD_SUB && !BAD_SUB)\n",
385]);
386write_file("$tdir/rulesrc/sandbox/foo/20_bbb.cf", [
387    "header GOOD_SUB Foo =~ /good/\n",
388    "header BAD_SUB Foo =~ /bad/\n",
389]);
390
391ok (mkrun ("--src $tdir/rulesrc --out $tdir/rules --manifest $tdir/MANIFEST --manifestskip $tdir/MANIFEST.SKIP --active $tdir/rules/active.list 2>&1", \&patterns_run_cb));
392checkfile("$tdir/rules/72_active.cf", \&patterns_run_cb);
393ok ok_all_patterns();
394save_tdir();
395
396# ---------------------------------------------------------------------------
397print " nested conditionals\n\n";
398
399%patterns = (
400  '72_active.cf: WARNING: not listed in manifest file' => manif_found,
401  "body GOOD /foo/"   => rule_line_1,
402  "describe GOOD desc_found"  => rule_line_2,
403  "ifplugin Mail::SpamAssassin::Plugin::DKIM" => 'ifplugin',
404  "if (version >= 3.002000)" => 'ifversion',
405);
406%anti_patterns = (
407  "describe T_GOOD desc_found"  => rule_line_2,
408);
409
410mkpath ([ "$tdir/rulesrc/sandbox/foo", "$tdir/rules" ]);
411
412write_file("$tdir/MANIFEST", [ ]);
413write_file("$tdir/MANIFEST.SKIP", [ "foo2\n" ]);
414write_file("$tdir/rules/active.list", [ "GOOD\n" ]);
415write_file("$tdir/rulesrc/sandbox/foo/20_foo.cf", [
416  "ifplugin Mail::SpamAssassin::Plugin::DKIM\n",
417  "if (version >= 3.002000)\n",
418  "body GOOD /foo/\n",
419  "describe GOOD desc_found\n",
420  "endif\n",
421  "endif\n",
422]);
423
424ok (mkrun ("--src $tdir/rulesrc --out $tdir/rules --manifest $tdir/MANIFEST --manifestskip $tdir/MANIFEST.SKIP --active $tdir/rules/active.list 2>&1", \&patterns_run_cb));
425checkfile("$tdir/rules/72_active.cf", \&patterns_run_cb);
426checkfile("$tdir/rules/70_sandbox.cf", \&patterns_run_cb);
427ok ok_all_patterns();
428save_tdir();
429
430# ---------------------------------------------------------------------------
431
432exit;
433
434sub write_file {
435  my $file = shift;
436  my $linesref = shift;
437  open (O, ">$file") or die "cannot write to $file";
438  print O @$linesref;
439  close O or die "cannot save $file";
440}
441
442
443sub mkrun {
444  my $args = shift;
445  my $read_sub = shift;
446
447  my $post_redir = '';
448  $args =~ s/ 2\>\&1$// and $post_redir = ' 2>&1';
449
450  rmtree ("$workdir/outputdir.tmp"); # some tests use this
451  mkdir ("$workdir/outputdir.tmp", 0755);
452
453  clear_pattern_counters();
454
455  my $scrargs = "$perl_path -I../lib ../build/mkrules $args";
456  print ("\t$scrargs\n");
457  my $test_number = test_number();
458  untaint_system ("$scrargs > $workdir/$testname.$test_number $post_redir");
459  $mk_exitcode = ($?>>8);
460  if ($mk_exitcode != 0) { return undef; }
461  &checkfile ("$workdir/$testname.$test_number", $read_sub) if (defined $read_sub);
462  1;
463}
464
465sub save_tdir {
466  my $test_number = test_number();
467
468  rmtree("$tdir.$test_number");
469  if (move( "$tdir", "$tdir.$test_number")) {
470    print "\ttest output tree copied to $tdir.$test_number\n";
471  }
472}
473
474