int test_unique(cl_cluster *clc)
{
	cl_object key;
	cl_object key2;
	cl_bin    bin;
	cl_rv     rv;
	cl_write_parameters cl_wp;

	citrusleaf_object_init_str(&key, myKey);
	citrusleaf_object_init_str(&key2, myKey2);
	strcpy(&bin.bin_name[0],bin1);
	citrusleaf_object_init_str(&bin.object, strData2);

	cl_write_parameters_set_default(&cl_wp);
	cl_wp.unique = 1;

	rv = citrusleaf_put(clc, ns, myset, &key, &bin, 1, &cl_wp);
	if( rv != CITRUSLEAF_FAIL_KEY_EXISTS ){
		printf(" TEST FAILED - test unique: should return key exists, returns %d\n", rv);
		return -1;
	}

	rv = citrusleaf_put(clc, ns, myset, &key2, &bin, 1, &cl_wp);
	if( rv != CITRUSLEAF_OK ){
		printf(" TEST FAILED - test unique: value should have been able to be written, actual value %d\n",rv);
		return -1;
	}
	
	return 0;
}
int test_batch(cl_cluster *clc)
{
	cl_rv rv;
	cl_bin bins[3];
	cl_object keys[2];
	cf_digest digests[2];
	char *userData = "foobar";

	citrusleaf_object_init_str(&keys[0],myKey);
	citrusleaf_object_init_str(&keys[1],myKey2);

	citrusleaf_object_init(&bins[0].object); 
	citrusleaf_object_init(&bins[1].object); 
	citrusleaf_object_init(&bins[2].object);

	strcpy(&bins[0].bin_name[0], bin1); 	
	strcpy(&bins[1].bin_name[0], bin2); 
	strcpy(&bins[2].bin_name[0], bin3); 

	citrusleaf_calculate_digest(myset, &keys[0], &digests[0]);
	citrusleaf_calculate_digest(myset, &keys[1], &digests[1]);
	
	rv = citrusleaf_get_many_digest(clc, (char *)ns, digests, 2, bins, 3, false, batch_cb, userData);

	if( rv != CITRUSLEAF_OK ){
		printf(" TEST FAILS - get many (batch) fails with %d\n", rv);
		return -1;
	}

	return 0;
}
int lset_create_test (char * keystr, char * ldt_bin ){
    static char * meth = "lset_create_test()";
    if( LSET_DEBUG ) {
        INFO("      [ENTER]:<%s:%s>:From %s", MOD, LDT, meth );
    }

    cl_cluster * c     = lset_g_config->asc;
    cl_object  o_key;
    char       * ns    = lset_g_config->ns;
    char       * set   = lset_g_config->set;
    char       * bname = ldt_bin;

    char * create_package = "StandardList";
    as_map *create_spec = as_hashmap_new(2);
    as_map_set(create_spec, (as_val *) as_string_new("Package", false),
    (as_val *) as_string_new( create_package, false));

    citrusleaf_object_init_str( &o_key, keystr );

    cl_rv rv = 0;
    rv = aerospike_lset_create( c, ns, set, &o_key, bname, create_spec, lset_g_config->timeout_ms);

    citrusleaf_object_free( &o_key );
    as_val_destroy( create_spec );
    return rv;

}
int lset_config_test(char * keystr, char * ldt_bin) {
    static char * meth = "lset_config_test()";
    if( LSET_DEBUG ) {
        INFO("      [ENTER]:<%s:%s>:From %s", MOD, LDT, meth );
    }

    int rc = 1;

    char * valstr;
    as_result * resultp;

    time_t cur_t;
    cur_t = time(NULL);

    cl_cluster * c     = lset_g_config->asc;
    cl_object  o_key;
    char       * ns    = lset_g_config->ns;
    char       * set   = lset_g_config->set;
    char       * bname = ldt_bin;

    citrusleaf_object_init_str( &o_key, keystr );

    //print config of lset
    rc = aerospike_lset_config( &resultp,
                c, ns, set, &o_key, bname, lset_g_config->timeout_ms);
    if ( rc == CITRUSLEAF_OK ) {
        valstr = as_val_tostring( resultp->value );
        printf("	Config parameters:\n	%s \n", valstr);
    }

    citrusleaf_object_free( &o_key );
    return rc;
}
int
write_new_value(uint32_t key, cl_object *key_o, cf_digest *d)
{
	uint64_t new_value_int;
	do {
		new_value_int = rand_64();
	} while (new_value_int == VALUE_UNINIT || new_value_int == VALUE_DELETED);
	
	g_config.values[key] = new_value_int;
	char new_value_str[g_config.value_len+1];
	my_itoa(new_value_str, new_value_int, g_config.value_len); 
	
	cl_bin values[1];
	strcpy(values[0].bin_name, g_config.bin);
	citrusleaf_object_init_str(&values[0].object, new_value_str);

	cl_write_parameters cl_w_p;	
	cl_write_parameters_set_default(&cl_w_p);
	cl_w_p.timeout_ms = g_config.timeout_ms;
	
	int rv;
	rv = citrusleaf_put(g_config.asc, g_config.ns, g_config.set, key_o, values, 1, &cl_w_p);
	if (rv != 0) {
		fprintf(stderr, "aerospike put returned error %d, fail digest %"PRIx64"\n",rv, *(uint64_t *)d);
		if (g_config.strict)
			return(-1);
	}
	
	citrusleaf_object_init(&values[0].object);
	
	rv = citrusleaf_verify(g_config.asc, g_config.ns, g_config.set, key_o, values, 1, g_config.timeout_ms, NULL);
	if (rv != 0) {
		fprintf(stderr, "aerospike get returned error %d digest %"PRIx64"\n",rv, *(uint64_t *)d);
		if (g_config.strict)
			return(-1);
	}

	// test!
	if (values[0].object.type != CL_STR) {
		fprintf(stderr, "read value has wrong type: expect string (3) got %d, fail digest %"PRIx64"\n",(int)values[0].object.type, *(uint64_t *)d);
		if (g_config.strict)
			return(-1);
	}
	if (strcmp(values[0].object.u.str, new_value_str) != 0) {
		fprintf(stderr, "read value does not match set value. digest %"PRIx64"\n", *(uint64_t *)d);
		fprintf(stderr, "  expecting: %s\n",new_value_str);
		fprintf(stderr, "  got: %s\n",values[0].object.u.str);
		if (g_config.strict)
			return( -1);
	}
	
	citrusleaf_object_free(&values[0].object);
	
	atomic_int_add(g_config.read_counter, 1);
	atomic_int_add(g_config.write_counter, 1);

	
	return(0);
}
Exemple #6
0
void asval_to_clobject(as_val * val, cl_object * obj)
{
	switch(val->type) {
		case AS_NIL: {
			citrusleaf_object_init_null(obj);
			break;
		}
		case AS_INTEGER: {
			as_integer * v = as_integer_fromval(val);
			citrusleaf_object_init_int(obj, as_integer_toint(v));
			break;
		}
		case AS_STRING: {
			as_string * v = as_string_fromval(val);
			citrusleaf_object_init_str(obj, as_string_get(v));
			break;
		}
		case AS_BYTES: {
			as_bytes * v = as_bytes_fromval(val);
			citrusleaf_object_init_blob2(obj, v->value, v->size, (cl_type)v->type);
			break;
		}
		case AS_LIST:{
			as_buffer buffer;
			as_buffer_init(&buffer);

			as_serializer ser;
			as_msgpack_init(&ser);
			as_serializer_serialize(&ser, val, &buffer);
			as_serializer_destroy(&ser);
			
			citrusleaf_object_init_blob_handoff(obj, buffer.data, buffer.size, CL_LIST);
			break;
		}
		case AS_MAP: {
			as_buffer buffer;
			as_buffer_init(&buffer);

			as_serializer ser;
			as_msgpack_init(&ser);
			as_serializer_serialize(&ser, val, &buffer);
			as_serializer_destroy(&ser);

			citrusleaf_object_init_blob_handoff(obj, buffer.data, buffer.size, CL_MAP);
			break;
		}
		default: {
			// raise an error
			break;
		}
	}
}
/** 
 *  LSET Search WITH TRANSFORM TEST
 *  For a single record, perform a series of SET Search.
 *  and do a server side transform of the byte-packed data
 *  Using the previously created record, repeatedly call stack search with
 *  varying numbers of search counts.
 */
