1 /*
2  * ====================================================================
3  *    Licensed to the Apache Software Foundation (ASF) under one
4  *    or more contributor license agreements.  See the NOTICE file
5  *    distributed with this work for additional information
6  *    regarding copyright ownership.  The ASF licenses this file
7  *    to you under the Apache License, Version 2.0 (the
8  *    "License"); you may not use this file except in compliance
9  *    with the License.  You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  *    Unless required by applicable law or agreed to in writing,
14  *    software distributed under the License is distributed on an
15  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  *    KIND, either express or implied.  See the License for the
17  *    specific language governing permissions and limitations
18  *    under the License.
19  * ====================================================================
20  */
21 
22 #ifndef SVN_TEST_H
23 #define SVN_TEST_H
24 
25 #ifndef SVN_ENABLE_DEPRECATION_WARNINGS_IN_TESTS
26 #undef SVN_DEPRECATED
27 #define SVN_DEPRECATED
28 #endif /* ! SVN_ENABLE_DEPRECATION_WARNINGS_IN_TESTS */
29 
30 #include <stdio.h>
31 
32 #include <apr_pools.h>
33 
34 #include "svn_delta.h"
35 #include "svn_path.h"
36 #include "svn_types.h"
37 #include "svn_error.h"
38 #include "svn_string.h"
39 #include "svn_auth.h"
40 
41 #ifdef __cplusplus
42 extern "C" {
43 #endif /* __cplusplus */
44 
45 
46 /** Handy macro to test a condition, returning SVN_ERR_TEST_FAILED if FALSE
47  *
48  * This macro should be used in place of SVN_ERR_ASSERT() since we don't
49  * want to core-dump the test.
50  */
51 #define SVN_TEST_ASSERT(expr)                                     \
52   do {                                                            \
53     if (!(expr))                                                  \
54       return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,         \
55                                "assertion '%s' failed at %s:%d",  \
56                                #expr, __FILE__, __LINE__);        \
57   } while (0)
58 
59 /**
60  * Macro for testing assumptions when the context does not allow
61  * returning an svn_error_t*.
62  *
63  * Will write to stderr and cause a segfault if EXPR is false.
64  */
65 #define SVN_TEST_ASSERT_NO_RETURN(expr)                           \
66   do {                                                            \
67     if (!(expr))                                                  \
68       {                                                           \
69         unsigned int z_e_r_o_p_a_g_e__;                           \
70         fprintf(stderr, "TEST ASSERTION FAILED: %s\n", #expr);    \
71         z_e_r_o_p_a_g_e__ = *(volatile unsigned int*)0;           \
72         *(volatile unsigned int*)0 = z_e_r_o_p_a_g_e__;           \
73       }                                                           \
74   } while (0)
75 
76 /** Handy macro for testing an expected svn_error_t return value.
77  * EXPECTED must be a real error (neither SVN_NO_ERROR nor APR_SUCCESS).
78  * The error returned by EXPR will be cleared.
79  */
80 #define SVN_TEST_ASSERT_ERROR(expr, expected)                             \
81   do {                                                                    \
82     svn_error_t *err__ = (expr);                                          \
83     SVN_ERR_ASSERT((expected));                                           \
84     if (err__ == SVN_NO_ERROR || err__->apr_err != (expected))            \
85       return err__ ? svn_error_createf(SVN_ERR_TEST_FAILED, err__,        \
86                                        "Expected error %s but got %s",    \
87                                        svn_error_symbolic_name(expected), \
88                                        svn_error_symbolic_name(           \
89                                          err__->apr_err))                 \
90                    : svn_error_createf(SVN_ERR_TEST_FAILED, err__,        \
91                                        "Expected error %s but got %s",    \
92                                        svn_error_symbolic_name(expected), \
93                                         "SVN_NO_ERROR");                  \
94     svn_error_clear(err__);                                               \
95   } while (0)
96 
97 /** Handy macro for testing that an svn_error_t is returned.
98  * The result must be neither SVN_NO_ERROR nor SVN_ERR_ASSERTION_FAIL.
99  * The error returned by EXPR will be cleared.
100  */
101 #define SVN_TEST_ASSERT_ANY_ERROR(expr)                                   \
102   do {                                                                    \
103     svn_error_t *err__ = (expr);                                          \
104     if (err__ == SVN_NO_ERROR || err__->apr_err == SVN_ERR_ASSERTION_FAIL)\
105       return err__ ? svn_error_createf(SVN_ERR_TEST_FAILED, err__,        \
106                                        "Expected error but got %s",       \
107                                        "SVN_ERR_ASSERTION_FAIL")          \
108                    : svn_error_createf(SVN_ERR_TEST_FAILED, err__,        \
109                                        "Expected error but got %s",       \
110                                        "SVN_NO_ERROR");                   \
111     svn_error_clear(err__);                                               \
112   } while (0)
113 
114 /** Handy macro for testing string equality.
115  *
116  * EXPR and/or EXPECTED_EXPR may be NULL which compares equal to NULL and
117  * not equal to any non-NULL string.
118  */
119 #define SVN_TEST_STRING_ASSERT(expr, expected_expr)                 \
120   do {                                                              \
121     const char *tst_str1 = (expr);                                  \
122     const char *tst_str2 = (expected_expr);                         \
123                                                                     \
124     if (tst_str2 == NULL && tst_str1 == NULL)                       \
125       break;                                                        \
126     if ((tst_str1 == NULL) || (tst_str2 == NULL)                    \
127         || (strcmp(tst_str2, tst_str1) != 0))                       \
128       return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,           \
129           "Strings not equal\n  Expected: '%s'\n  Found:    '%s'"   \
130           "\n  at %s:%d",                                           \
131           tst_str2, tst_str1, __FILE__, __LINE__);                  \
132   } while(0)
133 
134  /** Handy macro for testing integer equality.
135   */
136 #define SVN_TEST_INT_ASSERT(expr, expected_expr)                  \
137   do {                                                            \
138     apr_int64_t tst_int1 = (expr);                                \
139     apr_int64_t tst_int2 = (expected_expr);                       \
140                                                                   \
141     if (tst_int1 != tst_int2)                                     \
142       return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,         \
143           "Integers not equal\n"                                  \
144           "  Expected: %" APR_INT64_T_FMT "\n"                    \
145           "     Found: %" APR_INT64_T_FMT "\n"                    \
146           "  at %s:%d",                                           \
147           tst_int2, tst_int1, __FILE__, __LINE__);                \
148   } while(0)
149 
150 
151 /* Baton for any arguments that need to be passed from main() to svn
152  * test functions.
153  */
154 typedef struct svn_test_opts_t
155 {
156   /* The name of the application (to generate unique names) */
157   const char *prog_name;
158   /* Description of the fs backend that should be used for testing. */
159   const char *fs_type;
160   /* Config file. */
161   const char *config_file;
162   /* Source dir. */
163   const char *srcdir;
164   /* Repository dir: temporary directory to create repositories in as subdir */
165   const char *repos_dir;
166   /* Repository url: The url to access REPOS_DIR as */
167   const char *repos_url;
168   /* Memcached server. */
169   const char *memcached_server;
170   /* Repository template: pre-created repository to copy for tests */
171   const char *repos_template;
172   /* Minor version to use for servers and FS backends, or zero to use
173      the current latest version. */
174   int server_minor_version;
175   svn_boolean_t verbose;
176   /* Add future "arguments" here. */
177 } svn_test_opts_t;
178 
179 /* Prototype for test driver functions. */
180 typedef svn_error_t* (*svn_test_driver2_t)(apr_pool_t *pool);
181 
182 /* Prototype for test driver functions which need options. */
183 typedef svn_error_t* (*svn_test_driver_opts_t)(const svn_test_opts_t *opts,
184                                                apr_pool_t *pool);
185 
186 /* Prototype for test predicate functions. */
187 typedef svn_boolean_t (*svn_test_predicate_func_t)(const svn_test_opts_t *opts,
188                                                    const char *predicate_value,
189                                                    apr_pool_t *pool);
190 
191 /* Test modes. */
192 enum svn_test_mode_t
193   {
194     svn_test_pass,
195     svn_test_xfail,
196     svn_test_skip,
197     svn_test_all
198   };
199 
200 /* Structure for runtime test predicates. */
201 struct svn_test_predicate_t
202 {
203   /* The predicate function. */
204   svn_test_predicate_func_t func;
205 
206   /* The value that the predicate function tests. */
207   const char *value;
208 
209   /* The test mode that's used if the predicate matches. */
210   enum svn_test_mode_t alternate_mode;
211 
212   /* Description for the test log */
213   const char *description;
214 };
215 
216 
217 /* Each test gets a test descriptor, holding the function and other
218  * associated data.
219  */
220 struct svn_test_descriptor_t
221 {
222   /* Is the test marked XFAIL? */
223   enum svn_test_mode_t mode;
224 
225   /* A pointer to the test driver function. */
226   svn_test_driver2_t func2;
227 
228   /* A pointer to the test driver function. */
229   svn_test_driver_opts_t func_opts;
230 
231   /* A descriptive message for this test. */
232   const char *msg;
233 
234   /* An optional description of a work-in-progress test. */
235   const char *wip;
236 
237   /* An optional runtiume predicate. */
238   struct svn_test_predicate_t predicate;
239 };
240 
241 /* All Subversion test programs include an array of svn_test_descriptor_t's
242  * (all of our sub-tests) that begins and ends with a SVN_TEST_NULL entry.
243  * This descriptor must be passed to the svn_test_main function.
244  *
245  * MAX_THREADS is the number of concurrent tests to run.  Set to 1 if
246  * all tests must be executed serially.  Numbers less than 1 mean
247  * "unbounded".
248  */
249 int svn_test_main(int argc, const char *argv[], int max_threads,
250                   struct svn_test_descriptor_t *test_funcs);
251 
252 /* Boilerplate for the main function for each test program. */
253 #define SVN_TEST_MAIN                                 \
254   int main(int argc, const char *argv[])              \
255     {                                                 \
256       return svn_test_main(argc, argv,                \
257                            max_threads, test_funcs);  \
258     }
259 
260 /* A null initializer for the test descriptor. */
261 #define SVN_TEST_NULL  {0}
262 
263 /* Initializer for PASS tests */
264 #define SVN_TEST_PASS2(func, msg)  {svn_test_pass, func, NULL, msg}
265 
266 /* Initializer for XFAIL tests */
267 #define SVN_TEST_XFAIL2(func, msg) {svn_test_xfail, func, NULL, msg}
268 
269 /* Initializer for conditional XFAIL tests */
270 #define SVN_TEST_XFAIL_COND2(func, p, msg) \
271   {(p) ? svn_test_xfail : svn_test_pass, func, NULL, msg}
272 
273 /* Initializer for SKIP tests */
274 #define SVN_TEST_SKIP2(func, p, msg) \
275   {(p) ? svn_test_skip : svn_test_pass, func, NULL, msg}
276 
277 /* Similar macros, but for tests needing options.  */
278 #define SVN_TEST_OPTS_PASS(func, msg)  {svn_test_pass, NULL, func, msg}
279 #define SVN_TEST_OPTS_XFAIL(func, msg) {svn_test_xfail, NULL, func, msg}
280 #define SVN_TEST_OPTS_XFAIL_COND(func, p, msg) \
281   {(p) ? svn_test_xfail : svn_test_pass, NULL, func, msg}
282 #define SVN_TEST_OPTS_XFAIL_OTOH(func, msg, predicate) \
283   {svn_test_xfail, NULL, func, msg, NULL, predicate}
284 #define SVN_TEST_OPTS_SKIP(func, p, msg) \
285   {(p) ? svn_test_skip : svn_test_pass, NULL, func, msg}
286 
287 /* Initializer for XFAIL tests for works-in-progress. */
288 #define SVN_TEST_WIMP(func, msg, wip) \
289   {svn_test_xfail, func, NULL, msg, wip}
290 #define SVN_TEST_WIMP_COND(func, p, msg, wip) \
291   {(p) ? svn_test_xfail : svn_test_pass, func, NULL, msg, wip}
292 #define SVN_TEST_OPTS_WIMP(func, msg, wip) \
293   {svn_test_xfail, NULL, func, msg, wip}
294 #define SVN_TEST_OPTS_WIMP_COND(func, p, msg, wip) \
295   {(p) ? svn_test_xfail : svn_test_pass, NULL, func, msg, wip}
296 
297 
298 /* Return a pseudo-random number based on SEED, and modify SEED.
299  *
300  * This is a "good" pseudo-random number generator, intended to replace
301  * all those "bad" rand() implementations out there.
302  */
303 apr_uint32_t svn_test_rand(apr_uint32_t *seed);
304 
305 
306 /* Add PATH to the test cleanup list.  */
307 void svn_test_add_dir_cleanup(const char *path);
308 
309 
310 /* A simple representation for a tree node. */
311 typedef struct svn_test__tree_entry_t
312 {
313   const char *path;     /* relpath of this node */
314   const char *contents; /* text contents, or NULL for a directory */
315 }
316 svn_test__tree_entry_t;
317 
318 /* Wrapper for an array of svn_test__tree_entry_t's. */
319 typedef struct svn_test__tree_t
320 {
321   svn_test__tree_entry_t *entries;
322   int num_entries;
323 }
324 svn_test__tree_t;
325 
326 
327 /* The standard Greek tree, terminated by a node with path=NULL. */
328 extern const svn_test__tree_entry_t svn_test__greek_tree_nodes[21];
329 
330 
331 /* Returns a path to BASENAME within the transient data area for the
332    current test. */
333 const char *
334 svn_test_data_path(const char* basename, apr_pool_t *result_pool);
335 
336 
337 /* Some tests require the --srcdir option and should use this function
338  * to get it. If not provided, print a warning and attempt to run the
339  * tests under the assumption that --srcdir is the current directory. */
340 svn_error_t *
341 svn_test_get_srcdir(const char **srcdir,
342                     const svn_test_opts_t *opts,
343                     apr_pool_t *pool);
344 
345 /* Initializes a standard auth baton for accessing the repositories */
346 svn_error_t *
347 svn_test__init_auth_baton(svn_auth_baton_t **baton,
348                           apr_pool_t *result_pool);
349 
350 /* Create a temp folder for test & schedule it for automatic cleanup.
351  * Uses POOL for all allocations. */
352 svn_error_t *
353 svn_test_make_sandbox_dir(const char **sb_dir_p,
354                           const char *sb_name,
355                           apr_pool_t *pool);
356 
357 /*
358  * Test predicates
359  */
360 
361 #define SVN_TEST_PASS_IF_FS_TYPE_IS(fs_type) \
362   { svn_test__fs_type_is, fs_type, svn_test_pass, \
363     "PASS if fs-type = " fs_type }
364 
365 #define SVN_TEST_PASS_IF_FS_TYPE_IS_NOT(fs_type) \
366   { svn_test__fs_type_not, fs_type, svn_test_pass, \
367     "PASS if fs-type != " fs_type }
368 
369 /* Return TRUE if the fs-type in OPTS matches PREDICATE_VALUE. */
370 svn_boolean_t
371 svn_test__fs_type_is(const svn_test_opts_t *opts,
372                      const char *predicate_value,
373                      apr_pool_t *pool);
374 
375 
376 /* Return TRUE if the fs-type in OPTS does not matches PREDICATE_VALUE. */
377 svn_boolean_t
378 svn_test__fs_type_not(const svn_test_opts_t *opts,
379                       const char *predicate_value,
380                       apr_pool_t *pool);
381 
382 
383 #ifdef __cplusplus
384 }
385 #endif /* __cplusplus */
386 
387 #endif /* SVN_TEST_H */
388