diff --git a/fhem/.proverc b/fhem/.proverc new file mode 100644 index 000000000..43482d511 --- /dev/null +++ b/fhem/.proverc @@ -0,0 +1 @@ +--exec 'perl fhem.pl -t' diff --git a/fhem/CHANGED b/fhem/CHANGED index e423821a3..9f32d5610 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,6 @@ # 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. + - feature: module test framework (fhem.pl -t / FhemTestUtils) - change: 00_MYSENSORS: apply some PBP recommendations, integrate libs from fib/Device/MySensors - feature: 10_MYSENSORS_DEVICE: PBP, see 00_MYSENSORS; diff --git a/fhem/FHEM/98_FhemTestUtils.pm b/fhem/FHEM/98_FhemTestUtils.pm new file mode 100644 index 000000000..360105c0a --- /dev/null +++ b/fhem/FHEM/98_FhemTestUtils.pm @@ -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 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 + + +

FhemTestUtils

+ + +=end html + + +=cut diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt index c818e54df..53f4a745e 100644 --- a/fhem/MAINTAINER.txt +++ b/fhem/MAINTAINER.txt @@ -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_feels_like.pm hotbso Wettermodule FHEM/98_fhemdebug.pm rudolfkoenig Sonstiges +FHEM/98_FhemTestUtils.pm rudolfkoenig Development FHEM/98_fheminfo.pm betateilchen Sonstiges FHEM/98_freezemon KernSani Unterstützende Dienste FHEM/98_FReplacer.pm StefanStrobel Sonstiges diff --git a/fhem/fhem.pl b/fhem/fhem.pl index a9a7524de..a297b9719 100755 --- a/fhem/fhem.pl +++ b/fhem/fhem.pl @@ -127,6 +127,7 @@ sub devspec2array($;$$); sub doGlobalDef($); sub escapeLogLine($); sub evalStateFormat($); +sub execFhemTestFile(); sub fhem($@); sub fhemTimeGm($$$$$$); sub fhemTimeLocal($$$$$$); @@ -143,6 +144,8 @@ sub latin1ToUtf8($); sub myrename($$$); sub notifyRegexpChanged($$); sub parseParams($;$$$); +sub prepareFhemTestFile(); +sub execFhemTestFile(); sub perlSyntaxCheck($%); sub readingsBeginUpdate($); 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($featurelevel); 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($haveInet6); # Using INET6 use vars qw($init_done); # @@ -478,14 +482,16 @@ my %ra = ( # Start the program my $fhemdebug; $fhemdebug = shift @ARGV if($ARGV[0] eq "-d"); +prepareFhemTestFile(); if(int(@ARGV) < 1) { print "Usage:\n"; - print "as server: fhem configfile\n"; - print "as client: fhem [host:]port cmd cmd cmd...\n"; + print "as server: perl fhem.pl [-d] {|configDb}\n"; + print "as client: perl fhem.pl [host:]port cmd cmd cmd...\n"; + print "testing: perl fhem.pl -t .t\n"; if($^O =~ m/Win/) { - print "install as windows service: fhem.pl configfile -i\n"; - print "uninstall the windows service: fhem.pl -u\n"; + print "install as windows service: perl fhem.pl configfile -i\n"; + print "uninstall the windows service: perl fhem.pl -u\n"; } exit(1); } @@ -569,8 +575,9 @@ if(configDBUsed()) { # 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 -# read them here. +# config file after the fork. But we need some global attr parameters before, +# so we read them here. FHEM_GLOBALATTR is for docker, as it needs to overwrite +# fhem.cfg my (undef, $globalAttrFromEnv) = parseParams($ENV{FHEM_GLOBALATTR}); setGlobalAttrBeforeFork($attr{global}{configfile}); applyGlobalAttrFromEnv(); @@ -661,6 +668,7 @@ my $osuser = "os:$^O user:".(getlogin || getpwuid($<) || "unknown"); Log 0, "Featurelevel: $featurelevel"; Log 0, "Server started with ".int(keys %defs). " defined entities ($attr{global}{version} perl:$] $osuser pid:$$)"; +execFhemTestFile(); ################################################ # Main Loop @@ -990,7 +998,7 @@ Log3($$$) no strict "refs"; 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"); } else { delete $logInform{$li}; @@ -4378,6 +4386,7 @@ setGlobalAttrBeforeFork($) next if($l !~ m/^attr\s+global\s+([^\s]+)\s+(.*)$/); AnalyzeCommand(undef, $l); } + CommandAttr(undef, "global modpath .") if(!AttrVal("global","modpath","")); } 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; diff --git a/fhem/t/00_MQTT2_SERVER/00_parseMsg.t b/fhem/t/00_MQTT2_SERVER/00_parseMsg.t new file mode 100644 index 000000000..fbb136187 --- /dev/null +++ b/fhem/t/00_MQTT2_SERVER/00_parseMsg.t @@ -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; diff --git a/fhem/t/00_MQTT2_SERVER/01_autocreate.cfg b/fhem/t/00_MQTT2_SERVER/01_autocreate.cfg new file mode 100644 index 000000000..c3bb6d916 --- /dev/null +++ b/fhem/t/00_MQTT2_SERVER/01_autocreate.cfg @@ -0,0 +1,2 @@ +define m2s MQTT2_SERVER 1883 +define ac autocreate diff --git a/fhem/t/00_MQTT2_SERVER/01_autocreate.t b/fhem/t/00_MQTT2_SERVER/01_autocreate.t new file mode 100644 index 000000000..427a1904b --- /dev/null +++ b/fhem/t/00_MQTT2_SERVER/01_autocreate.t @@ -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; diff --git a/fhem/t/00_MQTT2_SERVER/fhem.cfg b/fhem/t/00_MQTT2_SERVER/fhem.cfg new file mode 100644 index 000000000..97c4eb28c --- /dev/null +++ b/fhem/t/00_MQTT2_SERVER/fhem.cfg @@ -0,0 +1 @@ +define m2s MQTT2_SERVER 1883