mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-01-31 00:36:25 +00:00
98_FhemTestUtils.pm: Fhem Test Utils and test framework (Forum #111061)
git-svn-id: https://svn.fhem.de/fhem/trunk@21926 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
2e16ea71c6
commit
3978ce1663
1
fhem/.proverc
Normal file
1
fhem/.proverc
Normal file
@ -0,0 +1 @@
|
|||||||
|
--exec 'perl fhem.pl -t'
|
@ -1,5 +1,6 @@
|
|||||||
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
|
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
|
||||||
# Do not insert empty lines here, update check depends on it.
|
# Do not insert empty lines here, update check depends on it.
|
||||||
|
- feature: module test framework (fhem.pl -t / FhemTestUtils)
|
||||||
- change: 00_MYSENSORS: apply some PBP recommendations,
|
- change: 00_MYSENSORS: apply some PBP recommendations,
|
||||||
integrate libs from fib/Device/MySensors
|
integrate libs from fib/Device/MySensors
|
||||||
- feature: 10_MYSENSORS_DEVICE: PBP, see 00_MYSENSORS;
|
- feature: 10_MYSENSORS_DEVICE: PBP, see 00_MYSENSORS;
|
||||||
|
130
fhem/FHEM/98_FhemTestUtils.pm
Normal file
130
fhem/FHEM/98_FhemTestUtils.pm
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
##############################################
|
||||||
|
# $Id$
|
||||||
|
package main;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
my @events;
|
||||||
|
my @logs;
|
||||||
|
sub FhemTestUtils_gotLog($);
|
||||||
|
sub FhemTestUtils_resetLogs();
|
||||||
|
sub FhemTestUtils_gotEvent($);
|
||||||
|
sub FhemTestUtils_resetEvents();
|
||||||
|
|
||||||
|
sub
|
||||||
|
FhemTestUtils_Initialize($)
|
||||||
|
{
|
||||||
|
my ($hash) = @_;
|
||||||
|
|
||||||
|
$hash->{DefFn} = "FhemTestUtils_Define";
|
||||||
|
$hash->{NotifyFn} = "FhemTestUtils_Notify";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
FhemTestUtils_Define($$)
|
||||||
|
{
|
||||||
|
my ($hash, $def) = @_;
|
||||||
|
my @a = split("[ \t][ \t]*", $def);
|
||||||
|
|
||||||
|
return "Wrong syntax: use define <name> dummy" if(int(@a) != 2);
|
||||||
|
$logInform{$a[0]} = sub() { push @logs, $_[1] };
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub
|
||||||
|
FhemTestUtils_Notify($$)
|
||||||
|
{
|
||||||
|
my ($ntfy, $dev) = @_;
|
||||||
|
|
||||||
|
my $events = deviceEvents($dev, 0);
|
||||||
|
my $n = $dev->{NAME};
|
||||||
|
#map { print "$n:$_\n" } @{$events};
|
||||||
|
return if(!$events); # Some previous notify deleted the array.
|
||||||
|
push @events, map { "$n:$_" } @{$events};
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub
|
||||||
|
FhemTestUtils_gotEvent($)
|
||||||
|
{
|
||||||
|
my ($arg) = @_;
|
||||||
|
return grep /$arg/,@events;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
FhemTestUtils_resetEvents()
|
||||||
|
{
|
||||||
|
@events=();
|
||||||
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
FhemTestUtils_gotLog($)
|
||||||
|
{
|
||||||
|
my ($arg) = @_;
|
||||||
|
return grep /$arg/,@logs;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
FhemTestUtils_resetLogs()
|
||||||
|
{
|
||||||
|
@logs=();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
=pod
|
||||||
|
=item helper
|
||||||
|
=item summary Utility functions for testing FHEM modules
|
||||||
|
=item summary_DE Hilfsfunktionen, um FHEM Module zu testen
|
||||||
|
=begin html
|
||||||
|
|
||||||
|
<a name="FhemTestUtils"></a>
|
||||||
|
<h3>FhemTestUtils</h3>
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
An instance of this module will be automatically defined, if fhem.pl is
|
||||||
|
called with the -t option.
|
||||||
|
The module will collect all events and Log messages, so a test program is
|
||||||
|
able to check, if an event or log message was generated.<br>
|
||||||
|
Available functions:
|
||||||
|
<ul>
|
||||||
|
<li>FhemTestUtils_gotEvent($)<br>
|
||||||
|
Return the events matching the regexp argument (with grep).
|
||||||
|
</li>
|
||||||
|
<li>FhemTestUtils_gotLog($)<br>
|
||||||
|
Return the logs matching the regexp argument (with grep).
|
||||||
|
Note, that loglevel filtering with verbose ist still active.
|
||||||
|
</li>
|
||||||
|
<li>FhemTestUtils_resetEvents()<br>
|
||||||
|
Reset the internal event buffer.
|
||||||
|
</li>
|
||||||
|
<li>FhemTestUtils_resetLogs()<br>
|
||||||
|
Reset the internal log buffer.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
<a name="FhemTestUtilsdefine"></a>
|
||||||
|
<b>Define</b>
|
||||||
|
<ul>
|
||||||
|
<code>define <name> FhemTestUtils</code>
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<a name="FhemTestUtilsset"></a>
|
||||||
|
<b>Set</b> <ul>N/A</ul><br>
|
||||||
|
<a name="FhemTestUtilsget"></a>
|
||||||
|
<b>Get</b> <ul>N/A</ul><br>
|
||||||
|
<a name="FhemTestUtilsattr"></a>
|
||||||
|
<b>Attributes</b> <ul>N/A</ul><br>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
=end html
|
||||||
|
|
||||||
|
|
||||||
|
=cut
|
@ -480,6 +480,7 @@ FHEM/98_DSBMobile KernSani https://forum.fhem.de/index.php/topic,107104
|
|||||||
FHEM/98_expandJSON.pm dev0 Unterstützende Dienste
|
FHEM/98_expandJSON.pm dev0 Unterstützende Dienste
|
||||||
FHEM/98_feels_like.pm hotbso Wettermodule
|
FHEM/98_feels_like.pm hotbso Wettermodule
|
||||||
FHEM/98_fhemdebug.pm rudolfkoenig Sonstiges
|
FHEM/98_fhemdebug.pm rudolfkoenig Sonstiges
|
||||||
|
FHEM/98_FhemTestUtils.pm rudolfkoenig Development
|
||||||
FHEM/98_fheminfo.pm betateilchen Sonstiges
|
FHEM/98_fheminfo.pm betateilchen Sonstiges
|
||||||
FHEM/98_freezemon KernSani Unterstützende Dienste
|
FHEM/98_freezemon KernSani Unterstützende Dienste
|
||||||
FHEM/98_FReplacer.pm StefanStrobel Sonstiges
|
FHEM/98_FReplacer.pm StefanStrobel Sonstiges
|
||||||
|
48
fhem/fhem.pl
48
fhem/fhem.pl
@ -127,6 +127,7 @@ sub devspec2array($;$$);
|
|||||||
sub doGlobalDef($);
|
sub doGlobalDef($);
|
||||||
sub escapeLogLine($);
|
sub escapeLogLine($);
|
||||||
sub evalStateFormat($);
|
sub evalStateFormat($);
|
||||||
|
sub execFhemTestFile();
|
||||||
sub fhem($@);
|
sub fhem($@);
|
||||||
sub fhemTimeGm($$$$$$);
|
sub fhemTimeGm($$$$$$);
|
||||||
sub fhemTimeLocal($$$$$$);
|
sub fhemTimeLocal($$$$$$);
|
||||||
@ -143,6 +144,8 @@ sub latin1ToUtf8($);
|
|||||||
sub myrename($$$);
|
sub myrename($$$);
|
||||||
sub notifyRegexpChanged($$);
|
sub notifyRegexpChanged($$);
|
||||||
sub parseParams($;$$$);
|
sub parseParams($;$$$);
|
||||||
|
sub prepareFhemTestFile();
|
||||||
|
sub execFhemTestFile();
|
||||||
sub perlSyntaxCheck($%);
|
sub perlSyntaxCheck($%);
|
||||||
sub readingsBeginUpdate($);
|
sub readingsBeginUpdate($);
|
||||||
sub readingsBulkUpdate($$$@);
|
sub readingsBulkUpdate($$$@);
|
||||||
@ -241,6 +244,7 @@ use vars qw($cvsid); # used in 98_version.pm
|
|||||||
use vars qw($devcount); # Maximum device number, used for storing
|
use vars qw($devcount); # Maximum device number, used for storing
|
||||||
use vars qw($featurelevel);
|
use vars qw($featurelevel);
|
||||||
use vars qw($fhemForked); # 1 in a fhemFork()'ed process, else undef
|
use vars qw($fhemForked); # 1 in a fhemFork()'ed process, else undef
|
||||||
|
use vars qw($fhemTestFile); # file to include if -t is specified
|
||||||
use vars qw($fhem_started); # used for uptime calculation
|
use vars qw($fhem_started); # used for uptime calculation
|
||||||
use vars qw($haveInet6); # Using INET6
|
use vars qw($haveInet6); # Using INET6
|
||||||
use vars qw($init_done); #
|
use vars qw($init_done); #
|
||||||
@ -478,14 +482,16 @@ my %ra = (
|
|||||||
# Start the program
|
# Start the program
|
||||||
my $fhemdebug;
|
my $fhemdebug;
|
||||||
$fhemdebug = shift @ARGV if($ARGV[0] eq "-d");
|
$fhemdebug = shift @ARGV if($ARGV[0] eq "-d");
|
||||||
|
prepareFhemTestFile();
|
||||||
|
|
||||||
if(int(@ARGV) < 1) {
|
if(int(@ARGV) < 1) {
|
||||||
print "Usage:\n";
|
print "Usage:\n";
|
||||||
print "as server: fhem configfile\n";
|
print "as server: perl fhem.pl [-d] {<configfile>|configDb}\n";
|
||||||
print "as client: fhem [host:]port cmd cmd cmd...\n";
|
print "as client: perl fhem.pl [host:]port cmd cmd cmd...\n";
|
||||||
|
print "testing: perl fhem.pl -t <testfile>.t\n";
|
||||||
if($^O =~ m/Win/) {
|
if($^O =~ m/Win/) {
|
||||||
print "install as windows service: fhem.pl configfile -i\n";
|
print "install as windows service: perl fhem.pl configfile -i\n";
|
||||||
print "uninstall the windows service: fhem.pl -u\n";
|
print "uninstall the windows service: perl fhem.pl -u\n";
|
||||||
}
|
}
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@ -569,8 +575,9 @@ if(configDBUsed()) {
|
|||||||
|
|
||||||
|
|
||||||
# As newer Linux versions reset serial parameters after fork, we parse the
|
# As newer Linux versions reset serial parameters after fork, we parse the
|
||||||
# config file after the fork. But we need some global attr parameters before, so we
|
# config file after the fork. But we need some global attr parameters before,
|
||||||
# read them here.
|
# so we read them here. FHEM_GLOBALATTR is for docker, as it needs to overwrite
|
||||||
|
# fhem.cfg
|
||||||
my (undef, $globalAttrFromEnv) = parseParams($ENV{FHEM_GLOBALATTR});
|
my (undef, $globalAttrFromEnv) = parseParams($ENV{FHEM_GLOBALATTR});
|
||||||
setGlobalAttrBeforeFork($attr{global}{configfile});
|
setGlobalAttrBeforeFork($attr{global}{configfile});
|
||||||
applyGlobalAttrFromEnv();
|
applyGlobalAttrFromEnv();
|
||||||
@ -661,6 +668,7 @@ my $osuser = "os:$^O user:".(getlogin || getpwuid($<) || "unknown");
|
|||||||
Log 0, "Featurelevel: $featurelevel";
|
Log 0, "Featurelevel: $featurelevel";
|
||||||
Log 0, "Server started with ".int(keys %defs).
|
Log 0, "Server started with ".int(keys %defs).
|
||||||
" defined entities ($attr{global}{version} perl:$] $osuser pid:$$)";
|
" defined entities ($attr{global}{version} perl:$] $osuser pid:$$)";
|
||||||
|
execFhemTestFile();
|
||||||
|
|
||||||
################################################
|
################################################
|
||||||
# Main Loop
|
# Main Loop
|
||||||
@ -990,7 +998,7 @@ Log3($$$)
|
|||||||
|
|
||||||
no strict "refs";
|
no strict "refs";
|
||||||
foreach my $li (keys %logInform) {
|
foreach my $li (keys %logInform) {
|
||||||
if($defs{$li}) {
|
if($defs{$li}) { # Function wont be called for WARNING, don't know why
|
||||||
&{$logInform{$li}}($li, "$tim $loglevel : $text");
|
&{$logInform{$li}}($li, "$tim $loglevel : $text");
|
||||||
} else {
|
} else {
|
||||||
delete $logInform{$li};
|
delete $logInform{$li};
|
||||||
@ -4378,6 +4386,7 @@ setGlobalAttrBeforeFork($)
|
|||||||
next if($l !~ m/^attr\s+global\s+([^\s]+)\s+(.*)$/);
|
next if($l !~ m/^attr\s+global\s+([^\s]+)\s+(.*)$/);
|
||||||
AnalyzeCommand(undef, $l);
|
AnalyzeCommand(undef, $l);
|
||||||
}
|
}
|
||||||
|
CommandAttr(undef, "global modpath .") if(!AttrVal("global","modpath",""));
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub
|
||||||
@ -6070,4 +6079,29 @@ applyGlobalAttrFromEnv()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# set the test config file: either the corresponding X.cfg, or fhem.cfg
|
||||||
|
sub
|
||||||
|
prepareFhemTestFile()
|
||||||
|
{
|
||||||
|
return if($ARGV[0] ne "-t" || @ARGV < 2);
|
||||||
|
shift @ARGV;
|
||||||
|
|
||||||
|
return if($ARGV[0] !~ m,^(.*?)([^/]+)\.t$,);
|
||||||
|
my ($dir, $fileBase) = ($1, $2);
|
||||||
|
|
||||||
|
$fhemTestFile = $ARGV[0];
|
||||||
|
$ARGV[0] = "${dir}fhem.cfg" if(-r "${dir}fhem.cfg");
|
||||||
|
$ARGV[0] = "$dir$fileBase.cfg" if(-r "$dir$fileBase.cfg");
|
||||||
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
execFhemTestFile()
|
||||||
|
{
|
||||||
|
return if(!$fhemTestFile);
|
||||||
|
$attr{global}{autosave} = 0;
|
||||||
|
AnalyzeCommand(undef, "define .ftu FhemTestUtils")
|
||||||
|
if(!grep { $defs{$_}{TYPE} eq "FhemTestUtils" } keys %defs);
|
||||||
|
InternalTimer(1, sub { require $fhemTestFile }, 0 ) if($fhemTestFile);
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
15
fhem/t/00_MQTT2_SERVER/00_parseMsg.t
Normal file
15
fhem/t/00_MQTT2_SERVER/00_parseMsg.t
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Simple test. NOTE: exit(0) is necessary
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Test::More;
|
||||||
|
|
||||||
|
{ MQTT2_SERVER_ReadDebug($defs{m2s}, '0(12)(0)(5)helloworld') }
|
||||||
|
is(FhemTestUtils_gotLog("ERROR:.*bogus data"), 0, "Correct MQTT message");
|
||||||
|
|
||||||
|
FhemTestUtils_resetLogs();
|
||||||
|
{MQTT2_SERVER_ReadDebug($defs{m2s}, '(162)(50)(164)(252)(0).7c:2f:80:97:b0:98/GenericAc(130)(26)(212)4(0)(21)BLE2MQTT/OTA/')}
|
||||||
|
is(FhemTestUtils_gotLog("ERROR:.*bogus data"), 1, "Bogus message, as expected");
|
||||||
|
|
||||||
|
done_testing;
|
||||||
|
exit(0);
|
||||||
|
1;
|
2
fhem/t/00_MQTT2_SERVER/01_autocreate.cfg
Normal file
2
fhem/t/00_MQTT2_SERVER/01_autocreate.cfg
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
define m2s MQTT2_SERVER 1883
|
||||||
|
define ac autocreate
|
25
fhem/t/00_MQTT2_SERVER/01_autocreate.t
Normal file
25
fhem/t/00_MQTT2_SERVER/01_autocreate.t
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# More complex test, with external program and delayed log/event checking
|
||||||
|
# Note: exit(0) must be called in the delayed code.
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Test::More;
|
||||||
|
|
||||||
|
my $usage = `mosquitto_pub 2>&1`;
|
||||||
|
if(!$usage) { # mosquitto not installed
|
||||||
|
ok(1);
|
||||||
|
done_testing;
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fhem('"mosquitto_pub -i test -t hallo -m world"');
|
||||||
|
InternalTimer(time()+1, sub() {
|
||||||
|
is(FhemTestUtils_gotLog(
|
||||||
|
"autocreate: define MQTT2_test MQTT2_DEVICE test m2s"), 1,
|
||||||
|
"autocreate log");
|
||||||
|
is(FhemTestUtils_gotEvent("MQTT2_test:hallo: world"), 1,
|
||||||
|
"autocreate event");
|
||||||
|
done_testing;
|
||||||
|
exit(0);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
1;
|
1
fhem/t/00_MQTT2_SERVER/fhem.cfg
Normal file
1
fhem/t/00_MQTT2_SERVER/fhem.cfg
Normal file
@ -0,0 +1 @@
|
|||||||
|
define m2s MQTT2_SERVER 1883
|
Loading…
Reference in New Issue
Block a user