static void tests(void)
{
    __block CFErrorRef testError = NULL;
    
    CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
    
    CFStringRef circleName = CFSTR("Woot Circle");
    
    /* DataSource */
    SOSDataSourceRef aliceDs = SOSTestDataSourceCreate();
    SOSDataSourceRef bobDs = SOSTestDataSourceCreate();
    
    SOSDataSourceFactoryRef aliceDsf = SOSTestDataSourceFactoryCreate();
    SOSTestDataSourceFactoryAddDataSource(aliceDsf, circleName, aliceDs);
    
    SOSDataSourceFactoryRef bobDsf = SOSTestDataSourceFactoryCreate();
    SOSTestDataSourceFactoryAddDataSource(bobDsf, circleName, bobDs);
    
    CFDictionaryRef alice_gestalt = SOSCreatePeerGestaltFromName(CFSTR("Alice Device"));
    CFDictionaryRef bob_gestalt = SOSCreatePeerGestaltFromName(CFSTR("Bob Device"));
    
    SOSAccountRef alice_account = SOSAccountCreate(kCFAllocatorDefault, alice_gestalt, aliceDsf);
    SOSAccountRef bob_account = SOSAccountCreate(kCFAllocatorDefault, bob_gestalt, bobDsf);
    
    SOSAccountAssertUserCredentials(alice_account, CFSTR("alice"), cfpassword, &testError);
    SOSAccountAssertUserCredentials(bob_account, CFSTR("bob"), cfpassword, &testError);
    
    CFReleaseNull(cfpassword);
    
    SOSAccountJoinCircles(alice_account, &testError);
    SOSAccountJoinCircles(bob_account, &testError);
    
    CFDataRef alice_account_data = SOSAccountCopyEncodedData(alice_account, kCFAllocatorDefault, &testError);
    CFDataRef bob_account_data = SOSAccountCopyEncodedData(bob_account, kCFAllocatorDefault, &testError);
    
    CFArrayRef alice_peers = SOSAccountCopyPeers(alice_account, &testError);
    CFArrayRef bob_peers = SOSAccountCopyPeers(bob_account, &testError);
    
    SOSPeerInfoRef alice_peer_info = (SOSPeerInfoRef)CFArrayGetValueAtIndex(alice_peers, 0);
    SOSPeerInfoRef bob_peer_info = (SOSPeerInfoRef)CFArrayGetValueAtIndex(bob_peers, 0);
    
    CFStringRef alice_peer_id = SOSPeerInfoGetPeerID(alice_peer_info);
    CFStringRef bob_peer_id = SOSPeerInfoGetPeerID(bob_peer_info);
    
    CFDataRef alice_peer_external_form = CFStringCreateExternalRepresentation(kCFAllocatorDefault, alice_peer_id, kCFStringEncodingUTF8, '?');
    CFDataRef bob_peer_external_form = CFStringCreateExternalRepresentation(kCFAllocatorDefault, bob_peer_id, kCFStringEncodingUTF8, '?');
    
    bool aliceReady = false;
    bool bobReady = false;
    
    CFDataRef aliceSideSession = SecOTRSessionCreateRemote_internal(bob_account_data, bob_peer_external_form, alice_account_data, &testError);
    RegressionsLogError(testError);
    CFReleaseNull(testError);
    
    ok(aliceSideSession != NULL, "Make Alice side remote session");
    
    CFDataRef bobSideSession = SecOTRSessionCreateRemote_internal(alice_account_data, alice_peer_external_form, bob_account_data, &testError);
    RegressionsLogError(testError);
    CFReleaseNull(testError);
    
    ok(bobSideSession != NULL, "Make Bob side remote session");
    
    CFDataRef aliceSideSessionResult = NULL;
    CFDataRef bobSideSessionResult = NULL;
    CFDataRef aliceToBob = NULL;
    CFDataRef bobToAlice = NULL;
    
    do {
        bool aliceStatus = SecOTRSessionProcessPacketRemote(aliceSideSession, bobToAlice, &aliceSideSessionResult, &aliceToBob, &aliceReady, &testError);
        ok (aliceStatus, "Alice sent packet OK");
        RegressionsLogError(testError);
        CFReleaseNull(testError);
        CFReleaseSafe(aliceSideSession);
        aliceSideSession = aliceSideSessionResult;
        
        if (aliceReady) {
            break;
        }
        
        bool bobStatus = SecOTRSessionProcessPacketRemote(bobSideSession, aliceToBob, &bobSideSessionResult, &bobToAlice, &bobReady, &testError);
        ok (bobStatus, "Bob sent packet OK");
        RegressionsLogError(testError);
        CFReleaseNull(testError);
        CFReleaseSafe(bobSideSession);
        bobSideSession = bobSideSessionResult;
    } while (1);
    
    ok(bobReady, "Bob finished negotiating at the same time as Alice.");
    
    CFReleaseNull(aliceSideSession);
    CFReleaseNull(bobSideSession);
    
    SecOTRFIPurgeAllFromKeychain(&testError);
    RegressionsLogError(testError);
    CFReleaseNull(bob_peer_external_form);
    CFReleaseNull(alice_peer_external_form);
    CFReleaseNull(alice_account_data);
    CFReleaseNull(bob_account_data);
    CFReleaseNull(alice_peers);
    CFReleaseNull(bob_peers);
    CFReleaseNull(aliceSideSession);
    CFReleaseNull(bobSideSession);
    CFReleaseNull(testError);
}
static void tests()
{
    CFErrorRef error = NULL;

    CFStringRef aliceID = CFSTR("Alice");
    CFStringRef bobID = CFSTR("Bob");    // not really remote, just another client on same machine

    SecKeyRef alice_key = NULL;
    SecKeyRef bob_key = NULL;

    CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);

    CFDataRef parameters = SOSUserKeyCreateGenerateParameters(&error);
    ok(parameters, "No parameters!");
    ok(error == NULL, "Error: (%@)", error);
    CFReleaseNull(error);

    SecKeyRef user_privkey = SOSUserKeygen(cfpassword, parameters, &error);
    CFReleaseNull(parameters);
    CFReleaseSafe(cfpassword);

    CFStringRef circleName = CFSTR("Woot Circle");

    SOSFullPeerInfoRef alice_full_peer_info = SOSCreateFullPeerInfoFromName(aliceID, &alice_key, &error);
    SOSPeerInfoRef alice_peer_info = SOSFullPeerInfoGetPeerInfo(alice_full_peer_info);

    SOSFullPeerInfoRef bob_full_peer_info = SOSCreateFullPeerInfoFromName(bobID, &bob_key, &error);
    SOSPeerInfoRef bob_peer_info = SOSFullPeerInfoGetPeerInfo(bob_full_peer_info);

    SOSCircleRef aliceCircle = SOSCircleCreate(kCFAllocatorDefault, circleName, &error);

    ok(SOSCircleRequestAdmission(aliceCircle, user_privkey, alice_full_peer_info, &error));
    ok(SOSCircleAcceptRequests(aliceCircle, user_privkey, alice_full_peer_info, NULL));
    ok(SOSCircleRequestAdmission(aliceCircle, user_privkey, bob_full_peer_info, &error), "requested admission");
    ok(SOSCircleAcceptRequests(aliceCircle, user_privkey, bob_full_peer_info, &error), "accepted them all!");

    alice_peer_info = SOSFullPeerInfoGetPeerInfo(alice_full_peer_info);
    bob_peer_info = SOSFullPeerInfoGetPeerInfo(bob_full_peer_info);

    CFDataRef aliceCircleEncoded;
    ok(aliceCircleEncoded = SOSCircleCopyEncodedData(aliceCircle, kCFAllocatorDefault, &error), "encode alice circle: %@", error);
    CFReleaseNull(error);
    SOSCircleRef bobCircle;
    ok(bobCircle = SOSCircleCreateFromData(0, aliceCircleEncoded, &error), "decode bobCircle: %@", error);
    CFReleaseNull(aliceCircleEncoded);
    CFReleaseNull(error);

    /* Transport. */
    __block CFDataRef queued_message = NULL;
    
    SOSPeerSendBlock enqueueMessage =  ^bool (CFDataRef message, CFErrorRef *error) {
        if (queued_message)
            fail("We already had an unproccessed message");
        
        queued_message = (CFDataRef) CFRetain(message);
        return true;
    };
    
    CFDataRef (^dequeueMessage)() = ^CFDataRef () {
        CFDataRef result = queued_message;
        queued_message = NULL;
        
        return result;
    };

    /* DataSource */
    SOSDataSourceRef aliceDs = SOSTestDataSourceCreate();
    SOSDataSourceRef bobDs = SOSTestDataSourceCreate();
    
    SOSDataSourceFactoryRef aliceDsf = SOSTestDataSourceFactoryCreate();
    SOSTestDataSourceFactoryAddDataSource(aliceDsf, circleName, aliceDs);

    SOSDataSourceFactoryRef bobDsf = SOSTestDataSourceFactoryCreate();
    SOSTestDataSourceFactoryAddDataSource(bobDsf, circleName, bobDs);
    
    /* Test passing peer messages to the engine. */
    CFDataRef message;

    CFStringRef bob_peer_id = SOSPeerInfoGetPeerID(bob_peer_info);

    /* Hand an empty message to the engine for handeling. */
    message = CFDataCreate(NULL, NULL, 0);
    is(SOSCircleHandlePeerMessage(aliceCircle, alice_full_peer_info, aliceDsf, enqueueMessage, bob_peer_id, message, &error), false,
       "empty message rejected, %@", error);

    CFReleaseNull(error);
    CFReleaseNull(message);

    ok(SOSCircleSyncWithPeer(alice_full_peer_info, aliceCircle, aliceDsf, enqueueMessage, bob_peer_id, &error), "Start sync [error %@]", error);
    CFReleaseNull(error);

    ok(message = dequeueMessage(), "Alice sent message");
    CFStringRef alice_peer_id = SOSPeerInfoGetPeerID(alice_peer_info);
    is(SOSCircleHandlePeerMessage(bobCircle, bob_full_peer_info, bobDsf, enqueueMessage, alice_peer_id, message, &error), true,
       "Bob accepted message: %@", error);
    CFReleaseNull(message);

