Browse code

test_options_parse: Start new UT for options_parse.c

For now contains one test case for parse_line.

Change-Id: I95032d2539d994abf69fc17319ed1a429c3bb948
Signed-off-by: Frank Lichtenheld <frank@lichtenheld.com>
Acked-by: Gert Doering <gert@greenie.muc.de>
Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1244
Message-Id: <20251008101014.5691-1-gert@greenie.muc.de>
URL: https://sourceforge.net/p/openvpn/mailman/message/59243816/
Signed-off-by: Gert Doering <gert@greenie.muc.de>

Frank Lichtenheld authored on 2025/10/08 19:10:09
Showing 3 changed files
... ...
@@ -685,6 +685,7 @@ if (BUILD_TESTING)
685 685
     # Clang-cl (which is also MSVC) is wrongly detected to support wrap
686 686
     if (NOT MSVC AND "${LD_SUPPORTS_WRAP}")
687 687
         list(APPEND unit_tests
688
+            "test_options_parse"
688 689
             "test_tls_crypt"
689 690
             )
690 691
     endif ()
... ...
@@ -826,6 +827,20 @@ if (BUILD_TESTING)
826 826
         src/compat/compat-strsep.c
827 827
         )
828 828
 
829
+    if (TARGET test_options_parse)
830
+        target_link_options(test_options_parse PRIVATE
831
+            -Wl,--wrap=add_option
832
+            -Wl,--wrap=remove_option
833
+            -Wl,--wrap=update_option
834
+	    -Wl,--wrap=usage
835
+        )
836
+        target_sources(test_options_parse PRIVATE
837
+            tests/unit_tests/openvpn/mock_get_random.c
838
+            src/openvpn/options_parse.c
839
+            src/openvpn/options_util.c
840
+        )
841
+    endif ()
842
+
829 843
     target_sources(test_packet_id PRIVATE
830 844
         tests/unit_tests/openvpn/mock_get_random.c
831 845
         src/openvpn/otime.c
... ...
@@ -9,6 +9,7 @@ test_binaries = argv_testdriver buffer_testdriver crypto_testdriver packet_id_te
9 9
 	user_pass_testdriver push_update_msg_testdriver provider_testdriver socket_testdriver
10 10
 
11 11
 if HAVE_LD_WRAP_SUPPORT
12
+test_binaries += options_parse_testdriver
12 13
 if !WIN32
13 14
 test_binaries += tls_crypt_testdriver
14 15
 endif
... ...
@@ -190,6 +191,21 @@ networking_testdriver_SOURCES = test_networking.c mock_msg.c \
190 190
 	$(top_srcdir)/src/openvpn/platform.c
191 191
 endif
192 192
 
193
+options_parse_testdriver_CFLAGS  = -I$(top_srcdir)/src/openvpn -I$(top_srcdir)/src/compat @TEST_CFLAGS@
194
+options_parse_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(top_srcdir)/src/openvpn \
195
+	-Wl,--wrap=add_option \
196
+	-Wl,--wrap=update_option \
197
+	-Wl,--wrap=remove_option \
198
+	-Wl,--wrap=usage
199
+options_parse_testdriver_SOURCES = test_options_parse.c \
200
+	mock_msg.c mock_msg.h test_common.h \
201
+	mock_get_random.c \
202
+	$(top_srcdir)/src/openvpn/options_parse.c \
203
+	$(top_srcdir)/src/openvpn/options_util.c \
204
+	$(top_srcdir)/src/openvpn/buffer.c \
205
+	$(top_srcdir)/src/openvpn/win32-util.c \
206
+	$(top_srcdir)/src/openvpn/platform.c
207
+
193 208
 provider_testdriver_CFLAGS  = \
194 209
 	-I$(top_srcdir)/include -I$(top_srcdir)/src/compat -I$(top_srcdir)/src/openvpn \
195 210
 	@TEST_CFLAGS@ $(OPTIONAL_CRYPTO_CFLAGS)
196 211
new file mode 100644
... ...
@@ -0,0 +1,196 @@
0
+/*
1
+ *  OpenVPN -- An application to securely tunnel IP networks
2
+ *             over a single UDP port, with support for SSL/TLS-based
3
+ *             session authentication and key exchange,
4
+ *             packet encryption, packet authentication, and
5
+ *             packet compression.
6
+ *
7
+ *  Copyright (C) 2025 OpenVPN Inc <sales@openvpn.net>
8
+ *
9
+ *  This program is free software; you can redistribute it and/or modify
10
+ *  it under the terms of the GNU General Public License version 2
11
+ *  as published by the Free Software Foundation.
12
+ *
13
+ *  This program is distributed in the hope that it will be useful,
14
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ *  GNU General Public License for more details.
17
+ *
18
+ *  You should have received a copy of the GNU General Public License along
19
+ *  with this program; if not, see <https://www.gnu.org/licenses/>.
20
+ */
21
+
22
+#ifdef HAVE_CONFIG_H
23
+#include "config.h"
24
+#endif
25
+
26
+#include "syshead.h"
27
+
28
+#include <stdio.h>
29
+#include <stdlib.h>
30
+#include <stdarg.h>
31
+#include <string.h>
32
+#include <setjmp.h>
33
+#include <cmocka.h>
34
+
35
+#include "options.h"
36
+#include "test_common.h"
37
+#include "mock_msg.h"
38
+
39
+void
40
+__wrap_add_option(struct options *options, char *p[], bool is_inline, const char *file,
41
+                  int line, const int level, const msglvl_t msglevel,
42
+                  const unsigned int permission_mask, unsigned int *option_types_found,
43
+                  struct env_set *es)
44
+{
45
+}
46
+
47
+void
48
+__wrap_remove_option(struct context *c, struct options *options, char *p[], bool is_inline,
49
+                     const char *file, int line, const msglvl_t msglevel,
50
+                     const unsigned int permission_mask, unsigned int *option_types_found,
51
+                     struct env_set *es)
52
+{
53
+}
54
+
55
+void
56
+__wrap_update_option(struct context *c, struct options *options, char *p[], bool is_inline,
57
+                     const char *file, int line, const int level, const msglvl_t msglevel,
58
+                     const unsigned int permission_mask, unsigned int *option_types_found,
59
+                     struct env_set *es, unsigned int *update_options_found)
60
+{
61
+}
62
+
63
+void
64
+__wrap_usage(void)
65
+{
66
+}
67
+
68
+/* for building long texts */
69
+#define A_TIMES_256 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO"
70
+
71
+static void
72
+test_parse_line(void **state)
73
+{
74
+    char *p[MAX_PARMS + 1] = { 0 };
75
+    struct gc_arena gc = gc_new();
76
+    int res = 0;
77
+
78
+#define PARSE_LINE_TST(string)                                                          \
79
+    do                                                                                  \
80
+    {                                                                                   \
81
+        CLEAR(p);                                                                       \
82
+        res = parse_line(string, p, SIZE(p) - 1, "test_options_parse", 1, M_INFO, &gc); \
83
+    } while (0);
84
+
85
+    /* basic example */
86
+    PARSE_LINE_TST("some-opt firstparm second-parm");
87
+    assert_int_equal(res, 3);
88
+    assert_string_equal(p[0], "some-opt");
89
+    assert_string_equal(p[1], "firstparm");
90
+    assert_string_equal(p[2], "second-parm");
91
+    assert_null(p[res]);
92
+
93
+    /* basic quoting, -- is not handled special */
94
+    PARSE_LINE_TST("--some-opt 'first parm' \"second' 'parm\"");
95
+    assert_int_equal(res, 3);
96
+    assert_string_equal(p[0], "--some-opt");
97
+    assert_string_equal(p[1], "first parm");
98
+    assert_string_equal(p[2], "second' 'parm");
99
+    assert_null(p[res]);
100
+
101
+    /* escaped quotes */
102
+    PARSE_LINE_TST("\"some opt\" 'first\" \"parm' \"second\\\" \\\"parm\"");
103
+    assert_int_equal(res, 3);
104
+    assert_string_equal(p[0], "some opt");
105
+    assert_string_equal(p[1], "first\" \"parm");
106
+    assert_string_equal(p[2], "second\" \"parm");
107
+    assert_null(p[res]);
108
+
109
+    /* missing closing quote */
110
+    PARSE_LINE_TST("--some-opt 'first parm \"second parm\"");
111
+    assert_int_equal(res, 0);
112
+
113
+    /* escaped backslash */
114
+    PARSE_LINE_TST("some\\\\opt C:\\\\directory\\\\file");
115
+    assert_int_equal(res, 2);
116
+    assert_string_equal(p[0], "some\\opt");
117
+    assert_string_equal(p[1], "C:\\directory\\file");
118
+    assert_null(p[res]);
119
+
120
+    /* comment chars are not special inside parameter */
121
+    PARSE_LINE_TST("some-opt firstparm; second#parm");
122
+    assert_int_equal(res, 3);
123
+    assert_string_equal(p[0], "some-opt");
124
+    assert_string_equal(p[1], "firstparm;");
125
+    assert_string_equal(p[2], "second#parm");
126
+    assert_null(p[res]);
127
+
128
+    /* comment */
129
+    PARSE_LINE_TST("some-opt firstparm # secondparm");
130
+    assert_int_equal(res, 2);
131
+    assert_string_equal(p[0], "some-opt");
132
+    assert_string_equal(p[1], "firstparm");
133
+    assert_null(p[res]);
134
+
135
+    /* parameter just long enough */
136
+    PARSE_LINE_TST("opt " A_TIMES_256);
137
+    assert_int_equal(res, 2);
138
+    assert_string_equal(p[0], "opt");
139
+    assert_string_equal(p[1], A_TIMES_256);
140
+    assert_null(p[res]);
141
+
142
+    /* quoting doesn't count for parameter length */
143
+    PARSE_LINE_TST("opt \"" A_TIMES_256 "\"");
144
+    assert_int_equal(res, 2);
145
+    assert_string_equal(p[0], "opt");
146
+    assert_string_equal(p[1], A_TIMES_256);
147
+    assert_null(p[res]);
148
+
149
+    /* very long line */
150
+    PARSE_LINE_TST("opt " A_TIMES_256 " " A_TIMES_256 " " A_TIMES_256 " " A_TIMES_256);
151
+    assert_int_equal(res, 5);
152
+    assert_string_equal(p[0], "opt");
153
+    assert_string_equal(p[1], A_TIMES_256);
154
+    assert_string_equal(p[2], A_TIMES_256);
155
+    assert_string_equal(p[3], A_TIMES_256);
156
+    assert_string_equal(p[4], A_TIMES_256);
157
+    assert_null(p[res]);
158
+
159
+    /* parameter too long */
160
+    PARSE_LINE_TST("opt " A_TIMES_256 "B");
161
+    assert_int_equal(res, 0);
162
+
163
+    /* max parameters */
164
+    PARSE_LINE_TST("0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15");
165
+    assert_int_equal(res, MAX_PARMS);
166
+    char num[3];
167
+    for (int i = 0; i < MAX_PARMS; i++)
168
+    {
169
+        assert_true(snprintf(num, 3, "%d", i) < 3);
170
+        assert_string_equal(p[i], num);
171
+    }
172
+    assert_null(p[res]);
173
+
174
+    /* too many parameters, overflow is ignored */
175
+    PARSE_LINE_TST("0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16");
176
+    assert_int_equal(res, MAX_PARMS);
177
+    for (int i = 0; i < MAX_PARMS; i++)
178
+    {
179
+        assert_true(snprintf(num, 3, "%d", i) < 3);
180
+        assert_string_equal(p[i], num);
181
+    }
182
+    assert_null(p[res]);
183
+
184
+    gc_free(&gc);
185
+}
186
+
187
+int
188
+main(void)
189
+{
190
+    const struct CMUnitTest tests[] = {
191
+        cmocka_unit_test(test_parse_line),
192
+    };
193
+
194
+    return cmocka_run_group_tests_name("options_parse", tests, NULL, NULL);
195
+}