int lset_search_with_transform_test(char * keystr, char * ldt_bin,
                                 char * filter_function,
                                 as_list * fargs,
                                 int iterations ) {
    static char * meth = "lset_search_with_transform_test()";
    if( LSET_DEBUG ) {
        INFO("      [ENTER]:<%s:%s>:From %s", MOD, LDT, meth );
    }

    cl_rv rc = 0;

    INFO("[ENTER]:<%s:%s>: Iterations(%d) Key(%s) LSOBin(%s)",
            MOD, meth, iterations, keystr, ldt_bin );

    cl_cluster * c     = lset_g_config->asc;
    cl_object  o_key;
    char       * ns    = lset_g_config->ns;
    char       * set   = lset_g_config->set;
    char       * bname = ldt_bin;
    int        vals_read;
    int        misses;
    int        errs;
    int        i;
    as_result * resultp;

    INFO("[DEBUG]:<%s:%s>: Run search() iterations(%d)", MOD, meth, iterations );

    // NOTE: Must FREE the result (resultp) for EACH ITERATION.
    int search_count = 2; // Soon -- set by Random Number
    citrusleaf_object_init_str( &o_key, keystr );
    for ( i = 0; i < iterations ; i ++ ){
        search_count++;
        rc = aerospike_lset_search_then_filter(
                &resultp, c, ns, set, &o_key, bname, search_count,
                filter_function, fargs, lset_g_config->timeout_ms);

        //lset_process_read_results( meth, rc, resultp, i, &vals_read, &misses,
          //      &errs, search_count );

        // Count up the reads (total)
        lset_g_config->read_vals_counter += search_count;
        lset_g_config->read_ops_counter += 1;
    } // end for each search iteration
    citrusleaf_object_free( &o_key );

    INFO("[EXIT]:<%s:%s>: RC(%d)", MOD, meth, rc );
    return rc;
} // end lset_search_with_transform_test()
int lset_size_test(char * keystr, char * ldt_bin, uint32_t   * size) {
    static char * meth = "lset_size_test()";
    if( LSET_DEBUG ) {
        INFO("      [ENTER]:<%s:%s>:From %s", MOD, LDT, meth );
    }

    int rc = 0;

    cl_cluster * c     = lset_g_config->asc;
    cl_object  o_key;
    char       * ns    = lset_g_config->ns;
    char       * set   = lset_g_config->set;
    char       * bname = ldt_bin;

    citrusleaf_object_init_str( &o_key, keystr );

    //check size of lset
    rc = aerospike_lset_size( size,
                c, ns, set, &o_key, bname, lset_g_config->timeout_ms);

    citrusleaf_object_free( &o_key );
    return rc;
} 
/**
 *  LSET READ TEST
 *  For a single record, perform a series of SET READS.
 *  Using the previously created record, repeatedly call set read with
 *  varying keys (value type is passed in via "format").
 *  NOTE: We must EXPLICITLY FREE the result, as it is a malloc'd
 *  object that is handed to us.
 *  + keystr: String Key to find the record
 *  + ldt_bin: Bin Name of the LDT
 *  + iterations: Number of iterations to run this test
 *  + seed:  Seed value for the random number pattern
 *  + data_format: Type of value (number, string, list)
 */
