+#include <yaz/diagbib1.h>
+#include <yaz/otherinfo.h>
+#include <yaz/facet.h>
+
+#include "ztest.h"
+
+static int log_level=0;
+static int log_level_set=0;
+
+struct delay {
+ double d1;
+ double d2;
+};
+
+struct result_set {
+ char *name;
+ char *db;
+ Odr_int hits;
+ struct delay search_delay;
+ struct delay present_delay;
+ struct delay fetch_delay;
+ struct result_set *next;
+};
+
+struct session_handle {
+ struct result_set *result_sets;
+};
+
+int ztest_search(void *handle, bend_search_rr *rr);
+int ztest_sort(void *handle, bend_sort_rr *rr);
+int ztest_present(void *handle, bend_present_rr *rr);
+int ztest_esrequest(void *handle, bend_esrequest_rr *rr);
+int ztest_delete(void *handle, bend_delete_rr *rr);
+
+static struct result_set *get_set(struct session_handle *sh, const char *name)
+{
+ struct result_set *set = sh->result_sets;
+ for (; set; set = set->next)
+ if (!strcmp(name, set->name))
+ return set;
+ return 0;
+}
+
+static void remove_sets(struct session_handle *sh)
+{
+ struct result_set *set = sh->result_sets;
+ while (set)
+ {
+ struct result_set *set_next = set->next;
+ xfree(set->name);
+ xfree(set->db);
+ xfree(set);
+ set = set_next;
+ }
+ sh->result_sets = 0;
+}
+
+/** \brief use term value as hit count
+ \param s RPN structure
+ \param hash value for compuation
+ \return >= 0: search term number or -1: not found
+
+ Traverse RPN tree 'in order' and use term value as hit count.
+ Only terms that looks a numeric is used.. Returns -1 if
+ no sub tree has a hit count term
+*/
+static Odr_int get_term_hit(Z_RPNStructure *s, unsigned *hash)
+{
+ Odr_int h = -1;
+ switch(s->which)
+ {
+ case Z_RPNStructure_simple:
+ if (s->u.simple->which == Z_Operand_APT)
+ {
+ Z_AttributesPlusTerm *apt = s->u.simple->u.attributesPlusTerm;
+ if (apt->term->which == Z_Term_general)
+ {
+ Odr_oct *oct = apt->term->u.general;
+ if (oct->len > 0 && oct->buf[0] >= '0' && oct->buf[0] <= '9')
+ {
+ WRBUF hits_str = wrbuf_alloc();
+ wrbuf_write(hits_str, (const char *) oct->buf, oct->len);
+ h = odr_atoi(wrbuf_cstr(hits_str));
+ wrbuf_destroy(hits_str);
+ }
+ else
+ {
+ int i;
+ for (i = 0; i < oct->len; i++)
+ *hash = *hash * 65509 + oct->buf[i];
+ }
+ }
+ }
+ break;
+ case Z_RPNStructure_complex:
+ h = get_term_hit(s->u.complex->s1, hash);
+ if (h == -1)
+ h = get_term_hit(s->u.complex->s2, hash);
+ break;
+ }
+ return h;
+}
+
+/** \brief gets hit count for numeric terms in RPN queries
+ \param q RPN Query
+ \return number of hits
+
+ This is just for testing.. A real database of course uses
+ the content of a database to establish a value.. In our case, we
+ have a way to trigger a certain hit count. Good for testing of
+ client applications etc
+*/
+static Odr_int get_hit_count(Z_Query *q)
+{
+ if (q->which == Z_Query_type_1 || q->which == Z_Query_type_101)
+ {
+ unsigned hash = 0;
+ Odr_int h = -1;
+ h = get_term_hit(q->u.type_1->RPNStructure, &hash);
+ if (h == -1)
+ h = hash % 24;
+ return h;
+ }
+ else if (q->which == Z_Query_type_104 &&
+ q->u.type_104->which == Z_External_CQL)
+ {
+ unsigned hash = 0;
+ const char *cql = q->u.type_104->u.cql;
+ int i;
+ for (i = 0; cql[i]; i++)
+ hash = hash * 65509 + cql[i];
+ return hash % 24;
+ }
+ else
+ return 24;
+}
+
+/** \brief checks if it's a dummy Slow database
+ \param basename database name to check
+ \param association backend association (or NULL if not available)
+ \retval 1 is slow database
+ \retval 0 is not a slow database
+
+ The Slow database is for testing.. It allows us to simulate
+ a slow server...
+*/
+static int check_slow(const char *basename, bend_association association)
+{
+ if (strncmp(basename, "Slow", 4) == 0)
+ {
+#if HAVE_UNISTD_H
+ int i, w = 3;
+ if (basename[4])
+ sscanf(basename+4, "%d", &w);
+ /* wait up to 3 seconds and check if connection is still alive */
+ for (i = 0; i < w; i++)
+ {
+ if (association && !bend_assoc_is_alive(association))
+ {
+ yaz_log(YLOG_LOG, "search aborted");
+ break;
+ }
+ sleep(1);
+ }
+#endif
+ return 1;
+ }
+ return 0;
+}
+
+static int strcmp_prefix(const char *s, const char *p)
+{
+ size_t l = strlen(p);
+ if (strlen(s) >= l && !memcmp(s, p, l))
+ return 1;
+ return 0;
+}
+
+static void init_delay(struct delay *delayp)
+{
+ delayp->d1 = delayp->d2 = 0.0;
+}
+
+static int parse_delay(struct delay *delayp, const char *value)
+{
+ if (sscanf(value, "%lf:%lf", &delayp->d1, &delayp->d2) == 2)
+ ;
+ else if (sscanf(value, "%lf", &delayp->d1) == 1)
+ delayp->d2 = 0.0;
+ else
+ return -1;
+ return 0;
+}
+
+static void ztest_sleep(double d)
+{
+#ifdef WIN32
+ Sleep( (DWORD) (d * 1000));
+#else
+ struct timeval tv;
+ tv.tv_sec = d;
+ tv.tv_usec = (d - (long) d) * 1000000;
+ select(0, 0, 0, 0, &tv);
+#endif
+}
+
+static void do_delay(const struct delay *delayp)
+{
+ double d = delayp->d1;
+
+ if (d > 0.0)
+ {
+ if (delayp->d2 > d)
+ d += (rand()) * (delayp->d2 - d) / RAND_MAX;
+ ztest_sleep(d);
+ }
+}
+
+static void addterms(ODR odr, Z_FacetField *facet_field, const char *facet_name)
+{
+ int index;
+ int freq = 100;
+ int length = strlen(facet_name) + 10;
+ char *key = odr_malloc(odr, length);
+ key[0] = '\0';
+ for (index = 0; index < facet_field->num_terms; index++)
+ {
+ Z_FacetTerm *facet_term;
+ sprintf(key, "%s%d", facet_name, index);
+ yaz_log(YLOG_DEBUG, "facet add term %s %d %s", facet_name, index, key);
+
+ facet_term = facet_term_create_cstr(odr, key, freq);
+ freq = freq - 10 ;
+ facet_field_term_set(odr, facet_field, facet_term, index);
+ }
+}