Make more desent display of test progress.
[yazpp-moved-to-github.git] / zlint / zlint.cpp
1 /*
2  * Copyright (c) 2004, Index Data.
3  * See the file LICENSE for details.
4  * 
5  * $Id: zlint.cpp,v 1.2 2004-02-19 15:25:46 adam Exp $
6  */
7
8 #include <yaz/pquery.h>
9 #include <yaz/options.h>
10 #include <yaz/otherinfo.h>
11 #include <yaz/charneg.h>
12 #include <yaz/sortspec.h>
13 #include <yaz/log.h>
14 #include <yaz++/pdu-assoc.h>
15 #include <yaz++/socket-manager.h>
16 #include <yaz++/z-assoc.h>
17
18 #define REFID_BUF1 "zlint\000check1"
19 #define REFID_LEN1 12
20 #define REFID_BUF2 "zlint\000check2"
21 #define REFID_LEN2 12
22
23 enum test_code {
24     TEST_FINISHED,
25     TEST_CONTINUE,
26 };
27
28 #if 0
29 class Zlint;
30 class Zlint_test {
31 public:
32     virtual void init_send(Zlint *z) = 0;
33     virtual test_code init_recv(Zlint *z, Z_InitResponse *ir) = 0;
34     virtual test_code other_recv(Zlint *z, Z_APDU *ir, Z_InitResponse *ir) = 0;
35 };
36 #endif
37
38 const char *try_syntax [] = {
39     "usmarc",
40     "unimarc",
41     "danmarc",
42     "sutrs",
43     "grs1",
44     "xml",
45     "normarc",
46     0
47 };
48 const char *try_query[] = {
49     "@attr 1=4 petersson",
50     "@attr 1=1016 petersson",
51     "@attr 1=4 kingdom",
52     "@attr 1=1016 kingdom",
53     "@attr 1=62 sword",
54     "sword"
55     "seven",
56     "@attr 1=4 water",
57     "@attr 1=1016 water",
58     "computer",
59     "@attr 1=4 computer",
60     "@attr 1=1016 computer",
61     "water",
62     "join",
63     "about",
64     "map",
65     0,
66 };
67
68 const char *try_sort [] = {
69     "1=4 <",
70     "1=4 >",
71     "1=62 >",
72     "1=62 <",
73     0
74 };
75 const char *try_scan [] = {
76     "@attr 1=4 ab",
77     "@attr 1=1003 ab",
78     "@attr 1=1016 ab",
79     0
80 };
81
82 #define TEST_STATE_FAIL 0
83 #define TEST_STATE_UNKNOWN 1
84 #define TEST_STATE_OK   2
85 #define TEST_STATE_NOT_APPLIC 3
86
87 class Zlint : public Yaz_Z_Assoc {
88     int m_tst_no;
89
90     int m_subtst_no;
91
92     int m_test_state;
93     int m_query_no;
94     int m_scan_no;
95     int m_sort_no;
96     int m_record_syntax_no;
97     int m_got_result_set;
98     IYaz_PDU_Observable *m_PDU_Observable;
99     char *m_host;
100     char *m_database;
101     int m_timeout_init;
102     int m_timeout_connect;
103     int m_protocol_version;
104     char m_session_str[20];
105     int initResponseGetVersion(Z_InitResponse *init);
106 public:
107     void prepare();
108     void recv_GDU(Z_GDU *apdu, int len);
109     Zlint(IYaz_PDU_Observable *the_PDU_Observable);
110     void args(int argc, char **argv);
111     void connectNotify();
112     void failNotify();
113     void closeNextTest();
114     void sendTest();
115     int nextTest();
116     void testContinue();
117     void timeoutNotify();
118     IYaz_PDU_Observer *sessionNotify(
119         IYaz_PDU_Observable *the_PDU_Observable, int fd);
120     void connect();
121     Z_ReferenceId *mk_refid(const char *buf, int len);
122 };
123
124 int Zlint::initResponseGetVersion(Z_InitResponse *init)
125 {
126     int no = 0;
127     int off = 0;
128     int i;
129     for (i = 0; i<12; i++)
130         if (ODR_MASK_GET(init->protocolVersion, no))
131         {
132             no = i+1;
133             if (off)
134                 yaz_log(LOG_WARN, "%sbad formatted version");
135         }
136         else
137             off = 1;
138     return no;
139 }
140
141 Z_ReferenceId *Zlint::mk_refid(const char *buf, int len)
142 {
143     Z_ReferenceId *id = 
144         (Z_ReferenceId *) odr_malloc(odr_encode(), sizeof(*id));
145     id->size = id->len = len;
146     id->buf = (unsigned char*) odr_malloc(odr_encode(), len);
147     memcpy(id->buf, buf, len);
148     return id;
149 }
150
151 void Zlint::recv_GDU(Z_GDU *gdu, int len)
152 {
153     if (gdu->which != Z_GDU_Z3950)
154     {
155         yaz_log(LOG_LOG, "%sreceived non-Z39.50 response", m_session_str);
156         closeNextTest();
157     }
158     if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_initResponse)
159     {
160         int i;
161         Z_InitResponse *init = gdu->u.z3950->u.initResponse;
162         int ver = initResponseGetVersion(init);
163         int result = init->result ? *init->result : 0;
164         if (!result)
165             yaz_log(LOG_WARN, "%sinit rejected");
166         switch(m_tst_no)
167         {
168         case 0:
169             if (ver > 3 || ver < 2)
170                 yaz_log(LOG_WARN, "%sgot version %d, expected 2 or 3",
171                         m_session_str, ver);
172             else
173                 m_test_state = TEST_STATE_OK;
174             m_protocol_version = ver;
175             if (!result)
176                 closeNextTest();
177             else
178             {
179                 close();
180                 nextTest();
181             }
182             break;
183         case 1:
184             if (ver != 2)
185                 yaz_log(LOG_WARN, "%sgot version %d, expected 2",
186                         m_session_str, ver);
187             else
188                 m_test_state = TEST_STATE_OK;
189             closeNextTest();
190             break;
191         case 2:
192             if (ver < 2 || ver > 5)
193                 yaz_log(LOG_WARN, "%sgot version %d, expected 2-5",
194                         m_session_str,ver);
195             else
196                 m_test_state = TEST_STATE_OK;
197             closeNextTest();
198             break;
199         case 3:
200             if (!init->referenceId)
201                 yaz_log(LOG_WARN, "%smissing referenceID from init response",
202                         m_session_str);
203             else if (init->referenceId->len != REFID_LEN1
204                      || memcmp(init->referenceId->buf, REFID_BUF1, REFID_LEN1))
205                 yaz_log(LOG_WARN, "%sreference ID does not match");
206             else
207                 m_test_state = TEST_STATE_OK;
208             closeNextTest();
209             break;
210         case 4:
211             if (m_subtst_no == 0)
212             {
213                 if (!init->referenceId)
214                     yaz_log(LOG_WARN, "%smissing referenceID from first init response",
215                             m_session_str);
216                 else if (init->referenceId->len != REFID_LEN1
217                          || memcmp(init->referenceId->buf, REFID_BUF1, REFID_LEN1))
218                     yaz_log(LOG_WARN, "%sreference ID does not match");
219                 m_subtst_no++;
220             }
221             else
222             {
223                 if (!init->referenceId)
224                     yaz_log(LOG_WARN, "%smissing referenceID from second init response",
225                             m_session_str);
226                 else if (init->referenceId->len != REFID_LEN2
227                          || memcmp(init->referenceId->buf, REFID_BUF2, REFID_LEN2))
228                     yaz_log(LOG_WARN, "%sreference ID does not match");
229                 else
230                     m_test_state = TEST_STATE_OK;
231                 closeNextTest();
232             }       
233             break;
234         case 5:
235             if (init->options)
236             {
237                 int i;
238                 int no_set = 0;
239                 int no_reset = 0;
240                 for (i = 0; i <= 24; i++)
241                     if (ODR_MASK_GET(init->options, i))
242                         no_set++;
243                     else
244                         no_reset++;
245                 if (no_set < 2)
246                     yaz_log(LOG_WARN, "%ssuspicuously few option bits set",
247                             m_session_str);
248                 if (no_reset == 0)
249                     yaz_log(LOG_WARN, "%ssuspicuously many option bits set",
250                             m_session_str);
251                 if (no_set >= 2 && no_reset)
252                     m_test_state = TEST_STATE_OK;
253             }
254             closeNextTest();
255             break;
256         case 6:
257             if (ODR_MASK_GET(init->options, Z_Options_negotiationModel))
258             {
259                 Z_CharSetandLanguageNegotiation *p =
260                     yaz_get_charneg_record(init->otherInfo);
261                 
262                 if (p) {
263                     
264                     char *charset=NULL, *lang=NULL;
265                     int selected;
266                     NMEM m = nmem_create();
267                     
268                     yaz_get_response_charneg(m, p, &charset, &lang,
269                                              &selected);
270                     yaz_log(LOG_DEBUG, "%sAccepted character set : %s",
271                             m_session_str, charset);
272                     yaz_log(LOG_DEBUG, "%sAccepted code language : %s",
273                             m_session_str, lang ? lang : "none");
274                     yaz_log(LOG_DEBUG, "%sAccepted records in ...: %d",
275                             m_session_str, selected );
276                     nmem_destroy(m);
277                     m_test_state = TEST_STATE_OK;
278                 }
279             }
280             else
281                 m_test_state = TEST_STATE_NOT_APPLIC;
282             closeNextTest();
283             break;
284         case 7:
285             if (m_subtst_no * m_subtst_no * 100000 + 2000 < *init->maximumRecordSize)
286             {
287                 yaz_log(LOG_WARN, "%smaximumRecordSize bigger than proposed size");
288                 closeNextTest();
289             }
290             else if (m_subtst_no * m_subtst_no * 100000 + 2000 < *init->preferredMessageSize)
291             {
292                 yaz_log(LOG_WARN, "%smaximumRecordSize bigger than proposed size");
293                 closeNextTest();
294             }
295             else if (m_subtst_no < 3)
296             {
297                 close();
298                 m_subtst_no++;
299                 connect();
300             }
301             else
302             {
303                 m_test_state = TEST_STATE_OK;
304                 closeNextTest();
305             }
306             break;
307         case 9:
308             if (result && ODR_MASK_GET(init->options, Z_Options_scan))
309                 sendTest();
310             else
311             {
312                 m_test_state = TEST_STATE_NOT_APPLIC;
313                 closeNextTest();
314             }
315             break;
316         case 10:
317             if (result && ODR_MASK_GET(init->options, Z_Options_sort))
318                 sendTest();
319             else
320             {
321                 m_test_state = TEST_STATE_NOT_APPLIC;
322                 closeNextTest();
323             }
324             break;
325         default:
326             if (result)
327                 sendTest();
328             else
329             {
330                 m_test_state = TEST_STATE_NOT_APPLIC;
331                 closeNextTest();
332             }
333         }
334     }
335     else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_searchResponse)
336     {
337         Z_SearchResponse *sr = gdu->u.z3950->u.searchResponse;
338         switch(m_tst_no)
339         {
340         case 8:
341             if (sr->records && (sr->records->which == Z_Records_NSD 
342                                 || 
343                                 sr->records->which == Z_Records_multipleNSD))
344             {
345                 yaz_log(LOG_WARN, "%sSearch Error", m_session_str);
346                 m_query_no++;
347                 sendTest();
348             }
349             else if (!sr->resultCount || *sr->resultCount == 0)
350             {
351                 m_query_no++;
352                 sendTest();
353             }
354             else
355             {
356                 yaz_log(LOG_DEBUG, "%sgot %d result count with %s",
357                         m_session_str, *sr->resultCount,
358                         try_query[m_query_no]);
359                 m_got_result_set = 1;
360                 sendTest();
361             }
362             break;
363         case 10:
364             if (sr->resultCount && *sr->resultCount > 0)
365             {
366                 m_got_result_set = 1;
367                 sendTest();
368             }
369             else
370             {
371                 closeNextTest();
372             }
373             break;
374         default:
375             closeNextTest();
376         }
377     }
378     else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_presentResponse)
379     {
380         Z_PresentResponse *sr = gdu->u.z3950->u.presentResponse;
381         switch(m_tst_no)
382         {
383         case 8:
384             if (sr->records && (sr->records->which == Z_Records_NSD 
385                                 || 
386                                 sr->records->which == Z_Records_multipleNSD))
387             {
388                 yaz_log(LOG_LOG, "%spresent returned NSD for %s",
389                         m_session_str, try_syntax[m_record_syntax_no]);
390             }
391             else if (sr->records && sr->records->which == Z_Records_DBOSD
392                      && sr->records->u.databaseOrSurDiagnostics->num_records>0
393                      && sr->records->u.databaseOrSurDiagnostics->records[0])
394             {
395                 if (sr->records->u.databaseOrSurDiagnostics->records[0]->which == Z_NamePlusRecord_databaseRecord)
396                 {
397                     Z_External *ext = sr->records->u.databaseOrSurDiagnostics->records[0]->u.databaseRecord;
398                     Odr_oid *expectRecordSyntax =
399                         yaz_str_to_z3950oid(odr_decode(), CLASS_RECSYN,
400                                             try_syntax[m_record_syntax_no]);
401                     if (oid_oidcmp(expectRecordSyntax,
402                                ext->direct_reference))
403                         yaz_log(LOG_WARN, "%sGot Record in different syntax from that required %s",
404                                 m_session_str,
405                                 try_syntax[m_record_syntax_no]);
406                     else
407                     {
408                         yaz_log(LOG_DEBUG, "%spresent OK for %s", m_session_str,
409                                 try_syntax[m_record_syntax_no]);
410                         m_test_state = TEST_STATE_OK;
411                     }
412                 }
413                 else if (sr->records->u.databaseOrSurDiagnostics->records[0]->which == Z_NamePlusRecord_surrogateDiagnostic)
414                     yaz_log(LOG_DEBUG, "%spresent returned SD %s", m_session_str,
415                             try_syntax[m_record_syntax_no]);
416                 else
417                     yaz_log(LOG_WARN, "%spresent returned fragment %s",
418                             m_session_str,
419                             try_syntax[m_record_syntax_no]);
420             }
421             else
422             {
423                 yaz_log(LOG_WARN, "%spresent returned no records or diagnostics", m_session_str);
424                 
425             }
426             m_record_syntax_no++;
427             sendTest();
428         }
429     }
430     else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_scanResponse)
431     {
432         Z_ScanResponse *sr =  gdu->u.z3950->u.scanResponse;
433         switch(m_tst_no)
434         {
435         case 9:
436             if (sr->entries->nonsurrogateDiagnostics)
437             {
438                 yaz_log(LOG_LOG, "%sscan NSD for %s", m_session_str,
439                         try_scan[m_scan_no]);
440                 m_scan_no++;
441                 sendTest();
442             }
443             else if (sr->entries->entries && sr->entries->num_entries > 0)
444             {
445                 yaz_log(LOG_DEBUG, "%sscan OK for %s", m_session_str,
446                         try_scan[m_scan_no]);
447                 m_test_state = TEST_STATE_OK;
448                 closeNextTest();
449             }
450             else
451             {
452                 yaz_log(LOG_WARN, "%sscan no entries/diagnostics for %s",
453                         m_session_str,
454                         try_scan[m_scan_no]);
455                 m_scan_no++;
456                 sendTest();
457             }
458             break;
459         default:
460             closeNextTest();
461         }
462     }
463     else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_sortResponse)
464     {
465         Z_SortResponse *sr =  gdu->u.z3950->u.sortResponse;
466         switch(m_tst_no)
467         {
468         case 10:
469             if (sr->diagnostics)
470             {
471                 yaz_log(LOG_LOG, "%ssort NSD for %s", m_session_str,
472                         try_sort[m_sort_no]);
473                 m_sort_no++;
474                 sendTest();
475             }
476             else
477             {
478                 yaz_log(LOG_DEBUG, "%ssort OK for %s", m_session_str,
479                         try_sort[m_sort_no]);
480                 m_test_state = TEST_STATE_OK;
481                 closeNextTest();
482             }
483             break;
484         default:
485             closeNextTest();
486         }
487     }
488     else
489         closeNextTest();
490 }
491
492 Zlint::Zlint(IYaz_PDU_Observable *the_PDU_Observable) : 
493     Yaz_Z_Assoc(the_PDU_Observable)
494 {
495     m_PDU_Observable = the_PDU_Observable;
496     m_host = 0;
497     m_database = 0;
498     m_timeout_connect = 30;
499     m_timeout_init = 30;
500     m_tst_no = -1;
501     m_subtst_no = 0;
502     m_protocol_version = 0;
503     sprintf(m_session_str, "%d ", m_tst_no);
504 }
505
506 void Zlint::connectNotify()
507 {
508     Z_APDU *apdu = create_Z_PDU(Z_APDU_initRequest);
509     Z_InitRequest *init = apdu->u.initRequest;
510     int len;
511     Z_OtherInformation **oi;
512
513     timeout(m_timeout_init);
514
515     switch(m_tst_no)
516     {
517     case 0:
518         /* check if target properly negotiates to v3 .. */
519         ODR_MASK_ZERO(init->protocolVersion);
520         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_1);
521         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_2);
522         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
523         break;
524     case 1:
525         /* check if target properly negotiates to v2 .. */
526         ODR_MASK_ZERO(init->protocolVersion);
527         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_1);
528         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_2);
529         break;
530     case 2:
531         /* check latest version of target  - up to v9 */
532         ODR_MASK_ZERO(init->protocolVersion);
533         int i;
534         for (i = 0; i< 9; i++)
535             ODR_MASK_SET(init->protocolVersion, i);
536         break;
537     case 3:
538         /* send refID in init request */
539         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
540         init->referenceId = mk_refid(REFID_BUF1, REFID_LEN1);
541         break;
542     case 4:
543         /* send double init with differnet refID's */
544         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
545         ODR_MASK_SET(init->options, Z_Options_concurrentOperations);
546         init->referenceId = mk_refid(REFID_BUF1, REFID_LEN1);
547         send_Z_PDU(apdu, &len);
548         
549         apdu = create_Z_PDU(Z_APDU_initRequest);
550         init = apdu->u.initRequest;
551
552         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
553         ODR_MASK_SET(init->options, Z_Options_concurrentOperations);
554
555         init->referenceId = mk_refid(REFID_BUF2, REFID_LEN2);
556         break;
557     case 5:
558         /* set all options.. see what target really supports .. */
559         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
560         ODR_MASK_ZERO(init->options);
561         for (i = 0; i <= 24; i++)
562             ODR_MASK_SET(init->options, i);
563         break;
564     case 6:
565         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
566         yaz_oi_APDU(apdu, &oi);
567         if (oi)
568         {
569             Z_OtherInformationUnit *p0;
570             const char *negotiationCharset[] = {
571                 "UTF-8",
572                 "UTF-16",
573                 "UCS-2",
574                 "UCS-4",
575                 "ISO-8859-1"
576             };
577             char *yazLang = 0;
578
579             if ((p0=yaz_oi_update(oi, odr_encode(), NULL, 0, 0))) {
580                 ODR_MASK_SET(init->options, Z_Options_negotiationModel);
581
582                 p0->which = Z_OtherInfo_externallyDefinedInfo;
583                 p0->information.externallyDefinedInfo =
584
585                     yaz_set_proposal_charneg(
586                         odr_encode(),
587                         negotiationCharset, 5,
588                         (const char**)&yazLang, yazLang ? 1 : 0, 1);
589             }
590         }
591         break;
592     case 7:
593         *init->maximumRecordSize = m_subtst_no * m_subtst_no* 100000 + 2000;
594         *init->preferredMessageSize = m_subtst_no * m_subtst_no *100000 + 2000;
595         break;
596     case 8:
597         /* search */
598         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
599         ODR_MASK_SET(init->options, Z_Options_namedResultSets);
600         break;
601     case 9:
602         /* scan */
603         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
604         ODR_MASK_SET(init->options, Z_Options_namedResultSets);
605         ODR_MASK_SET(init->options, Z_Options_scan);
606         break;
607     case 10:
608         /* sort */
609         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
610         ODR_MASK_SET(init->options, Z_Options_namedResultSets);
611         ODR_MASK_SET(init->options, Z_Options_sort);
612         break;
613     }
614     int r = send_Z_PDU(apdu, &len);
615 }
616
617 int Zlint::nextTest()
618 {
619     if (m_tst_no >= 0)
620     {
621         switch(m_test_state)
622         {
623         case TEST_STATE_FAIL:
624             yaz_log(LOG_LOG, "%sTest Failed", m_session_str);
625             break;
626         case TEST_STATE_OK:
627             yaz_log(LOG_LOG, "%sTest Passed", m_session_str);
628             break;
629         case TEST_STATE_NOT_APPLIC:
630             yaz_log(LOG_LOG, "%sTest Not Applicable", m_session_str);
631             break;
632         case TEST_STATE_UNKNOWN:
633             yaz_log(LOG_LOG, "%sTest Could not be performed", m_session_str);
634         }
635     }
636     m_test_state = TEST_STATE_FAIL;
637     m_subtst_no = 0;
638     connect();
639     while(1)
640     {
641         m_tst_no++;
642         sprintf(m_session_str, "%d ", m_tst_no);
643         switch(m_tst_no)
644         {
645         case 0:
646             yaz_log(LOG_LOG, "%sCheck for init v3",
647                     m_session_str);
648             return 1;
649         case 1:
650             yaz_log(LOG_LOG, "%sCheck for init v2",
651                     m_session_str);
652             return 1;
653         case 2:
654             yaz_log(LOG_LOG, "%sCheck for init protocol version negotiation",
655                     m_session_str);
656             return 1;
657         case 3:
658             yaz_log(LOG_LOG, "%sCheck for init reference ID",
659                     m_session_str);
660             return 1;
661         case 4:
662             yaz_log(LOG_LOG, "%sCheck for double init request",
663                     m_session_str);
664             return 1;
665         case 5:
666             yaz_log(LOG_LOG, "%sCheck for init options",
667                     m_session_str);
668             return 1;
669         case 6:
670             yaz_log(LOG_LOG, "%sCheck for character set negotiation",
671                     m_session_str);
672             return 1;
673         case 7:
674             yaz_log(LOG_LOG, "%sCheck for messages size negotiation",
675                     m_session_str);
676             return 1;
677         case 8:
678             yaz_log(LOG_LOG, "%sCheck for basic search and retrieve",
679                     m_session_str);
680             m_query_no = 0;
681             m_record_syntax_no = 0;
682             m_got_result_set = 0;
683             return 1;
684         case 9:
685             yaz_log(LOG_LOG, "%sCheck for scan", m_session_str);
686             m_scan_no = 0;
687             return 1;
688         case 10:
689             yaz_log(LOG_LOG, "%sCheck for sort", m_session_str);
690             m_got_result_set = 0;
691             m_sort_no = 0;
692             return 1;
693         default:
694             close();
695             return 0;
696         }
697     }
698     return 0;
699 }
700
701 // current test failed badly - goto next or stop..
702 void Zlint::closeNextTest()
703 {
704     close();
705     if (m_tst_no != 0)
706     {
707         nextTest();
708     }
709 }
710
711 void Zlint::failNotify()
712 {
713     yaz_log(LOG_WARN, "%sconnection closed by foreign host", m_session_str);
714     testContinue();
715 }
716
717 void Zlint::timeoutNotify()
718 {
719     yaz_log(LOG_WARN, "%sconnection timed out", m_session_str);
720     testContinue();
721 }
722
723 void Zlint::testContinue()
724 {
725     close();
726     switch(m_tst_no)
727     {
728     case 8:
729         if (m_got_result_set)
730         {
731             // must search again to establish.. keep query
732             m_got_result_set = 0;
733             m_record_syntax_no++;
734         }
735         else
736         {
737             // try new search ..
738             m_query_no++;
739         }
740         connect();
741         return;
742     case 9:
743         m_scan_no++;
744         connect();
745         return;
746     case 10:
747         if (m_got_result_set)
748         {
749             // if sort test fails during sort, we'll continue to next
750             m_got_result_set = 0;
751             m_sort_no++;
752             connect();
753             return;
754         }
755     }
756     nextTest();
757 }
758
759 void Zlint::sendTest()
760 {
761     Z_APDU *apdu;
762     switch(m_tst_no)
763     {
764     case 8:
765         if (!m_got_result_set)
766         {
767             apdu = zget_APDU(odr_encode(), Z_APDU_searchRequest);
768             Z_SearchRequest *sr;
769             sr = apdu->u.searchRequest;
770             sr->query = (Z_Query *) odr_malloc(odr_encode(), sizeof(*sr->query));
771             if (try_query[m_query_no] && sr)
772             {
773                 sr->query->which = Z_Query_type_1;
774                 Z_RPNQuery *rpn;
775                 YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
776                 
777                 sr->databaseNames = &m_database;
778                 sr->num_databaseNames = 1;
779                 
780                 rpn = yaz_pqf_parse(pqf_parser, odr_encode(), try_query[m_query_no]);
781
782                 yaz_pqf_destroy (pqf_parser);
783
784                 if (rpn)
785                 {
786                     int len;
787                     yaz_log(LOG_DEBUG, "%spqf: %s",
788                             m_session_str, try_query[m_query_no]);
789
790                     sr->query->u.type_1 = rpn;
791                     send_Z_PDU(apdu, &len);
792                 }
793                 else
794                     closeNextTest();
795             }
796             else
797             {
798                 yaz_log(LOG_WARN, "%sunable to get any hit count", 
799                         m_session_str);
800                 closeNextTest();
801             }
802         }
803         else if (m_got_result_set && try_syntax[m_record_syntax_no])
804         {
805             int len;
806             apdu = zget_APDU(odr_encode(), Z_APDU_presentRequest);
807             Z_PresentRequest *pr = apdu->u.presentRequest;
808             *pr->numberOfRecordsRequested = 1;
809             *pr->resultSetStartPoint = 1;
810             
811             pr->preferredRecordSyntax =
812                 yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN,
813                                     try_syntax[m_record_syntax_no]);
814             send_Z_PDU(apdu, &len);
815         }
816         else
817             closeNextTest();
818         break;
819     case 9:
820         apdu = zget_APDU(odr_encode(), Z_APDU_scanRequest);
821         if (apdu && try_scan[m_scan_no])
822         {
823             int len;
824             YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
825             Z_ScanRequest *sr = apdu->u.scanRequest;
826             sr->termListAndStartPoint = yaz_pqf_scan(pqf_parser,
827                                                      odr_encode(),
828                                                      &sr->attributeSet,
829                                                      try_scan[m_scan_no]);
830
831             sr->databaseNames = &m_database;
832             sr->num_databaseNames = 1;
833
834             yaz_pqf_destroy (pqf_parser);
835             send_Z_PDU(apdu, &len);
836         }
837         else
838             closeNextTest();
839         break;
840     case 10:
841         if (!m_got_result_set)
842         {
843             apdu = zget_APDU(odr_encode(), Z_APDU_searchRequest);
844             Z_SearchRequest *sr;
845             sr = apdu->u.searchRequest;
846             sr->query = (Z_Query *) odr_malloc(odr_encode(), sizeof(*sr->query));
847             if (try_query[m_query_no] && sr)
848             {
849                 sr->query->which = Z_Query_type_1;
850                 Z_RPNQuery *rpn;
851                 YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
852                 
853                 sr->databaseNames = &m_database;
854                 sr->num_databaseNames = 1;
855                 
856                 rpn = yaz_pqf_parse(pqf_parser, odr_encode(), try_query[m_query_no]);
857
858                 yaz_pqf_destroy (pqf_parser);
859
860                 if (rpn)
861                 {
862                     int len;
863                     yaz_log(LOG_DEBUG, "%spqf: %s",
864                             m_session_str, try_query[m_query_no]);
865
866                     sr->query->u.type_1 = rpn;
867                     send_Z_PDU(apdu, &len);
868                 }
869                 else
870                     closeNextTest();
871             }
872             else
873             {
874                 yaz_log(LOG_WARN, "%sunable to get any hit count", 
875                         m_session_str);
876                 closeNextTest();
877             }
878         }
879         else 
880         {
881             apdu = zget_APDU(odr_encode(), Z_APDU_sortRequest);
882             if (apdu && try_sort[m_sort_no])
883             {
884                 char *setstring = "default";
885                 int len;
886                 Z_SortRequest *sr = apdu->u.sortRequest;
887                 
888                 sr->num_inputResultSetNames = 1;
889                 sr->num_inputResultSetNames = 1;
890                 sr->inputResultSetNames = (Z_InternationalString **)
891                     odr_malloc (odr_encode(), sizeof(*sr->inputResultSetNames));
892                 sr->inputResultSetNames[0] = odr_strdup (odr_encode(), setstring);
893                 sr->sortedResultSetName = odr_strdup(odr_encode(), setstring);
894                 sr->sortSequence = yaz_sort_spec(odr_encode(), try_sort[m_sort_no]);
895                 send_Z_PDU(apdu, &len);
896             }
897             else
898                 closeNextTest();
899         }
900         break;
901     default:
902         closeNextTest();
903     }
904 }
905
906 IYaz_PDU_Observer *Zlint::sessionNotify(
907     IYaz_PDU_Observable *the_PDU_Observable, int fd)
908 {
909     return 0;
910 }
911
912 void Zlint::connect()
913 {
914     if (m_host)
915     {
916         yaz_log(LOG_DEBUG, "%sconnecting to %s", m_session_str, m_host);
917         timeout(m_timeout_connect);
918         client(m_host);
919     }
920 }
921
922 void Zlint::args(int argc, char **argv)
923 {
924     char *arg;
925     int ret;
926     while ((ret = options("v", argv, argc, &arg)) != -2)
927     {
928         switch (ret)
929         {
930         case 'v':
931             break;
932         case 0:
933             if (arg)
934             {
935                 const char *basep;
936                 m_host = xstrdup(arg);
937                 cs_get_host_args(m_host, &basep);
938                 if (!basep || !*basep)
939                     basep = "Default";
940                 m_database = xstrdup(basep);
941             }
942             break;
943         }
944     }
945 }
946
947 int main(int argc, char **argv)
948 {
949     Yaz_SocketManager mySocketManager;
950     Zlint z(new Yaz_PDU_Assoc(&mySocketManager));
951     
952     z.args(argc, argv);
953
954     z.nextTest();
955
956     while (mySocketManager.processEvent() > 0)
957         ;
958     exit (0);
959 }