File Coverage

blib/lib/Config/Model/Tester.pm
Criterion Covered Total %
statement 67 415 16.1
branch 6 202 2.9
condition 2 74 2.7
subroutine 17 46 36.9
pod 0 24 0.0
total 92 761 12.0


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-2020 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.006;
11             # ABSTRACT: Test framework for Config::Model
12              
13 1     1   679 use warnings;
  1         8  
  1         44  
14 1     1   7 use strict;
  1         1  
  1         31  
15 1     1   521 use locale;
  1         688  
  1         5  
16 1     1   672 use utf8;
  1         15  
  1         4  
17 1     1   46 use 5.12.0;
  1         5  
18              
19 1     1   1006 use Test::More;
  1         68369  
  1         8  
20 1     1   1193 use Log::Log4perl 1.11 qw(:easy :levels);
  1         48341  
  1         6  
21 1     1   1695 use Path::Tiny;
  1         13929  
  1         69  
22 1     1   711 use File::Copy::Recursive qw(fcopy rcopy dircopy);
  1         6966  
  1         75  
23              
24 1     1   584 use Test::Warn;
  1         4134  
  1         69  
25 1     1   522 use Test::Exception;
  1         1729  
  1         4  
26 1     1   778 use Test::File::Contents ;
  1         11597  
  1         111  
27 1     1   596 use Test::Differences;
  1         8923  
  1         74  
28 1     1   492 use Test::Memory::Cycle ;
  1         5093  
  1         6  
29              
30 1     1   611 use Config::Model::Tester::Setup qw/init_test setup_test_dir/;
  1         3  
  1         138  
31              
32             # use eval so this module does not have a "hard" dependency on Config::Model
33             # This way, Config::Model can build-depend on Config::Model::Tester without
34             # creating a build dependency loop.
35             eval {
36             require Config::Model;
37             require Config::Model::Lister;
38             require Config::Model::Value;
39             require Config::Model::BackendMgr;
40             } ;
41              
42 1     1   7 use vars qw/@ISA @EXPORT/;
  1         3  
  1         6350  
