1 /*
2  * SPDX-License-Identifier: ISC
3  *
4  * Copyright (c) 2020 Robert Manner <robert.manner@oneidentity.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #ifndef PYTHON_TESTHELPERS
20 #define PYTHON_TESTHELPERS
21 
22 #include "iohelpers.h"
23 
24 #include "../pyhelpers.h"
25 
26 #include "sudo_conf.h"
27 
28 // just for the IDE
29 #ifndef SRC_DIR
30 #define SRC_DIR ""
31 #endif
32 #define TESTDATA_DIR SRC_DIR "/regress/testdata/"
33 
34 extern const char *sudo_conf_developer_mode;
35 extern const char *sudo_conf_normal_mode;
36 
37 #define TEMP_PATH_TEMPLATE "/tmp/sudo_check_python_exampleXXXXXX"
38 
39 extern struct TestData {
40     char *tmp_dir;
41     char *tmp_dir2;
42     char stdout_str[MAX_OUTPUT];
43     char stderr_str[MAX_OUTPUT];
44 
45     char conv_str[MAX_OUTPUT];
46     const char *conv_replies[8];
47 
48     // some example test data used by multiple test cases:
49     char ** settings;
50     char ** user_info;
51     char ** command_info;
52     char ** plugin_argv;
53     int plugin_argc;
54     char ** user_env;
55     char ** plugin_options;
56 } data;
57 
58 const char * expected_path(const char *format, ...);
59 
60 char ** create_str_array(size_t count, ...);
61 
62 #define RUN_TEST(testcase) \
63     do { \
64         int success = 1; \
65         printf("Running test " #testcase " ... \n"); \
66         if (!init()) { \
67             printf("FAILED: initialization of testcase %s at %s:%d\n", #testcase, __FILE__, __LINE__); \
68             success = 0; \
69         } else \
70         if (!testcase) { \
71             printf("FAILED: testcase %s at %s:%d\n", #testcase, __FILE__, __LINE__); \
72             success = 0; \
73         } \
74         if (!cleanup(success)) { \
75             printf("FAILED: deitialization of testcase %s at %s:%d\n", #testcase, __FILE__, __LINE__); \
76             success = 0; \
77         } \
78         if (!success) { \
79             errors++; \
80         } \
81     } while(false)
82 
83 #define VERIFY_PRINT_MSG(fmt, actual_str, actual, expected_str, expected, expected_to_be_message) \
84     printf("Expectation failed at %s:%d:\n  actual is <<" fmt ">>: %s\n  %s <<" fmt ">>: %s\n", \
85            __FILE__, __LINE__, actual, actual_str, expected_to_be_message, expected, expected_str)
86 
87 #define VERIFY_CUSTOM(fmt, type, actual, expected, invert) \
88     do { \
89         type actual_value = (type)(actual); \
90         int failed = (actual_value != expected); \
91         if (invert) \
92             failed = !failed; \
93         if (failed) { \
94             VERIFY_PRINT_MSG(fmt, #actual, actual_value, #expected, expected, invert ? "not expected to be" : "expected to be"); \
95             return false; \
96         } \
97     } while(false)
98 
99 #define VERIFY_EQ(fmt, type, actual, expected) VERIFY_CUSTOM(fmt, type, actual, expected, false)
100 #define VERIFY_NE(fmt, type, actual, not_expected) VERIFY_CUSTOM(fmt, type, actual, not_expected, true)
101 
102 #define VERIFY_INT(actual, expected) VERIFY_EQ("%d", int, actual, expected)
103 
104 #define VERIFY_PTR(actual, expected) VERIFY_EQ("%p", const void *, (const void *)actual, (const void *)expected)
105 #define VERIFY_PTR_NE(actual, not_expected) VERIFY_NE("%p", const void *, (const void *)actual, (const void *)not_expected)
106 
107 #define VERIFY_TRUE(actual) VERIFY_NE("%d", int, actual, 0)
108 #define VERIFY_FALSE(actual) VERIFY_INT(actual, false)
109 
110 #define VERIFY_NOT_NULL(actual) VERIFY_NE("%p", const void *, actual, NULL)
111 
112 #define VERIFY_STR(actual, expected) \
113     do { \
114         const char *actual_str = actual; \
115         if (!actual_str || strcmp(actual_str, expected) != 0) { \
116             VERIFY_PRINT_MSG("%s", #actual, actual_str ? actual_str : "(null)", #expected, expected, "expected to be"); \
117             return false; \
118         } \
119     } while(false)
120 
121 #define VERIFY_STR_CONTAINS(actual, expected) \
122     do { \
123         const char *actual_str = actual; \
124         if (!actual_str || strstr(actual_str, expected) == NULL) { \
125             VERIFY_PRINT_MSG("%s", #actual, actual_str ? actual_str : "(null)", #expected, expected, "expected to contain the string"); \
126             return false; \
127         } \
128     } while(false)
129 
130 int is_update(void);
131 
132 int verify_content(char *actual_content, const char *reference_path);
133 
134 #define VERIFY_CONTENT(actual_output, reference_path) \
135     VERIFY_TRUE(verify_content(actual_output, reference_path))
136 
137 #define VERIFY_STDOUT(reference_path) \
138     VERIFY_CONTENT(data.stdout_str, reference_path)
139 
140 #define VERIFY_STDERR(reference_path) \
141     VERIFY_CONTENT(data.stderr_str, reference_path)
142 
143 #define VERIFY_CONV(reference_name) \
144     VERIFY_CONTENT(data.conv_str, reference_name)
145 
146 int verify_file(const char *actual_dir, const char *actual_file_name, const char *reference_path);
147 
148 #define VERIFY_FILE(actual_file_name, reference_path) \
149     VERIFY_TRUE(verify_file(data.tmp_dir, actual_file_name, reference_path))
150 
151 int fake_conversation(int num_msgs, const struct sudo_conv_message msgs[],
152                       struct sudo_conv_reply replies[], struct sudo_conv_callback *callback);
153 
154 int fake_conversation_with_suspend(int num_msgs, const struct sudo_conv_message msgs[],
155                                    struct sudo_conv_reply replies[], struct sudo_conv_callback *callback);
156 
157 int fake_printf(int msg_type, const char *fmt, ...);
158 
159 int verify_log_lines(const char *reference_path);
160 
161 int mock_python_datetime_now(const char *plugin_name, const char *date_str);
162 
163 #define VERIFY_LOG_LINES(reference_path) \
164     VERIFY_TRUE(verify_log_lines(reference_path))
165 
166 int verify_str_set(char **actual_set, char **expected_set, const char *actual_variable_name);
167 
168 #define VERIFY_STR_SET(actual_set, ...) \
169     do { \
170         char **expected_set = create_str_array(__VA_ARGS__); \
171         VERIFY_TRUE(verify_str_set(actual_set, expected_set, #actual_set)); \
172         str_array_free(&expected_set); \
173     } while(false)
174 
175 #endif // PYTHON_TESTHELPERS
176