int lset_search_test(char * keystr, char * ldt_bin, int iterations,
        int seed, int data_format ) {
    static char * meth = "lset_search_test()";

    cl_rv rc = CITRUSLEAF_OK;
    as_result * resultp;

    char * valstr;

//    INFO("[ENTER]:<%s:%s>: Iterations(%d) Key(%s) LSOBin(%s) Sd(%d) DF(%d)",
//            MOD, meth, iterations, keystr, ldt_bin, seed, data_format);

    cl_cluster * c     = lset_g_config->asc;
    cl_object  o_key;
    char       * ns    = lset_g_config->ns;
    char       * set   = lset_g_config->set;
    char       * bname = ldt_bin;
    int        vals_read = 0;
    int        misses = 0;
    int        errs = 0;

//    INFO("[DEBUG]:<%s:%s>: Run search() iterations(%d)", MOD, meth, iterations );

    as_val * valp ;
    srand( seed );
    // NOTE: Must FREE the result for EACH ITERATION.
    citrusleaf_object_init_str( &o_key, keystr );

    int          iseed;
    if(iterations == 0) {
            rc = aerospike_lset_search( &resultp,
                c, ns, set, &o_key, bname, NULL, lset_g_config->timeout_ms);
            //printf("search result is %s\n", as_val_tostring(resultp->value));
            if( resultp != NULL ) as_result_destroy( resultp );
    } else { 
        for ( int i = 0; i < iterations ; i ++ ){
            iseed = i * 10;
            lset_generate_value( &valp, iseed, data_format );

            //     INFO("[DEBUG]:<%s:%s>: Peek(%d)", MOD, meth, iterations );
            rc = aerospike_lset_search( &resultp,
                c, ns, set, &o_key, bname, valp, lset_g_config->timeout_ms);
            if(rc == CITRUSLEAF_OK) {
                rc = lset_process_read_results( meth, rc, resultp, i, &vals_read, &misses,
                          &errs, valp);
            }
	    //printf("compared result: exp-%s act-%s ",as_val_tostring(valp),as_val_tostring(resultp->value));
            // Clean up -- release the result object
            if( resultp != NULL ) as_result_destroy( resultp );

            // Count up the reads (total)
            if(rc == 0) {
                lset_g_config->read_vals_counter += 1;
            }
            lset_g_config->read_ops_counter += 1;
        } // end for each search iteration
    }
    citrusleaf_object_free( &o_key );

//    INFO("[EXIT]:<%s:%s>: RC(%d)", MOD, meth, rc );
    return rc;
} // end lset_search_test()
int test_operate(cl_cluster *clc)
{
	cl_object key;
	cl_operation ops[3];
	cl_rv rv;
	
	citrusleaf_object_init_str(&key, myKey);
	strcpy(&ops[0].bin.bin_name[0],bin1);
	strcpy(&ops[1].bin.bin_name[0],bin2);
	strcpy(&ops[2].bin.bin_name[0],bin3);
	citrusleaf_object_init(&ops[0].bin.object);
	citrusleaf_object_init_int(&ops[1].bin.object, 2);
	citrusleaf_object_init_blob(&ops[2].bin.object, blobData2, strlen(blobData2)+1);
	
	ops[0].op = CL_OP_READ;
	ops[1].op = CL_OP_INCR;
	ops[2].op = CL_OP_WRITE;
	

	rv = citrusleaf_operate(clc, ns, myset, &key, &ops[0], 3, NULL, false, NULL);
	if( rv != CITRUSLEAF_OK ){
		printf(" TEST FAILED - go-right case of Operate is failing with %d\n", rv);
		return -1;
	}
	// and look at the value we read...
	if( strcmp(ops[0].bin.object.u.str, strData) ){
		printf( "TEST FAILED - Operate did not read back correct data! %s, %s\n", ops[0].bin.object.u.str, strData);
		return -1;
	}

	// and release that value...
	citrusleaf_object_free(&ops[0].bin.object);

	// now read the values back.
	ops[0].op = CL_OP_READ;
	ops[1].op = CL_OP_READ;
	ops[2].op = CL_OP_READ;
	
	citrusleaf_object_init(&ops[0].bin.object);
	citrusleaf_object_init(&ops[1].bin.object);
	citrusleaf_object_init(&ops[2].bin.object);


	rv = citrusleaf_operate(clc, ns, myset, &key, &ops[0], 3, NULL ,false, NULL);
	if( rv != CITRUSLEAF_OK ){
		printf(" TEST FAILED - go-right case of Operate is failing with %d\n", rv);
		return -1;
	}

	// check the values...
	if( strcmp(ops[0].bin.object.u.str, strData) ){
		printf(" TEST FAILED - did not read back the same string\n");
		return -1;
	}

	if( ops[1].bin.object.u.i64 != intData+2 ){
		printf(" TEST FAILED - did not read back correct int %lu %lu\n", ops[1].bin.object.u.i64, intData+2);
		return -1;
	}

	if( strcmp(ops[2].bin.object.u.blob, blobData2 ) ){
		printf(" TEST FAILED - did not read back blob correctly %s, %s\n", (char *)ops[2].bin.object.u.blob, blobData2);
		return -1;
	}

	// and free them all...
	citrusleaf_object_free(&ops[0].bin.object);	
	citrusleaf_object_free(&ops[1].bin.object);	
	citrusleaf_object_free(&ops[2].bin.object);
	
	// what happens if I request something that doesn't exist?  // XXX - should do this elsewhere...
/*	strcpy(&ops[0].bin.bin_name[0], "doesnt exist");
	rv = citrusleaf_operate(clc, mySet, myKey, &ops[3], 3, NULL );
*/		

	return 0;	
}
void *
work_fn(void *gcc_is_ass)
{
	// Forever,
	do {
		// Pick a key to use. Look it up to see if anyone else is using it.
		uint32_t	key = rand_64() % g_config.n_keys;
		
		uint32_t    die = rand_64() & 0x03;
		
		if (SHASH_OK == shash_put_unique(g_config.in_progress_hash, &key, 0) ) 
		{
			cl_rv rv;
		
			// Make the key into a string
			char key_s[g_config.key_len+1];
			my_itoa(key_s, key, g_config.key_len);
			
			// Make an cl_object that represents the key
			cl_object key_o;
			citrusleaf_object_init_str(&key_o, key_s);
			

			cf_digest d;
			citrusleaf_calculate_digest(g_config.set, &key_o, &d);

			if (VALUE_UNINIT == g_config.values[key]) {

				// simply set the value to something - can't really check anything because we don't know the state				
				if (0 != write_new_value(key, &key_o, &d)) {
					if (g_config.strict)   					goto Fail;
				}
				atomic_int_add(g_config.key_counter, 1);
			}
			else if (VALUE_DELETED == g_config.values[key]) {
				
				// Shouldn't exist
				cl_bin *cl_v = 0;
				int		cl_v_len;
				rv = citrusleaf_get_all(g_config.asc, g_config.ns, g_config.set, &key_o, &cl_v, &cl_v_len, g_config.timeout_ms, NULL);
				if (rv != CITRUSLEAF_FAIL_NOTFOUND) {
					fprintf(stderr, "Get after delete returned improper value when should be deleted %d key %s digest %"PRIx64"\n",rv,key_s, *(uint64_t *)&d);
					if (g_config.strict)    goto Fail;
				}
				if (cl_v)	free(cl_v);

				atomic_int_add(g_config.read_counter, 1);  // did two ops here						
				
				// write a new value
				if (die < 2) {
					if (0 != write_new_value(key, &key_o, &d)) {
						if (g_config.strict)   goto Fail;
					}
					atomic_int_add(g_config.key_counter, 1);
				}
			}
			// Value is well known. Check to see that it's still right.
			else {			
				
				cl_bin values[1];
				strcpy(values[0].bin_name, g_config.bin);
				citrusleaf_object_init(&values[0].object);
				
				// Make string version of old value for checking
				char new_value_str[g_config.value_len+1];
				my_itoa(new_value_str, g_config.values[key], g_config.value_len); 
				citrusleaf_object_init_str(&values[0].object, new_value_str);
				
				rv = citrusleaf_verify(g_config.asc, g_config.ns, g_config.set, &key_o, values, 1, g_config.timeout_ms, NULL);
				if (rv != 0) {
					fprintf(stderr, "Get returned improper value %d when should be set : key %d digest %"PRIx64"\n",rv,key, *(uint64_t *)&d);
					if (g_config.strict)   goto Fail;
					goto V1;
				}
				
				// test!
				if (values[0].object.type != CL_STR) {
					fprintf(stderr, "read value has wrong type: expect string (3) got %d\n",(int)values[0].object.type);  
					if (g_config.strict)   return((void *)-1);
				}
				else if (strcmp(values[0].object.u.str, new_value_str) != 0) {
					fprintf(stderr, "read value does not match set value.\n");
					fprintf(stderr, "  expecting: %s\n",new_value_str);
					fprintf(stderr, "  got: %s\n",values[0].object.u.str);
					if (g_config.strict)   goto Fail;
				}
				
				citrusleaf_object_free(&values[0].object);
				atomic_int_add(g_config.read_counter, 1);
				
				// Delete, write new value, what's your pleasure?
			V1:				
				if (die < 2) {
					if (0 != write_new_value(key, &key_o, &d)) {
						if (g_config.strict)   return((void *)-1);
					}
				}
				// Delete!
				else if (die == 2) {
					rv = citrusleaf_delete_verify(g_config.asc, g_config.ns, g_config.set, &key_o, 0);
					if (rv != 0) {
						fprintf(stderr, "Delete returned improper value %d, fail: key %d digest %"PRIx64"\n",rv, key, *(uint64_t *)&d);
						if (g_config.strict)   goto Fail;
					}

					cl_bin values[1];
					strcpy(values[0].bin_name, g_config.bin);
					citrusleaf_object_init(&values[0].object);
					
					rv = citrusleaf_get(g_config.asc, g_config.ns, g_config.set, &key_o, values, 1, g_config.timeout_ms, NULL);
					if (rv != CITRUSLEAF_FAIL_NOTFOUND) {
						fprintf(stderr, "Get after delete returned improper value %d digest %"PRIx64"\n",rv, *(uint64_t *)&d);
						if (g_config.strict)   goto Fail;
					}
					
					citrusleaf_object_free(&values[0].object);
					
					g_config.values[key] = VALUE_DELETED;
					atomic_int_add(g_config.read_counter, 1);  // did two ops here
					atomic_int_add(g_config.delete_counter, 1);
					atomic_int_add(g_config.key_counter, -1);
					
				}
				
			}
			
			// remove my lock on this key
			shash_delete(g_config.in_progress_hash, &key);
			

		}		
	} while (1);
	
	
Fail:	
	abort();
	return((void *)-1);
}
int main(int argc, char **argv){
	cl_cluster   *clc;
	cl_rv        return_value;
	cl_object key1;
	cl_object key2;

	printf(" STARTING TESTS\n");
	// initialize internal citrusleaf structures just once
	citrusleaf_init();

	// Create a cluster with a particular starting host
	printf(" STARTING CLUSTER CREATION TEST .... \n");
	clc = citrusleaf_cluster_create();
	if (!clc){
		printf("TEST FAILED: Could not create cluster object");
	    return(-1);
	}

	return_value = citrusleaf_cluster_add_host(clc, host, 3000, 1000);
	if( return_value != CITRUSLEAF_OK ){
		printf("TEST FAILED - cannot connect to host\n");
		return -1;
	}

	// XXX - need to do some info calls with a bigger cluster!

	printf(" DONE\n");


	// set up the key. Create a stack object, set its value to a string
	cl_object    key_obj;
	citrusleaf_object_init_str(&key_obj, myKey);

	// set up a specific bins to fetch
	// the response will be in this value
	cl_bin       values[3];
	strcpy( &values[0].bin_name[0], bin1 );
	citrusleaf_object_init_str( &values[0].object, strData);
	strcpy( &values[1].bin_name[0], bin2);
	citrusleaf_object_init_int( &values[1].object, intData );
	strcpy( &values[2].bin_name[0], bin3);
	citrusleaf_object_init_blob( &values[2].object, blobData, strlen(blobData)+1);

	printf("params to put are clc %p, ns %s, set %s, key %p, values %p\n", clc, ns, myset, &key_obj, values);
	return_value = citrusleaf_put(clc, ns, myset, &key_obj, values, 3, NULL);
	if( return_value != CITRUSLEAF_OK ){
		printf(" TEST FAILS - INITIAL PUT FAILS, value is %d\n", return_value);
		return(-1);
	}

	citrusleaf_object_init(&values[0].object);
	citrusleaf_object_init(&values[1].object);
	citrusleaf_object_init(&values[2].object);

	return_value = citrusleaf_get(clc, ns, myset, &key_obj, 
		values, 3, 0, NULL); 

	switch (return_value) {
    	case CITRUSLEAF_OK:
       		if (values[0].object.type != CL_STR) {
			printf(" TEST FAILS - value has unexpected type %d\n",values[0].object.type);
			goto cleanup;
       		} else if( strcmp(values[0].object.u.str, strData) ){
			printf("TEST FAILS - WRITE DOES NOT RETURN WHAT WAS WRITTEN: %s, %s\n", values[0].object.u.str, strData);
			goto cleanup;
		} 
	
       		if (values[1].object.type != CL_INT) {     
			printf(" TEST FAILS - value has unexpected type %d\n",values[1].object.type);
			goto cleanup;
       		} else if( values[1].object.u.i64 != intData){
			printf("TEST FAILS - WRITE DOES NOT RETURN WHAT WAS WRITTEN, %lu, %lu\n", values[1].object.u.i64, intData);
			goto cleanup;
       		} 
		if( values[2].object.type != CL_BLOB) {
			printf(" TEST FAILS - value has unexpected type %d\n",values[1].object.type);
			goto cleanup;
		}else if( strcmp(values[2].object.u.blob, blobData) ){
			printf(" TEST FAILS - WRITE DOES NOT RETURN CORRECT BLOB DATA\n");
			goto cleanup;
		}
      		break;
    	case CITRUSLEAF_FAIL_NOTFOUND:
    		printf(" TEST FAILS - citrusleaf says that key does not exist\n");
		goto cleanup;
    		break;
    	case CITRUSLEAF_FAIL_CLIENT:
    		printf(" TEST FAILS - citrusleaf client error: local error\n");
		goto cleanup;
    		break;
    	case CITRUSLEAF_FAIL_PARAMETER:
    		printf(" TEST FAILS - citrusleaf - bad parameter passed in \n");
		goto cleanup;
    		break;
    	case CITRUSLEAF_FAIL_TIMEOUT:
		printf(" TEST FAILS - citrusleaf - timeout on get\n");
		goto cleanup;
		break;
    	case CITRUSLEAF_FAIL_UNKNOWN:
		printf(" TEST _FAILS - citrusleaf - unknown server error\n");
		goto cleanup;
		break;
    	default :
		printf(" TEST_FAILS - error %d\n", return_value);
		goto cleanup;

	}
	// clean up the retrieved objects
	citrusleaf_object_free(&values[0].object);
	citrusleaf_object_free(&values[1].object);

	if( test_getall(clc) )    goto cleanup;
	if( read_mod_write(clc) ) goto cleanup;
	if( test_unique(clc) )    goto cleanup;
	if( test_operate(clc) )   goto cleanup;
	if( test_batch(clc) )     goto cleanup;
	
	printf("TEST SUCCESSFUL!\n");

cleanup:

	citrusleaf_object_init_str(&key1,myKey);
	citrusleaf_object_init_str(&key2,myKey2);
	citrusleaf_delete(clc, ns, myset, &key1, NULL);
	citrusleaf_delete(clc, ns, myset, &key2, NULL);
	// Clean up the cluster object
	citrusleaf_cluster_destroy(clc);
	// Clean up the unit
	citrusleaf_shutdown();
  
}
// This is a read-modify-write test. We read the data and the generation count, and 
// then write the data using varous wp parameters
int read_mod_write(cl_cluster *clc)
{
	cl_object key;
	cl_bin    bin;
	cl_rv     rv;
	uint32_t  gen_count;

	citrusleaf_object_init_str(&key, myKey);
	strcpy(&bin.bin_name[0],bin1);
	citrusleaf_object_init(&bin.object);
	rv = citrusleaf_get(clc, ns, myset, &key, &bin, 1, 0, &gen_count);

	if( rv != CITRUSLEAF_OK ){
		printf(" TEST FAILED - Get returns value %d\n", rv);
		return -1;
	}

	// reuse old bin - must free memory allocated by system first
	citrusleaf_object_free(&bin.object); // check - does free reset the free pointer? XXX
	if( bin.object.free ){
		printf(" TEST FAILED - free pointer not reset on object_free \n");
		return -1;
	}
	citrusleaf_object_init_str(&bin.object,strData2); 
	cl_write_parameters cl_wp;
	cl_write_parameters_set_default(&cl_wp);
	cl_write_parameters_set_generation(&cl_wp, gen_count);

	// now attempt to write with the same gen count - should work just fine
	citrusleaf_object_init_str(&bin.object, strData2);
	rv = citrusleaf_put(clc, ns, myset, &key, &bin, 1, &cl_wp);

	if( rv != CITRUSLEAF_OK ){
		printf(" TEST FAILED - put with gen count fails!\n");
		return -1;
	} 

	// now attempt to write again - gen count on server should have changed!
	citrusleaf_object_init_str(&bin.object, "badData");
	rv = citrusleaf_put(clc, ns, myset, &key, &bin, 1, &cl_wp);
	if( rv != CITRUSLEAF_FAIL_GENERATION ){
		printf(" TEST FAILED - generation count should fail, actual return value is %d\n", rv);
		return -1;
	}

	// check that value has not changed
	citrusleaf_object_init(&bin.object);
	uint32_t new_gen;
	rv = citrusleaf_get(clc, ns, myset, &key, &bin, 1,0, &new_gen);
	if( rv != CITRUSLEAF_OK ){
		printf(" TEST FAILED - get in rmw is failing\n");
		return -1;
	}

	if( strcmp(bin.object.u.str, strData2) ){
		printf(" TEST FAILED - data on server changes despite generation count!!\n");
		return -1;
	}
	citrusleaf_object_free(&bin.object);


	// one more time - use the generation gt thing...
	cl_write_parameters_set_default(&cl_wp);
	cl_write_parameters_set_generation_gt(&cl_wp, gen_count+2);

	citrusleaf_object_init_str(&bin.object, strData);
	rv = citrusleaf_put(clc, ns, myset, &key, &bin, 1, &cl_wp);

	if( rv != CITRUSLEAF_OK ){
		printf(" TEST FAILED - put with gen count gt fails! err %d gen count %d\n", rv, gen_count);
		return -1;
	} 

	// check that value is correct - and get the new gen_count
	citrusleaf_object_init(&bin.object);
	rv = citrusleaf_get(clc, ns, myset, &key, &bin, 1, 0, &gen_count);
	if( rv != CITRUSLEAF_OK ){
		printf(" TEST FAILED - get in rmw is failing\n");
		return -1;
	}

	if( strcmp(bin.object.u.str, strData) ){
		printf(" TEST FAILED - data on server changes despite generation count!!\n");
		return -1;
	}
	citrusleaf_object_free(&bin.object);

	// now attempt to write again - gen count on server should have changed!
	citrusleaf_object_init_str(&bin.object, "badData");
	cl_write_parameters_set_default(&cl_wp);
	cl_write_parameters_set_generation_gt(&cl_wp, gen_count);
	rv = citrusleaf_put(clc, ns, myset, &key, &bin, 1, &cl_wp);
	if( rv != CITRUSLEAF_FAIL_GENERATION ){
		printf(" TEST FAILED - generation count should fail, actual return value is %d\n", rv);
		return -1;
	}

	// check that value has not changed
	citrusleaf_object_init(&bin.object);
	rv = citrusleaf_get(clc, ns, myset, &key, &bin, 1, 0, NULL);
	if( rv != CITRUSLEAF_OK ){
		printf(" TEST FAILED - get in rmw is failing\n");
		return -1;
	}

	if( strcmp(bin.object.u.str, strData) ){
		printf(" TEST FAILED - data on server changes despite generation count!!\n");
		return -1;
	}

	// at the end of this function, bin1 is strdata
	return 0;
}
int test_getall(cl_cluster *clc){

	// set up the key.
	cl_object      key_obj;
	citrusleaf_object_init_str(&key_obj, myKey);

	// create variables to return all values
	cl_bin         *bins;
	int            n_bins;

	// do the get
	citrusleaf_get_all(clc, ns, myset, &key_obj, 
		&bins, &n_bins, 0, NULL);

	// check the contained values
	int haveStr = 0;
	int haveInt = 0;
	int haveBlob = 0;
	if( n_bins != 3 ){
		printf(" TEST FAILED - get_all returns wrong number of bins, %d\n", n_bins);
		return -1;
	}

	for (int i=0;i<n_bins;i++) {
     		printf (" bin %d name %s\n",i,bins[i].bin_name);
     		if (bins[i].object.type == CL_STR){
			if( strcmp(bins[i].object.u.str, strData) ){
				printf(" TEST FAILED - str output of get_all does not match input\n"); 
				return -1;
			}
			if( !bins[i].object.free ){
				printf(" TEST FAILED - string allocated, but free pointer not set\n");
				return -1;
			}
			haveStr = 1;
     		}else if (bins[i].object.type == CL_INT ){
			if( bins[i].object.u.i64 != intData ){
				printf(" TEST FAILED - int output of get_all does not match input\n");
				return -1;
			}
			if( bins[i].object.free ){
				printf(" TEST FAILED - int output indicated as allocated but is not\n");
				return -1;
			}
			haveInt = 1;
		}else if( bins[i].object.type == CL_BLOB ){
			haveBlob = 1; 
			if( strcmp(bins[i].object.u.blob, blobData) ){
				printf(" TEST FAILED - blob output does not match input\n");
				return -1;
			}
			// check - free pointer set?
			if( !bins[i].object.free ){
				printf(" TEST FAILED - blob allocated, but free pointer not set\n");
				return -1;
			}
    		}else{ 
       			printf("TEST FAILED - unexpected bin type %d\n",(int) bins[i].object.type);
			return(-1);
		}
    	}

	if( !(haveInt && haveStr && haveBlob ) ){
		printf("TEST FAILED - not all values have correct types\n");
		return -1;
	}
	// free the allocated memory
	for( int i=0; i<n_bins;i++) {
    		citrusleaf_object_free(&bins[i].object);
	}	
	free(bins);
	return 0;
}
/**
 *  LSET Insert WITH_TRANSFORM TEST
 *  For a single record, perform a series of SET Insert of BYTE-PACKED data.
 *  Create a new record, then repeatedly call stack insert.
 */
int lset_insert_with_transform_test(char * keystr, char * ldt_bin, int iterations) {
    static char * meth = "lset_insert_with_transform_test()";
    if( LSET_DEBUG ) {
        INFO("      [ENTER]:<%s:%s>:From %s", MOD, LDT, meth );
    }

    int rc = 0;
    int i;

    INFO("[ENTER]:<%s:%s>: It(%d) Key(%s) LSOBin(%s)",
            MOD, meth, iterations, keystr, ldt_bin );

    // Abbreviate for simplicity.
    cl_cluster * c  = lset_g_config->asc;
    char       * ns = lset_g_config->ns;
    char       * set  = lset_g_config->set;
    char       * bname  = ldt_bin;
    cl_object o_key;

    // Set up the Creation Spec parameter -- mostly setting the Package
    // (which is the name for a canned set of settings).
    char * create_package = "ProdListValBinStore";
    as_map *create_spec = as_hashmap_new(2);
    as_map_set(create_spec,
            (as_val *) as_string_new("Package", false),
            (as_val *) as_string_new( create_package, false));

    INFO("[DEBUG]:<%s:%s>: Run insert_with_transform() iterations(%d)",
          MOD, meth, iterations );
    citrusleaf_object_init_str( &o_key, keystr );
    for ( i = 0; i < iterations; i++ ) {
        int val         = i * 10;
        as_list * listp = as_arraylist_new( 5, 5 );
        int64_t urlid   = val + 1;
        as_list_add_integer( listp, urlid );
        int64_t created = val + 2;
        as_list_add_integer( listp, created );
        int64_t meth_a  = val + 3;
        as_list_add_integer( listp, meth_a );
        int64_t meth_b  = val + 4;
        as_list_add_integer( listp, meth_b );
        int64_t status  = val + 5;
        as_list_add_integer( listp, status );

        rc = aerospike_lset_create_and_insert( c, ns, set, &o_key, bname,
                (as_val *)listp, create_spec, lset_g_config->timeout_ms);
        if ( rc != CITRUSLEAF_OK ) {
            INFO("[ERROR]:<%s:%s>:LSO PUSH WITH TRANSFROM Error: i(%d) rc(%d)",
                  MOD, meth, i, rc );
            as_val_destroy ( listp );
            goto cleanup;
        }
        // Count the write operation for stats gathering
        lset_g_config->write_ops_counter += 1;
        lset_g_config->write_vals_counter += 1;
        as_val_destroy( listp ); // must destroy every iteration.
        listp = NULL;
    } // end for

cleanup:
    citrusleaf_object_free( &o_key );
    as_val_destroy( create_spec );

    return rc;
} // end lset_insert_with_transform_test()
int
do_example(config *c)
{
	int rv;
	
	// Put some test values
	cl_object o_key;
	citrusleaf_object_init_str(&o_key, "example_key");
	cl_bin values[2];
	strcpy(values[0].bin_name, "test_bin_one");
	citrusleaf_object_init_str(&values[0].object, "example_value_one");
	strcpy(values[1].bin_name, "test_bin_two");
	citrusleaf_object_init_int(&values[1].object, 0xDEADBEEF);
	
	// set a non-default write parameter
	cl_write_parameters cl_wp;
	cl_write_parameters_set_default(&cl_wp);
	cl_wp.timeout_ms = 1000;
	
	if (0 != (rv = citrusleaf_put(c->asc, c->ns, c->set, &o_key, values, 2, &cl_wp))) {
		fprintf(stderr, "citrusleaf put failed: error %d\n",rv);
		return(-1);
	}
	fprintf(stderr, "citrusleaf put succeeded\n");
	
	// Get all the values in this key (enjoy the fine c99 standard)
	cl_bin *cl_v = 0;
	uint32_t generation;
	int 	cl_v_len;
	if (0 != (rv = citrusleaf_get_all(c->asc, c->ns, c->set, &o_key, &cl_v, &cl_v_len, c->timeout_ms, &generation))) {
		fprintf(stderr, "get after put failed, but there should be a key here - %d\n",rv);
		if (cl_v)	free(cl_v);
		return(-1);
	}
	fprintf(stderr, "get all returned %d bins\n",cl_v_len);
	for (int i=0;i<cl_v_len;i++) {
		fprintf(stderr, "%d:  bin %s ",i,cl_v[i].bin_name);
		switch (cl_v[i].object.type) {
			case CL_STR:
				fprintf(stderr, "type string: value %s\n", cl_v[i].object.u.str);
				break;
			case CL_INT:
				fprintf(stderr, "type int: value %"PRId64"\n",cl_v[i].object.u.i64);
				break;
			default:
				fprintf(stderr, "type unknown! (%d)\n",(int)cl_v[i].object.type);
				break;
		}
		// could have done this -- but let's free the objects in the bins later
		// citrusleaf_object_free(&cl_v[i].object);
	}
	if (cl_v)	{
		citrusleaf_bins_free(cl_v, cl_v_len);
		free(cl_v); // only one free for all bins
	}
	fprintf(stderr,"citrusleaf getall succeeded\n");
	
	// Delete the key you just set
	if (0 != (rv = citrusleaf_delete(c->asc, c->ns, c->set, &o_key, 0/*default write params*/))) {
		fprintf(stderr, "citrusleaf delete failed: error %d\n",rv);
		return(-1);
	}
	fprintf(stderr, "citrusleaf delete succeeded\n");
	
	return(0);
}
/**
 *  LSET INSERT TEST
 *  For a single record, perform a series of SET insert.
 *  Create a new record, then repeatedly call lset insert.
 *  This should work for data that is a NUMBER, a STRING or a LIST.
 *  Parms:
 *  + keystr: String Key to find the record
 *  + ldt_bin: Bin Name of the LDT
 *  + iterations: Number of iterations to run this test
 *  + seed:  Seed value for the random number pattern
 *  + data_format: Type of value (number, string, list)
 */
int lset_insert_test(char * keystr, char * ldt_bin, int iterations, int seed,
        int data_format ) {
    static char * meth = "lset_insert_test()";
    if( LSET_DEBUG ) {
        INFO("      [ENTER]:<%s:%s>:From %s", MOD, LDT, meth );
    }

    int rc = CITRUSLEAF_OK;
    int i;
    as_val *valp;

    time_t cur_t;
    cur_t = time(NULL);

    //    INFO("[ENTER]:<%s:%s>: It(%d) Key(%s) LSOBin(%s) Seed(%d)",
    //            MOD, meth, iterations, keystr, ldt_bin, seed);

    // We have two choices:  We can create the LSO bin here, and then
    // do a bunch of inserts into it -- or we can just do the combined
    // "create_and_insert" insert, which upon reflection, is really the
    // most likely mode we'll be in. We'll choose the later.

    // Set up the Creation Spec parameter -- mostly setting the Package
    // (which is the name for a canned set of settings).
    char * create_package = "StandardList";
    as_map *create_spec = as_hashmap_new(2);
    as_map_set(create_spec, (as_val *) as_string_new("Package", false),
    (as_val *) as_string_new( create_package, false));

    cl_cluster * c     = lset_g_config->asc;
    cl_object  o_key;
    char       * ns    = lset_g_config->ns;
    char       * set   = lset_g_config->set;
    char       * bname = ldt_bin;
    int          iseed;

    //INFO("[DEBUG]:<%s:%s>: Run insert() iterations(%d)", MOD, meth, iterations );
    citrusleaf_object_init_str( &o_key, keystr );
    for ( i = 0; i < iterations; i++ ) {
        iseed = i * 10;
        lset_generate_value( &valp, iseed, data_format );

        rc = aerospike_lset_create_and_insert(
                c, ns, set, &o_key, bname, valp, create_spec,
                lset_g_config->timeout_ms);

        if ( rc != CITRUSLEAF_OK ) {
        //INFO("[ERROR]:<%s:%s>:H Error: i(%d) rc(%d)", MOD, meth,i,rc );
            as_val_destroy ( valp );
            goto cleanup;
        } 
        // Count the write operation for stats gathering
        lset_g_config->write_ops_counter += 1;
        lset_g_config->write_vals_counter += 1;
        as_val_destroy( valp ); // must destroy every iteration.
        valp = NULL; // unnecessary insurance
    } // end for
cleanup:
    citrusleaf_object_free( &o_key );
    as_val_destroy( create_spec );
    return rc;
} // end lset_insert_test()