Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
56e754e
drivers/bestfortress.c: do not use whitespace in status_set() args [#…
jimklimov Feb 10, 2025
225d12b
drivers/liebert-gxe.c: do not use whitespace in status_set() args [#2…
jimklimov Feb 10, 2025
472eac5
tests/generic_gpio_utest.c: piggy-back to test for duplicates with st…
jimklimov Feb 10, 2025
d43377b
drivers/dstate.c: fix status_get(), got inverted maths in a check [#2…
jimklimov Feb 10, 2025
cd95c9e
drivers/dstate.c, NEWS.adoc: status_set(): if we are reluctantly addi…
jimklimov Feb 10, 2025
2d4f931
tests/generic_gpio_utest.c: check that whitespace chars around status…
jimklimov Feb 10, 2025
6ecb7fe
docs/new-drivers.txt: describe in ups.status values also that/how ARA…
jimklimov Feb 10, 2025
853de1f
appveyor.yml: ensure we can create version-less snapshot dir symlink
jimklimov Feb 11, 2025
6e9637e
Introduce tests/driver_methods_utest [#2708]
jimklimov Feb 11, 2025
3d68374
tests/driver_methods_utest.c: refactor pass.fail reporting [#2708]
jimklimov Feb 11, 2025
053bef8
tests/driver_methods_utest.c: test ALARM status [#2708]
jimklimov Feb 11, 2025
9ec4b82
tests/driver_methods_utest.c: test ALARM contents (with dupes) [#2708]
jimklimov Feb 11, 2025
7416633
tests/driver_methods_utest.c: test ups.status sub-token detection [#2…
jimklimov Feb 11, 2025
332cd9e
drivers/dstate.c: status_get(): properly handle tokens that are subst…
jimklimov Feb 11, 2025
b529f0b
drivers/main.c: avoid declaring "mutex" in WIN32 builds without main(…
jimklimov Feb 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions NEWS.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -479,8 +479,8 @@ relocated into new `shutdown.default` INSTCMD definitions. [#2670]
* custom `distcheck-something` targets did not inherit `DISTCHECK_FLAGS`
properly. [#2541]
* added `status_get()` in NUT driver state API, to check if a status
token string had been set recently, and to avoid duplicate settings.
[PR #2565]
token string had been set recently, and to avoid duplicate settings;
fixed `status_set()` for multi-token arguments. [PR #2565, issue #2708]
* local socket/pipe protocol introduced a `LOGOUT` command for cleaner
disconnection handling. [#2572]
* codebase adapted to the liking of `clang-18` and newer revisions of
Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ after_test:
set MSYSTEM=MINGW64
REM Oh the joys of shell scripting with strings passed through CMD:
REM Note: currently Python installation path with MSYS is buggy [#1584]
C:\msys64\usr\bin\bash -lc 'date -u; set -e ; if ! rm -rf ".inst" ; then echo "WARNING: Failed to clean away .inst" ; fi ; PATH="/mingw64/lib/ccache/bin:/mingw64/bin:$PATH" make -s install-win-bundle DESTDIR="`pwd`/.inst/NUT-for-Windows-x86_64-SNAPSHOT-%APPVEYOR_BUILD_VERSION%" ; ln -fs "NUT-for-Windows-x86_64-SNAPSHOT-%APPVEYOR_BUILD_VERSION%" ./.inst/NUT-for-Windows-x86_64-SNAPSHOT ; ( cd .inst/NUT-for-Windows-x86_64-SNAPSHOT ; find . -ls ; ) ; date -u'
C:\msys64\usr\bin\bash -lc 'date -u; set -e ; if ! rm -rf ".inst" ; then echo "WARNING: Failed to clean away .inst" ; fi ; PATH="/mingw64/lib/ccache/bin:/mingw64/bin:$PATH" make -s install-win-bundle DESTDIR="`pwd`/.inst/NUT-for-Windows-x86_64-SNAPSHOT-%APPVEYOR_BUILD_VERSION%" ; rm -rf ./.inst/NUT-for-Windows-x86_64-SNAPSHOT || true ; ln -fs "NUT-for-Windows-x86_64-SNAPSHOT-%APPVEYOR_BUILD_VERSION%" ./.inst/NUT-for-Windows-x86_64-SNAPSHOT ; ( cd .inst/NUT-for-Windows-x86_64-SNAPSHOT ; find . -ls ; ) ; date -u'
cd .inst
7z a ../NUT-for-Windows-x86_64-SNAPSHOT-%APPVEYOR_BUILD_VERSION%.7z NUT*
- cmd: |
Expand Down
4 changes: 4 additions & 0 deletions docs/new-drivers.txt
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,10 @@ Possible values for status_set:
BOOST -- UPS is boosting incoming voltage
FSD -- Forced Shutdown (restricted use, see the note below)

Internally, an `ALARM` value would be added (typically as first in the list)
if the `ups.alarm` is currently not empty. For more details, see below in
`alarm_set()` description.

Anything else will not be recognized by the usual clients expecting a
particular NUT standard release. New tokens may appear over time, but
driver developers should coordinate with the nut-upsdev list before creating
Expand Down
6 changes: 3 additions & 3 deletions drivers/bestfortress.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
#endif

#define DRIVER_NAME "Best Fortress UPS driver"
#define DRIVER_VERSION "0.10"
#define DRIVER_VERSION "0.11"

/* driver description structure */
upsdrv_info_t upsdrv_info = {
Expand Down Expand Up @@ -401,13 +401,13 @@ void upsdrv_updateinfo(void)

status_init();
if (low_batt)
status_set("LB ");
status_set("LB");
else if (trimming)
status_set("TRIM");
else if (boosting)
status_set("BOOST");
else
status_set(is_online ? (is_off ? "OFF " : "OL ") : "OB ");
status_set(is_online ? (is_off ? "OFF" : "OL") : "OB");

/* setinfo(INFO_STATUS, "%s%s",
* (util < lownorm) ? "BOOST ", "",
Expand Down
47 changes: 43 additions & 4 deletions drivers/dstate.c
Original file line number Diff line number Diff line change
Expand Up @@ -1644,11 +1644,16 @@ int status_get(const char *buf)
s = strstr(status_buf, buf);
buflen = strlen(buf);

/* not found */
if (!s)
repeat:
/* not found or hit end of line */
if (!s || !*s)
return 0;

offset = status_buf - s;
offset = s - status_buf;
#if 0
upsdebugx(3, "%s: '%s' in '%s': offset=%" PRIuSIZE" buflen=%" PRIuSIZE" s[buflen]='0x%2X'\n",
__func__, buf, status_buf, offset, buflen, s[buflen]);
#endif
if (offset == 0 || status_buf[offset - 1] == ' ') {
/* We have hit the start of token */
if (s[buflen] == '\0' || s[buflen] == ' ') {
Expand All @@ -1658,12 +1663,46 @@ int status_get(const char *buf)
}

/* buf was a substring of some other token */
return 0;
s = strstr(s + 1, buf);
goto repeat;
}

/* add a status element */
void status_set(const char *buf)
{
#if 0
upsdebugx(3, "%s: '%s'\n", __func__, buf);
#endif
if (strstr(buf, " ")) {
/* Recurse adding each sub-status one by one (avoid duplicates)
* We frown upon adding "A FEW TOKENS" at once, but in e.g.
* snmp-ups subdrivers with a mapping table this is not easily
* avoidable...
*/
char *tmp = xstrdup(buf), *p = tmp, *s = tmp;
while (*p) {
if (*p == ' ') {
*p = '\0';
if (s != p) {
/* Only recurse to set non-trivial tokens */
status_set(s);
}
p++;
s = p; /* Start of new word... or a consecutive space to ignore on next cycle */
} else {
p++;
}
}

if (s != p) {
/* Last valid token did end with (*p=='\0') */
status_set(s);
}

free(tmp);
return;
}

if (ignorelb && !strcasecmp(buf, "LB")) {
upsdebugx(2, "%s: ignoring LB flag from device", __func__);
return;
Expand Down
9 changes: 5 additions & 4 deletions drivers/liebert-gxe.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
#include "ydn23.h"

#define DRIVER_NAME "Liebert GXE Series UPS driver"
#define DRIVER_VERSION "0.02"
#define DRIVER_VERSION "0.03"

#define PROBE_RETRIES 3
#define DEFAULT_STALE_RETRIES 3
Expand Down Expand Up @@ -173,9 +173,10 @@ static void upsdrv_updateinfo_onoff(void)
status_set("OB");
else if (pwrval == 0x01)
status_set("OL");
else if (pwrval == 0x02)
status_set("OL BYPASS");
else
else if (pwrval == 0x02) {
status_set("OL");
status_set("BYPASS");
} else
upslogx(LOG_WARNING, "unknown ups state: %x %x",
(unsigned int)pwrval,
(unsigned int)rectval);
Expand Down
6 changes: 4 additions & 2 deletions drivers/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,11 @@ const char *progname = NULL, *upsname = NULL, *device_name = NULL;

/* may be set by the driver to wake up while in dstate_poll_fds */
TYPE_FD extrafd = ERROR_FD;
#ifdef WIN32
#ifndef DRIVERS_MAIN_WITHOUT_MAIN
# ifdef WIN32
static HANDLE mutex = INVALID_HANDLE_VALUE;
#endif
# endif /* WIN32 */
#endif /* DRIVERS_MAIN_WITHOUT_MAIN */

/* Set by INSTCMD to killpower or by running `drivername -k` to
* help differentiate calls into upsdrv_shutdown() and further
Expand Down
3 changes: 3 additions & 0 deletions tests/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
/cppnit
/cppnit.log
/cppnit.trs
/driver_methods_utest
/driver_methods_utest.log
/driver_methods_utest.trs
/gpiotest
/gpiotest.log
/gpiotest.trs
Expand Down
5 changes: 5 additions & 0 deletions tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ endif !WITH_GPIO
CLEANFILES += generic_gpio_libgpiod.c generic_gpio_common.c
EXTRA_DIST += generic_gpio_utest.h generic_gpio_test.txt

TESTS += driver_methods_utest
driver_methods_utest_SOURCES = driver_methods_utest.c
driver_methods_utest_LDADD = $(top_builddir)/drivers/libdummy_mockdrv.la
driver_methods_utest_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/tests -DDRIVERS_MAIN_WITHOUT_MAIN=1

# Make sure out-of-dir dependencies exist (especially when dev-building parts):
$(top_builddir)/drivers/libdummy_mockdrv.la \
$(top_builddir)/common/libnutconf.la \
Expand Down
134 changes: 134 additions & 0 deletions tests/driver_methods_utest.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/* driver_methods_utest.c - NUT driver code test tool
*
* Copyright (C)
* 2025 Jim Klimov <jimklimov+nut@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/

#include "config.h"
#include "main.h"
#include "dstate.h"
#include "attribute.h"
#include "nut_stdint.h"

/* driver version */
#define DRIVER_NAME "Mock driver for unit tests"
#define DRIVER_VERSION "0.01"

/* driver description structure */
upsdrv_info_t upsdrv_info = {
DRIVER_NAME,
DRIVER_VERSION,
"Jim Klimov <jimklimov+nut@gmail.com>",
DRV_EXPERIMENTAL,
{ NULL }
};

static int cases_passed = 0;
static int cases_failed = 0;

static char * pass_fail[2] = {"pass", "fail"};

void upsdrv_cleanup(void) {}
void upsdrv_shutdown(void) {}

static void report_pass(void) {
printf("%s", pass_fail[0]);
cases_passed++;
}

static void report_fail(void) {
printf("%s", pass_fail[1]);
cases_failed++;
}

static int report_0_means_pass(int i) {
if (i == 0) {
report_pass();
} else {
report_fail();
}
return i;
}
int main(int argc, char **argv) {
const char *valueStr = NULL;

NUT_UNUSED_VARIABLE(argc);
NUT_UNUSED_VARIABLE(argv);

cases_passed = 0;
cases_failed = 0;

/* test case #1 */
status_init();
nut_debug_level = 6;
status_set(" OL ");
status_set("OL BOOST");
status_set("OB ");
status_set(" BOOST");
status_commit();
valueStr = dstate_getinfo("ups.status");
nut_debug_level = 0;
report_0_means_pass(strcmp(valueStr, "OL BOOST OB"));
printf(" test for ups.status: '%s'; any duplicates?\n", NUT_STRARG(valueStr));

/* test case #2, build on top of #1 */
alarm_init();
alarm_set("Test alarm 1");
alarm_set("Test alarm 2");
alarm_set("Test alarm 1");
alarm_commit();
/* Note: normally we re-init and re-set the values */
status_commit();
valueStr = dstate_getinfo("ups.status");
report_0_means_pass(strcmp(valueStr, "ALARM OL BOOST OB"));
printf(" test for ups.status: '%s'; got alarm?\n", NUT_STRARG(valueStr));

/* test case #3, build on top of #2 */
valueStr = dstate_getinfo("ups.alarm");
/* NOTE: no dedup here! */
report_0_means_pass(strcmp(valueStr, "Test alarm 1 Test alarm 2 Test alarm 1"));
printf(" test for ups.alarm: '%s'; got 3 alarms?\n", NUT_STRARG(valueStr));

/* test case #4, build on top of #1 and #2 */
/* Note: normally we re-init and re-set the values */
status_set("BOO");
status_set("BOO");
status_set("OST");
status_set("OST");
status_set("OOS");
status_set("OOS");
status_commit();
valueStr = dstate_getinfo("ups.status");
nut_debug_level = 0;
report_0_means_pass(strcmp(valueStr, "ALARM OL BOOST OB BOO OST OOS"));
printf(" test for ups.status: '%s'; any duplicates?\n", NUT_STRARG(valueStr));

/* finish */
printf("test_rules completed. Total cases %d, passed %d, failed %d\n",
cases_passed+cases_failed, cases_passed, cases_failed);

dstate_free();
upsdrv_cleanup();

/* Return 0 (exit-code OK, boolean false) if no tests failed and some ran */
if ( (cases_failed == 0) && (cases_passed > 0) )
return 0;

return 1;
}