File Coverage

blib/lib/Config/Model/Tester.pm
Criterion Covered Total %
statement 69 413 16.7
branch 6 198 3.0
condition 2 71 2.8
subroutine 18 47 38.3
pod 0 24 0.0
total 95 753 12.6


line stmt bran cond sub pod time code
1             #
2             # This file is part of Config-Model-Tester
3             #
4             # This software is Copyright (c) 2013-2019 by Dominique Dumont.
5             #
6             # This is free software, licensed under:
7             #
8             # The GNU Lesser General Public License, Version 2.1, February 1999
9             #
10             package Config::Model::Tester 4.005;
11             # ABSTRACT: Test framework for Config::Model
12              
13 1     1   557 use warnings;
  1         7  
  1         26  
14 1     1   34 use strict;
  1         2  
  1         29  
15 1     1   435 use locale;
  1         521  
  1         4  
16 1     1   564 use utf8;
  1         12  
  1         4  
17 1     1   40 use 5.12.0;
  1         4  
18              
19 1     1   532 use Test::More;
  1         56133  
  1         8  
20 1     1   1081 use Log::Log4perl 1.11 qw(:easy :levels);
  1         39711  
  1         6  
21 1     1   1357 use Path::Tiny;
  1         11039  
  1         51  
22 1     1   556 use File::Copy::Recursive qw(fcopy rcopy dircopy);
  1         5664  
  1         62  
23              
24 1     1   497 use Test::Warn;
  1         3341  
  1         58  
25 1     1   447 use Test::Exception;
  1         1300  
  1         3  
26 1     1   684 use Test::File::Contents ;
  1         9402  
  1         94  
27 1     1   513 use Test::Differences;
  1         7697  
  1         60  
28 1     1   421 use Test::Memory::Cycle ;
  1         4205  
  1         5  
29 1     1   525 use Test::Log::Log4perl;
  1         2672  
  1         42  
30              
31 1     1   423 use Config::Model::Tester::Setup qw/init_test setup_test_dir/;
  1         2  
  1         105  
32              
33             Test::Log::Log4perl->ignore_priority("info");
34              
35              
36             # use eval so this module does not have a "hard" dependency on Config::Model
37             # This way, Config::Model can build-depend on Config::Model::Tester without
38             # creating a build dependency loop.
39             eval {
40             require Config::Model;
41             require Config::Model::Lister;
42             require Config::Model::Value;
43             require Config::Model::BackendMgr;
44             } ;
45              
46 1     1   7 use vars qw/@ISA @EXPORT/;
  1         2  
  1         5063  
