summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Jones <philj56@gmail.com>2022-12-06 11:51:41 +0000
committerPhil Jones <philj56@gmail.com>2022-12-06 11:51:41 +0000
commit0efb3c61a9575eede8984f362dfa6cd0b7562f4a (patch)
tree9dd96f4b9c4382886c3981a8c7ca222966d0c571
parent4e06e2e09c5473f6aac630373a7bfe47e6258464 (diff)
Add config file unit tests and fix some bugs.
-rw-r--r--src/color.c31
-rw-r--r--src/config.c30
-rw-r--r--src/config.h3
-rw-r--r--src/main.c8
-rw-r--r--test/config.c113
-rw-r--r--test/meson.build1
6 files changed, 165 insertions, 21 deletions
diff --git a/src/color.c b/src/color.c
index 3fd9516..4b6b356 100644
--- a/src/color.c
+++ b/src/color.c
@@ -1,3 +1,4 @@
+#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "color.h"
@@ -10,15 +11,22 @@ struct color hex_to_color(const char *hex)
}
uint32_t val = 0;
+ int64_t tmp;
size_t len = strlen(hex);
+ errno = 0;
if (len == 3) {
char str[] = {
hex[0], hex[0],
hex[1], hex[1],
hex[2], hex[2],
'\0'};
- val = strtol(str, NULL, 16);
+ char *endptr;
+ tmp = strtol(str, &endptr, 16);
+ if (errno || *endptr != '\0' || tmp < 0) {
+ return (struct color) { -1, -1, -1, -1 };
+ }
+ val = tmp;
val <<= 8;
val |= 0xFFu;
} else if (len == 4) {
@@ -28,13 +36,28 @@ struct color hex_to_color(const char *hex)
hex[2], hex[2],
hex[3], hex[3],
'\0'};
- val = strtol(str, NULL, 16);
+ char *endptr;
+ tmp = strtol(str, &endptr, 16);
+ if (errno || *endptr != '\0' || tmp < 0) {
+ return (struct color) { -1, -1, -1, -1 };
+ }
+ val = tmp;
} else if (len == 6) {
- val = strtol(hex, NULL, 16);
+ char *endptr;
+ tmp = strtol(hex, &endptr, 16);
+ if (errno || *endptr != '\0' || tmp < 0) {
+ return (struct color) { -1, -1, -1, -1 };
+ }
+ val = tmp;
val <<= 8;
val |= 0xFFu;
} else if (len == 8) {
- val = strtol(hex, NULL, 16);
+ char *endptr;
+ tmp = strtol(hex, &endptr, 16);
+ if (errno || *endptr != '\0' || tmp < 0) {
+ return (struct color) { -1, -1, -1, -1 };
+ }
+ val = tmp;
} else {
return (struct color) { -1, -1, -1, -1 };
}
diff --git a/src/config.c b/src/config.c
index 0cfd566..99a4494 100644
--- a/src/config.c
+++ b/src/config.c
@@ -710,11 +710,9 @@ bool parse_option(struct tofi *tofi, const char *filename, size_t lineno, const
return !err;
}
-void config_apply(struct tofi *tofi, const char *option, const char *value)
+bool config_apply(struct tofi *tofi, const char *option, const char *value)
{
- if (!parse_option(tofi, "", 0, option, value)) {
- exit(EXIT_FAILURE);
- };
+ return parse_option(tofi, "", 0, option, value);
}
uint32_t fixup_percentage(uint32_t value, uint32_t base, bool is_percent, uint32_t scale, bool use_scale)
@@ -905,6 +903,9 @@ uint32_t parse_char(const char *filename, size_t lineno, const char *str, bool *
ch = utf8_to_utf32_validate(tmp);
if (ch == (uint32_t)-2 || ch == (uint32_t)-1 || *utf8_next_char(tmp) != '\0') {
PARSE_ERROR(filename, lineno, "Failed to parse \"%s\" as a character.\n", str);
+ if (err) {
+ *err = true;
+ }
}
free(tmp);
@@ -961,13 +962,13 @@ uint32_t parse_uint32(const char *filename, size_t lineno, const char *str, bool
{
errno = 0;
char *endptr;
- int32_t ret = strtoul(str, &endptr, 0);
- if (endptr == str) {
+ int64_t ret = strtoul(str, &endptr, 0);
+ if (endptr == str || *endptr != '\0') {
PARSE_ERROR(filename, lineno, "Failed to parse \"%s\" as unsigned int.\n", str);
if (err) {
*err = true;
}
- } else if (errno || ret < 0) {
+ } else if (errno || ret < 0 || ret > UINT32_MAX) {
PARSE_ERROR(filename, lineno, "Unsigned int value \"%s\" out of range.\n", str);
if (err) {
*err = true;
@@ -980,13 +981,13 @@ int32_t parse_int32(const char *filename, size_t lineno, const char *str, bool *
{
errno = 0;
char *endptr;
- int32_t ret = strtol(str, &endptr, 0);
- if (endptr == str) {
+ int64_t ret = strtol(str, &endptr, 0);
+ if (endptr == str || *endptr != '\0') {
PARSE_ERROR(filename, lineno, "Failed to parse \"%s\" as int.\n", str);
if (err) {
*err = true;
}
- } else if (errno) {
+ } else if (errno || ret < INT32_MIN || ret > INT32_MAX) {
PARSE_ERROR(filename, lineno, "Int value \"%s\" out of range.\n", str);
if (err) {
*err = true;
@@ -999,14 +1000,14 @@ struct uint32_percent parse_uint32_percent(const char *filename, size_t lineno,
{
errno = 0;
char *endptr;
- int32_t val = strtoul(str, &endptr, 0);
+ int64_t val = strtoul(str, &endptr, 0);
bool percent = false;
- if (endptr == str) {
+ if (endptr == str || (*endptr != '\0' && *endptr != '%')) {
PARSE_ERROR(filename, lineno, "Failed to parse \"%s\" as unsigned int.\n", str);
if (err) {
*err = true;
}
- } else if (errno || val < 0) {
+ } else if (errno || val < 0 || val > UINT32_MAX) {
PARSE_ERROR(filename, lineno, "Unsigned int value \"%s\" out of range.\n", str);
if (err) {
*err = true;
@@ -1022,7 +1023,8 @@ struct uint32_percent parse_uint32_percent(const char *filename, size_t lineno,
struct directional parse_directional(const char *filename, size_t lineno, const char *str, bool *err)
{
- int32_t values[4];
+ /* One extra value to act as a sentinel for too many values in str. */
+ int32_t values[5];
char *saveptr = NULL;
char *tmp = xstrdup(str);
char *val = strtok_r(tmp, ",", &saveptr);
diff --git a/src/config.h b/src/config.h
index 8dbd241..a9f0c56 100644
--- a/src/config.h
+++ b/src/config.h
@@ -1,10 +1,11 @@
#ifndef TOFI_CONFIG_H
#define TOFI_CONFIG_H
+#include <stdbool.h>
#include "tofi.h"
void config_load(struct tofi *tofi, const char *filename);
-void config_apply(struct tofi *tofi, const char *option, const char *value);
+bool config_apply(struct tofi *tofi, const char *option, const char *value);
void config_fixup_values(struct tofi *tofi);
#endif /* TOFI_CONFIG_H */
diff --git a/src/main.c b/src/main.c
index cde56b4..0c5c6cb 100644
--- a/src/main.c
+++ b/src/main.c
@@ -935,14 +935,18 @@ static void parse_args(struct tofi *tofi, int argc, char *argv[])
opt = getopt_long(argc, argv, short_options, long_options, &option_index);
while (opt != -1) {
if (opt == 0) {
- config_apply(tofi, long_options[option_index].name, optarg);
+ if (!config_apply(tofi, long_options[option_index].name, optarg)) {
+ exit(EXIT_FAILURE);
+ }
} else if (opt == 'k') {
/*
* Backwards compatibility for --late-keyboard-init not
* taking an argument.
*/
if (optarg) {
- config_apply(tofi, long_options[option_index].name, optarg);
+ if (!config_apply(tofi, long_options[option_index].name, optarg)) {
+ exit(EXIT_FAILURE);
+ }
} else {
tofi->late_keyboard_init = true;
}
diff --git a/test/config.c b/test/config.c
new file mode 100644
index 0000000..c69e25b
--- /dev/null
+++ b/test/config.c
@@ -0,0 +1,113 @@
+#include <assert.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "config.h"
+#include "tofi.h"
+#include "tap.h"
+
+void is_valid(const char *option, const char *value, const char *message)
+{
+ struct tofi tofi;
+ bool res = config_apply(&tofi, option, value);
+ tap_is(res, true, message);
+}
+
+void isnt_valid(const char *option, const char *value, const char *message)
+{
+ struct tofi tofi;
+ bool res = config_apply(&tofi, option, value);
+ tap_is(res, false, message);
+}
+
+int main(int argc, char *argv[])
+{
+ setlocale(LC_ALL, "");
+
+ tap_version(14);
+
+ /* Anchors */
+ is_valid("anchor", "top-left", "Anchor top-left");
+ is_valid("anchor", "top", "Anchor top");
+ is_valid("anchor", "top-right", "Anchor top-right");
+ is_valid("anchor", "right", "Anchor right");
+ is_valid("anchor", "bottom-right", "Anchor bottom-right");
+ is_valid("anchor", "bottom", "Anchor bottom");
+ is_valid("anchor", "bottom-left", "Anchor bottom-left");
+ is_valid("anchor", "left", "Anchor left");
+ is_valid("anchor", "center", "Anchor center");
+ isnt_valid("anchor", "left-bottom", "Invalid anchor");
+
+ /* Bools */
+ is_valid("horizontal", "tRuE", "Boolean true");
+ is_valid("horizontal", "fAlSe", "Boolean false");
+ isnt_valid("horizontal", "truefalse", "Invalid boolean");
+
+ /* Password characters */
+ is_valid("hidden-character", "O", "Single Latin character");
+ is_valid("hidden-character", "Д", "Single Cyrillic character");
+ is_valid("hidden-character", "Ξ", "Single Greek character");
+ is_valid("hidden-character", "ọ", "Single character with decomposed diacritic");
+ is_valid("hidden-character", "漢", "Single CJK character");
+ isnt_valid("hidden-character", "ae", "Multiple characters");
+
+ /* Colours */
+ is_valid("text-color", "46B", "Three character color without hash");
+ is_valid("text-color", "#46B", "Three character color with hash");
+ is_valid("text-color", "46BA", "Four character color without hash");
+ is_valid("text-color", "#46BA", "Four character color with hash");
+ is_valid("text-color", "4466BB", "Six character color without hash");
+ is_valid("text-color", "#4466BB", "Six character color with hash");
+ is_valid("text-color", "4466BBAA", "Eight character color without hash");
+ is_valid("text-color", "#4466BBAA", "Eight character color with hash");
+ isnt_valid("text-color", "4466BBA", "Five character color without hash");
+ isnt_valid("text-color", "#4466BBA", "Five character color with hash");
+ isnt_valid("text-color", "9GB", "Three character color with invalid characters");
+ isnt_valid("text-color", "95GB", "Four character color with invalid characters");
+ isnt_valid("text-color", "95XGUB", "Six character color with invalid characters");
+ isnt_valid("text-color", "950-4GBY", "Eight character color with invalid characters");
+ isnt_valid("text-color", "-99", "Negative two character color");
+ isnt_valid("text-color", "-999", "Negative three character color");
+ isnt_valid("text-color", "-9999", "Negative four character color");
+ isnt_valid("text-color", "-99999", "Negative five character color");
+ isnt_valid("text-color", "-999999", "Negative six character color");
+ isnt_valid("text-color", "-9999999", "Negative seven character color");
+ isnt_valid("text-color", "-99999999", "Negative eight character color");
+
+ /* Signed values */
+ is_valid("result-spacing", "-2147483648", "INT32 Min");
+ is_valid("result-spacing", "2147483647", "INT32 Max");
+ isnt_valid("result-spacing", "-2147483649", "INT32 Min - 1");
+ isnt_valid("result-spacing", "2147483648", "INT32 Max + 1");
+ isnt_valid("result-spacing", "6A", "INT32 invalid character");
+
+ /* Unsigned values */
+ is_valid("corner-radius", "0", "UINT32 0");
+ is_valid("corner-radius", "4294967295", "UINT32 Max");
+ isnt_valid("corner-radius", "4294967296", "UINT32 Max + 1");
+ isnt_valid("corner-radius", "-1", "UINT32 -1");
+ isnt_valid("corner-radius", "6A", "UINT32 invalid character");
+
+ /* Unsigned percentages */
+ is_valid("width", "0", "UINT32 0 percent without sign");
+ is_valid("width", "0%", "UINT32 0 percent with sign");
+ is_valid("width", "4294967295", "UINT32 Max percent without sign");
+ is_valid("width", "4294967295%", "UINT32 Max percent with sign");
+ isnt_valid("width", "4294967296", "UINT32 Max + 1 percent without sign");
+ isnt_valid("width", "4294967296%", "UINT32 Max + 1 percent with sign");
+ isnt_valid("width", "-1", "UINT32 -1 percent without sign");
+ isnt_valid("width", "-1%", "UINT32 -1 percent with sign");
+
+ /* Directional values */
+ is_valid("prompt-background-padding", "0", "Single directional value");
+ is_valid("prompt-background-padding", "0,1", "Two directional values");
+ is_valid("prompt-background-padding", "0,1,-2", "Three directional values");
+ is_valid("prompt-background-padding", "0,1,-2,3", "Four directional values");
+ isnt_valid("prompt-background-padding", "0,1,-2,3,-4", "Five directional values");
+ isnt_valid("prompt-background-padding", "0,1,-2,3,-4,5", "Six directional values");
+
+ tap_plan();
+
+ return EXIT_SUCCESS;
+}
diff --git a/test/meson.build b/test/meson.build
index 49f6870..45df68f 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -1,4 +1,5 @@
tests = [
+ 'config',
'utf8'
]