1 /* $Id: test_ncbi_linkerd.c 575619 2018-12-02 03:24:59Z mcelhany $
2  * ===========================================================================
3  *
4  *                            PUBLIC DOMAIN NOTICE
5  *               National Center for Biotechnology Information
6  *
7  *  This software/database is a "United States Government Work" under the
8  *  terms of the United States Copyright Act.  It was written as part of
9  *  the author's official duties as a United States Government employee and
10  *  thus cannot be copyrighted.  This software/database is freely available
11  *  to the public for use. The National Library of Medicine and the U.S.
12  *  Government have not placed any restriction on its use or reproduction.
13  *
14  *  Although all reasonable efforts have been taken to ensure the accuracy
15  *  and reliability of the software and data, the NLM and the U.S.
16  *  Government do not and cannot warrant the performance or results that
17  *  may be obtained by using this software or data. The NLM and the U.S.
18  *  Government disclaim all warranties, express or implied, including
19  *  warranties of performance, merchantability or fitness for any particular
20  *  purpose.
21  *
22  *  Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Authors:  David McElhany
27  *
28  * File Description:
29  *   Standard test for LINKERD service resolution facility.
30  *
31  */
32 
33 
34 #include "../ncbi_ansi_ext.h"
35 #include "../ncbi_priv.h"               /* CORE logging facilities */
36 #include "../ncbi_linkerd.h"
37 #include "../ncbi_servicep.h"
38 #include "../parson.h"
39 
40 #include <connect/ncbi_server_info.h>
41 #include <connect/ncbi_service.h>
42 #include <connect/ncbi_tls.h>
43 
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <time.h>
48 
49 
50 /* ---------------------------------------------------------------------------
51     Stub out the whole program for Windows DLL configurations.
52     The problem is that the program uses PARSON, which does not have the
53     dllexport/dllimport specifiers, resulting in link errors when built
54     with DLL on Windows.
55     */
56 #if defined(NCBI_OS_MSWIN)  &&  defined(NCBI_DLL_BUILD)
main(int argc,const char * argv[])57 int main(int argc, const char *argv[])
58 {
59     printf("NCBI_UNITTEST_DISABLED - This program is disabled for WinDLL.\n");
60     return 1;
61 }
62 #else
63 
64 #ifdef _MSC_VER
65 #define unsetenv(n)     _putenv_s(n,"")
66 #define setenv(n,v,w)   _putenv_s(n,v)
67 #define FMT_SIZE_T      "%llu"
68 #else
69 #define FMT_SIZE_T      "%zu"
70 #endif
71 
72 #define LEN_HOST        77
73 #define LEN_HDR         99
74 #define MAX_TESTS       100
75 
76 #define NIL             '\0'
77 
78 
79 /*  ----------------------------------------------------------------------------
80     Local Types
81 */
82 
83 typedef struct
84 {
85     char            host[LEN_HOST+1];
86     char            hdr [LEN_HDR +1];
87     unsigned short  port;
88 } SEndpoint;
89 
90 typedef enum {
91     fMatch_None     = 0,
92     fMatch_Host     = 0x01,
93     fMatch_Port     = 0x02,
94     fMatch_Hdr      = 0x04,
95     fMatch_All      = -1,
96     fMatch_Default  = fMatch_All
97 } EMatch;
98 
99 typedef struct
100 {
101     const char*     name;
102     const char*     result;
103 } STestResult;
104 
105 
106 
107 
108 /*  ----------------------------------------------------------------------------
109     Static Variables
110 */
111 
112 static SEndpoint    s_end_exp;
113 static SEndpoint    s_end_got;
114 static const char*  s_json_file = "test_ncbi_linkerd_tests.json";
115 static STestResult  s_results[MAX_TESTS];
116 
117 
118 
119 
120 /*  ----------------------------------------------------------------------------
121     Static Function Declarations
122 */
123 
124 static int check_match(EMatch flags);
125 
126 static int run_a_test(size_t test_idx, const char *svc, const char *sch,
127                       const char *user, const char *pass, const char *path,
128                       const char *args, int exp_err, int exp_warn, int repop,
129                       int reset);
130 
131 static int run_tests(const char *test_nums);
132 
133 
134 
135 
136 /*  ----------------------------------------------------------------------------
137     Static Functions
138 */
139 
check_match(EMatch flags)140 static int check_match(EMatch flags)
141 {
142     if ((flags & fMatch_Host)  &&  strcmp(s_end_exp.host, s_end_got.host) != 0) {
143         /* always consider localhost a match */
144         if (strcmp("127.0.0.1", s_end_got.host) != 0)
145             return 0;
146     }
147 
148     if ((flags & fMatch_Hdr)  &&  strcmp(s_end_exp.hdr, s_end_got.hdr) != 0)
149         return 0;
150 
151     if ((flags & fMatch_Port)  &&  s_end_exp.port != s_end_got.port)
152         return 0;
153 
154     return 1;
155 }
156 
157 
run_a_test(size_t test_idx,const char * svc,const char * sch,const char * user,const char * pass,const char * path,const char * args,int exp_err,int exp_warn,int repop,int reset)158 static int run_a_test(size_t test_idx, const char *svc, const char *sch,
159                       const char *user, const char *pass, const char *path,
160                       const char *args, int exp_err, int exp_warn, int repop,
161                       int reset)
162 {
163     const SSERV_Info    *info = NULL;
164     SConnNetInfo        *net_info;
165     SERV_ITER           iter;
166     int                 success = 0, errors = 0;
167     int                 retval = -1;
168 
169     /* Set up the net_info */
170     if ( ! svc) {
171         CORE_LOG(eLOG_Critical, "Unexpected empty service name.");
172         return 0;
173     }
174     net_info = ConnNetInfo_Create(svc);
175     if (sch) {
176         if      (strcasecmp(sch, "http" ) == 0) net_info->scheme = eURL_Http;
177         else if (strcasecmp(sch, "https") == 0) net_info->scheme = eURL_Https;
178         else {
179             CORE_LOG(eLOG_Critical, "Unexpected non-http(s) scheme.");
180             CORE_LOG(eLOG_Note, "Test result:  FAIL.");
181             return 0;
182         }
183     }
184     if (user  &&  strlen(user) >= sizeof(net_info->user)) {
185         CORE_LOG(eLOG_Critical, "Unexpected too-long user.");
186         CORE_LOG(eLOG_Note, "Test result:  FAIL.");
187         return 0;
188     }
189     if (pass  &&  strlen(pass) >= sizeof(net_info->pass)) {
190         CORE_LOG(eLOG_Critical, "Unexpected too-long password.");
191         CORE_LOG(eLOG_Note, "Test result:  FAIL.");
192         return 0;
193     }
194     if ((path ? strlen(path) : 0) + 1 +
195         (args ? strlen(args) : 0) >= sizeof(net_info->path))
196     {
197         CORE_LOG(eLOG_Critical, "Unexpected too-long path / args.");
198         CORE_LOG(eLOG_Note, "Test result:  FAIL.");
199         return 0;
200     }
201     strcpy(net_info->user, user ? user : "");
202     strcpy(net_info->pass, pass ? pass : "");
203     ConnNetInfo_SetPath(net_info, path ? path : "");
204     ConnNetInfo_SetArgs(net_info, args ? args : "");
205 
206     /* Set up the server iterator */
207     iter = SERV_OpenP(svc, fSERV_All |
208                       (strpbrk(svc, "?*") ? fSERV_Promiscuous : 0),
209                       SERV_LOCALHOST, 0/*port*/, 0.0/*preference*/,
210                       net_info, 0/*skip*/, 0/*n_skip*/,
211                       0/*external*/, 0/*arg*/, 0/*val*/);
212     ConnNetInfo_Destroy(net_info);
213 
214     /* Fetch the linkerd info */
215     if ( ! iter) {
216         CORE_LOG(eLOG_Error, "SERV_OpenP failed.");
217         errors = 1;
218     } else {
219         info = SERV_GetNextInfo(iter);
220         if ( ! info) {
221             CORE_LOG(eLOG_Error, "SERV_GetNextInfo failed.");
222             errors = 1;
223         } else {
224             SOCK_ntoa(info->host, s_end_got.host, LEN_HOST);
225             s_end_got.port = info->port;
226             /* s_end_got.hdr is not currently set because it isn't strictly
227                 needed for comparison (plus it would be very ugly code) */
228 
229             char    *info_str;
230             info_str = SERV_WriteInfo(info);
231             CORE_LOGF(eLOG_Note, ("    Found server:   %s",
232                                   info_str ? info_str : "?"));
233             if (info_str)
234                 free(info_str);
235             /* linkerd must return only 1 hit */
236             info = SERV_GetNextInfo(iter);
237             if (info) {
238                 CORE_LOG(eLOG_Error, "linkerd unexpectedly returned a hit.");
239                 errors = 1;
240             } else {
241 
242                 /* Make sure endpoint data can be repopulated and reset */
243                 if (repop) {
244                     /* repopulate */
245                     CORE_LOG(eLOG_Trace, "Repopulating the service mapper.");
246                     if ( ! SERV_GetNextInfo(iter)) {
247                         CORE_LOG(eLOG_Error, "Unable to repopulate endpoint data.");
248                         errors = 1;
249                     }
250                 }
251                 if (reset) {
252                     /* reset */
253                     CORE_LOG(eLOG_Trace, "Resetting the service mapper.");
254                     SERV_Reset(iter);
255                     if ( ! SERV_GetNextInfo(iter)) {
256                         CORE_LOG(eLOG_Error, "No services found after reset.");
257                         errors = 1;
258                     }
259                 }
260             }
261 
262             SERV_Close(iter);
263         }
264     }
265 
266     /* Check for match */
267     /* Note that the host IP may change, and the header isn't parsed,
268         so currently only the port is checked - but that is a
269         reserved port, so it should be sufficient. */
270     if (check_match(fMatch_Port)) {
271         CORE_LOG(eLOG_Note, "    Found server matched expected server.");
272         if ( ! errors)
273             success = 1;
274     } else {
275         CORE_LOG(eLOG_Note, "    Found server didn't match expected server.");
276     }
277 
278     retval = (success != exp_err ? 1 : 0);
279     CORE_LOGF(eLOG_Note, ("Test result:  %s.",
280         retval ?
281         (success ? "PASS" : "PASS (with expected error)") :
282         (success ? "FAIL (success when error expected)" : "FAIL")));
283 
284     return retval;
285 }
286 
287 
288 /* wrapper around x_json_object_dotget_string() to support platform-specific
289    overrides */
p_json_object_dotget_string(x_JSON_Object * obj,const char * path)290 static const char * p_json_object_dotget_string(x_JSON_Object *obj,
291                                                 const char *path)
292 {
293 #define PLATFORM_BUFLEN 200
294     char        platform_buf[PLATFORM_BUFLEN+1];
295     const char  *platform = NULL;
296     const char  *val;
297 
298 #if defined(NCBI_OS_MSWIN)
299     platform = "win";
300 #elif defined(NCBI_OS_UNIX)
301     platform = "nix";
302 #endif
303     if (platform) {
304         if (strlen(path) + strlen(platform) + 1 > PLATFORM_BUFLEN) {
305             strncpy(platform_buf, path, PLATFORM_BUFLEN);
306             platform_buf[PLATFORM_BUFLEN] = NIL;
307             CORE_LOGF(eLOG_Error, ("Overlong path: '%s'...", platform_buf));
308         }
309         sprintf(platform_buf, "%s_%s", path, platform);
310         val = x_json_object_dotget_string(obj, platform_buf);
311         if (val)
312             return val;
313     }
314     return x_json_object_dotget_string(obj, path);
315 #undef PLATFORM_BUFLEN
316 }
317 
318 
319 /* mostly error-handling-free for the moment  :-/  */
run_tests(const char * test_nums)320 static int run_tests(const char *test_nums)
321 {
322     x_JSON_Value    *root_value = NULL;
323     x_JSON_Object   *root_obj;
324     x_JSON_Array    *test_arr;
325     const char      *test_num = test_nums;
326     size_t          n_tests = 0, n_pass = 0, n_fail = 0, n_skip = 0;
327     size_t          n_comm_unset, n_comm_set;
328     int             all_enabled = 0;
329     const char      *cmn_svc  = NULL, *cmn_sch  = NULL;
330     const char      *cmn_user = NULL, *cmn_pass = NULL;
331     const char      *cmn_path = NULL, *cmn_args = NULL;
332     const char      *cmn_exp_host = NULL, *cmn_exp_hdr  = NULL;
333     unsigned short   cmn_exp_port = 0;
334 
335 
336     CORE_LOG(eLOG_Note,
337         "============================================================");
338     CORE_LOGF(eLOG_Note, ("Test file: %s", s_json_file));
339 
340     root_value = x_json_parse_file(s_json_file);
341     root_obj   = x_json_value_get_object(root_value);
342 
343     if (x_json_object_has_value_of_type(root_obj, "all_enabled", JSONNumber)  &&
344         (int)x_json_object_get_number(root_obj, "all_enabled") != 0)
345     {
346         all_enabled = 1;
347     }
348 
349     cmn_svc  = p_json_object_dotget_string(root_obj, "common.service");
350     cmn_sch  = p_json_object_dotget_string(root_obj, "common.scheme");
351     cmn_user = p_json_object_dotget_string(root_obj, "common.user");
352     cmn_pass = p_json_object_dotget_string(root_obj, "common.pass");
353     cmn_path = p_json_object_dotget_string(root_obj, "common.path");
354     cmn_args = p_json_object_dotget_string(root_obj, "common.args");
355 
356     cmn_exp_host = p_json_object_dotget_string(root_obj, "common.expect.host");
357     cmn_exp_hdr  = p_json_object_dotget_string(root_obj,
358                         "common.expect.host_hdr");
359     cmn_exp_port = (unsigned short)x_json_object_dotget_number(root_obj,
360                         "common.expect.port");
361 
362     test_arr = x_json_object_get_array(root_obj, "tests");
363     n_tests = x_json_array_get_count(test_arr);
364     if ( ! (n_tests == x_json_array_get_count(test_arr))) {
365         CORE_LOG(eLOG_Critical, "JSON array count mismatch.");
366         return 0;
367     }
368     size_t it;
369     for (it = 0; it < n_tests; ++it) {
370         x_JSON_Object   *test_obj;
371         const char      *svc  = NULL, *sch  = NULL;
372         const char      *user = NULL, *pass = NULL;
373         const char      *path = NULL, *args = NULL;
374         const char      *exp_host = NULL, *exp_hdr  = NULL;
375         unsigned short   exp_port = 0;
376         int             err = 0, warn = 0;
377 
378         test_obj = x_json_array_get_object(test_arr, it);
379         s_results[it].name = x_json_object_get_string(test_obj, "title");
380 
381         /* Skip tests not in user-supplied test numbers list */
382         if (test_nums  &&  *test_nums) {
383             size_t next_test;
384             if ( ! test_num)
385                 break;
386             if (sscanf(test_num, FMT_SIZE_T, &next_test) == 1) {
387                 if (it+1 != next_test) {
388                     s_results[it].result = "-";
389                     continue;
390                 } else {
391                     test_num = strchr(test_num, ',');
392                     if (test_num  &&  *test_num)
393                         test_num++;
394                 }
395             } else {
396                 CORE_LOGF(eLOG_Error, ("Invalid test numbers list: %s",
397                     test_num));
398                 return 0;
399             }
400         }
401 
402         CORE_LOG(eLOG_Note,
403             "============================================================");
404 
405         /* Skip disabled tests */
406         if ( ! all_enabled) {
407             if (x_json_object_has_value_of_type(test_obj, "disabled",
408                 JSONNumber))
409             {
410                 if ((int)x_json_object_get_number(test_obj, "disabled") == 1) {
411                     ++n_skip;
412                     CORE_LOGF(eLOG_Note, ("Skipping test " FMT_SIZE_T ": %s",
413                         it+1, x_json_object_get_string(test_obj, "title")));
414                     s_results[it].result = "skip";
415                     continue;
416                 }
417             }
418         }
419 
420         CORE_LOGF(eLOG_Note, ("Running test " FMT_SIZE_T ": %s", it+1,
421             s_results[it].name));
422 
423         svc  = x_json_object_get_string(test_obj, "service");
424         sch  = x_json_object_get_string(test_obj, "scheme");
425         user = x_json_object_get_string(test_obj, "user");
426         pass = x_json_object_get_string(test_obj, "pass");
427         path = x_json_object_get_string(test_obj, "path");
428         args = x_json_object_get_string(test_obj, "args");
429 
430         exp_host = p_json_object_dotget_string(test_obj, "expect.host");
431         exp_hdr  = p_json_object_dotget_string(test_obj, "expect.hdr");
432         exp_port = (unsigned short)x_json_object_dotget_number(test_obj,
433                         "expect.port");
434 
435         if ( ! svc ) svc  = cmn_svc;
436         if ( ! sch ) sch  = cmn_sch;
437         if ( ! user) user = cmn_user;
438         if ( ! pass) pass = cmn_pass;
439         if ( ! path) path = cmn_path;
440         if ( ! args) path = cmn_args;
441 
442         if ( ! exp_host) exp_host = cmn_exp_host;
443         if ( ! exp_hdr ) exp_hdr  = cmn_exp_hdr;
444         if ( ! exp_port) exp_port = cmn_exp_port;
445 
446         if (x_json_object_has_value_of_type(test_obj, "expect_error",
447             JSONString))
448         {
449             if (strcmp(x_json_object_get_string(test_obj, "expect_error"),
450                        "yes") == 0)
451             {
452                 err = 1;
453             }
454         }
455 
456         if (x_json_object_has_value_of_type(test_obj, "expect_warn",
457             JSONString))
458         {
459             if (strcmp(x_json_object_get_string(test_obj, "expect_warn"),
460                        "yes") == 0)
461             {
462                 /* LINKERD_TODO - compare exp warn to got warn;
463                     add to determination of success */
464                 warn = 1;
465             }
466         }
467 
468         const char *msg_svc      = svc      ? svc      : "(not set)";
469         const char *msg_sch      = sch      ? sch      : "(not set)";
470         const char *msg_user     = user     ? user     : "(not set)";
471         const char *msg_pass     = pass     ? pass     : "(not set)";
472         const char *msg_path     = path     ? path     : "(not set)";
473         const char *msg_args     = args     ? args     : "(not set)";
474         const char *msg_exp_host = exp_host ? exp_host : "(not set)";
475         const char *msg_exp_hdr  = exp_hdr  ? exp_hdr  : "(not set)";
476         const char *msg_err      = err      ? "yes"    : "no";
477         const char *msg_warn     = warn     ? "yes"    : "no";
478         CORE_LOGF(eLOG_Note, ("    service:          %s",  msg_svc     ));
479         CORE_LOGF(eLOG_Note, ("    scheme:           %s",  msg_sch     ));
480         CORE_LOGF(eLOG_Note, ("    user:             %s",  msg_user    ));
481         CORE_LOGF(eLOG_Note, ("    password:         %s",  msg_pass    ));
482         CORE_LOGF(eLOG_Note, ("    path:             %s",  msg_path    ));
483         CORE_LOGF(eLOG_Note, ("    args:             %s",  msg_args    ));
484         CORE_LOGF(eLOG_Note, ("    expected host:    %s",  msg_exp_host));
485         CORE_LOGF(eLOG_Note, ("    expected header:  %s",  msg_exp_hdr ));
486         CORE_LOGF(eLOG_Note, ("    expected port:    %hu", exp_port    ));
487         CORE_LOGF(eLOG_Note, ("    expected error:   %s",  msg_err     ));
488         CORE_LOGF(eLOG_Note, ("    expected warning: %s",  msg_warn    ));
489 
490         /* Redo common unset/set - they could have been changed by a prior
491             test */
492         if (x_json_object_dothas_value_of_type(root_obj, "common.env_unset",
493             JSONArray))
494         {
495             x_JSON_Array    *comm_unset_arr;
496 
497             comm_unset_arr = x_json_object_dotget_array(root_obj,
498                                 "common.env_unset");
499             n_comm_unset   = x_json_array_get_count(comm_unset_arr);
500 
501             size_t it2;
502             for (it2 = 0; it2 < n_comm_unset; ++it2) {
503                 const char  *name = x_json_array_get_string(
504                                     comm_unset_arr, it2);
505 
506                 CORE_LOGF(eLOG_Note, ("    Unsetting common var: %s", name));
507                 unsetenv(name);
508                 if (getenv(name) != NULL) {
509                     CORE_LOGF(eLOG_Critical,
510                         ("Unable to unset common env var %s.", name));
511                     return 0;
512                 }
513             }
514         }
515         if (x_json_object_dothas_value_of_type(root_obj, "common.env_set",
516             JSONArray))
517         {
518             x_JSON_Array    *comm_set_arr;
519 
520             comm_set_arr   = x_json_object_dotget_array(root_obj,
521                                 "common.env_set");
522             n_comm_set     = x_json_array_get_count(comm_set_arr);
523 
524             size_t it2;
525             for (it2 = 0; it2 < n_comm_set; ++it2) {
526                 x_JSON_Object   *env_obj = x_json_array_get_object(comm_set_arr,
527                                             it2);
528                 const char      *name = x_json_object_get_name(env_obj, 0);
529                 const char      *val = x_json_value_get_string(
530                                         x_json_object_get_value_at(env_obj, 0));
531 
532                 CORE_LOGF(eLOG_Note, ("    Setting common var: %s=%s", name,
533                           val));
534                 setenv(name, val, 1);
535                 if (strcmp(val, getenv(name)) != 0) {
536                     CORE_LOGF(eLOG_Critical,
537                         ("Unable to set common env var %s to '%s'.",
538                          name, val));
539                     return 0;
540                 }
541             }
542         }
543 
544         /* Per-test unset/set */
545         if (x_json_object_has_value_of_type(test_obj, "env_unset", JSONArray)) {
546             x_JSON_Array    *unset_arr;
547             size_t          n_unset;
548 
549             unset_arr = x_json_object_get_array(test_obj, "env_unset");
550             n_unset = x_json_array_get_count(unset_arr);
551             CORE_LOGF(eLOG_Note, (
552                 "    Unset per-test environment variables: " FMT_SIZE_T,
553                 n_unset));
554             if (n_unset) {
555                 size_t it2;
556                 for (it2 = 0; it2 < n_unset; ++it2) {
557                     const char  *name = x_json_array_get_string(unset_arr, it2);
558 
559                     CORE_LOGF(eLOG_Note, ("        %s", name));
560                     unsetenv(name);
561                     if (getenv(name) != NULL) {
562                         CORE_LOGF(eLOG_Critical,
563                             ("Unable to unset per-test env var %s.", name));
564                         return 0;
565                     }
566                 }
567             }
568         }
569         if (x_json_object_has_value_of_type(test_obj, "env_set", JSONArray)) {
570             x_JSON_Array    *set_arr;
571             size_t          n_set;
572 
573             set_arr = x_json_object_get_array(test_obj, "env_set");
574             n_set = x_json_array_get_count(set_arr);
575             CORE_LOGF(eLOG_Note, (
576                 "    Set per-test environment variables: " FMT_SIZE_T,
577                 n_set));
578             if (n_set) {
579                 size_t it2;
580                 for (it2 = 0; it2 < n_set; ++it2) {
581                     x_JSON_Object   *env_obj = x_json_array_get_object(set_arr,
582                                                 it2);
583                     const char      *name = x_json_object_get_name(env_obj, 0);
584                     const char      *val = x_json_value_get_string(
585                                         x_json_object_get_value_at(env_obj, 0));
586 
587                     CORE_LOGF(eLOG_Note, ("        %s=%s", name, val));
588                     setenv(name, val, 1);
589                     if (strcmp(val, getenv(name)) != 0) {
590                         CORE_LOGF(eLOG_Critical,
591                             ("Unable to set per-test env var %s to '%s'.",
592                              name, val));
593                         return 0;
594                     }
595                 }
596             }
597         }
598 
599         if (strlen(exp_host) > LEN_HOST) {
600             CORE_LOG(eLOG_Critical, "Too-long exp_host.");
601             return 0;
602         }
603         if (strlen(exp_hdr) > LEN_HDR) {
604             CORE_LOG(eLOG_Critical, "Too-long exp_hdr.");
605             return 0;
606         }
607         strcpy(s_end_exp.host, exp_host);
608         strcpy(s_end_exp.hdr , exp_hdr);
609         s_end_exp.port = exp_port;
610 
611         CORE_LOGF(eLOG_Note, (
612             "        Expected server:   %s:%hu %s",
613             s_end_exp.host, s_end_exp.port, s_end_exp.hdr));
614 
615         int repop = 0, reset = 0;
616         if (x_json_object_has_value_of_type(
617                 test_obj, "iter-repop", JSONString)  &&
618             strcmp(x_json_object_get_string(
619                 test_obj, "iter-repop"), "yes") == 0)
620         {
621             repop = 1;
622         }
623         if (x_json_object_has_value_of_type(
624                 test_obj, "iter-reset", JSONString)  &&
625             strcmp(x_json_object_get_string(
626                 test_obj, "iter-reset"), "yes") == 0)
627         {
628             reset = 1;
629         }
630 
631         /* Run the test */
632         if (run_a_test(it, svc, sch, user, pass, path, args, err, warn, repop,
633                        reset))
634         {
635             ++n_pass;
636             s_results[it].result = "ok";
637         } else {
638             ++n_fail;
639             s_results[it].result = "FAIL";
640         }
641     }
642 
643     CORE_LOG (eLOG_Note,
644         "============================================================");
645     CORE_LOGF(eLOG_Note,
646         (FMT_SIZE_T " tests:  " FMT_SIZE_T " passed, "
647          FMT_SIZE_T " failed, " FMT_SIZE_T " skipped",
648          n_tests, n_pass, n_fail, n_skip));
649     CORE_LOG (eLOG_Note,
650         "============================================================");
651     CORE_LOG (eLOG_Note,
652         "Test  Result  Description");
653     CORE_LOG (eLOG_Note,
654         "----  ------  ----------------------------------------------");
655     for (it = 0; it < n_tests; ++it) {
656         if ( ! s_results[it].result  ||  ! *s_results[it].result  ||
657               *s_results[it].result == '-')
658         {
659           continue;
660         }
661         char tests_buf[6];
662         if (it+1 > 9999)
663             strcpy (tests_buf, "####");
664         else
665             sprintf(tests_buf, FMT_SIZE_T, it+1);
666         CORE_LOGF(eLOG_Note, ("%4s  %6s  %s",
667             tests_buf, s_results[it].result, s_results[it].name));
668     }
669 
670     if (root_value)
671         x_json_value_free(root_value);
672 
673     return (n_tests > 0  &&  n_pass == n_tests - n_skip) ? 1 : 0;
674 }
675 
676 
677 
678 
679 /*  ----------------------------------------------------------------------------
680     Main
681 */
682 
main(int argc,const char * argv[])683 int main(int argc, const char *argv[])
684 {
685     const char  *retmsg = "";
686     const char  *test_nums = "";
687     int         retval = 1;
688 
689     int i;
690     for (i=1; i < argc; ++i) {
691         if (strcmp(argv[i], "-f") == 0  &&  i < argc-1) {
692             ++i;
693             s_json_file = argv[i];
694         } else if (strcmp(argv[i], "-n") == 0  &&  i < argc-1) {
695             ++i;
696             test_nums = argv[i];
697             retmsg = "Not all tests run.";
698         } else {
699             fprintf(stderr, "USAGE:  %s [OPTIONS...]\n", argv[0]);
700             fprintf(stderr,
701                 "    [-h]       help\n"
702                 "    [-f ARG]   test file\n"
703                 "    [-n ARG]   comma-separated test selections (eg 1,2,5)\n");
704             goto out;
705         }
706     }
707 
708     CORE_SetLOGFormatFlags(fLOG_None | fLOG_Level | fLOG_OmitNoteLevel);
709     CORE_SetLOGFILE(stderr, 0/*false*/);
710 
711     SOCK_SetupSSL(NcbiSetupTls);
712 
713     if ( ! run_tests(test_nums)  &&  ! *retmsg)
714         retmsg = "Not all tests passed.";
715 
716 out:
717     CORE_LOG(eLOG_Note, "");
718     if (strcmp(retmsg, "") == 0) {
719         /* The only successful condition is a run of all tests */
720         CORE_LOG(eLOG_Note, "SUCCESS - All tests passed.");
721         retval = 0;
722     } else {
723         CORE_LOGF(eLOG_Note, ("FAIL - %s", retmsg));
724     }
725     CORE_SetLOG(0);
726     return retval;
727 }
728 
729 
730 /* END -----------------------------------------------------------------------
731     Stub out the program for Windows DLL configurations.
732     */
733 #endif
734