47              
48             require Exporter;
49             @ISA = qw(Exporter);
50             @EXPORT = qw(run_tests);
51              
52             $File::Copy::Recursive::DirPerms = oct(755);
53              
54             sub setup_test {
55 0     0 0 0 my ( $test_group, $t_name, $wr_root, $trace, $test_suite_data, $t_data ) = @_;
56              
57             # cleanup before tests
58 0         0 $wr_root->remove_tree();
59 0         0 $wr_root->mkpath( { mode => oct(755) } );
60             my ($conf_dir, $conf_file_name, $home_for_test)
61 0         0 = @$test_suite_data{qw/conf_dir conf_file_name home_for_test/};
62              
63 0 0 0     0 if ($conf_dir and $home_for_test) {
64 0         0 $conf_dir =~ s!~/!$home_for_test/!;
65 0         0 $test_suite_data->{conf_dir} = $conf_dir;
66             }
67              
68 0         0 my $wr_dir = $wr_root->child('test-' . $t_name);
69 0         0 my $wr_dir2 = $wr_root->child('test-' . $t_name.'-w');
70 0         0 $wr_dir->mkpath;
71 0         0 $wr_dir2->mkpath;
72              
73 0         0 my $conf_file ;
74 0 0 0     0 $conf_file = $wr_dir->child($conf_dir,$conf_file_name)
75             if $conf_dir and $conf_file_name;
76              
77 0         0 my $ex_dir = path('t')->child('model_tests.d', "$test_group-examples");
78 0   0     0 my $ex_data = $ex_dir->child($t_data->{data_from} // $t_name);
79              
80 0         0 my @file_list;
81              
82 0 0       0 if (my $setup = $t_data->{setup}) {
    0          
    0          
83 0         0 foreach my $file (keys %$setup) {
84 0         0 my $map = $setup->{$file} ;
85             my $destination_str
86             = ref ($map) eq 'HASH' ? $map->{$^O} // $map->{default}
87 0 0 0     0 : ref ($map) eq 'ARRAY' ? $map->[-1]
    0          
88             : $map;
89 0 0       0 if (not defined $destination_str) {
90 0         0 die "$test_group $t_name setup error: cannot find destination for test file $file" ;
91             }
92 0 0       0 $destination_str =~ s!~/!$home_for_test/! if $home_for_test;
93 0         0 my $destination = $wr_dir->child($destination_str) ;
94 0         0 $destination->parent->mkpath( { mode => oct(755) }) ;
95 0         0 my $data_file = $ex_data->child($file);
96 0 0       0 die "cannot find $data_file" unless $data_file->exists;
97 0         0 my $data = $data_file->slurp() ;
98 0         0 $destination->spew( $data );
99 0 0       0 if (ref $map eq 'ARRAY') {
100 0         0 my @tmp = @$map;
101 0         0 pop @tmp; # remove destination
102 0         0 foreach my $link_str (@tmp) {
103 0 0       0 $link_str =~ s!~/!$home_for_test/! if $home_for_test;
104 0         0 my $link = $wr_dir->child($link_str);
105 0         0 $link->parent->mkpath( { mode => oct(755) }) ;
106 0         0 symlink $destination->absolute->stringify, $link->stringify;
107             }
108             }
109 0         0 @file_list = list_test_files ($wr_dir);
110             }
111             }
112             elsif ( $ex_data->is_dir ) {
113             # copy whole dir
114 0 0       0 my $destination_dir = $conf_dir ? $wr_dir->child($conf_dir) : $wr_dir ;
115 0         0 $destination_dir->mkpath( { mode => oct(755) });
116 0 0       0 say "dircopy ". $ex_data->stringify . '->'. $destination_dir->stringify
117             if $trace ;
118 0 0       0 dircopy( $ex_data->stringify, $destination_dir->stringify )
119             || die "dircopy $ex_data -> $destination_dir failed:$!";
120 0         0 @file_list = list_test_files ($destination_dir);
121             }
122             elsif ( $ex_data->exists ) {
123             # either one if true if $conf_file is undef
124 0 0       0 die "test data is missing global \$conf_dir" unless defined $conf_dir;
125 0 0       0 die "test data is missing global \$conf_file_name" unless defined $conf_file;
126              
127             # just copy file
128 0 0       0 say "file copy ". $ex_data->stringify . '->'. $conf_file->stringify
129             if $trace ;
130 0 0       0 fcopy( $ex_data->stringify, $conf_file->stringify )
131             || die "copy $ex_data -> $conf_file failed:$!";
132             }
133             else {
134 0         0 note ('starting test without original config data, i.e. from scratch');
135             }
136 0         0 ok( 1, "Copied $test_group example $t_name" );
137              
138 0         0 return ( $wr_dir, $wr_dir2, $conf_file, $ex_data, @file_list );
139             }
140              
141             #
142             # New subroutine "list_test_files" extracted - Thu Nov 17 17:27:20 2011.
143             #
144             sub list_test_files {
145 0     0 0 0 my $debian_dir = shift;
146 0         0 my @file_list ;
147              
148 0         0 my $iter = $debian_dir->iterator({ recurse => 1 });
149 0         0 my $debian_str = $debian_dir->stringify;
150              
151 0         0 while ( my $child = $iter->() ) {
152 0 0       0 next if $child->is_dir ;
153              
154 0         0 push @file_list, '/' . $child->relative($debian_str)->stringify;
155             };
156              
157             # don't use return sort -> undefined behavior in scalar context.
158 0         0 my @res = sort @file_list;
159 0         0 return @res;
160             }
161              
162             sub write_config_file {
163 0     0 0 0 my ($conf_dir,$wr_dir,$t) = @_;
164              
165 0 0       0 if ($t->{config_file}) {
166 0 0       0 my $file = $conf_dir ? "$conf_dir/" : '';
167 0         0 $file .= $t->{config_file} ;
168 0         0 $wr_dir->child($file)->parent->mkpath({mode => oct(755)} ) ;
169             }
170             }
171              
172             sub check_load_warnings {
173 0     0 0 0 my ($root,$t) = @_ ;
174              
175 0 0 0     0 if ( my $info = $t->{log4perl_load_warnings} or $::_use_log4perl_to_warn) {
    0 0        
      0        
176 0   0     0 my $tw = Test::Log::Log4perl->expect( @{ $info // [] } );
  0         0  
177 0         0 $root->init;
178             }
179             elsif ( ($t->{no_warnings} or exists $t->{load_warnings}) and not defined $t->{load_warnings}) {
180 0         0 local $Config::Model::Value::nowarning = 1;
181 0         0 $root->init;
182 0         0 note("load_warnings param is DEPRECATED. Please use log4perl_load_warnings");
183 0         0 ok( 1,"Read configuration and created instance with init() method without warning check" );
184             }
185             else {
186 0     0   0 warnings_like { $root->init; } $t->{load_warnings},
187 0         0 "Read configuration and created instance with init() method with warning check ";
188             }
189             }
190              
191             sub run_update {
192 0     0 0 0 my ($inst, $dir, $t) = @_;
193 0         0 my %args = %{$t->{update}};
  0         0  
194              
195 0         0 my $ret = delete $args{returns};
196              
197 0   0     0 local $Config::Model::Value::nowarning = $args{no_warnings} || $t->{no_warnings} || 0;
198              
199 0         0 my $res ;
200 0 0       0 if ( my $info = $t->{log4perl_update_warnings}) {
    0          
201 0         0 my $tw = Test::Log::Log4perl->expect( $info );
202 0         0 note("updating config with log4perl warning check and args: ". join(' ',%args));
203 0         0 $res = $inst->update( from_dir => $dir, %args ) ;
204             }
205             elsif (my $uw = delete $args{update_warnings}) {
206 0         0 note("update_warnings param is DEPRECATED. Please use log4perl_update_warnings");
207 0         0 note("updating config with warning check and args: ". join(' ',%args));
208 0     0   0 warnings_like { $res = $inst->update( from_dir => $dir, %args ); } $uw,
  0         0  
209             "Updated configuration with warning check ";
210             }
211             else {
212 0         0 note("updating config with no warning check and args: ". join(' ',%args));
213 0         0 $res = $inst->update( from_dir => $dir, %args ) ;
214             }
215              
216 0 0       0 if (defined $ret) {
217 0         0 is($res,$ret,"updated configuration, got expected return value");
218             }
219             else {
220 0         0 ok(1,"dumped configuration");
221             }
222             }
223              
224             sub load_instructions {
225 0     0 0 0 my ($root,$steps,$trace) = @_ ;
226              
227 0 0       0 print "Loading $steps\n" if $trace ;
228 0         0 $root->load( $steps );
229 0         0 ok( 1, "load called" );
230             }
231              
232             sub apply_fix {
233 0     0 0 0 my $inst = shift;
234 0         0 local $Config::Model::Value::nowarning = 1;
235 0         0 $inst->apply_fixes;
236 0         0 ok( 1, "apply_fixes called" );
237             }
238              
239             sub dump_tree {
240 0     0 0 0 my ($test_group, $root, $mode, $no_warnings, $t, $trace) = @_;
241              
242 0 0       0 print "dumping tree ...\n" if $trace;
243 0         0 my $dump = '';
244             my $risky = sub {
245 0     0   0 $dump = $root->dump_tree( mode => $mode );
246 0         0 };
247              
248 0 0       0 if ( defined $t->{dump_errors} ) {
249 0         0 my $nb = 0;
250 0         0 my @tf = @{ $t->{dump_errors} };
  0         0  
251 0         0 while (@tf) {
252 0         0 my $qr = shift @tf;
253 0     0   0 throws_ok { &$risky } $qr, "Failed dump $nb of $test_group config tree";
  0         0  
254 0         0 my $fix = shift @tf;
255 0         0 $root->load($fix);
256 0         0 ok( 1, "Fixed error nb " . $nb++ );
257             }
258             }
259              
260 0 0 0     0 if ( my $info = $t->{log4perl_dump_warnings} or $::_use_log4perl_to_warn) {
    0 0        
      0        
261 0         0 note("checking logged warning while dumping");
262 0   0     0 my $tw = Test::Log::Log4perl->expect( @{$info // [] } );
  0         0  
263 0         0 $risky->();
264             }
265             elsif ( ($no_warnings or (exists $t->{dump_warnings}) and not defined $t->{dump_warnings}) ) {
266 0         0 local $Config::Model::Value::nowarning = 1;
267 0 0       0 note("dump_warnings parameter is DEPRECATED") if exists $t->{dump_warnings};
268 0         0 &$risky;
269 0         0 ok( 1, "Ran dump_tree (no warning check)" );
270             }
271             else {
272 0 0       0 note("dump_warnings parameter is DEPRECATED") if $t->{dump_warnings};
273 0     0   0 warnings_like { &$risky; } $t->{dump_warnings}, "Ran dump_tree";
  0         0  
274             }
275 0         0 ok( $dump, "Dumped $test_group config tree in $mode mode" );
276              
277 0 0       0 print $dump if $trace;
278 0         0 return $dump;
279             }
280              
281             sub check_data {
282 0     0 0 0 my ($label, $root, $c, $nw) = @_;
283              
284 0   0     0 local $Config::Model::Value::nowarning = $nw || 0;
285             my @checks = ref $c eq 'ARRAY' ? @$c
286 0 0       0 : map { ( $_ => $c->{$_})} sort keys %$c ;
  0         0  
287              
288 0         0 while (@checks) {
289 0         0 my $path = shift @checks;
290 0         0 my $v = shift @checks;
291 0         0 check_one_item($label, $root,$path, $v);
292             }
293             }
294              
295             sub check_one_item {
296 0     0 0 0 my ($label, $root,$path, $check_data_l) = @_;
297              
298 0 0       0 my @checks = ref $check_data_l eq 'ARRAY' ? @$check_data_l : ($check_data_l);
299              
300 0         0 foreach my $check_data (@checks) {
301 0 0       0 my $check_v_l = ref $check_data eq 'HASH' ? delete $check_data->{value} : $check_data;
302 0 0       0 my @check_args = ref $check_data eq 'HASH' ? %$check_data : ();
303 0 0       0 my $check_str = @check_args ? " (@check_args)" : '';
304 0         0 my $obj = $root->grab( step => $path, type => ['leaf','check_list'], @check_args );
305 0         0 my $got = $obj->fetch(@check_args);
306              
307 0 0       0 my @check_v = ref($check_v_l) eq 'ARRAY' ? @$check_v_l : ($check_v_l);
308 0         0 foreach my $check_v (@check_v) {
309 0 0       0 if (ref $check_v eq 'Regexp') {
310 0         0 like( $got, $check_v, "$label check '$path' value with regexp$check_str" );
311             }
312             else {
313 0         0 is( $got, $check_v, "$label check '$path' value$check_str" );
314             }
315             }
316             }
317             }
318              
319             sub check_annotation {
320 0     0 0 0 my ($root, $t) = @_;
321              
322 0         0 my $annot_check = $t->{verify_annotation};
323 0         0 foreach my $path (keys %$annot_check) {
324 0         0 my $note = $annot_check->{$path};
325 0         0 is( $root->grab($path)->annotation, $note, "check $path annotation" );
326             }
327             }
328              
329             sub has_key {
330 0     0 0 0 my ($root, $c, $nw) = @_;
331              
332 0         0 _test_key($root, $c, $nw, 0);
333             }
334              
335             sub has_not_key {
336 0     0 0 0 my ($root, $c, $nw) = @_;
337              
338 0         0 _test_key($root, $c, $nw, 1);
339             }
340              
341             sub _test_key {
342 0     0   0 my ($root, $c, $nw, $invert) = @_;
343              
344             my @checks = ref $c eq 'ARRAY' ? @$c
345 0 0       0 : map { ( $_ => $c->{$_})} sort keys %$c ;
  0         0  
346              
347 0         0 while (@checks) {
348 0         0 my $path = shift @checks;
349 0         0 my $spec = shift @checks;
350 0 0       0 my @key_checks = ref $spec eq 'ARRAY' ? @$spec: ($spec);
351              
352 0         0 my $obj = $root->grab( step => $path, type => 'hash' );
353 0         0 my @keys = $obj->fetch_all_indexes;
354 0         0 my $res = 0;
355 0         0 foreach my $check (@key_checks) {
356 0         0 my @match ;
357 0         0 foreach my $k (@keys) {
358 0 0       0 if (ref $check eq 'Regexp') {
359 0 0       0 push @match, $k if $k =~ $check;
360             }
361             else {
362 0 0       0 push @match, $k if $k eq $check;
363             }
364             }
365 0 0       0 if ($invert) {
366 0         0 is(scalar @match,0, "check $check matched no key" );
367             }
368             else {
369 0         0 ok(scalar @match, "check $check matched with keys @match" );
370             }
371             }
372             }
373             }
374              
375             sub write_data_back {
376 0     0 0 0 my ($test_group, $inst, $t) = @_;
377 0   0     0 local $Config::Model::Value::nowarning = $t->{no_warnings} || 0;
378 0         0 $inst->write_back( force => 1 );
379 0         0 ok( 1, "$test_group write back done" );
380             }
381              
382             sub check_file_mode {
383 0     0 0 0 my ($wr_dir, $t) = @_;
384              
385 0 0 0     0 if ($^O eq 'MSWin32' and my $fm = $t->{file_mode}) {
386 0         0 note("skipping file mode tests on Windows");
387 0         0 return;
388             }
389              
390 0 0       0 if (my $fm = $t->{file_mode}) {
391 0         0 foreach my $f (keys %$fm) {
392 0         0 my $expected_mode = $fm->{$f} ;
393 0         0 my $stat = $wr_dir->child($f)->stat;
394 0         0 ok($stat ,"stat found file $f");
395 0 0       0 if ($stat) {
396 0         0 my $mode = $stat->mode & oct(7777) ;
397 0         0 is($mode, $expected_mode, sprintf("check $f mode (got %o vs %o)",$mode,$expected_mode));
398             }
399             }
400             }
401             }
402              
403             sub check_file_content {
404 0     0 0 0 my ($wr_dir, $t) = @_;
405              
406 0 0 0     0 if (my $fc = $t->{file_contents} || $t->{file_content}) {
407 0         0 foreach my $f (keys %$fc) {
408 0         0 my $t = $fc->{$f} ;
409 0 0       0 my @tests = ref $t eq 'ARRAY' ? @$t : ($t) ;
410 0         0 foreach my $subtest (@tests) {
411 0         0 file_contents_eq_or_diff $wr_dir->child($f)->stringify, $subtest, { encoding => 'UTF-8' },
412             "check that $f contains $subtest";
413             }
414             }
415             }
416              
417 0 0       0 if (my $fc = $t->{file_contents_like}) {
418 0         0 foreach my $f (keys %$fc) {
419 0         0 my $t = $fc->{$f} ;
420 0 0       0 my @tests = ref $t eq 'ARRAY' ? @$t : ($t) ;
421 0         0 foreach my $subtest (@tests) {
422 0         0 file_contents_like $wr_dir->child($f)->stringify, $subtest, { encoding => 'UTF-8' },
423             "check that $f matches regexp $subtest";
424             }
425             }
426             }
427              
428 0 0       0 if (my $fc = $t->{file_contents_unlike}) {
429 0         0 foreach my $f (keys %$fc) {
430 0         0 my $t = $fc->{$f} ;
431 0 0       0 my @tests = ref $t eq 'ARRAY' ? @$t : ($t) ;
432 0         0 foreach my $subtest (@tests) {
433 0         0 file_contents_unlike $wr_dir->child($f)->stringify, $subtest, { encoding => 'UTF-8' },
434             "check that $f does not match regexp $subtest";
435             }
436             }
437             }
438             }
439              
440             sub check_added_or_removed_files {
441 0     0 0 0 my ( $conf_dir, $wr_dir, $t, @file_list) = @_;
442              
443             # copy whole dir
444             my $destination_dir
445 0 0       0 = $t->{setup} ? $wr_dir
    0          
446             : $conf_dir ? $wr_dir->child($conf_dir)
447             : $wr_dir ;
448 0         0 my @new_file_list = list_test_files($destination_dir) ;
449 0 0       0 $t->{file_check_sub}->( \@file_list ) if defined $t->{file_check_sub};
450 0         0 eq_or_diff( \@new_file_list, [ sort @file_list ], "check added or removed files" );
451             }
452              
453             sub create_second_instance {
454 0     0 0 0 my ($model, $test_group, $t_name, $wr_dir, $wr_dir2, $test_suite_data, $t, $config_dir_override) = @_;
455              
456             # create another instance to read the conf file that was just written
457 0 0       0 dircopy( $wr_dir->stringify, $wr_dir2->stringify )
458             or die "can't copy from $wr_dir to $wr_dir2: $!";
459              
460 0         0 my @options;
461 0 0       0 push @options, backend_arg => $t->{backend_arg} if $t->{backend_arg};
462              
463             my $i2_test = $model->instance(
464             root_class_name => $test_suite_data->{model_to_test},
465             root_dir => $wr_dir2->stringify,
466             config_file => $t->{config_file} ,
467             instance_name => "$test_group-$t_name-w",
468             application => $test_suite_data->{app_to_test},
469 0   0     0 check => $t->{load_check2} || 'yes',
470             config_dir => $config_dir_override,
471             @options
472             );
473              
474 0         0 ok( $i2_test, "Created instance $test_group-test-$t_name-w" );
475              
476 0   0     0 local $Config::Model::Value::nowarning = $t->{no_warnings} || 0;
477 0         0 my $i2_root = $i2_test->config_root;
478 0         0 $i2_root->init;
479              
480 0         0 return $i2_root;
481             }
482              
483             sub create_test_class {
484 0     0 0 0 my ($model, $config_classes) = @_;
485 0 0       0 return unless $config_classes;
486              
487 0         0 foreach my $c ( @$config_classes) {
488 0 0       0 my @parms = ref($c) eq 'HASH' ? %$c : @$c;
489 0         0 $model->create_config_class(@parms);
490             }
491             }
492              
493             our ($model, $conf_file_name, $conf_dir, $model_to_test, $app_to_test, $home_for_test, @tests, $skip);
494              
495             sub load_test_suite_data {
496 0     0 0 0 my ($model_obj, $test_group, $test_group_conf) = @_;
497              
498 0         0 local ($model, $conf_file_name, $conf_dir, $model_to_test, $app_to_test, $home_for_test, @tests, $skip);
499              
500 0         0 $skip = 0;
501 0         0 undef $conf_file_name ;
502 0         0 undef $conf_dir ;
503 0         0 undef $home_for_test ;
504 0         0 undef $model_to_test ; # deprecated
505 0         0 undef $app_to_test;
506 0         0 $model = $model_obj; # $model is used by Config::Model tests
507              
508 0         0 note("Beginning $test_group test ($test_group_conf)");
509              
510 0         0 my $result;
511 0 0       0 unless ( $result = do "./$test_group_conf" ) {
512 0 0       0 warn "couldn't parse $test_group_conf: $@" if $@;
513 0 0       0 warn "couldn't do $test_group_conf: $!" unless defined $result;
514 0 0       0 warn "couldn't run $test_group_conf" unless $result;
515             }
516              
517 0         0 my $test_suite_data;
518 0 0       0 if (ref($result) eq 'ARRAY') {
    0          
519             # simple list of tests
520 0         0 $test_suite_data = { tests => $result };
521             }
522             elsif (ref($result) eq 'HASH') {
523 0         0 $test_suite_data = $result;
524             }
525             else {
526 0         0 note(qq!warning: $test_group_conf should return a data structure instead of "1;". !
527             . qq!See Config::Model::Tester for details!);
528 0         0 $test_suite_data = {
529             tests => [ @tests ],
530             skip => $skip,
531             conf_file_name => $conf_file_name ,
532             conf_dir => $conf_dir ,
533             home_for_test => $home_for_test ,
534             model_to_test => $model_to_test,
535             app_to_test => $app_to_test,
536             };
537             }
538              
539 0         0 create_test_class($model, $test_suite_data->{config_classes});
540              
541 0   0     0 $test_suite_data->{app_to_test} ||= $test_group;
542              
543 0 0       0 if ($test_suite_data->{skip}) {
544 0         0 note("Skipped $test_group test ($test_group_conf)");
545 0         0 return;
546             }
547              
548 0         0 my ($trash, $appli_info, $applications) = Config::Model::Lister::available_models(1);
549 0         0 $test_suite_data->{appli_info} = $appli_info;
550              
551             # even undef, this resets the global variable there
552 0         0 Config::Model::BackendMgr::_set_test_home($test_suite_data->{home_for_test}) ;
553              
554 0 0       0 if (not defined $test_suite_data->{model_to_test}) {
555 0         0 $test_suite_data->{model_to_test} = $applications->{$test_suite_data->{app_to_test}};
556 0 0       0 if (not defined $test_suite_data->{model_to_test}) {
557 0         0 my @k = sort values %$applications;
558 0   0     0 my @files = map { $_->{_file} // 'unknown' } values %$appli_info ;
  0         0  
559 0         0 die "Cannot find application or model for $test_group in files >@files<. Known applications are",
560             sort keys %$applications, ". Known models are >@k<. ".
561             "Check your test name (the file ending with -test-conf.pl) or set app_to_test parameter\n";
562             }
563             }
564              
565 0         0 return $test_suite_data;
566             }
567              
568             sub run_model_test {
569 0     0 0 0 my ($test_group, $test_group_conf, $do, $model, $trace, $wr_root) = @_ ;
570              
571 0         0 my $test_suite_data = load_test_suite_data($model,$test_group, $test_group_conf);
572 0         0 my $appli_info = $test_suite_data->{appli_info};
573              
574 0         0 my $config_dir_override = $appli_info->{$test_group}{config_dir}; # may be undef
575              
576 0         0 my $note ="$test_group uses ".$test_suite_data->{model_to_test}." model";
577 0         0 my $conf_file_name = $test_suite_data->{conf_file_name};
578 0 0       0 $note .= " on file $conf_file_name" if defined $conf_file_name;
579 0         0 note($note);
580              
581 0         0 my $idx = 0;
582 0         0 foreach my $t (@{$test_suite_data->{tests}}) {
  0         0  
583 0         0 translate_test_data($t);
584 0   0     0 my $t_name = $t->{name} || "t$idx";
585 0 0 0     0 if ( defined $do and $t_name !~ /$do/) {
586 0         0 $idx++;
587 0         0 next;
588             }
589 0         0 note("Beginning subtest $test_group $t_name");
590              
591 0         0 my ($wr_dir, $wr_dir2, $conf_file, $ex_data, @file_list)
592             = setup_test ($test_group, $t_name, $wr_root,$trace, $test_suite_data, $t);
593              
594 0         0 write_config_file($test_suite_data->{conf_dir},$wr_dir,$t);
595              
596 0         0 my $inst_name = "$test_group-" . $t_name;
597              
598 0 0       0 die "Duplicated test name $t_name for app $test_group\n"
599             if $model->has_instance ($inst_name);
600              
601 0         0 my @options;
602 0 0       0 push @options, backend_arg => $t->{backend_arg} if $t->{backend_arg};
603              
604             # eventually, we may end up with several instances of Dpkg
605             # model in the same process. So we can't play with chdir
606             my $inst = $model->instance(
607             root_class_name => $test_suite_data->{model_to_test},
608             # need to keed root_dir to handle config files like
609             # /etc/foo.ini (absolute path, like in /etc/)
610             root_dir => $wr_dir->stringify,
611             instance_name => $inst_name,
612             application => $test_suite_data->{app_to_test},
613             config_file => $t->{config_file} ,
614 0   0     0 check => $t->{load_check} || 'yes',
615             config_dir => $config_dir_override,
616             @options
617             );
618              
619 0         0 my $root = $inst->config_root;
620              
621 0         0 check_load_warnings ($root,$t);
622              
623 0 0       0 run_update($inst,$wr_dir,$t) if $t->{update};
624              
625 0 0       0 load_instructions ($root,$t->{load},$trace) if $t->{load} ;
626              
627             dump_tree ('before fix '.$test_group , $root, 'full', $t->{no_warnings}, $t->{check_before_fix}, $trace)
628 0 0       0 if $t->{check_before_fix};
629              
630 0 0       0 apply_fix($inst) if $t->{apply_fix};
631              
632 0         0 dump_tree ($test_group, $root, 'full', $t->{no_warnings}, $t->{full_dump}, $trace) ;
633              
634 0         0 my $dump = dump_tree ($test_group, $root, 'custom', $t->{no_warnings}, {}, $trace) ;
635              
636 0 0       0 check_data("first", $root, $t->{check}, $t->{no_warnings}) if $t->{check};
637              
638 0 0       0 has_key ( $root, $t->{has_key}, $t->{no_warnings}) if $t->{has_key} ;
639 0 0       0 has_not_key ( $root, $t->{has_not_key}, $t->{no_warnings}) if $t->{has_not_key} ;
640              
641 0 0       0 check_annotation($root,$t) if $t->{verify_annotation};
642              
643 0         0 write_data_back ($test_group, $inst, $t) ;
644              
645 0         0 check_file_content($wr_dir,$t) ;
646              
647 0         0 check_file_mode($wr_dir,$t) ;
648              
649 0 0       0 check_added_or_removed_files ($test_suite_data->{conf_dir}, $wr_dir, $t, @file_list) if $ex_data->is_dir;
650              
651 0         0 my $i2_root = create_second_instance ($model, $test_group, $t_name, $wr_dir, $wr_dir2, $test_suite_data, $t, $config_dir_override);
652              
653 0 0       0 load_instructions ($i2_root,$t->{load2},$trace) if $t->{load2} ;
654              
655 0         0 my $p2_dump = dump_tree("second $test_group", $i2_root, 'custom', $t->{no_warnings},{}, $trace) ;
656              
657 0         0 unified_diff;
658 0         0 eq_or_diff(
659             [ split /\n/,$p2_dump ],
660             [ split /\n/,$dump ],
661             "compare original $test_group custom data with 2nd instance custom data",
662             );
663              
664             ok( -s "$wr_dir2/$test_suite_data->{conf_dir}/$test_suite_data->{conf_file_name}" ,
665             "check that original $test_group file was not clobbered" )
666 0 0       0 if defined $test_suite_data->{conf_file_name} ;
667              
668 0 0       0 check_data("second", $i2_root, $t->{wr_check}, $t->{no_warnings}) if $t->{wr_check} ;
669              
670 0         0 note("End of subtest $test_group $t_name");
671              
672 0         0 $idx++;
673             }
674 0         0 note("End of $test_group test");
675              
676             }
677              
678             sub translate_test_data {
679 0     0 0 0 my $t = shift;
680 0 0       0 map {$t->{full_dump}{$_} = delete $t->{$_} if $t->{$_}; } qw/dump_warnings dump_errors/;
  0         0  
681             }
682              
683             sub create_model_object {
684 0     0 0 0 my $new_model ;
685 0         0 eval { $new_model = Config::Model->new(); } ;
  0         0  
686 0 0       0 if ($@) {
687             # necessary to run smoke test (no Config::Model to avoid dependency loop)
688 0         0 plan skip_all => 'Config::Model is not loaded' ;
689 0         0 return;
690             }
691 0         0 return $new_model;
692             }
693              
694             sub run_tests {
695 1     1 0 80 my ( $test_only_app, $do, $trace, $wr_root );
696 1         0 my $model;
697 1 50       4 if (@_) {
698 1         2 my $arg;
699 1         6 note ("Calling run_tests with argument is deprecated");
700 1         489 ( $arg, $test_only_app, $do ) = @_;
701              
702 1         4 my $log = 0;
703              
704 1 50       4 $trace = ($arg =~ /t/) ? 1 : 0;
705 1 50       4 $log = 1 if $arg =~ /l/;
706              
707 1   50     6 my $log4perl_user_conf_file = ($ENV{HOME} || '') . '/.log4config-model';
708              
709 1 50 33     6 if ( $log and -e $log4perl_user_conf_file ) {
710 0         0 Log::Log4perl::init($log4perl_user_conf_file);
711             }
712             else {
713 1 50       9 Log::Log4perl->easy_init( $log ? $WARN : $ERROR );
714             }
715              
716 1 50       3857 Config::Model::Exception::Any->Trace(1) if $arg =~ /e/;
717              
718 1         6 ok( 1, "compiled" );
719              
720             # pseudo root where config files are written by config-model
721 1         318 $wr_root = path('wr_root');
722             }
723             else {
724 0         0 ($model, $trace) = init_test();
725 0         0 ( $test_only_app, $do) = @ARGV;
726             # pseudo root where config files are written by config-model
727 0         0 $wr_root = setup_test_dir();
728             }
729              
730 1         88 my @group_of_tests = grep { /-test-conf.pl$/ } glob("t/model_tests.d/*");
  0         0  
731              
732 1         5 foreach my $test_group_conf (@group_of_tests) {
733 0         0 my ($test_group) = ( $test_group_conf =~ m!\.d/([\w\-]+)-test-conf! );
734 0 0 0     0 next if ( $test_only_app and $test_only_app ne $test_group ) ;
735 0         0 $model = create_model_object();
736 0 0       0 return unless $model;
737 0         0 run_model_test($test_group, $test_group_conf, $do, $model, $trace, $wr_root) ;
738             }
739              
740 1         7 memory_cycle_ok($model,"test memory cycle") ;
741              
742 1         293 done_testing;
743              
744             }
745             1;
746              
747             __END__