#if 1
    CFStringRef desc = NULL;
    ok(message = dequeueMessage(), "we got a message from Bob %@", desc = SOSMessageCopyDescription(message));
    ok(SOSCircleHandlePeerMessage(aliceCircle, alice_full_peer_info, aliceDsf, enqueueMessage, bob_peer_id, message, &error),
       "Alice accepted message: %@", error);
    CFReleaseNull(message);
    CFReleaseNull(desc);

    ok(message = dequeueMessage(), "we got a reply from Alice %@", desc = SOSMessageCopyDescription(message));
    ok(SOSCircleHandlePeerMessage(bobCircle, bob_full_peer_info, bobDsf, enqueueMessage, alice_peer_id, message, &error),
       "Bob accepted message: %@", error);
    CFReleaseNull(message);
    CFReleaseNull(desc);
#endif

#if 0
    message = dequeueMessage();
    ok(NULL == message, "we got no message from Bob %@", desc = SOSMessageCopyDescription(message));

    SOSObjectRef object = SOSDataSourceCreateGenericItem(aliceDs, CFSTR("75_circle_engine_account"), CFSTR("test service"));
    ok(SOSTestDataSourceAddObject(aliceDs, object, &error), "add empty object to datasource: %@", error);
    CFReleaseNull(error);
    CFReleaseNull(object);

    ok(SOSCircleSyncWithPeer(alice_full_peer_info, aliceCircle, aliceDsf, enqueueMessage, bob_peer_id, &error), "Restart sync [error %@]", error);
    CFReleaseNull(error);
    
    ok(message = dequeueMessage(), "Alice started again %@", desc = SOSMessageCopyDescription(message));
    is(SOSCircleHandlePeerMessage(bobCircle, bob_full_peer_info, bobDsf, enqueueMessage, alice_peer_id, message, &error), true,
       "bob accepted %@: %@", SOSMessageCopyDescription(message), error);
    CFReleaseNull(error);
    CFReleaseNull(message);
#endif

#if 1
    bool alice = true;
    int max_loops = 50;
    while (max_loops-- && NULL != (message = dequeueMessage())) {
        if (alice) {
            ok(SOSCircleHandlePeerMessage(aliceCircle, alice_full_peer_info, aliceDsf, enqueueMessage, bob_peer_id, message, &error),
               "alice accepted %@: %@", desc = SOSMessageCopyDescription(message), error);
        } else {
            ok(SOSCircleHandlePeerMessage(bobCircle, bob_full_peer_info, bobDsf, enqueueMessage, alice_peer_id, message, &error),
               "bob accepted %@: %@", desc = SOSMessageCopyDescription(message), error);
        }
        alice = !alice;
        CFRelease(message);
        CFReleaseNull(desc);
    }
#endif

    CFReleaseNull(aliceCircle);
    CFReleaseNull(bobCircle);

    CFReleaseNull(alice_peer_info);
    CFReleaseNull(bob_peer_info);

    CFReleaseNull(alice_key);
    CFReleaseNull(bob_key);
    aliceDsf->release(aliceDsf);
    bobDsf->release(bobDsf);
}