15
15
16
16
#include < string.h>
17
17
#include < cstring>
18
-
18
+ #include < cassert>
19
+ #include < thread>
20
+ #include < array>
19
21
#include < iostream>
20
22
#include < ctime>
21
23
#include < string>
38
40
39
41
40
42
using modsecurity_test::UnitTest;
43
+ using modsecurity_test::UnitTestResult;
41
44
using modsecurity_test::ModSecurityTest;
42
45
using modsecurity_test::ModSecurityTestResults;
43
46
using modsecurity::actions::transformations::Transformation;
@@ -53,64 +56,149 @@ void print_help() {
53
56
}
54
57
55
58
56
- void perform_unit_test (ModSecurityTest<UnitTest> *test, UnitTest *t,
57
- ModSecurityTestResults<UnitTest>* res) {
58
- std::string error;
59
+ struct OperatorTest {
60
+ using ItemType = Operator;
61
+
62
+ static ItemType* init (const UnitTest &t) {
63
+ auto op = Operator::instantiate (t.name , t.param );
64
+ assert (op != nullptr );
65
+
66
+ std::string error;
67
+ op->init (t.filename , &error);
68
+
69
+ return op;
70
+ }
71
+
72
+ static UnitTestResult eval (ItemType &op, const UnitTest &t) {
73
+ return {op.evaluate (nullptr , nullptr , t.input , nullptr ), {}};
74
+ }
75
+
76
+ static bool check (const UnitTestResult &result, const UnitTest &t) {
77
+ return result.ret != t.ret ;
78
+ }
79
+ };
80
+
81
+
82
+ struct TransformationTest {
83
+ using ItemType = Transformation;
84
+
85
+ static ItemType* init (const UnitTest &t) {
86
+ auto tfn = Transformation::instantiate (" t:" + t.name );
87
+ assert (tfn != nullptr );
88
+
89
+ return tfn;
90
+ }
91
+
92
+ static UnitTestResult eval (ItemType &tfn, const UnitTest &t) {
93
+ return {1 , tfn.evaluate (t.input , nullptr )};
94
+ }
95
+
96
+ static bool check (const UnitTestResult &result, const UnitTest &t) {
97
+ return result.output != t.output ;
98
+ }
99
+ };
100
+
101
+
102
+ template <typename TestType>
103
+ UnitTestResult perform_unit_test_once (const UnitTest &t) {
104
+ std::unique_ptr<typename TestType::ItemType> item (TestType::init (t));
105
+ assert (item.get () != nullptr );
106
+
107
+ return TestType::eval (*item.get (), t);
108
+ }
109
+
110
+
111
+ template <typename TestType>
112
+ UnitTestResult perform_unit_test_multithreaded (const UnitTest &t) {
113
+
114
+ constexpr auto NUM_THREADS = 50 ;
115
+ constexpr auto ITERATIONS = 5'000 ;
116
+
117
+ std::array<std::thread, NUM_THREADS> threads;
118
+ std::array<UnitTestResult, NUM_THREADS> results;
119
+
120
+ std::unique_ptr<typename TestType::ItemType> item (TestType::init (t));
121
+ assert (item.get () != nullptr );
122
+
123
+ for (auto i = 0 ; i != threads.size (); ++i)
124
+ {
125
+ auto &result = results[i];
126
+ threads[i] = std::thread (
127
+ [&item, &t, &result]()
128
+ {
129
+ for (auto j = 0 ; j != ITERATIONS; ++j)
130
+ result = TestType::eval (*item.get (), t);
131
+ });
132
+ }
133
+
134
+ UnitTestResult ret;
135
+
136
+ for (auto i = 0 ; i != threads.size (); ++i)
137
+ {
138
+ threads[i].join ();
139
+ if (TestType::check (results[i], t))
140
+ ret = results[i]; // error value, keep iterating to join all threads
141
+ else if (i == 0 )
142
+ ret = results[i]; // initial value
143
+ }
144
+
145
+ return ret; // cppcheck-suppress uninitvar ; false positive, ret assigned at least once in previous loop
146
+ }
147
+
148
+
149
+ template <typename TestType>
150
+ void perform_unit_test_helper (const ModSecurityTest<UnitTest> &test, UnitTest &t,
151
+ ModSecurityTestResults<UnitTest> &res) {
152
+
153
+ if (!test.m_test_multithreaded )
154
+ t.result = perform_unit_test_once<TestType>(t);
155
+ else
156
+ t.result = perform_unit_test_multithreaded<TestType>(t);
157
+
158
+ if (TestType::check (t.result , t)) {
159
+ res.push_back (&t);
160
+ if (test.m_automake_output ) {
161
+ std::cout << " FAIL " ;
162
+ }
163
+ } else if (test.m_automake_output ) {
164
+ std::cout << " PASS " ;
165
+ }
166
+ }
167
+
168
+
169
+ void perform_unit_test (const ModSecurityTest<UnitTest> &test, UnitTest &t,
170
+ ModSecurityTestResults<UnitTest> &res) {
59
171
bool found = true ;
60
172
61
- if (test-> m_automake_output ) {
173
+ if (test. m_automake_output ) {
62
174
std::cout << " :test-result: " ;
63
175
}
64
176
65
- if (t-> resource .empty () == false ) {
66
- found = ( std::find (resources.begin (), resources.end (), t-> resource )
67
- != resources.end ()) ;
177
+ if (t. resource .empty () == false ) {
178
+ found = std::find (resources.begin (), resources.end (), t. resource )
179
+ != resources.end ();
68
180
}
69
181
70
182
if (!found) {
71
- t-> skipped = true ;
72
- res-> push_back (t);
73
- if (test-> m_automake_output ) {
183
+ t. skipped = true ;
184
+ res. push_back (& t);
185
+ if (test. m_automake_output ) {
74
186
std::cout << " SKIP " ;
75
187
}
76
188
}
77
189
78
- if (t->type == " op" ) {
79
- Operator *op = Operator::instantiate (t->name , t->param );
80
- op->init (t->filename , &error);
81
- int ret = op->evaluate (NULL , NULL , t->input , NULL );
82
- t->obtained = ret;
83
- if (ret != t->ret ) {
84
- res->push_back (t);
85
- if (test->m_automake_output ) {
86
- std::cout << " FAIL " ;
87
- }
88
- } else if (test->m_automake_output ) {
89
- std::cout << " PASS " ;
90
- }
91
- delete op;
92
- } else if (t->type == " tfn" ) {
93
- Transformation *tfn = Transformation::instantiate (" t:" + t->name );
94
- std::string ret = tfn->evaluate (t->input , NULL );
95
- t->obtained = 1 ;
96
- t->obtainedOutput = ret;
97
- if (ret != t->output ) {
98
- res->push_back (t);
99
- if (test->m_automake_output ) {
100
- std::cout << " FAIL " ;
101
- }
102
- } else if (test->m_automake_output ) {
103
- std::cout << " PASS " ;
104
- }
105
- delete tfn;
190
+ if (t.type == " op" ) {
191
+ perform_unit_test_helper<OperatorTest>(test, t, res);
192
+ } else if (t.type == " tfn" ) {
193
+ perform_unit_test_helper<TransformationTest>(test, t, res);
106
194
} else {
107
- std::cerr << " Failed. Test type is unknown: << " << t-> type ;
195
+ std::cerr << " Failed. Test type is unknown: << " << t. type ;
108
196
std::cerr << std::endl;
109
197
}
110
198
111
- if (test-> m_automake_output ) {
112
- std::cout << t-> name << " "
113
- << modsecurity::utils::string::toHexIfNeeded (t-> input )
199
+ if (test. m_automake_output ) {
200
+ std::cout << t. name << " "
201
+ << modsecurity::utils::string::toHexIfNeeded (t. input )
114
202
<< std::endl;
115
203
}
116
204
}
@@ -151,17 +239,15 @@ int main(int argc, char **argv) {
151
239
test.load_tests (" test-cases/secrules-language-tests/transformations" );
152
240
}
153
241
154
- for (std::pair<std::string, std::vector<UnitTest *> *> a : test) {
155
- std::vector<UnitTest *> *tests = a.second ;
156
-
242
+ for (auto & [filename, tests] : test) {
157
243
total += tests->size ();
158
- for (UnitTest * t : *tests) {
244
+ for (auto t : *tests) {
159
245
ModSecurityTestResults<UnitTest> r;
160
246
161
247
if (!test.m_automake_output ) {
162
- std::cout << " " << a. first << " ...\t " ;
248
+ std::cout << " " << filename << " ...\t " ;
163
249
}
164
- perform_unit_test (& test, t, & r);
250
+ perform_unit_test (test, * t, r);
165
251
166
252
if (!test.m_automake_output ) {
167
253
int skp = 0 ;
@@ -191,7 +277,7 @@ int main(int argc, char **argv) {
191
277
std::cout << " Total >> " << total << std::endl;
192
278
}
193
279
194
- for (UnitTest * t : results) {
280
+ for (const auto t : results) {
195
281
std::cout << t->print () << std::endl;
196
282
}
197
283
@@ -216,8 +302,8 @@ int main(int argc, char **argv) {
216
302
}
217
303
218
304
for (auto a : test) {
219
- auto * vec = a.second ;
220
- for (auto * t : *vec)
305
+ auto vec = a.second ;
306
+ for (auto t : *vec)
221
307
delete t;
222
308
delete vec;
223
309
}
0 commit comments