43              
44             require Exporter;
45             @ISA = qw(Exporter);
46             @EXPORT = qw(run_tests);
47              
48             $File::Copy::Recursive::DirPerms = oct(755);
49              
50             sub setup_test {
51 0     0 0 0 my ( $test_group, $t_name, $wr_root, $trace, $test_suite_data, $t_data ) = @_;
52              
53             # cleanup before tests
54 0         0 $wr_root->remove_tree();
55 0         0 $wr_root->mkpath( { mode => oct(755) } );
56             my ($conf_dir, $conf_file_name, $home_for_test)
57 0         0 = @$test_suite_data{qw/conf_dir conf_file_name home_for_test/};
58              
59 0 0 0     0 if ($conf_dir and $home_for_test) {
60 0         0 $conf_dir =~ s!~/!$home_for_test/!;
61 0         0 $test_suite_data->{conf_dir} = $conf_dir;
62             }
63              
64 0         0 my $wr_dir = $wr_root->child('test-' . $t_name);
65 0         0 my $wr_dir2 = $wr_root->child('test-' . $t_name.'-w');
66 0         0 $wr_dir->mkpath;
67 0         0 $wr_dir2->mkpath;
68              
69 0         0 my $conf_file ;
70 0 0 0     0 $conf_file = $wr_dir->child($conf_dir,$conf_file_name)
71             if $conf_dir and $conf_file_name;
72              
73 0         0 my $ex_dir = path('t')->child('model_tests.d', "$test_group-examples");
74 0   0     0 my $ex_data = $ex_dir->child($t_data->{data_from} // $t_name);
75              
76 0         0 my @file_list;
77              
78 0 0       0 if (my $setup = $t_data->{setup}) {
    0          
    0          
79 0         0 foreach my $file (keys %$setup) {
80 0         0 my $map = $setup->{$file} ;
81             my $destination_str
82             = ref ($map) eq 'HASH' ? $map->{$^O} // $map->{default}
83 0 0 0     0 : ref ($map) eq 'ARRAY' ? $map->[-1]
    0          
84             : $map;
85 0 0       0 if (not defined $destination_str) {
86 0         0 die "$test_group $t_name setup error: cannot find destination for test file $file" ;
87             }
88 0 0       0 $destination_str =~ s!~/!$home_for_test/! if $home_for_test;
89 0         0 my $destination = $wr_dir->child($destination_str) ;
90 0         0 $destination->parent->mkpath( { mode => oct(755) }) ;
91 0         0 my $data_file = $ex_data->child($file);
92 0 0       0 die "cannot find $data_file" unless $data_file->exists;
93 0         0 my $data = $data_file->slurp() ;
94 0         0 $destination->spew( $data );
95 0 0       0 if (ref $map eq 'ARRAY') {
96 0         0 my @tmp = @$map;
97 0         0 pop @tmp; # remove destination
98 0         0 foreach my $link_str (@tmp) {
99 0 0       0 $link_str =~ s!~/!$home_for_test/! if $home_for_test;
100 0         0 my $link = $wr_dir->child($link_str);
101 0         0 $link->parent->mkpath( { mode => oct(755) }) ;
102 0         0 symlink $destination->absolute->stringify, $link->stringify;
103             }
104             }
105 0         0 @file_list = list_test_files ($wr_dir);
106             }
107             }
108             elsif ( $ex_data->is_dir ) {
109             # copy whole dir
110 0 0       0 my $destination_dir = $conf_dir ? $wr_dir->child($conf_dir) : $wr_dir ;
111 0         0 $destination_dir->mkpath( { mode => oct(755) });
112 0 0       0 say "dircopy ". $ex_data->stringify . '->'. $destination_dir->stringify
113             if $trace ;
114 0 0       0 dircopy( $ex_data->stringify, $destination_dir->stringify )
115             || die "dircopy $ex_data -> $destination_dir failed:$!";
116 0         0 @file_list = list_test_files ($destination_dir);
117             }
118             elsif ( $ex_data->exists ) {
119             # either one if true if $conf_file is undef
120 0 0       0 die "test data is missing global \$conf_dir" unless defined $conf_dir;
121 0 0       0 die "test data is missing global \$conf_file_name" unless defined $conf_file;
122              
123             # just copy file
124 0 0       0 say "file copy ". $ex_data->stringify . '->'. $conf_file->stringify
125             if $trace ;
126 0 0       0 fcopy( $ex_data->stringify, $conf_file->stringify )
127             || die "copy $ex_data -> $conf_file failed:$!";
128             }
129             else {
130 0         0 note ('starting test without original config data, i.e. from scratch');
131             }
132 0         0 ok( 1, "Copied $test_group example $t_name" );
133              
134 0         0 return ( $wr_dir, $wr_dir2, $conf_file, $ex_data, @file_list );
135             }
136              
137             #
138             # New subroutine "list_test_files" extracted - Thu Nov 17 17:27:20 2011.
139             #
140             sub list_test_files {
141 0     0 0 0 my $debian_dir = shift;
142 0         0 my @file_list ;
143              
144 0         0 my $iter = $debian_dir->iterator({ recurse => 1 });
145 0         0 my $debian_str = $debian_dir->stringify;
146              
147 0         0 while ( my $child = $iter->() ) {
148 0 0       0 next if $child->is_dir ;
149              
150 0         0 push @file_list, '/' . $child->relative($debian_str)->stringify;
151             };
152              
153             # don't use return sort -> undefined behavior in scalar context.
154 0         0 my @res = sort @file_list;
155 0         0 return @res;
156             }
157              
158             sub write_config_file {
159 0     0 0 0 my ($conf_dir,$wr_dir,$t) = @_;
160              
161 0 0       0 if ($t->{config_file}) {
162 0 0       0 my $file = $conf_dir ? "$conf_dir/" : '';
163 0         0 $file .= $t->{config_file} ;
164 0         0 $wr_dir->child($file)->parent->mkpath({mode => oct(755)} ) ;
165             }
166             }
167              
168             sub check_load_warnings {
169 0     0 0 0 my ($root,$t) = @_ ;
170              
171 0 0 0     0 if ( my $info = $t->{log4perl_load_warnings} or $::_use_log4perl_to_warn) {
    0 0        
      0        
172 0   0     0 my $tw = Test::Log::Log4perl->expect( @{ $info // [] } );
  0         0  
173 0         0 $root->init;
174             }
175             elsif ( ($t->{no_warnings} or exists $t->{load_warnings}) and not defined $t->{load_warnings}) {
176 0         0 local $Config::Model::Value::nowarning = 1;
177 0         0 $root->init;
178 0         0 note("load_warnings param is DEPRECATED. Please use log4perl_load_warnings");
179 0         0 ok( 1,"Read configuration and created instance with init() method without warning check" );
180             }
181             else {
182 0     0   0 warnings_like { $root->init; } $t->{load_warnings},
183 0         0 "Read configuration and created instance with init() method with warning check ";
184             }
185             }
186              
187             sub run_update {
188 0     0 0 0 my ($inst, $dir, $t) = @_;
189 0         0 my %args = %{$t->{update}};
  0         0  
190              
191 0         0 my $ret = delete $args{returns};
192              
193 0   0     0 local $Config::Model::Value::nowarning = $args{no_warnings} || $t->{no_warnings} || 0;
194              
195 0         0 my $res ;
196 0 0       0 if ( my $info = $t->{log4perl_update_warnings}) {
    0          
197 0         0 my $tw = Test::Log::Log4perl->expect( $info );
198 0         0 note("updating config with log4perl warning check and args: ". join(' ',%args));
199 0         0 $res = $inst->update( from_dir => $dir, %args ) ;
200             }
201             elsif (my $uw = delete $args{update_warnings}) {
202 0         0 note("update_warnings param is DEPRECATED. Please use log4perl_update_warnings");
203 0         0 note("updating config with warning check and args: ". join(' ',%args));
204 0     0   0 warnings_like { $res = $inst->update( from_dir => $dir, %args ); } $uw,
  0         0  
205             "Updated configuration with warning check ";
206             }
207             else {
208 0         0 note("updating config with no warning check and args: ". join(' ',%args));
209 0         0 $res = $inst->update( from_dir => $dir, %args ) ;
210             }
211              
212 0 0       0 if (defined $ret) {
213 0         0 is($res,$ret,"updated configuration, got expected return value");
214             }
215             else {
216 0         0 ok(1,"dumped configuration");
217             }
218             }
219              
220             sub load_instructions {
221 0     0 0 0 my ($root,$steps,$trace) = @_ ;
222              
223 0 0       0 print "Loading $steps\n" if $trace ;
224 0         0 $root->load( $steps );
225 0         0 ok( 1, "load called" );
226             }
227              
228             sub apply_fix {
229 0     0 0 0 my $inst = shift;
230 0         0 local $Config::Model::Value::nowarning = 1;
231 0         0 $inst->apply_fixes;
232 0         0 ok( 1, "apply_fixes called" );
233             }
234              
235             sub dump_tree {
236 0     0 0 0 my ($test_group, $root, $mode, $no_warnings, $t, $test_logs, $trace) = @_;
237              
238 0 0       0 print "dumping tree ...\n" if $trace;
239 0         0 my $dump = '';
240             my $risky = sub {
241 0     0   0 $dump = $root->dump_tree( mode => $mode );
242 0         0 };
243              
244 0 0       0 if ( defined $t->{dump_errors} ) {
245 0         0 my $nb = 0;
246 0         0 my @tf = @{ $t->{dump_errors} };
  0         0  
247 0         0 while (@tf) {
248 0         0 my $qr = shift @tf;
249 0     0   0 throws_ok { &$risky } $qr, "Failed dump $nb of $test_group config tree";
  0         0  
250 0         0 my $fix = shift @tf;
251 0         0 $root->load($fix);
252 0         0 ok( 1, "Fixed error nb " . $nb++ );
253             }
254             }
255              
256 0 0 0     0 if ( $test_logs and (my $info = $t->{log4perl_dump_warnings} or $::_use_log4perl_to_warn)) {
    0 0        
    0 0        
      0        
257 0         0 note("checking logged warning while dumping");
258 0   0     0 my $tw = Test::Log::Log4perl->expect( @{$info // [] } );
  0         0  
259 0         0 $risky->();
260             }
261             elsif ( not $test_logs or $no_warnings ) {
262 0         0 local $Config::Model::Value::nowarning = 1;
263 0         0 &$risky;
264 0         0 ok( 1, "Ran dump_tree (no warning check)" );
265             }
266             elsif ( exists $t->{dump_warnings} and not defined $t->{dump_warnings} ) {
267 0         0 local $Config::Model::Value::nowarning = 1;
268 0         0 &$risky;
269 0         0 ok( 1, "Ran dump_tree with DEPRECATED dump_warnings parameter (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, $test_logs) = @_ ;
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       0 check_load_warnings ($root,$t) if $test_logs;
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}, $test_logs, $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}, $test_logs, $trace) ;
633              
634 0         0 my $dump = dump_tree ($test_group, $root, 'custom', $t->{no_warnings}, {}, $test_logs, $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},{}, $test_logs, $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 95 my ( $test_only_app, $do, $trace, $wr_root );
696 1         0 my $model;
697 1         0 my $test_logs;
698 1 50       4 if (@_) {
699 1         2 my $arg;
700 1         5 note ("Calling run_tests with argument is deprecated");
701 1         568 ( $arg, $test_only_app, $do ) = @_;
702              
703 1         3 my $log = 0;
704              
705 1 50       5 $trace = ($arg =~ /t/) ? 1 : 0;
706 1 50       4 $log = 1 if $arg =~ /l/;
707              
708 1   50     7 my $log4perl_user_conf_file = ($ENV{HOME} || '') . '/.log4config-model';
709              
710 1 50 33     6 if ( $log and -e $log4perl_user_conf_file ) {
711 0         0 Log::Log4perl::init($log4perl_user_conf_file);
712             }
713             else {
714 1 50       10 Log::Log4perl->easy_init( $log ? $WARN : $ERROR );
715             }
716              
717 1 50       4177 Config::Model::Exception::Any->Trace(1) if $arg =~ /e/;
718              
719 1         6 ok( 1, "compiled" );
720              
721             # pseudo root where config files are written by config-model
722 1         384 $wr_root = path('wr_root');
723             }
724             else {
725 0         0 my $opts;
726 0         0 ($model, $trace, $opts) = init_test();
727 0 0       0 $test_logs = $opts->{log} ? 0 : 1;
728 0         0 ( $test_only_app, $do) = @ARGV;
729             # pseudo root where config files are written by config-model
730 0         0 $wr_root = setup_test_dir();
731             }
732              
733 1         96 my @group_of_tests = grep { /-test-conf.pl$/ } glob("t/model_tests.d/*");
  0         0  
734              
735 1         6 foreach my $test_group_conf (@group_of_tests) {
736 0         0 my ($test_group) = ( $test_group_conf =~ m!\.d/([\w\-]+)-test-conf! );
737 0 0 0     0 next if ( $test_only_app and $test_only_app ne $test_group ) ;
738 0         0 $model = create_model_object();
739 0 0       0 return unless $model;
740 0         0 run_model_test($test_group, $test_group_conf, $do, $model, $trace, $wr_root, $test_logs) ;
741             }
742              
743 1         7 memory_cycle_ok($model,"test memory cycle") ;
744              
745 1         374 done_testing;
746              
747             }
748             1;
749              
750             __END__