You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pe...@apache.org on 2026/08/10 04:38:45 UTC
(celix) 15/35: Eliminate dead code and improve code coverage for string format checker.
This is an automated email from the ASF dual-hosted git repository.
PengZheng pushed a commit to branch feature/json-schema
in repository https://gitbox.apache.org/repos/asf/celix.git
commit 02471885f18a6485920b4b44448a2cb91c472188
Author: PengZheng <ho...@gmail.com>
AuthorDate: Fri Aug 7 17:01:20 2026 +0800
Eliminate dead code and improve code coverage for string format checker.
---
libs/jansson_ext/gtest/src/test_format_check.cpp | 35 ++++++++++++++++++++++
.../gtest/src/test_format_check_extended.cpp | 17 +++++++++--
libs/jansson_ext/src/celix_string_format_check.c | 26 +++++++++-------
3 files changed, 65 insertions(+), 13 deletions(-)
diff --git a/libs/jansson_ext/gtest/src/test_format_check.cpp b/libs/jansson_ext/gtest/src/test_format_check.cpp
index 578eba60f..d7a5495fd 100644
--- a/libs/jansson_ext/gtest/src/test_format_check.cpp
+++ b/libs/jansson_ext/gtest/src/test_format_check.cpp
@@ -41,6 +41,13 @@ TEST(FormatCheckTest, DateTime) {
{"date-time", "1985-04-12t23:20:50Z", true}, /* lowercase T */
{"date-time", "1985-04-12T23:20:50+24:00", false}, /* offset too large */
{"date-time", "not-a-date", false},
+ {"date-time", "2020-01-01", false}, /* missing T separator */
+ {"date-time", "2020-01-01T10:30:00", false}, /* missing timezone */
+ {"date-time", "2020-01-01T+01:00", false}, /* empty time part */
+ {"date-time", "2020-01-01T10:30:00.123456789012345678901234567890Z", false}, /* time part >= 32 chars */
+ {"date-time", "2020-01-01T00:00:60Z", false}, /* leap second not at 23:59:60 UTC */
+ {"date-time", "2020-01-01T10:30:00+12x30", false}, /* malformed timezone */
+ {"date-time", "2020-01-01T10:30:00+1230", false}, /* timezone missing colon */
};
for (auto& c : cases) {
@@ -151,6 +158,34 @@ TEST(FormatCheckTest, UriInvalidChars) {
celix_jansson_schema_default_format_check("uri", "http://example.com/path\"quote", nullptr));
}
+TEST(FormatCheckTest, UriAuthority) {
+ /* Valid URIs */
+ EXPECT_EQ(CELIX_JANSSON_SCHEMA_OK,
+ celix_jansson_schema_default_format_check("uri", "http://[::1]:8080/", nullptr));
+ EXPECT_EQ(CELIX_JANSSON_SCHEMA_OK,
+ celix_jansson_schema_default_format_check("uri", "http://a%20b/", nullptr));
+ EXPECT_EQ(CELIX_JANSSON_SCHEMA_OK,
+ celix_jansson_schema_default_format_check("uri", "http://example.com:8080/", nullptr));
+ /* Invalid: empty URI */
+ EXPECT_NE(CELIX_JANSSON_SCHEMA_OK,
+ celix_jansson_schema_default_format_check("uri", "", nullptr));
+ /* Invalid: unclosed IPv6 literal */
+ EXPECT_NE(CELIX_JANSSON_SCHEMA_OK,
+ celix_jansson_schema_default_format_check("uri", "http://[::1", nullptr));
+ /* Invalid: bad percent-encoding in reg-name */
+ EXPECT_NE(CELIX_JANSSON_SCHEMA_OK,
+ celix_jansson_schema_default_format_check("uri", "http://a%zz/", nullptr));
+ /* Invalid: non-digit in port */
+ EXPECT_NE(CELIX_JANSSON_SCHEMA_OK,
+ celix_jansson_schema_default_format_check("uri", "http://example.com:8a80/", nullptr));
+ /* Invalid: space in query */
+ EXPECT_NE(CELIX_JANSSON_SCHEMA_OK,
+ celix_jansson_schema_default_format_check("uri", "http://a.b/?x y", nullptr));
+ /* Invalid: space in fragment */
+ EXPECT_NE(CELIX_JANSSON_SCHEMA_OK,
+ celix_jansson_schema_default_format_check("uri", "http://a.b/#x y", nullptr));
+}
+
/* ── NULL argument guard ─────────────────────────────────────────────────── */
TEST(FormatCheckTest, NullArgumentDispatch) {
diff --git a/libs/jansson_ext/gtest/src/test_format_check_extended.cpp b/libs/jansson_ext/gtest/src/test_format_check_extended.cpp
index c983eb75c..b330cedae 100644
--- a/libs/jansson_ext/gtest/src/test_format_check_extended.cpp
+++ b/libs/jansson_ext/gtest/src/test_format_check_extended.cpp
@@ -79,6 +79,7 @@ TEST(FormatCheckTest, Time) {
{"12:60:00Z", false}, /* minute > 59 */
{"12:34:56", false}, /* no timezone */
{"12:34:56+24:00", false}, /* offset hour > 23 */
+ {"10:30:00+01:00x", false}, /* trailing garbage after timezone */
{"", false},
};
@@ -100,11 +101,16 @@ TEST(FormatCheckTest, Time) {
*/
TEST(FormatCheckTest, Email) {
- /* Only test clearly invalid inputs — the SMTP validator rejects
- * seemingly valid addresses too, so positive cases are unreliable. */
+ /* NOTE: The Ragel-generated SMTP address validator (celix_smtp_address_validator.c)
+ * returns false for EVERY input — verified empirically with a probe harness, and
+ * consistent with the JSON Schema Test Suite treating email.json as expected-fail.
+ * The OK return path of check_email is therefore unreachable until the validator
+ * is fixed (separate investigation comparing with pboettch/json-schema-validator).
+ * Non-ASCII input fails earlier in is_ascii and is covered here. */
EXPECT_NE(CELIX_JANSSON_SCHEMA_OK, celix_jansson_check_email("not-an-email"));
EXPECT_NE(CELIX_JANSSON_SCHEMA_OK, celix_jansson_check_email("user@"));
EXPECT_NE(CELIX_JANSSON_SCHEMA_OK, celix_jansson_check_email("@example.com"));
+ EXPECT_NE(CELIX_JANSSON_SCHEMA_OK, celix_jansson_check_email("\xc3\xbc@example.com")); /* ü@example.com — non-ASCII */
}
/* ── International Email (RFC 6531) ──────────────────────────────────────── */
@@ -112,6 +118,7 @@ TEST(FormatCheckTest, Email) {
TEST(FormatCheckTest, IdnEmail) {
EXPECT_NE(CELIX_JANSSON_SCHEMA_OK, celix_jansson_check_idn_email("not-an-email"));
EXPECT_NE(CELIX_JANSSON_SCHEMA_OK, celix_jansson_check_idn_email("user@"));
+ EXPECT_NE(CELIX_JANSSON_SCHEMA_OK, celix_jansson_check_idn_email("\xc3\xbc@example.com"));
}
/* ── Hostname (RFC 3986 Appendix A labels) ──────────────────────────────── */
@@ -133,6 +140,11 @@ TEST(FormatCheckTest, Hostname) {
{"a b.example", false}, /* space in label */
};
+ /* 4 labels of 63 chars + 3 dots = 255 chars > 253 limit */
+ std::string long_label(63, 'a');
+ std::string long_host = long_label + "." + long_label + "." + long_label + "." + long_label;
+ EXPECT_EQ(CELIX_JANSSON_SCHEMA_ERROR_INVALID_ARGUMENT, celix_jansson_check_hostname(long_host.c_str()));
+
for (auto& c : cases) {
int rc = celix_jansson_check_hostname(c.value);
if (c.valid)
@@ -159,6 +171,7 @@ TEST(FormatCheckTest, Ipv6) {
{"2001:db8", false}, /* incomplete */
{"2001:db8:::1", false}, /* triple colon */
{"gggg:db8::1", false}, /* invalid hex */
+ {"fe80::1%eth0", false}, /* zone-id form (glibc's inet_pton rejects '%' itself) */
{"", false},
};
diff --git a/libs/jansson_ext/src/celix_string_format_check.c b/libs/jansson_ext/src/celix_string_format_check.c
index 0291d752d..76f310cfe 100644
--- a/libs/jansson_ext/src/celix_string_format_check.c
+++ b/libs/jansson_ext/src/celix_string_format_check.c
@@ -19,6 +19,7 @@
#include "celix_string_format_check.h"
#include "celix_smtp_address_validator.h"
#include <arpa/inet.h>
+#include <assert.h>
#include <ctype.h>
#include <regex.h>
#include <stdbool.h>
@@ -33,8 +34,8 @@ static int is_leap_year(int year) { return (year % 4 == 0 && year % 100 != 0) ||
static int days_in_month(int year, int month) {
static const int days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
- if (month < 1 || month > 12)
- return 0;
+ /* Caller (check_date_part) guarantees 1 <= month <= 12 */
+ assert(month >= 1 && month <= 12);
if (month == 2 && is_leap_year(year))
return 29;
return days[month - 1];
@@ -54,8 +55,10 @@ static int check_date_part(const char* s) {
return -1;
}
int year, month, day;
- if (sscanf(s, "%4d-%2d-%2d", &year, &month, &day) != 3)
- return -1;
+ /* All non-separator chars were validated as digits above, so the parse always succeeds */
+ int n = sscanf(s, "%4d-%2d-%2d", &year, &month, &day);
+ assert(n == 3);
+ (void)n;
if (month < 1 || month > 12)
return -1;
if (day < 1 || day > days_in_month(year, month))
@@ -109,20 +112,19 @@ static int check_leap_second(int hour, int min, int sec, int offset_hour, int of
}
static int parse_timezone(const char* s, int* out_hh, int* out_mm) {
- if (!s || *s == '\0')
- return -1;
+ /* Callers pass the result of strpbrk(s, "+-Zz"), so s is non-NULL and non-empty */
+ assert(s != NULL && *s != '\0');
if (*s == 'Z' || *s == 'z') {
*out_hh = 0;
*out_mm = 0;
return 0;
}
- /* ±HH:MM */
+ /* ±HH:MM — s[0] is '+' or '-' (Z/z handled above) */
int hh, mm;
char sign;
if (sscanf(s, "%c%2d:%2d", &sign, &hh, &mm) != 3)
return -1;
- if (sign != '+' && sign != '-')
- return -1;
+ assert(sign == '+' || sign == '-');
if (hh < 0 || hh > 23)
return -1;
if (mm < 0 || mm > 59)
@@ -309,8 +311,10 @@ int celix_jansson_check_ipv4(const char* value) {
return CELIX_JANSSON_SCHEMA_ERROR_INVALID_ARGUMENT;
/* Reject leading zeros (e.g., "192.168.01.001") */
char octet[16];
- if (snprintf(octet, sizeof(octet), "%u.%u.%u.%u", a, b, c, d) >= (int)sizeof(octet))
- return CELIX_JANSSON_SCHEMA_ERROR_INVALID_ARGUMENT;
+ /* a,b,c,d <= 255 was validated above, so the formatted string always fits in octet */
+ int n = snprintf(octet, sizeof(octet), "%u.%u.%u.%u", a, b, c, d);
+ assert(n < (int)sizeof(octet));
+ (void)n;
if (strcmp(value, octet) != 0)
return CELIX_JANSSON_SCHEMA_ERROR_INVALID_ARGUMENT;
return CELIX_JANSSON_SCHEMA_OK;