Use the "modulino" pattern to facilitate unit tests

Now the `ddclient` file can be used as a script or as a module. For
details, see: https://www.drdobbs.com/scripts-as-modules/184416165

Addresses #147
This commit is contained in:
Richard Hansen 2020-06-07 22:55:52 -04:00
parent 8cbcecba99
commit f6f920eb39

View file

@ -18,6 +18,7 @@
#
#
######################################################################
package ddclient;
require v5.10.1;
use strict;
use warnings;
@ -25,7 +26,8 @@ use Getopt::Long;
use Sys::Hostname;
use IO::Socket;
my $version = "3.9.1";
use version 0.77; our $VERSION = version->declare('v3.9.1');
(my $version = $VERSION->stringify()) =~ s/^v//;
my $programd = $0;
$programd =~ s%^.*/%%;
my $program = $programd;
@ -64,6 +66,11 @@ local $lineno = '';
$ENV{'PATH'} = (exists($ENV{PATH}) ? "$ENV{PATH}:" : "") . "/sbin:/usr/sbin:/bin:/usr/bin:/etc:/usr/lib:";
my ($result, %config, %globals, %cache);
my $saved_cache;
my %saved_opt;
my $daemon;
sub T_ANY { 'any' }
sub T_STRING { 'string' }
sub T_EMAIL { 'e-mail address' }
@ -829,35 +836,35 @@ my @opt = (
" project now maintained on https://github.com/ddclient/ddclient"
);
## process args
my $opt_usage = process_args(@opt);
my ($result, %config, %globals, %cache);
my $saved_cache = '';
my %saved_opt = %opt;
$result = 'OK';
sub main {
## process args
my $opt_usage = process_args(@opt);
$saved_cache = '';
%saved_opt = %opt;
$result = 'OK';
test_geturl(opt('geturl')) if opt('geturl');
test_geturl(opt('geturl')) if opt('geturl');
if (opt('help')) {
if (opt('help')) {
printf "%s\n", $opt_usage;
exit 0;
}
}
## read config file because 'daemon' mode may be defined there.
read_config($opt{'file'} // default('file'), \%config, \%globals);
init_config();
test_possible_ip() if opt('query');
## read config file because 'daemon' mode may be defined there.
read_config($opt{'file'} // default('file'), \%config, \%globals);
init_config();
test_possible_ip() if opt('query');
my $caught_hup = 0;
my $caught_term = 0;
my $caught_int = 0;
$SIG{'HUP'} = sub { $caught_hup = 1; };
$SIG{'TERM'} = sub { $caught_term = 1; };
$SIG{'INT'} = sub { $caught_int = 1; };
# don't fork() if foreground
if (opt('foreground')) {
my $caught_hup = 0;
my $caught_term = 0;
my $caught_int = 0;
$SIG{'HUP'} = sub { $caught_hup = 1; };
$SIG{'TERM'} = sub { $caught_term = 1; };
$SIG{'INT'} = sub { $caught_int = 1; };
# don't fork() if foreground
if (opt('foreground')) {
;
} elsif (opt('daemon')) {
} elsif (opt('daemon')) {
$SIG{'CHLD'} = 'IGNORE';
my $pid = fork;
if ($pid < 0) {
@ -871,11 +878,10 @@ if (opt('foreground')) {
open(STDERR, ">/dev/null");
open(STDIN, "</dev/null");
write_pid();
}
}
umask 077;
my $daemon;
do {
umask 077;
do {
$now = time;
$result = 'OK';
%opt = %saved_opt;
@ -921,13 +927,14 @@ do {
} else {
$result = $result eq 'OK' ? 0 : 1;
}
} while ($daemon && !$result && !$caught_term && !$caught_int);
} while ($daemon && !$result && !$caught_term && !$caught_int);
warning("caught SIGINT; exiting") if $caught_int;
unlink_pid();
sendmail();
warning("caught SIGINT; exiting") if $caught_int;
unlink_pid();
sendmail();
exit($result);
exit($result);
}
######################################################################
## runpostscript
@ -5240,6 +5247,12 @@ sub nic_cloudns_update {
}
}
# Execute main() if this file is run as a script or run via PAR (https://metacpan.org/pod/PAR),
# otherwise do nothing. This "modulino" pattern makes it possible to import this file as a module
# and test its functions directly; there's no need for test-only command-line arguments or stdout
# parsing.
__PACKAGE__->main() unless caller() && caller() ne 'PAR';
######################################################################
# vim: ai et ts=4 sw=4 tw=78: