content: /*
   *   Copyright 2009 Joubin Houshyar
   * 
   *   Licensed under the Apache License, Version 2.0 (the "License");
   *   you may not use this file except in compliance with the License.
   *   You may obtain a copy of the License at
   *    
   *   http://www.apache.org/licenses/LICENSE-2.0
   *    
   *   Unless required by applicable law or agreed to in writing, software
   *   distributed under the License is distributed on an "AS IS" BASIS,
   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *   See the License for the specific language governing permissions and
   *   limitations under the License.
   */
  
  package org.jredis.ri.alphazero;
  
  import static org.jredis.ri.alphazero.support.DefaultCodec.toStr;
  import static org.testng.Assert.assertEquals;
  import static org.testng.Assert.assertFalse;
  import static org.testng.Assert.assertNotNull;
  import static org.testng.Assert.assertNull;
  import static org.testng.Assert.assertTrue;
  import static org.testng.Assert.fail;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  import java.util.Map;
  import java.util.concurrent.ExecutionException;
  import java.util.concurrent.Future;
  import org.jredis.ClientRuntimeException;
  import org.jredis.JRedisFuture;
  import org.jredis.ObjectInfo;
  import org.jredis.Query;
  import org.jredis.RedisException;
  import org.jredis.RedisType;
  import org.jredis.ZSetEntry;
  import org.jredis.protocol.Command;
  import org.jredis.protocol.ResponseStatus;
  import org.jredis.ri.JRedisTestSuiteBase;
  import org.jredis.ri.alphazero.support.Convert;
  import org.jredis.ri.alphazero.support.DefaultCodec;
  import org.jredis.ri.alphazero.support.Log;
  import org.testng.annotations.Test;
  
  /**
   * [TODO: cleanup the unit test comments]
   * 
   * Provides the comprehensive set of tests of all {@link JRedisFuture} methods.
   *
   * @author  Joubin Houshyar (alphazero@sensesay.net)
   * @version alpha.0, Oct 10, 2009
   * @since   alpha.0
   * 
   */
  
  public abstract class JRedisFutureProviderTestsBase extends JRedisTestSuiteBase<JRedisFuture> {
  
  	// ------------------------------------------------------------------------
  	// Properties
  	// ------------------------------------------------------------------------
  
  	/** JRedis Command being tested -- for log info */
  	private String cmd;
  	
  	// ------------------------------------------------------------------------
  	// The Tests
  	// ======================================================= JRedisFuture ===
  	/*
  	 * We define and run provider agnostic tests here.  This means we run a set
  	 * of JRedisFuture interface method tests that every connected JRedisFuture
  	 * implementation should be able to support. 
  	 * 
  	 * The following commands are omitted:
  	 * 1 - QUIT: since we may be testing a multi-connection provier
  	 * 2 - SHUTDOWN: for the same reason as QUIT 
  	 * 3 - MOVE and SELECT
  	 */
  	// ------------------------------------------------------------------------
  
  
  //	@Test
  //	public void testTemplate() throws InterruptedException {
  //		cmd = Command.PING.code + " | " + Command.SETNX.code + " byte[] | " + Command.GET;
  //		Log.log("TEST: %s command", cmd);
  //		
  //		try {
  //			provider.flushdb();
  //			try {
  //			}
  //			catch(ExecutionException e){
  //				Throwable cause = e.getCause();
  //				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  //			}
  //		} 
  //		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  //	}
  
  	@Test
  	public void testSrandmember () throws InterruptedException {
  		cmd = Command.SRANDMEMBER.code + " String | " + Command.SMEMBERS;
  		Log.log("TEST: %s command", cmd);
  
  		try {
  			provider.flushdb();
  			String setkey = keys.get(0);
  			
  			List<Future<Boolean>> saddResponses = new ArrayList<Future<Boolean>>();
  			for(int i=0;i<SMALL_CNT; i++)
  				saddResponses.add (provider.sadd(setkey, stringList.get(i)));
  			
  			Future<List<byte[]>>  smembersResponse = provider.smembers(setkey);
  			Future<byte[]>	      srandmemberResponse = provider.srandmember(setkey);
  			
  			try {
  				
  				for(Future<Boolean> resp : saddResponses)
  					assertTrue (resp.get().booleanValue(), "sadd of random element should have been true");
  				
  				List<String>  members = toStr(smembersResponse.get());
  				assertEquals(members.size(), SMALL_CNT, "set members count should be SMALL_CNT");
  				
  				String randmember = toStr(srandmemberResponse.get());
  				
  				boolean found = false;
  				for(String m : members) {
  					if(m.equals(randmember)) {
  						found = true;
  						break;
  					}
  				}
  				assertTrue(found, "random member should have been part of the members list");
  
  				// edge cases
  				
  				// empty set
  				String emptyset = "empty-set";
  				provider.sadd(emptyset, "delete-me");
  				provider.srem(emptyset, "delete-me");
  				assertEquals(provider.smembers(emptyset).get(), Collections.EMPTY_LIST, "smembers should return an empty list");
  				assertEquals(provider.srandmember(emptyset).get(), null, "random member of empty set should be null");
  				
  				// non existent key
  				String nonsuch = "no-such-key";
  				assertEquals(provider.smembers(nonsuch).get(), Collections.EMPTY_LIST, "members of non existent key set should be empty");
  				assertEquals (provider.srandmember(nonsuch).get(), null, "random member of non existent key set should be null");
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  
  
  	@Test
  	public void testSmoveStringByteArray() throws InterruptedException{
  		cmd = Command.SMOVE.code + " byte[]";
  		Log.log("TEST: %s command", cmd);
  
  		provider.flushdb();
  		
  		String srckey = keys.get(0);
  		String destkey = keys.get(1);
  		
  		List<Future<Boolean>> saddResponses = new ArrayList<Future<Boolean>>();
  		for(int i=0;i<MEDIUM_CNT; i++)
  			saddResponses.add (provider.sadd(srckey, dataList.get(i)));
  		
  		List<Future<Boolean>> smoveResponses = new ArrayList<Future<Boolean>>();
  		for(int i=0;i<MEDIUM_CNT; i++)
  			smoveResponses.add (provider.smove(srckey, destkey, dataList.get(i)));
  		
  		try {
  			try {
  				for(Future<Boolean> resp : saddResponses)
  					assertTrue (resp.get().booleanValue(), "sadd of random element should have been true");
  				
  				for(Future<Boolean> resp : smoveResponses)
  					assertTrue (resp.get().booleanValue(), "smove of element should have been true");
  				
  				for(int i=0;i<MEDIUM_CNT; i++){
  					assertTrue  (provider.sismember(destkey, dataList.get(i)).get(), "@ [" + i +"] should be a member of the dest after move");
  					assertFalse (provider.sismember(srckey, dataList.get(i)).get(), "@ [" + i +"] should NOT be a member of the src after move");
  				}
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  
  	@Test
  	public void testScard() throws InterruptedException {
  		cmd = Command.SCARD.code + " Java Object";
  		Log.log("TEST: %s command", cmd);
  
  		provider.flushdb();
  		
  		String setkey = keys.get(0);
  	
  		List<Future<Boolean>> saddResponses = new ArrayList<Future<Boolean>>();
  		for(int i=0;i<MEDIUM_CNT; i++)
  			saddResponses.add (provider.sadd(setkey, objectList.get(i)));
  		
  		Future<Long> scardResp = provider.scard(setkey);
  		
  		try {
  			provider.flushdb();
  			try {
  				for(Future<Boolean> resp : saddResponses)
  					assertTrue (resp.get().booleanValue(), "sadd of random object should have been true");
  				
  				assertEquals (scardResp.get().longValue(), MEDIUM_CNT, "scard value should have been MEDIUM_CNT");
  				
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  
  	@Test
  	public void testZcard() throws InterruptedException {
  		cmd = Command.ZCARD.code + " Java Object";
  		Log.log("TEST: %s command", cmd);
  
  		provider.flushdb();
  		
  		String setkey = keys.get(0);
  	
  		List<Future<Boolean>> zaddResponses = new ArrayList<Future<Boolean>>();
  		for(int i=0;i<MEDIUM_CNT; i++)
  			zaddResponses.add (provider.zadd(setkey, i, objectList.get(i)));
  		
  		Future<Long> zcardResp = provider.zcard(setkey);
  		
  		try {
  			provider.flushdb();
  			try {
  				for(Future<Boolean> resp : zaddResponses)
  					assertTrue (resp.get().booleanValue(), "zadd of random object should have been true");
  				
  				assertEquals (zcardResp.get().longValue(), MEDIUM_CNT, "zcard value should have been MEDIUM_CNT");
  				
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  
  	@Test
  	public void testSismemberStringByteArray() throws InterruptedException {
  		cmd = Command.SISMEMBER.code + " byte[]";
  		Log.log("TEST: %s command", cmd);
  
  		String setkey = keys.get(0);
  		
  		provider.flushdb();
  		
  		List<Future<Boolean>> saddResponses = new ArrayList<Future<Boolean>>();
  		for(int i=0;i<SMALL_CNT; i++)
  			saddResponses.add (provider.sadd(setkey, dataList.get(i)));
  		
  		List<Future<Boolean>> sismemberResponses = new ArrayList<Future<Boolean>>();
  		for(int i=0;i<SMALL_CNT; i++)
  			saddResponses.add (provider.sismember(setkey, dataList.get(i)));
  		
  		
  		try {
  			try {
  				for(Future<Boolean> resp : saddResponses)
  					assertTrue (resp.get().booleanValue(), "sadd of random element should have been true");
  				
  				for(Future<Boolean> resp : sismemberResponses)
  					assertTrue (resp.get().booleanValue(), "set membership test should have been true");
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	
  	@Test
  	public void testSmembers() throws InterruptedException{
  		cmd = Command.SMEMBERS.code + " byte[] | " + Command.SADD + "| " + Command.SCARD;
  		Log.log("TEST: %s command", cmd);
  
  		try {
  			provider.flushdb();
  			
  			String setkey = keys.get(0);
  			List<Future<Boolean>> saddResponses = new ArrayList<Future<Boolean>>();
  			for(int i=0;i<SMALL_CNT; i++)
  				saddResponses.add (provider.sadd(setkey, dataList.get(i)));
  			
  			Future<Long> scardResp = provider.scard(setkey);
  			Future<List<byte[]>> smembersResp = provider.smembers(setkey);			
  
  			String setkey2 = keys.get(2);
  			provider.sadd(setkey2, dataList.get(0));
  			provider.srem(setkey2, dataList.get(0));
  			Future<Long> scardResp2 = provider.scard(setkey2);
  			Future<List<byte[]>> smembersResp2 = provider.smembers(setkey2);			
  			
  			
  			try {
  				for(Future<Boolean> resp : saddResponses)
  					assertTrue (resp.get().booleanValue(), "sadd of random element should have been true");
  				
  				List<byte[]> members = smembersResp.get();
  				assertTrue(members.size() == SMALL_CNT, "smembers should have returned a list of SMALL_CNT size");
  				assertTrue(members.size() == scardResp.get().longValue(), "smembers should have returned a list of scard size");
  				
  				List<byte[]> members2 = smembersResp2.get();
  				assertEquals(scardResp2.get().longValue(), 0, "setkey2 should be zero");
  				assertEquals(members2, Collections.EMPTY_LIST, "smembers should have returned empty");
  //				assertTrue(members2.size() == scardResp2.get().longValue(), "smembers should have returned a list of scard size");
  				
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	@Test
  	public void testZremStringByteArray() throws InterruptedException{
  		cmd = Command.ZADD.code + " byte[] | " + Command.ZREM.code + " byte[]";
  		Log.log("TEST: %s command", cmd);
  
  		try {
  			provider.flushdb();
  			String setkey = keys.get(0);
  			List<Future<Boolean>> expectedOKResponses = new ArrayList<Future<Boolean>>();
  			for(int i=0;i<SMALL_CNT; i++)
  				expectedOKResponses.add (provider.zadd(setkey, i, dataList.get(i)));
  			
  			List<Future<Boolean>> expectedOKRemResponses = new ArrayList<Future<Boolean>>();
  			for(int i=0;i<SMALL_CNT; i++)
  				expectedOKRemResponses.add (provider.zrem(setkey, dataList.get(i)));
  			
  
  			try {
  				for(Future<Boolean> resp : expectedOKResponses)
  					assertTrue (resp.get().booleanValue(), "zadd of random element should have been true");
  				
  				for(Future<Boolean> resp : expectedOKRemResponses)
  					assertTrue (resp.get().booleanValue(), "zadd of existing element should have been false");
  				
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	@Test
  	public void testZaddStringByteArray() throws InterruptedException{
  		cmd = Command.ZADD.code + " byte[]";
  		Log.log("TEST: %s command", cmd);
  
  		try {
  			provider.flushdb();
  			String setkey = keys.get(0);
  			List<Future<Boolean>> expectedOKResponses = new ArrayList<Future<Boolean>>();
  			for(int i=0;i<SMALL_CNT; i++)
  				expectedOKResponses.add (provider.zadd(setkey, random.nextDouble(), dataList.get(i)));
  			
  			List<Future<Boolean>> expectedErrorResponses = new ArrayList<Future<Boolean>>();
  			for(int i=0;i<SMALL_CNT; i++)
  				expectedErrorResponses.add (provider.zadd(setkey, random.nextDouble(), dataList.get(i)));
  			
  
  			try {
  				for(Future<Boolean> resp : expectedOKResponses)
  					assertTrue (resp.get().booleanValue(), "zadd of random element should have been true");
  				
  				for(Future<Boolean> resp : expectedErrorResponses)
  					assertFalse (resp.get().booleanValue(), "zadd of existing element should have been false");
  				
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	@Test
  	public void testZscoreAndZincrbyStringByteArray() throws InterruptedException{
  		cmd = Command.ZSCORE.code + " byte[] | " + Command.ZINCRBY.code + " byte[]";
  		Log.log("TEST: %s command", cmd);
  
  		try {
  			provider.flushdb();
  			String setkey = keys.get(0);
  			List<Future<Boolean>> expectedOKResponses = new ArrayList<Future<Boolean>>();
  			for(int i=0;i<SMALL_CNT; i++)
  				expectedOKResponses.add (provider.zadd(setkey, doubleList.get(i), dataList.get(i)));
  			
  			List<Future<Double>> scores = new ArrayList<Future<Double>>();
  			for(int i=0;i<SMALL_CNT; i++)
  				scores.add (provider.zscore(setkey, dataList.get(i)));
  			
  			double increment = 0.05;
  			List<Future<Double>> incrementedScores = new ArrayList<Future<Double>>();
  			for(int i=0;i<SMALL_CNT; i++)
  				incrementedScores.add (provider.zincrby(setkey, increment, dataList.get(i)));
  			
  			Future<Double>  noneSuchKeyScore = provider.zscore(setkey, "no such member");
  			try {
  				for(Future<Boolean> resp : expectedOKResponses)
  					assertTrue (resp.get().booleanValue(), "zadd of random element should have been true");
  				
  				int i=0;
  				for(Future<Double> score : scores){
  					assertEquals (score.get(), doubleList.get(i), "zscore of element should have been " + doubleList.get(i));
  					i++;
  				}	
  				
  				i=0;
  				for(Future<Double> score : incrementedScores){
  					assertEquals (score.get(), doubleList.get(i) + increment, "zincr of element should be " + doubleList.get(i) + increment);
  					i++;
  				}	
  				
  				assertNull(noneSuchKeyScore.get(), "zscore of none existent member of sorted set should be null");
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	
  	@Test
  	public void testZrangebyscoreStringByteArray() throws InterruptedException{
  		cmd = Command.ZRANGEBYSCORE.code + " byte[] | " + Command.ZSCORE.code + " byte[]";
  		Log.log("TEST: %s command", cmd);
  
  		try {
  			provider.flushdb();
  			String setkey = keys.get(0);
  			List<Future<Boolean>> expectedOKResponses = new ArrayList<Future<Boolean>>();
  			for(int i=0;i<MEDIUM_CNT; i++)
  				expectedOKResponses.add (provider.zadd(setkey, i, dataList.get(i)));
  			
  			Future<List<byte[]>> frRange = provider.zrangebyscore(setkey, 0, SMALL_CNT); 
  			try {
  				for(Future<Boolean> resp : expectedOKResponses)
  					assertTrue (resp.get().booleanValue(), "zadd of random element should have been true");
  				
  				List<byte[]> range = frRange.get();
  				assertTrue(range.size() > 0, "should have non empty results for range by score here");
  				
  				for(int i=0;i<SMALL_CNT-1; i++){
  					assertEquals(range.get(i), dataList.get(i), "expected value in the range by score missing");
  				}
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  
  	@Test
  	public void testZremrangebyscoreStringByteArray() throws InterruptedException{
  		cmd = Command.ZREMRANGEBYSCORE.code + " byte[] | " + Command.ZSCORE.code + " byte[]";
  		Log.log("TEST: %s command", cmd);
  
  		try {
  			provider.flushdb();
  			String setkey = keys.get(0);
  			List<Future<Boolean>> expectedOKResponses = new ArrayList<Future<Boolean>>();
  			for(int i=0;i<MEDIUM_CNT; i++)
  				expectedOKResponses.add (provider.zadd(setkey, i, dataList.get(i)));
  			
  			Future<Long> frRemCnt = provider.zremrangebyscore(setkey, 0, SMALL_CNT); 
  			try {
  				for(Future<Boolean> resp : expectedOKResponses)
  					assertTrue (resp.get().booleanValue(), "zadd of random element should have been true");
  				
  				long remCnt = frRemCnt.get().longValue();
  				assertTrue(remCnt > 0, "should have non-zero number of rem cnt for zremrangebyscore");
  				assertEquals(remCnt, SMALL_CNT+1, "should have specific number of rem cnt for zremrangebyscore");
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  
  	@Test
  	public void testZcountStringByteArray() throws InterruptedException{
  		cmd = Command.ZCOUNT.code + " byte[] | " + Command.ZADD.code + " byte[]";
  		Log.log("TEST: %s command", cmd);
  
  		try {
  			provider.flushdb();
  			String setkey = keys.get(0);
  			List<Future<Boolean>> expectedOKResponses = new ArrayList<Future<Boolean>>();
  			for(int i=0;i<MEDIUM_CNT; i++)
  				expectedOKResponses.add (provider.zadd(setkey, i, dataList.get(i)));
  			
  			Future<Long> frCount = provider.zcount(setkey, 0, SMALL_CNT); 
  			try {
  				for(Future<Boolean> resp : expectedOKResponses)
  					assertTrue (resp.get().booleanValue(), "zadd of random element should have been true");
  				
  				long remCnt = frCount.get().longValue();
  				assertTrue(remCnt > 0, "should have non-zero number of rem cnt for zremrangebyscore");
  				assertEquals(remCnt, SMALL_CNT+1, "should have specific number for zcount");
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	
  	@Test
  	public void testZremrangebyrankStringByteArray() throws InterruptedException{
  		cmd = Command.ZREMRANGEBYRANK.code + " byte[] | " + Command.ZSCORE.code + " byte[]";
  		Log.log("TEST: %s command", cmd);
  
  		try {
  			provider.flushdb();
  			String setkey = keys.get(0);
  			List<Future<Boolean>> expectedOKResponses = new ArrayList<Future<Boolean>>();
  			for(int i=0;i<MEDIUM_CNT; i++)
  				expectedOKResponses.add (provider.zadd(setkey, i, dataList.get(i)));
  			
  			Future<Long> frRemCnt = provider.zremrangebyrank(setkey, 0, SMALL_CNT); 
  			try {
  				for(Future<Boolean> resp : expectedOKResponses)
  					assertTrue (resp.get().booleanValue(), "zadd of random element should have been true");
  				
  				long remCnt = frRemCnt.get().longValue();
  				assertTrue(remCnt > 0, "should have non-zero number of rem cnt for zremrangebyrank");
  				assertEquals(remCnt, SMALL_CNT+1, "should have specific number of rem cnt for zremrangebyrank");
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	
  	@Test
  	public void testZrankStringByteArray() throws InterruptedException{
  		cmd = Command.ZRANK.code;
  		Log.log("TEST: %s command", cmd);
  
  		try {
  			provider.flushdb();
  			String setkey = keys.get(0);
  			
  			List<Future<Boolean>> expectedOKResponses = new ArrayList<Future<Boolean>>();
  			for(int i=0;i<SMALL_CNT; i++)
  				expectedOKResponses.add (provider.zadd(setkey, i, dataList.get(i)));
  			
  			List<Future<Long>> rankingResps = new ArrayList<Future<Long>>();
  			for(int i=0;i<SMALL_CNT; i++)
  				rankingResps.add(provider.zrank(setkey, dataList.get(i)));
  
  			Future<Long>  frRankForMissingElement = provider.zrank(setkey, dataList.get(SMALL_CNT+1));
  			Future<Long>  frRankForNoSuchSet = provider.zrank("no-such-set", dataList.get(0));
  			try {
  				for(Future<Boolean> resp : expectedOKResponses)
  					assertTrue (resp.get().booleanValue(), "zadd of random element should have been true");
  				
  				int i=0;
  				for(Future<Long> resp : rankingResps)
  					assertEquals (resp.get().longValue(), i++, "zrank of element");
  				
  				assertEquals (frRankForMissingElement.get().longValue(), -1, "zrank against non-existent member should be -1");
  				assertEquals (frRankForNoSuchSet.get().longValue(), -1, "zrank against non-existent key should be -1");
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	
  	
  	@Test
  	public void testZrevrankStringByteArray() throws InterruptedException{
  		cmd = Command.ZREVRANK.code;
  		Log.log("TEST: %s command", cmd);
  
  		try {
  			provider.flushdb();
  			String setkey = keys.get(0);
  			
  			List<Future<Boolean>> expectedOKResponses = new ArrayList<Future<Boolean>>();
  			for(int i=0;i<=SMALL_CNT; i++)
  				expectedOKResponses.add (provider.zadd(setkey, i, dataList.get(i)));
  			
  			List<Future<Long>> rankingResps = new ArrayList<Future<Long>>();
  			for(int i=0;i<=SMALL_CNT; i++)
  				rankingResps.add(provider.zrevrank(setkey, dataList.get(i)));
  
  			Future<Long>  frRankForMissingElement = provider.zrevrank(setkey, dataList.get(SMALL_CNT+1));
  			Future<Long>  frRankForNoSuchSet = provider.zrevrank("no-such-set", dataList.get(0));
  			try {
  				for(Future<Boolean> resp : expectedOKResponses)
  					assertTrue (resp.get().booleanValue(), "zadd of random element should have been true");
  				
  				int i=0;
  				for(Future<Long> resp : rankingResps)
  					assertEquals (resp.get().longValue(), SMALL_CNT - i++, "zrevrank of element");
  				
  				assertEquals (frRankForMissingElement.get().longValue(), -1, "zrevrank against non-existent member should be -1");
  				assertEquals (frRankForNoSuchSet.get().longValue(), -1, "zrevrank against non-existent key should be -1");
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	@Test
  	public void testZrangeWithscoresStringByteArray() throws InterruptedException{
  		cmd = Command.ZRANGE$OPTS.code + " byte[] | " + Command.ZSCORE.code + " byte[]";
  		Log.log("TEST: %s command", cmd);
  
  		try {
  			provider.flushdb();
  			String setkey = keys.get(0);
  			List<Future<Boolean>> expectedOKResponses = new ArrayList<Future<Boolean>>();
  			for(int i=0;i<MEDIUM_CNT; i++)
  				expectedOKResponses.add (provider.zadd(setkey, i, dataList.get(i)));
  			
  			Future<List<byte[]>> frZValues = provider.zrange(setkey, 0, SMALL_CNT); 
  			Future<List<ZSetEntry>> frSubset = provider.zrangeSubset(setkey, 0, SMALL_CNT); 
  			
  			try {
  				for(Future<Boolean> resp : expectedOKResponses)
  					assertTrue (resp.get().booleanValue(), "zadd of random element should have been true");
  				
  				List<byte[]> zvalues = frZValues.get();
  				List<ZSetEntry> zsubset = frSubset.get();
  				for(int i=0;i<SMALL_CNT; i++){
  					assertEquals(zsubset.get(i).getValue(), dataList.get(i), "value of element from zrange_withscore");
  					assertEquals(zsubset.get(i).getValue(), zvalues.get(i), "value of element from zrange_withscore compared with zscore with same range query");
  					assertEquals (zsubset.get(i).getScore(), (double)i, "score of element from zrange_withscore");
  					assertTrue(zsubset.get(i).getScore() <= zsubset.get(i+1).getScore(), "range member score should be smaller or equal to previous range member.  idx: " + i);
  					if(i>0) assertTrue(zsubset.get(i).getScore() >= zsubset.get(i-1).getScore(), "range member score should be bigger or equal to previous range member.  idx: " + i);
  				}
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  
  	@Test
  	public void testZrevrangeWithscoresStringByteArray() throws InterruptedException{
  		cmd = Command.ZREVRANGE$OPTS.code + " byte[] | " + Command.ZSCORE.code + " byte[]";
  		Log.log("TEST: %s command", cmd);
  
  		try {
  			provider.flushdb();
  			String setkey = keys.get(0);
  			List<Future<Boolean>> expectedOKResponses = new ArrayList<Future<Boolean>>();
  			for(int i=0;i<MEDIUM_CNT; i++)
  				expectedOKResponses.add (provider.zadd(setkey, i, dataList.get(i)));
  			
  			Future<List<byte[]>> frZValues = provider.zrevrange(setkey, 0, SMALL_CNT); 
  			Future<List<ZSetEntry>> frSubset = provider.zrevrangeSubset(setkey, 0, SMALL_CNT); 
  			
  			try {
  				for(Future<Boolean> resp : expectedOKResponses)
  					assertTrue (resp.get().booleanValue(), "zadd of random element should have been true");
  				
  				List<byte[]> zvalues = frZValues.get();
  				List<ZSetEntry> zsubset = frSubset.get();
  				for(int i=0;i<SMALL_CNT; i++){
  					assertEquals(zsubset.get(i).getValue(), dataList.get(MEDIUM_CNT-i-1), "value of element from zrange_withscore");
  					assertEquals(zsubset.get(i).getValue(), zvalues.get(i), "value of element from zrange_withscore compared with zscore with same range query");
  					assertEquals (zsubset.get(i).getScore(), (double)MEDIUM_CNT-i-1, "score of element from zrange_withscore");
  					assertTrue(zsubset.get(i).getScore() >= zsubset.get(i+1).getScore(), "range member score should be smaller or equal to previous range member.  idx: " + i);
  					if(i>0) assertTrue(zsubset.get(i).getScore() <= zsubset.get(i-1).getScore(), "range member score should be bigger or equal to previous range member.  idx: " + i);
  				}
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	
  	@Test
  	public void testSaddStringByteArray() throws InterruptedException{
  		cmd = Command.SADD.code + " byte[]";
  		Log.log("TEST: %s command", cmd);
  
  		try {
  			provider.flushdb();
  			String setkey = keys.get(0);
  			List<Future<Boolean>> expectedOKResponses = new ArrayList<Future<Boolean>>();
  			for(int i=0;i<SMALL_CNT; i++)
  				expectedOKResponses.add (provider.sadd(setkey, dataList.get(i)));
  			
  			List<Future<Boolean>> expectedErrorResponses = new ArrayList<Future<Boolean>>();
  			for(int i=0;i<SMALL_CNT; i++)
  				expectedErrorResponses.add (provider.sadd(setkey, dataList.get(i)));
  			
  
  			try {
  				for(Future<Boolean> resp : expectedOKResponses)
  					assertTrue (resp.get().booleanValue(), "sadd of random element should have been true");
  				
  				for(Future<Boolean> resp : expectedErrorResponses)
  					assertFalse (resp.get().booleanValue(), "sadd of existing element should have been false");
  				
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	
  	@Test
  	public void testSort() throws InterruptedException{
  		cmd = Command.SORT.code;
  		Log.log("TEST: %s command", cmd);
  
  		final String setkey = "set-key";
  		final String listkey = "list-key";
  		try {
  			provider.flushdb();
  			
  			for(int i=0; i<MEDIUM_CNT; i++){
  				provider.sadd(setkey, stringList.get(i));
  				provider.lpush(listkey, stringList.get(i));
  			}
  			
  			int cnt1 = MEDIUM_CNT, cnt2 = 9, cnt3 = 1;
  			Future<List<byte[]>> sortListResp1 = provider.sort(listkey).ALPHA().LIMIT(0, cnt1).DESC().execAsync();
  			Future<List<byte[]>> sortListResp2 = provider.sort(listkey).ALPHA().LIMIT(10, cnt2).DESC().execAsync();
  			Future<List<byte[]>> sortListResp3 = provider.sort(listkey).ALPHA().LIMIT(MEDIUM_CNT-1, cnt3).DESC().execAsync();
  			
  			Future<List<byte[]>> sortSetResp = provider.sort(setkey).ALPHA().LIMIT(0, 555).DESC().execAsync();
  			
  			try {
  				assertEquals(sortListResp1.get().size(), cnt1, "expecting sort results of size MEDIUM_CNT");
  				assertEquals(sortListResp2.get().size(), cnt2, "expecting sort results of size 9");
  				assertEquals(sortListResp3.get().size(), cnt3, "expecting sort results of size 1");
  				
  				Log.log("TEST: SORTED LIST ");
  				for(String s : toStr(sortListResp1.get()))
  					System.out.format("[t.1] %s\n", s);
  				
  				Log.log("TEST: SORTED LIST ");
  				for(String s : toStr(sortListResp2.get()))
  					System.out.format("[t.1] %s\n", s);
  				
  				Log.log("TEST: SORTED LIST ");
  				for(String s : toStr(sortListResp3.get()))
  					System.out.format("[t.1] %s\n", s);
  				
  				Log.log("TEST: SORTED SET ");
  				for(String s : toStr(sortSetResp.get()))
  					System.out.format("%s\n", s);
  				
  				String destKey = String.format("%s_store", listkey);
  				List<byte[]> ssres = provider.sort(listkey).ALPHA().LIMIT(0, MEDIUM_CNT).DESC().STORE(destKey).execAsync().get();
  				assertNotNull(ssres, "result of srot with STORE should be non-null");
  				assertEquals(ssres.size(), 1, "result of sort with STORE should be a list of single entry (the stored list's size)");
  				long sortedListSize = Query.Support.unpackValue(ssres);
  				assertEquals(sortedListSize, MEDIUM_CNT);
  				RedisType type = provider.type(destKey).get();
  				assertEquals(type, RedisType.list, "dest key of SORT .. STORE should be a LIST");
  				long sslistSize = provider.llen(destKey).get();
  				assertEquals(sslistSize, sortedListSize, "result of SORT ... STORE and LLEN of destkey list should be same");
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  		
  		// force errors
  		
  		// count can't be zero
  		Runnable invalidLimitSpec = new Runnable() {
  			public void run() {
  				try { provider.sort(listkey).ALPHA().LIMIT(0, 0).DESC().exec(); }
                  catch (Throwable t) { throw new RuntimeException ("", t); }
  			}
  		};
  		assertDidRaiseRuntimeError(invalidLimitSpec, RuntimeException.class);
  		
  		// LIMIT from must be positive, {0...n}
  		Runnable invalidLimitSpec2 = new Runnable() {
  			public void run() {
  				try { provider.sort(listkey).ALPHA().LIMIT(-1, 1).DESC().exec(); }
                  catch (Throwable t) { throw new RuntimeException ("", t); }
  			}
  		};
  		assertDidRaiseRuntimeError(invalidLimitSpec2, RuntimeException.class);
  		
  	}
  	
  	@Test
  	public void testLsetStringIntByteArray() throws InterruptedException {
  		cmd = Command.LSET.code + " byte[] | " + Command.LLEN;
  		Log.log("TEST: %s command", cmd);
  
  		try {
  			provider.flushdb();
  			
  			// prep a MEDIUM list
  			String listkey = keys.get(0);
  			for(int i=0; i<SMALL_CNT; i++)
  				provider.rpush(listkey, dataList.get(i)); // use rpush (append) so ref list sequence order is preserved
  			Future<Long> llenResp = provider.llen(listkey);
  			
  			// now we'll change their values
  			for(int i=0; i<SMALL_CNT; i++)
  				provider.lset(listkey, i, dataList.get(SMALL_CNT+i));
  			Future<List<byte[]>>  lrangeResp1 = provider.lrange(listkey, 0, LARGE_CNT);
  			
  			// now we'll change their values using the negative index mode
  			int lim = SMALL_CNT*-1;
  			for(int i=-1; i>lim; i--)
  				provider.lset(listkey, i, dataList.get(i*-1));
  			Future<List<byte[]>>  lrangeResp2 = provider.lrange(listkey, 0, LARGE_CNT);
  
  			// test edge conditions
  			// out of range
  			Future<ResponseStatus> expectedErrorResp =  provider.lset(listkey, SMALL_CNT, dataList.get(0)); 
  
  			try {
  				assertTrue (llenResp.get().longValue() == SMALL_CNT, "list length should be SMALL_CNT");
  				
  				for(int i=0; i<SMALL_CNT; i++)
  					assertEquals (dataList.get(SMALL_CNT+i), lrangeResp1.get().get(i), "after LSET the expected and range item differ at idx: " + i);
  
  				for(int i=0; i<SMALL_CNT; i++)
  					assertEquals (dataList.get(SMALL_CNT-i), lrangeResp2.get().get(i), "after LSET the expected and range item differ at idx: " + i);
  				
  				boolean expectedError = false;
  				try {
  					Log.log("Expecting an out of range ERROR for LSET here..");
  	                expectedErrorResp.get(); // wait for response
  				}
  	            catch (ExecutionException e) {
  	            	expectedError = true;
  	            	Throwable cause = e.getCause();
  	            	if(cause instanceof RedisException)
  	            		Log.log("%s (as excepted)", cause);
  	            	else
  	            		fail("FAULT: the cause of ExecutionException was expected to be a RedisException");
  	            }
  				assertTrue(expectedError, "was expecting a Expecting an out of range ERROR for LSET here");
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	
  	@Test
  	public void testLremStringByteArrayInt() throws InterruptedException {
  		cmd = Command.LREM.code + " byte[] | " + Command.LLEN;
  		Log.log("TEST: %s command", cmd);
  
  		try {
  			provider.flushdb();
  			
  			// prep a MEDIUM list
  			String listkey = keys.get(0);
  			for(int i=0; i<MEDIUM_CNT; i++)
  				provider.rpush(listkey, dataList.get(i)); // use rpush (append) so ref list sequence order is preserved
  			
  			Future<Long> llenResp = provider.llen(listkey);
  			
  			// everysingle one of these should work and remove exactly 1 item
  			Future<Long> lremResp1 = provider.lrem(listkey, dataList.get(0), 0);
  			Future<Long> lremResp2 = provider.lrem(listkey, dataList.get(1), -1);
  			Future<Long> lremResp3 = provider.lrem(listkey, dataList.get(2), 1);
  			Future<Long> lremResp4 = provider.lrem(listkey, dataList.get(3), 2);
  			Future<Long> lremResp5 = provider.lrem(listkey, dataList.get(4), -2);
  			
  			// everysingle one of these should work and remove NOTHING
  			Future<Long> lremResp6 = provider.lrem(listkey, dataList.get(0), 0);
  			Future<Long> lremResp7 = provider.lrem(listkey, dataList.get(1), -1);
  			Future<Long> lremResp8 = provider.lrem(listkey, dataList.get(2), 1);
  			Future<Long> lremResp9 = provider.lrem(listkey, dataList.get(3), 2);
  			Future<Long> lremResp10 = provider.lrem(listkey, dataList.get(4), -2);
  			
  			try {
  				long listcnt = llenResp.get();
  				assertTrue (listcnt == MEDIUM_CNT, "list length should be MEDIUM_CNT");
  				
  				assertEquals(1, lremResp1.get().longValue());
  				assertEquals(1, lremResp2.get().longValue());
  				assertEquals(1, lremResp3.get().longValue());
  				assertEquals(1, lremResp4.get().longValue());
  				assertEquals(1, lremResp5.get().longValue());
  				assertEquals(0, lremResp6.get().longValue());
  				assertEquals(0, lremResp7.get().longValue());
  				assertEquals(0, lremResp8.get().longValue());
  				assertEquals(0, lremResp9.get().longValue());
  				assertEquals(0, lremResp10.get().longValue());
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	
  	@Test
  	public void testLrange() throws InterruptedException {
  		cmd = Command.LRANGE.code ;
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb();
  			
  			// prep a MEDIUM list
  			String listkey = keys.get(0);
  			for(int i=0; i<MEDIUM_CNT; i++)
  				provider.rpush(listkey, dataList.get(i)); // use rpush (append) so ref list sequence order is preserved
  			
  			Future<Long> llenResp = provider.llen(listkey);
  			Future<List<byte[]>>  lrangeResp = provider.lrange(listkey, 0, MEDIUM_CNT-1);
  
  			try {
  //				provider.ping().get();
  				// sanity check
  				long listcnt = llenResp.get();
  				assertTrue (listcnt == MEDIUM_CNT, "list length should be MEDIUM_CNT");
  				
  				List<byte[]> items = lrangeResp.get();
  				assertEquals (items.size(), MEDIUM_CNT, "list range 0->MEDIUM_CNT length should be MEDIUM_CNT");
  				for(int i=0; i<MEDIUM_CNT; i++)
  					assertEquals(items.get(i), dataList.get(i), 
  							"nth items of range 0->CNT should be the same as nth dataitem, where n is " + i);
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	
  	@Test
  	public void testSubstr() throws InterruptedException {
  		cmd = Command.SUBSTR.code ;
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb();
  			
  			String key = keys.get(0);
  			byte[] value = dataList.get(0);
  			provider.set(key, value);
  			
  			List<Future<byte[]>> frBytesRespList = new ArrayList<Future<byte[]>>();
  			for(int i=0; i<value.length; i++){
  				frBytesRespList.add(provider.substr(key, i, i));
  			}
  			
  			Future<byte[]> frFullValue1 = provider.substr(key, 0, -1);
  			Future<byte[]> frFullValue2 = provider.substr(key, 0, value.length);
  			Future<byte[]> frExpectedNull = provider.substr(key, -1, 0);
  
  			try {
  				assertEquals(frFullValue1.get(), value, "full range substr should be equal to value");
  				assertEquals(frFullValue2.get(), value, "full range substr should be equal to value");
  				assertEquals(frExpectedNull.get(), new byte[0], "substr with -1 from idx should be zero-length array");
  				for(int i=0; i<value.length; i++){
  					assertTrue(frBytesRespList.get(i).get().length == 1, "checking size: using substr to iterate over value bytes @ idx " + i);
  					assertEquals(frBytesRespList.get(i).get()[0], value[i], "checking value: using substr to iterate over value bytes @ idx " + i);
  				}
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	
  	@Test
  	public void testRpop() throws InterruptedException {
  		cmd = Command.RPOP.code ;
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb();
  
  			// prep a small list
  			String listkey = keys.get(0);
  			for(int i=0; i<SMALL_CNT; i++)
  				provider.lpush(listkey, dataList.get(i)); 
  			
  			Future<Long> llenResp = provider.llen(listkey);
  			
  			List<Future<byte[]>>  rpops = new ArrayList<Future<byte[]>>();
  			for(int i=0; i<SMALL_CNT; i++)
  				rpops.add(provider.rpop(listkey)); 
  			
  			try {
  				long listcnt = llenResp.get();
  				// sanity check
  				assertTrue (listcnt == SMALL_CNT, "list length should be SMALL_CNT");
  				
  				for(int i=0; i<SMALL_CNT; i++)
  					assertEquals(rpops.get(i).get(), dataList.get(i), 
  							"nth popped tail should be the same as nth dataitem, where n is " + i);
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	
  	@Test
  	public void testLpop() throws InterruptedException {
  		cmd = Command.LPOP.code ;
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb();
  
  			// prep a small list
  			String listkey = keys.get(0);
  			for(int i=0; i<SMALL_CNT; i++)
  				provider.rpush(listkey, dataList.get(i)); 
  			
  			Future<Long> llenResp = provider.llen(listkey);
  			List<Future<byte[]>>  lpops = new ArrayList<Future<byte[]>>();
  			for(int i=0; i<SMALL_CNT; i++)
  				lpops.add(provider.lpop(listkey)); 
  			
  			try {
  				long listcnt = llenResp.get();
  				// sanity check
  				assertTrue (listcnt == SMALL_CNT, "list length should be SMALL_CNT");
  				
  				for(int i=0; i<SMALL_CNT; i++)
  					assertEquals(lpops.get(i).get(), dataList.get(i), 
  							"nth popped head should be the same as nth dataitem, where n is " + i);
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	
  	@Test
  	public void testLindex() throws InterruptedException {
  		cmd = Command.LINDEX.code;
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb();
  
  			// prep a small list
  			String listkey = keys.get(0);
  			for(int i=0; i<SMALL_CNT; i++)
  				provider.rpush(listkey, dataList.get(i)); 
  			
  			try {
  				for(int i=0; i<SMALL_CNT; i++)
  					assertEquals (provider.lindex(listkey, i).get(), dataList.get(i), "list items should match ref data");
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	
  	@Test
  	public void testLtrim() throws InterruptedException {
  		cmd = Command.LTRIM.code + " | " + Command.LLEN.code + " | " + Command.LRANGE.code ;
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb();
  
  			// prep a small list
  			String listkey = keys.get(0);
  			for(int i=0; i<SMALL_CNT; i++)
  				provider.rpush(listkey, dataList.get(i)); 
  			
  			Future<Long> llenResp = provider.llen(listkey);
  			try {
  				long listcnt = llenResp.get();
  				// sanity check
  				assertTrue (listcnt == SMALL_CNT, "list length should be SMALL_CNT");
  				
  				provider.ltrim(listkey, 0,listcnt-1);	// trim nothing
  				assertTrue (provider.llen(listkey).get() == listcnt, "trim from end to end - no delta expected");
  				
  				provider.ltrim(listkey, 1, listcnt-1); 	// remove the head
  				assertTrue (provider.llen(listkey).get() == listcnt-1, "trim head - len should be --1 expected");
  				
  				listcnt = provider.llen(listkey).get();
  				assertEquals(listcnt, SMALL_CNT - 1, "list length should be SMALL_CNT - 1");
  				for(int i=0; i<SMALL_CNT-1; i++)
  					assertEquals(provider.lindex(listkey, i).get(), dataList.get(i+1), "list items should match ref data shifted by 1 after removing head");
  				
  				provider.ltrim(listkey, -2, -1);
  				assertTrue (provider.llen(listkey).get() == 2, "list length should be 2");
  				
  				provider.ltrim(listkey, 0, 0);
  				assertTrue (provider.llen(listkey).get() == 1, "list length should be 1");
  
  				byte[] lastItem = provider.lpop(listkey).get();
  				assertNotNull(lastItem, "last item should not have been null");
  				assertTrue (provider.llen(listkey).get() == 0, "expecting empty list after trims and pop");
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	
  	@Test
  	public void testLpushStringByteArray() throws InterruptedException {
  		cmd = Command.LPUSH.code + " byte[] | " + Command.LLEN + " | " + Command.LRANGE;
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb();
  			boolean expected = false;
  			try {
  				byte[] nil = null;
  				provider.lpush("foo", nil);
  			}
  			catch(IllegalArgumentException e) { expected = true; }
  			assertTrue(expected, "expecting exception for null value to RPUSH");
  			
  			String listkey = this.keys.get(0);
  			for(int i=0; i<SMALL_CNT; i++){
  				provider.lpush(listkey, dataList.get(i));
  			}
  			Future<Long> llenResp = provider.llen(listkey);
  			Future<List<byte[]>> lrangeResp = provider.lrange(listkey, 0, SMALL_CNT);
  
  			try {
  				// use LLEN: size should be small count
  				assertTrue(llenResp.get()==SMALL_CNT, "LLEN after LPUSH is wrong");
  				
  				// use LRANGE 0 cnt: equal size and data should be same in order
  				List<byte[]> range = lrangeResp.get();
  				assertTrue(range.size()==SMALL_CNT, "range size after LPUSH is wrong");
  				for(int i=0; i<SMALL_CNT; i++){
  					int r = SMALL_CNT - i - 1;
  					assertEquals (dataList.get(i), range.get(r), "range and reference list differ at i: " + i);
  				}
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	@Test
  	public void testLpoppushStringByteArray() throws InterruptedException {
  		cmd = Command.RPOPLPUSH.code;
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb();
  
  			String listkey = this.keys.get(0);
  			for(int i=0; i<SMALL_CNT; i++){
  				provider.lpush(listkey, dataList.get(i));
  			}
  			
  			Future<Long> llenRespBefore = provider.llen(listkey);
  			List<Future<byte[]>> poppushResponses = new ArrayList<Future<byte[]>>();
  			
  			for(int i=0; i<SMALL_CNT; i++){
  				poppushResponses.add(provider.rpoplpush (listkey, listkey));
  			}
  			
  			Future<Long> llenRespAfter = provider.llen(listkey);
  			try {
  				// use LLEN: size should be small count
  				assertTrue(llenRespBefore.get()==SMALL_CNT, "LLEN after LPUSH is wrong");
  				
  				for(int i=0; i<SMALL_CNT; i++){
  					assertEquals (poppushResponses.get(i).get(), dataList.get(i), "RPOPLPUSH result and reference list differ at i: " + i);
  				}
  				
  				// use LLEN: size should be small count
  				assertTrue(llenRespAfter.get()==SMALL_CNT, "LLEN after RPOPLPUSH is wrong");
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  
  	@Test
  	public void testRpushStringByteArray() throws InterruptedException {
  		cmd = Command.RPUSH.code + " byte[] | " + Command.LLEN + " | " + Command.LRANGE;
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb();
  
  			boolean expected = false;
  			try {
  				byte[] nil = null;
  				provider.rpush("foo", nil);
  			}
  			catch(IllegalArgumentException e) { expected = true; }
  			assertTrue(expected, "expecting exception for null value to RPUSH");
  			
  			String listkey = this.keys.get(0);
  			for(int i=0; i<SMALL_CNT; i++){
  				provider.rpush(listkey, dataList.get(i));
  			}
  			
  			Future<Long> llenResp = provider.llen(listkey);
  			Future<List<byte[]>> lrangeResp = provider.lrange(listkey, 0, SMALL_CNT);
  			
  			try {
  				// use LLEN: size should be small count
  				assertTrue(llenResp.get()==SMALL_CNT, "LLEN after RPUSH is wrong");
  				
  				// use LRANGE 0 cnt: equal size and data should be same in order
  				List<byte[]> range = lrangeResp.get();
  				assertTrue(range.size()==SMALL_CNT, "range size after RPUSH is wrong");
  				for(int i=0; i<SMALL_CNT; i++){
  					assertEquals (dataList.get(i), range.get(i), "range and reference list differ at i: " + i);
  				}
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	@Test
  	public void testMget() throws InterruptedException {
  		cmd = Command.MGET.code ;
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb();
  			for(int i=0; i<SMALL_CNT; i++){
  				provider.set (keys.get(i), dataList.get(i));
  			}
  			Future<List<byte[]>> mgetResp1 = provider.mget(keys.get(0));
  			Future<List<byte[]>> mgetResp2 = provider.mget(keys.get(0), keys.get(1)); 
  			Future<List<byte[]>> mgetResp3 = provider.mget(keys.get(0), keys.get(1), keys.get(2));
  			Future<List<byte[]>> mgetResp4 = provider.mget("foo", "bar", "paz"); 
  			
  			try {
  				List<byte[]>  values = null;
  				
  				values = mgetResp1.get();
  				assertEquals(values.size(), 1, "one value expected");
  				for(int i=0; i<1; i++)
  					assertEquals(values.get(i), dataList.get(i));
  				
  				values = mgetResp2.get();
  				assertEquals(values.size(), 2, "2 values expected");
  				for(int i=0; i<2; i++)
  					assertEquals(values.get(i), dataList.get(i));
  				
  				values = mgetResp3.get();
  				assertEquals(values.size(), 3, "3 values expected");
  				for(int i=0; i<3; i++)
  					assertEquals(values.get(i), dataList.get(i));
  				
  				values = mgetResp4.get();
  				assertEquals(values.size(), 3, "3 values expected");
  				for(int i=0; i<3; i++)
  					assertEquals(values.get(i), null, "nonexistent key value in list should be null");
  				
  				// edge cases
  				// all should through exceptions
  				boolean didRaiseEx;
  				didRaiseEx = false;
  				try {
  					String[] keys = null;
  					provider.mget(keys).get();
  				}
  				catch (IllegalArgumentException e) {didRaiseEx = true;}
  				catch (Throwable whatsthis) { fail ("unexpected exception raised", whatsthis);}
  				if(!didRaiseEx){ fail ("Expected exception not raised."); }
  
  				didRaiseEx = false;
  				try {
  					String[] keys = new String[0];
  					provider.mget(keys).get();
  				}
  				catch (IllegalArgumentException e) {didRaiseEx = true;}
  				catch (Throwable whatsthis) { fail ("unexpected exception raised", whatsthis);}
  				if(!didRaiseEx){ fail ("Expected exception not raised."); }
  
  				didRaiseEx = false;
  				try {
  					String[] keys = new String[3];
  					keys[0] = stringList.get(0);
  					keys[1] = null;
  					keys[2] = stringList.get(2);
  					provider.mget(keys).get();
  				}
  				catch (IllegalArgumentException e) {didRaiseEx = true;}
  				catch (Throwable whatsthis) { fail ("unexpected exception raised", whatsthis);}
  				if(!didRaiseEx){ fail ("Expected exception not raised."); }
  				
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  
  	@Test
  	public void testDel() throws InterruptedException {
  		cmd = Command.DEL.code;
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb();
  			
  			String key = this.keys.get(0);
  			provider.set (key, dataList.get(0));
  			Future<Boolean> existsResp1 = provider.exists(key); 
  			Future<Long> delResp = provider.del(key);
  			Future<Boolean> existsResp2 = provider.exists(key); 
  
  			try {
  				assertEquals(delResp.get().longValue(), 1, "one key should been deleted");
  				assertTrue (existsResp1.get(), "After set key should exist");
  				assertFalse (existsResp2.get(), "After del key should not exist");
  				
  				// delete many keys
  				provider.flushdb();
  				for(int i=0; i<SMALL_CNT; i++) provider.set(stringList.get(i), dataList.get(i));
  
  				String[] keysToDel = new String[SMALL_CNT];
  				for(int i=0; i<SMALL_CNT; i++) keysToDel[i] = stringList.get(i);
  
  				Future<Long> delCnt1 = provider.del(keysToDel);
  				for(int i=0; i<SMALL_CNT; i++) assertFalse (provider.exists(stringList.get(i)).get(), "key should have been deleted");
  				assertEquals(delCnt1.get().longValue(), SMALL_CNT, "SMALL_CNT keys were deleted");
  				
  				// delete many keys but also spec one non existent keys - delete result should be less than key cnt
  				provider.flushdb();
  				for(int i=0; i<SMALL_CNT-1; i++) provider.set(stringList.get(i), dataList.get(i));
  
  				keysToDel = new String[SMALL_CNT];
  				for(int i=0; i<SMALL_CNT; i++) keysToDel[i] = stringList.get(i);
  
  				Future<Long> delCnt2 = provider.del(keysToDel);
  				for(int i=0; i<SMALL_CNT; i++) assertFalse (provider.exists(stringList.get(i)).get(), "key should have been deleted");
  				assertEquals(delCnt2.get().longValue(), SMALL_CNT-1, "SMALL_CNT-1 keys were actually deleted");
  
  				// edge cases
  				// all should through exceptions
  				boolean didRaiseEx;
  				didRaiseEx = false;
  				try {
  					String[] keys = null;
  					provider.del(keys).get();
  				}
  				catch (IllegalArgumentException e) {didRaiseEx = true;}
  				catch (Throwable whatsthis) { fail ("unexpected exception raised", whatsthis);}
  				if(!didRaiseEx){ fail ("Expected exception not raised."); }
  
  				didRaiseEx = false;
  				try {
  					String[] keys = new String[0];
  					provider.del(keys).get();
  				}
  				catch (IllegalArgumentException e) {didRaiseEx = true;}
  				catch (Throwable whatsthis) { fail ("unexpected exception raised", whatsthis);}
  				if(!didRaiseEx){ fail ("Expected exception not raised."); }
  
  				didRaiseEx = false;
  				try {
  					String[] keys = new String[3];
  					keys[0] = stringList.get(0);
  					keys[1] = null;
  					keys[2] = stringList.get(2);
  					provider.del(keys).get();
  				}
  				catch (IllegalArgumentException e) {didRaiseEx = true;}
  				catch (Throwable whatsthis) { fail ("unexpected exception raised", whatsthis);}
  				if(!didRaiseEx){ fail ("Expected exception not raised."); }
  
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	
  	@Test
  	public void testIncrAndDecr() throws InterruptedException {
  		cmd = Command.INCR.code + " | " + Command.DECR.code;
  		Log.log("TEST: %s command", cmd);
  		try {
  			String cntr_key = keys.get(0);
  
  			provider.flushdb();
  
  			Future<Long> incrResp = null;
  			for(int i = 0; i<MEDIUM_CNT; i++){
  				incrResp = provider.incr(cntr_key);
  			}
  			Future<Long> decrResp = null;
  			for(int i = 0; i<MEDIUM_CNT; i++){
  				decrResp = provider.decr(cntr_key);
  			}
  			
  			try {
  				assertEquals(incrResp.get().longValue(), MEDIUM_CNT, "INCR should have counted counter to MEDIUM_CNT");
  				assertEquals(decrResp.get().longValue(), 0, "DECR should have counted counter to zero");
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  
  	@Test
  	public void testIncrbyAndDecrby() throws InterruptedException {
  		cmd = Command.INCRBY.code + " |" + Command.DECRBY.code;
  		Log.log("TEST: %s command", cmd);
  		try {
  			String cntr_key = keys.get(0);
  
  			provider.flushdb();
  
  			Future<Long> incrbyResp = null;
  			for(int i = 0; i<MEDIUM_CNT; i++){
  				incrbyResp = provider.incrby(cntr_key, 10);
  			}
  			Future<Long> decrbyResp = null;
  			for(int i = 0; i<MEDIUM_CNT; i++){
  				decrbyResp = provider.decrby(cntr_key, 10);
  			}
  			
  			try {
  				assertEquals(incrbyResp.get().longValue(), MEDIUM_CNT*10, "INCRBY should have counted counter to MEDIUM_CNT*10");
  				assertEquals(decrbyResp.get().longValue(), 0, "DECRBY should have counted counter to zero");
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  
  	@Test
  	public void testGetSetStringByteArray() throws InterruptedException {
  		cmd = Command.SET.code + " | " + Command.GETSET.code + " byte[] ";
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb();
  			provider.set(keys.get(0), dataList.get(0));
  			Future<byte[]> getResp = provider.get(keys.get(0));
  			Future<byte[]> getsetResp1 = provider.getset(keys.get(0), dataList.get(1));
  			Future<byte[]> getsetResp2 = provider.getset(keys.get(1), dataList.get(2));
  			
  			try {
  				assertEquals(getResp.get(), dataList.get(0), "get results doesn't match the expected data");
  				assertEquals(getsetResp1.get(), dataList.get(0), "getset results doesn't match the expected data");
  				assertEquals(getsetResp2.get(), null, "getset result for new key should have been null");
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  
  	@Test
  	public void testHsetHget() throws InterruptedException {
  		cmd = Command.HSET.code + " | " + Command.HGET + " | " + Command.HEXISTS;
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb();
  			Future<Boolean> hsetResp1 = provider.hset(keys.get(0), keys.get(1), dataList.get(0));
  			Future<Boolean> hexistsResp1 = provider.hexists(keys.get(0), keys.get(1));
  			Future<Boolean> hexistsResp2 = provider.hexists(keys.get(0), keys.get(2));
  			Future<Boolean> hsetResp1_1 = provider.hset(keys.get(0), keys.get(1), dataList.get(0));
  			Future<Boolean> hsetResp2 = provider.hset(keys.get(0), keys.get(2), stringList.get(0));
  			Future<Boolean> hsetResp3 = provider.hset(keys.get(0), keys.get(3), 222);
  			objectList.get(0).setName("Hash Stash");
  			Future<Boolean> hsetResp4 = provider.hset(keys.get(0), keys.get(4), objectList.get(0));
  			
  			Future<byte[]> hgetResp1 = provider.hget(keys.get(0), keys.get(1));
  			Future<byte[]> hgetResp2 = provider.hget(keys.get(0), keys.get(2));
  			Future<byte[]> hgetResp3 = provider.hget(keys.get(0), keys.get(3));
  			Future<byte[]> hgetResp4 = provider.hget(keys.get(0), keys.get(4));
  			
  			Future<Long> hlenResp1 = provider.hlen(keys.get(0));
  
  			Future<Boolean> hdelResp1 = provider.hdel(keys.get(0), keys.get(1));
  			Future<Boolean> hdelResp2 = provider.hdel(keys.get(0), keys.get(1));
  			
  			Future<Long> hlenResp2 = provider.hlen(keys.get(0));
  			Future<Long> hlenResp3 = provider.hlen("some-random-key");
  			
  			
  			try {
  				assertTrue (hsetResp1.get(), "hset using byte[] value");
  				assertTrue (hexistsResp1.get(), "hexists of field should be true");
  				assertTrue (!hexistsResp2.get(), "hexists of non existent field should be false");
  				assertTrue (!hsetResp1_1.get(), "second hset using byte[] value should return false");
  				assertTrue (hsetResp2.get(), "hset using String value");
  				assertTrue (hsetResp3.get(), "hset using Number value");
  				assertTrue (hsetResp4.get(), "hset using Object value");
  				
  				assertEquals (hgetResp1.get(), dataList.get(0), "hget of field with byte[] value");
  				assertEquals (DefaultCodec.toStr(hgetResp2.get()), stringList.get(0), "hget of field with String value");
  				assertEquals (DefaultCodec.toLong(hgetResp3.get()).longValue(), 222, "hget of field with Number value");
  				TestBean objval = DefaultCodec.decode(hgetResp4.get());
  				assertEquals (objval.getName(), objectList.get(0).getName(), "hget of field with Object value");
  				
  				assertTrue (hdelResp1.get(), "hdel of field should be true");
  				assertTrue (!hdelResp2.get(), "hdel of non-existent field should be false");
  				
  				assertEquals (hlenResp1.get().longValue(), 4, "hlen of hash should be 4");
  				assertEquals (hlenResp2.get().longValue(), 3, "hlen of hash should be 3");
  				assertEquals (hlenResp3.get().longValue(), 0, "hlen of non-existant hash should be 0");
  
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  
  
  	@Test
  	public void testHIncrBy() throws InterruptedException {
  		cmd = Command.HSET.code + " | " + Command.HGET + " | " + Command.HINCRBY;
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb();
  			Future<Boolean> hsetResp1 = provider.hset(keys.get(0), keys.get(1), 41);
  			Future<Long> hincrBy1 = provider.hincrby(keys.get(0), keys.get(1), 1);
  			Future<byte[]> hget = provider.hget(keys.get(0), keys.get(1));
  						
  			Future<Long> hincrBy2 = provider.hincrby(keys.get(0), keys.get(2), 4);
  			
  			try {
  				assertTrue (hsetResp1.get(), "hset using byte[] value");
  				assertEquals (hincrBy1.get(), (Long)42l, "hexists of field should be true");
  				assertEquals(hget.get(), Convert.toBytes(42l));
  				assertEquals(hincrBy2.get(), (Long)4l);
  
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	
  	@Test
  	public void testHkeys() throws InterruptedException {
  		cmd = Command.HSET.code + " | " + Command.HKEYS;
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb();
  			Future<Boolean> hsetResp1 = provider.hset(keys.get(0), keys.get(1), dataList.get(0));
  			Future<Boolean> hsetResp2 = provider.hset(keys.get(0), keys.get(2), stringList.get(0));
  			Future<Boolean> hsetResp3 = provider.hset(keys.get(0), keys.get(3), 222);
  			objectList.get(0).setName("Hash Stash");
  			Future<Boolean> hsetResp4 = provider.hset(keys.get(0), keys.get(4), objectList.get(0));
  			
  			// get keys
  			Future<List<byte[]>> hkeysResp1 = provider.hkeys(keys.get(0));
  			// alright - empty the hash
  			for(int i=1; i<5; i++)
  	            try {
  	                assertTrue(provider.hdel(keys.get(0), keys.get(i).getBytes()).get());
                  }
                  catch (ExecutionException e) {
      				Throwable cause = e.getCause();
      				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
                  }
  			// get keys again
  			Future<List<byte[]>> hkeysResp2 = provider.hkeys(keys.get(0));
  			// nil case
  			Future<List<byte[]>> hkeysResp3 = provider.hkeys("no-such-hash");
  			
  			
  			try {
  				assertTrue (hsetResp1.get(), "hset using byte[] value");
  				assertTrue (hsetResp2.get(), "hset using String value");
  				assertTrue (hsetResp3.get(), "hset using Number value");
  				assertTrue (hsetResp4.get(), "hset using Object value");
  				
  				List<byte[]> hkeys = hkeysResp1.get(); 
  				assertEquals (hkeys.size(), 4, "keys list size should be 4");
  				assertEquals(hkeysResp2.get(), Collections.EMPTY_LIST, "result should be empty");
  				assertEquals(hkeysResp3.get(), Collections.EMPTY_LIST, "list of keys of non-existent hash should be empty");
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	
  	@Test
  	public void testSetBitGetBit() throws InterruptedException {
  		cmd = Command.SETBIT.code + " | " + Command.GETBIT;
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb();
  			Future<Boolean> setbit1 = provider.setbit(keys.get(0), 1, true);
  			Future<Boolean> setbit32 = provider.setbit(keys.get(0), 32, true);
  
  			Future<Boolean> getbit1 = provider.getbit(keys.get(0), 1);
  			Future<Boolean> getbit2 = provider.getbit(keys.get(0), 2);
  			Future<Boolean> getbit32 = provider.getbit(keys.get(0), 32);
  			
  			try {
  				assertFalse (setbit1.get(), "original bit at 1");
  				assertFalse (setbit32.get(), "original bit at 32");
  				assertTrue (getbit1.get(), "getbit at 1");
  				assertFalse (getbit2.get(), "getbit at 2");
  				assertTrue (getbit32.get(), "getbit at 32");
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  
  
  	@Test
  	public void testHvals() throws InterruptedException {
  		cmd = Command.HSET.code + " | " + Command.HVALS;
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb();
  			Future<Boolean> hsetResp1 = provider.hset(keys.get(0), keys.get(1), dataList.get(0));
  			Future<Boolean> hsetResp2 = provider.hset(keys.get(0), keys.get(2), stringList.get(0));
  			Future<Boolean> hsetResp3 = provider.hset(keys.get(0), keys.get(3), 222);
  			objectList.get(0).setName("Hash Stash");
  			Future<Boolean> hsetResp4 = provider.hset(keys.get(0), keys.get(4), objectList.get(0));
  			
  			// get values
  			Future<List<byte[]>> hvalsResp1 = provider.hvals(keys.get(0));
  			// alright - empty the hash
  			for(int i=1; i<5; i++)
  	            try {
  	                assertTrue(provider.hdel(keys.get(0), keys.get(i)).get());
                  }
                  catch (ExecutionException e) {
      				Throwable cause = e.getCause();
      				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
                  }
  			// get values again
      			// get values
  			Future<List<byte[]>> hvalsResp2 = provider.hvals(keys.get(0));
  			// nil case
  			Future<List<byte[]>> hvalsResp3 = provider.hvals("no-such-hash");
  			
  			
  			try {
  				assertTrue (hsetResp1.get(), "hset using byte[] value");
  				assertTrue (hsetResp2.get(), "hset using String value");
  				assertTrue (hsetResp3.get(), "hset using Number value");
  				assertTrue (hsetResp4.get(), "hset using Object value");
  				
  				List<byte[]> hvals = hvalsResp1.get(); 
  				assertEquals(hvals.size(), 4, "values list size should be 4");
  				assertEquals(hvalsResp2.get(), Collections.EMPTY_LIST, "values list size should be empty");
  				assertEquals(hvalsResp3.get(), Collections.EMPTY_LIST, "list of values of non-existent hash should be empty");
  
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  
  
  	@Test
  	public void testHgetall() throws InterruptedException {
  		cmd = Command.HSET.code + " | " + Command.HGETALL;
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb();
  			Future<Boolean> hsetResp1 = provider.hset(keys.get(0), keys.get(1), dataList.get(0));
  			Future<Boolean> hsetResp2 = provider.hset(keys.get(0), keys.get(2), stringList.get(0));
  			Future<Boolean> hsetResp3 = provider.hset(keys.get(0), keys.get(3), 222);
  			objectList.get(0).setName("Hash Stash");
  			Future<Boolean> hsetResp4 = provider.hset(keys.get(0), keys.get(4), objectList.get(0));
  			
  			// get keys
  			Future<List<byte[]>> frHkeys = provider.hkeys(keys.get(0));
  			
  			// get all
  			Future<Map<byte[], byte[]>> frHmap1 = provider.hgetall(keys.get(0));
  			
  			// delete all keys
  			for(int i =1; i<5; i++) {
  	            try {
  	                provider.hdel(keys.get(0), keys.get(i)).get();
                  }
                  catch (ExecutionException e) {
      				Throwable cause = e.getCause();
      				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
                  }
  			}
  			// get all again
  			Future<Map<byte[], byte[]>> frHmap2 = provider.hgetall(keys.get(0));
      			
  			// get all for non-existent hash
  			Future<Map<byte[], byte[]>> frHmap3 = provider.hgetall("no-such-hash");
          			
  			try {
  				assertTrue (hsetResp1.get(), "hset using byte[] value");
  				assertTrue (hsetResp2.get(), "hset using String value");
  				assertTrue (hsetResp3.get(), "hset using Number value");
  				assertTrue (hsetResp4.get(), "hset using Object value");
  				
  				assertEquals( frHmap1.get().size(), 4, "hash map length");
  				assertEquals( frHkeys.get().size(), 4, "keys list length");
  
  				Map<String, byte[]> hmap = DefaultCodec.toDataDictionary(frHmap1.get());
  				assertEquals(hmap.get(keys.get(1)), dataList.get(0), "byte[] value mapping should correspond to prior HSET");
  				assertEquals(DefaultCodec.toStr(hmap.get(keys.get(2))), stringList.get(0), "String value mapping should correspond to prior HSET");
  				assertEquals(DefaultCodec.toLong(hmap.get(keys.get(3))).longValue(), 222, "Number value mapping should correspond to prior HSET");
  				assertEquals(DefaultCodec.decode(hmap.get(keys.get(4))), objectList.get(0), "Object value mapping should correspond to prior HSET");
  				
  				Map<String, byte[]> hmap2 = DefaultCodec.toDataDictionary(frHmap2.get());
  				assertEquals(hmap2, Collections.EMPTY_MAP, "result should be empty");
  				
  				Map<String, byte[]> hmap3 = DefaultCodec.toDataDictionary(frHmap3.get());
  				assertEquals(hmap3, Collections.EMPTY_MAP, "hgetall for non existent hash should be empty");
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  
  	
  	/**
  	 * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#set(java.lang.String, byte[])}.
  	 * @throws InterruptedException 
  	 */
  	@Test
  	public void testSetStringByteArray() throws InterruptedException {
  		cmd = Command.SET.code + " | " + Command.SETNX.code + " byte[] | " + Command.GET;
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb();
  			provider.set(keys.get(keys.size()-1), emptyBytes);
  			Future<byte[]> getResp = provider.get(keys.get(keys.size()-1));
  			Future<Boolean> setnxResp1 = provider.setnx(keys.get(1), dataList.get(1));
  			Future<Boolean> setnxResp2 = provider.setnx(keys.get(1), dataList.get(2));
  			
  			try {
  				assertEquals(getResp.get(), emptyBytes, "set and get results for empty byte[]");
  				assertTrue(setnxResp1.get());
  				assertFalse(setnxResp2.get());
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	
  	/**
  	 * Test method for {@link JRedisFuture#append(String, String)}
  	 * @throws InterruptedException 
  	 */
  	@Test
  	public void testAppendStringString() throws InterruptedException {
  		cmd = Command.APPEND.code + " | " + Command.GET.code + " String";
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb();
  			
  			// append to a non-existent key
  			// as of Redis 1.3.7 it behaves just like set but returns value len instead of status
  			String key1 = keys.get(0);
  			
  			Future<Long> frLen0 = provider.append(key1, emptyString);
  			Future<byte[]> frGet0 = provider.get(key1);
  			
  			Future<Long> frLen1 = provider.append(key1, stringList.get(0));
  			Future<byte[]> frGet1 = provider.get(key1);
  			
  			Future<Long> frLen2 = provider.append(key1, stringList.get(1));
  			Future<byte[]> frGet2 = provider.get(key1);
  			
  			provider.sadd(keys.get(1), stringList.get(0));
  			Future<Long>   frExpectedError = provider.append(keys.get(1), stringList.get(0));
  			
  			try {
  				assertEquals(frLen0.get().longValue(), 0, "append of emtpy string to new key should be zero");
  				assertEquals(DefaultCodec.toStr(frGet0.get()), emptyString, "get results after append to new key for empty string");
  				
  				assertEquals(frLen1.get().longValue(), stringList.get(0).length(), "append of emtpy string to new key should be zero");
  				assertEquals(DefaultCodec.toStr(frGet1.get()), stringList.get(0), "get results after append to new key for empty string");
  				
  				assertEquals(frLen2.get().longValue(), stringList.get(0).length() + stringList.get(1).length(), "append of emtpy string to new key should be zero");
  				StringBuffer appendedString = new StringBuffer();
  				appendedString.append(stringList.get(0));
  				appendedString.append(stringList.get(1));
  				assertEquals(DefaultCodec.toStr(frGet2.get()), appendedString.toString(), "get results after append to new key for empty string");
  				
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  
  			
  			// check see if we got the expected RedisException
  			boolean expected = false;
  			try {
  				frExpectedError.get();
  			}
  			catch(ExecutionException e){
  				Throwable cause = e.getCause();
  				if(cause instanceof RedisException)
  					expected = true;
  				else
  					fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  			assertTrue(expected, "expecting RedisException for append to a non-string key");
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  	/**
  	 * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#rename(java.lang.String, java.lang.String)}.
  	 * @throws InterruptedException 
  	 */
  	@Test
  	public void testRename() throws InterruptedException {
  		cmd = Command.RENAME.code;
  		Log.log("TEST: %s command", cmd);
  		try {
  			
  			String newkey = null;
  			byte[] value = dataList.get(0);
  			key = getRandomAsciiString (random.nextInt(24)+2);
  			newkey = getRandomAsciiString (random.nextInt(24)+2);
  			
  			Future<ResponseStatus> 	flushResp = provider.flushdb();
  			Future<ResponseStatus> 	setResp = provider.set (key, value);
  			Future<byte[]> 			getOldResp = provider.get(key);
  			Future<ResponseStatus> 	reanmeResp = provider.rename (key, newkey);
  			Future<byte[]> 			getNewResp = provider.get(newkey);
  			
  			try {
  				flushResp.get();
  				setResp.get();
  				assertEquals(value, getOldResp.get());
  				reanmeResp.get();
  				assertEquals(value, getNewResp.get());
  			}
  			catch(ExecutionException e){
  				// errors in response 
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  
  	/**
  	 * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#rename(java.lang.String, java.lang.String)}.
  	 * @throws InterruptedException 
  	 */
  	@Test
  	public void testRenamenx() throws InterruptedException {
  		cmd = Command.RENAMENX.code;
  		Log.log("TEST: %s command", cmd);
  		try {
  			// flush db and set a key
  			provider.flushdb();
  			provider.set (keys.get(0), dataList.get(0));
  			
  			// this should return true in future
  			Future<Boolean> 	reanmenxResp1 = provider.renamenx (keys.get(0), keys.get(2));
  
  			// flush db again and set 2 keys
  			provider.flushdb();
  			provider.set (keys.get(1), dataList.get(1));
  			provider.set (keys.get(2), dataList.get(2));
  			
  			// this should return false in future
  			Future<Boolean> 	reanmenxResp2 = provider.renamenx (keys.get(1), keys.get(2));
  			
  			try {
  				// note that we don't have to 'get' the future results for 
  				// commands we are not interested in.
  				assertTrue (reanmenxResp1.get(), "1st renamenx should have been true");
  				assertFalse (reanmenxResp2.get(), "2nd renamenx should have been false");
  			}
  			catch(ExecutionException e){
  				// errors in response 
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) {  fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e);  }
  	}
  
  	/**
  	 * Test settting a key in a flushed db, and then checking
  	 * exists, flushing again, and finally keys().  
  	 * Will invoke commands asynchronously, and after 
  	 * @throws InterruptedException 
  	 */
  	@Test
  	public void testSetAndFlushdbAndExistsAndKeys() throws InterruptedException {
  		cmd = 
  			Command.FLUSHDB.code + " | " +
  			Command.SET.code + " | " +
  			Command.EXISTS.code + " | " +
  			Command.FLUSHDB.code + " | " +
  			Command.KEYS.code;
  			
  		Log.log("TEST: %s commands", cmd);
  		try {
  			key = "woof";
  			Future<ResponseStatus> flushResp = provider.flushdb();
  			Future<ResponseStatus> setResp = provider.set(key, "meow");
  			Future<Boolean> existsResp = provider.exists(key);
  			Future<ResponseStatus> flush2Resp = provider.flushdb();
  			Future<List<byte[]>>  keysResp = provider.keys();
  			
  			try {
  				flushResp.get(); // no need to check status; if error exception is raised
  				setResp.get();
  				assertTrue(existsResp.get(), "key should exists at this point");
  				flush2Resp.get();
  				assertTrue(keysResp.get().size() == 0, "keys should have returned list of 0 items");
  			}
  			catch(ExecutionException e){
  				// errors in response 
  				Throwable cause = e.getCause();
  				fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); 
  			}
  		} 
  		catch (ClientRuntimeException e) { 
  			// errors in request time
  			fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); 
  		}
  	}
  	
  	/**
  	 * Tests to force RedisExceptions against various response types.
  	 * First we queue the requests and then we get the responses for all.
  	 * This tests both the primary goal and the correct behaviour of asynch
  	 * provider to queue the responses.
  	 * @throws InterruptedException 
  	 */
  	@Test
  	public void testElicitErrors() throws InterruptedException {
  		Log.log("TEST: Elicit errors");
  		try {
  			provider.flushdb();
  			
  			String key = keys.get(0);
  			try {
  	            provider.set(key, smallData).get();
              }
              catch (ExecutionException e) {
              	fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); 
              }
  			
              // queue a few commands -- all are expected to result in ExecutionExcetion on Future.get()
              //
  			Future<Boolean>	fSaddResp = provider.sadd(key, dataList.get(0)); 
  			Future<Long>	fScardResp = provider.scard(key); 
  			Future<byte[]>  fLpopResp = provider.lpop(key); 
  			Future<List<byte[]>> fSmembersResp = provider.smembers(key); 
  			
  			boolean expectedError;
  			
  			expectedError = false;
  			try {
  				Log.log("1 - Expecting an operation against key holding the wrong kind of value ERROR for SADD..");
  				fSaddResp.get();
  			}
  			catch (ExecutionException e) { expectedError = true; }
  			assertTrue(expectedError, "should have raised an exception but did not");
  			
  			expectedError = false;
  			try {
  				Log.log("2 - Expecting an operation against key holding the wrong kind of value ERROR for SCARD..");
  				fScardResp.get();
  			}
  			catch (ExecutionException e) { expectedError = true; }
  			assertTrue(expectedError, "should have raised an exception but did not");
  			
  			expectedError = false;
  			try {
  				Log.log("3 - Expecting an operation against key holding the wrong kind of value ERROR for LPOP ..");
  				fLpopResp.get();
  			}
  			catch (ExecutionException e) { expectedError = true; }
  			assertTrue(expectedError, "should have raised an exception but did not");
  			
  			expectedError = false;
  			try {
  				Log.log("4 - Expecting an operation against key holding the wrong kind of value ERROR for SMEMBERS ..");
  				fSmembersResp.get();
  			}
  			catch (ExecutionException e) { expectedError = true; }
  			assertTrue(expectedError, "should have raised an exception but did not");
  			
  			
  		} 
  		catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); }
  	}
  	
  	/**
  	 * Asynchronous responses must provide reference to {@link RedisException}
  	 * through the {@link ExecutionException#getCause()} per {@link Future} semantics.
  	 * @throws InterruptedException 
  	 */
  	@Test
  	public void testExecutionExceptionCauseType() throws InterruptedException {
  		boolean expectedError;
  		String key = keys.get(0);
  		try {
  			expectedError = false;
  			try {
  				Log.log("Expecting an operation against key holding the wrong kind of value ERROR..");
  				
  				provider.set(key, smallData);  // don't wait for response ..
  				Future<Boolean> fBool = provider.sadd(key, dataList.get(0));
  				@SuppressWarnings("unused")
                  boolean response = fBool.get(); // wait for response
  			}
              catch (ExecutionException e) {
              	expectedError = true;
              	Throwable cause = e.getCause();
              	if(cause instanceof RedisException)
              		Log.log("%s (as excepted)", cause);
              	else
              		fail("FAULT: the cause of ExecutionException was expected to be a RedisException");
              }
  			assertTrue(expectedError, "should have raised an exception but did not");
  		}
  		finally {
  			
  		}
  	}
  	
  	/**
  	 * Test {@link JRedisFuture#ping()}
  	 * @throws InterruptedException 
  	 */
  	@Test
  	public void testPing () throws InterruptedException {
  		Future<ResponseStatus> frStatus = null;
  		cmd = Command.PING.code;
  		Log.log("TEST: %s command", cmd);
  		try {
  			frStatus = provider.ping();
  			ResponseStatus status = frStatus.get();
  			assertTrue(!status.isError(), "ping return status");
  		}
          catch (ExecutionException e) {
  	        e.printStackTrace();
  	        fail(cmd + " FAULT: " + e.getCause().getLocalizedMessage(), e);
          }
  	}
  	/**
  	 * Test {@link JRedisFuture#flushdb()}
  	 * @throws InterruptedException 
  	 */
  	@Test
  	public void testFlushDb () throws InterruptedException {
  		Future<ResponseStatus> frStatus = null;
  		cmd = Command.FLUSHDB.code;
  		Log.log("TEST: %s command", cmd);
  		try {
  			frStatus = provider.flushdb();
  			ResponseStatus status = frStatus.get();
  			assertTrue(!status.isError(), "flushdb return status");
  		}
          catch (ExecutionException e) {
  	        e.printStackTrace();
  	        fail(cmd + " FAULT: " + e.getCause().getLocalizedMessage(), e);
          }
  	}
  	@Test
  	public void testEcho() throws InterruptedException {
  		Future<byte[]> echoResp = null;
  		cmd = Command.ECHO.code;
  		Log.log("TEST: %s command", cmd);
  		try {
  			echoResp = provider.echo(dataList.get(0));
  			assertEquals(dataList.get(0), echoResp.get(), "data and echo results");
  
  			byte[] zerolenData = new byte[0];
  			assertEquals(zerolenData, provider.echo(zerolenData).get(), "zero len byte[] and echo results");
  			boolean expected = false;
  			try {
  				provider.echo((byte[])null);
  			}
  			catch(IllegalArgumentException e) { expected = true; }
  			assertTrue(expected, "expecting exception for null value to ECHO");
  
  		}
          catch (ExecutionException e) {
  	        e.printStackTrace();
  	        fail(cmd + " FAULT: " + e.getCause().getLocalizedMessage(), e);
          }
  	}
  	
  	@Test
  	public void testBgrewriteaof() throws InterruptedException {
  		Future<String> cmdRespMsg = null;
  		cmd = Command.BGREWRITEAOF.code;
  		Log.log("TEST: %s command", cmd);
  		try {
  			cmdRespMsg = provider.bgrewriteaof();
  			assertTrue(cmdRespMsg.get() != null, "cmd response message should not be null");
  		}
          catch (ExecutionException e) {
  	        e.printStackTrace();
  	        fail(cmd + " FAULT: " + e.getCause().getLocalizedMessage(), e);
          }
  	}
  	
  	/**
  	 * Test {@link JRedisFuture#debug()}
  	 * @throws InterruptedException 
  	 */
  	@Test
  	public void testDebug () throws InterruptedException {
  		Future<ObjectInfo> frInfo = null;
  		cmd = Command.DEBUG.code;
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb();
  			provider.set("foo", "bar");
  			frInfo = provider.debug("foo");
  			ObjectInfo info = frInfo.get();
  			assertNotNull(info);
  			Log.log("DEBUG of key => %s", info);
  		}
          catch (ExecutionException e) {
  	        e.printStackTrace();
  	        fail(cmd + " FAULT: " + e.getCause().getLocalizedMessage(), e);
          }
  	}
  	
  	@Test
  	public void testExpireat() throws InterruptedException {
  		cmd = Command.EXPIREAT.code;
  		Log.log("TEST: %s command", cmd);
  		try {
  			provider.flushdb().get();
  			String keyToExpire = "expire-me";
  			provider.set(keyToExpire, dataList.get(0)).get();
  
  			Log.log("TEST: %s with expire time 1000 msecs in future", Command.EXPIREAT);
  			assertTrue(provider.expireat(keyToExpire, System.currentTimeMillis() + 2000).get(), "expireat for existing key should be true");
        assertTrue (provider.exists(keyToExpire).get());
  			assertTrue(!provider.expireat("no-such-key", System.currentTimeMillis() + 500).get(), "expireat for non-existant key should be false");
  			
  			
  			// NOTE: IT SIMPLY WON'T WORK WITHOUT GIVING REDIS A CHANCE
  			// could be network latency, or whatever, but the expire command is NOT
  			// that precise, so we need to wait a bit longer
  			
  			Thread.sleep(5000);
  			assertTrue (!provider.exists(keyToExpire).get(), "key should have expired by now");
  		}
          catch (ExecutionException e) {
  	        e.printStackTrace();
  	        fail(cmd + " FAULT: " + e.getCause().getLocalizedMessage(), e);
          }
  	}
  
  }
  