コード例 #1
0
void AppInterfaceImpl::groupUpdateSendDone(const string& groupId)
{
    LOGGER(DEBUGGING, __func__, " -->");

    unique_lock<mutex> lck(currentChangeSetLock);

    if (!updateInProgress) {
        return;
    }
    memset(updateIdGlobal, 0, sizeof(updateIdGlobal));

    // We've sent out the new change set to all devices. This change set contains
    // the current status of the group attributes (with vector clocks) and the collapsed
    // information of new and removed members. The current change set now becomes the pending
    // change set, waiting for ACKs
    PtrChangeSet changeSet = getGroupChangeSet(groupId);
    if (!changeSet) {
        return;
    }
    // Remove old pending change set, makes sure we have only _one_ pending change
    // set at a time
    removeFromPendingChangeSets(groupId);

    // Add it to pending change set cache map and save in persistent store
    pendingChangeSets.insert(pair<string, PtrChangeSet>(groupId, changeSet));
    changeSet->SerializeAsString();
    store_->insertChangeSet(groupId, changeSet->SerializeAsString());

    // Remove as the the current change set
    currentChangeSets.erase(groupId);

    updateInProgress = false;

    LOGGER(DEBUGGING, __func__, " <-- ", groupId);
}
コード例 #2
0
TEST_F(ChangeSetTestsFixtureSimple, NewGroupTestsEmpty) {
    string groupId = appInterface_1->createNewGroup(Empty, Empty);
    ASSERT_FALSE(groupId.empty());

    PtrChangeSet changeSet = getCurrentGroupChangeSet(groupId, *store);
    ASSERT_TRUE((bool)changeSet);

    ASSERT_FALSE(changeSet->has_updatename());
}
コード例 #3
0
TEST_F(ChangeSetTestsFixtureSimple, NewGroupTests) {
    string groupId = appInterface_1->createNewGroup(groupName_1, Empty);
    ASSERT_FALSE(groupId.empty());

    PtrChangeSet changeSet = getCurrentGroupChangeSet(groupId, *store);
    ASSERT_TRUE((bool)changeSet);

    ASSERT_TRUE(changeSet->has_updatename());
    ASSERT_EQ(groupName_1, changeSet->updatename().name());

    // cancel and remove the changes
    appInterface_1->cancelGroupChangeSet(groupId);
    changeSet = getCurrentGroupChangeSet(groupId, *store);
    ASSERT_FALSE((bool)changeSet);

}
コード例 #4
0
static int32_t addMissingMetaData(PtrChangeSet changeSet, const string& groupId, const string& binDeviceId, const uint8_t *updateId, SQLiteStoreConv &store)
{
    int32_t result;
    auto groupShared = store.listGroup(groupId, &result);
    auto group = groupShared.get();

    if (!changeSet->has_updatename()) {
        string name = Utilities::getJsonString(group, GROUP_NAME, "");
        changeSet->mutable_updatename()->set_name(name);
        result = prepareChangeSetClocks(groupId, binDeviceId, changeSet, GROUP_SET_NAME, updateId, store, false);
        if (result < 0) {
            return result;
        }
    }

    if (!changeSet->has_updateavatar()) {
        string avatar = Utilities::getJsonString(group, GROUP_AVATAR, "");
        changeSet->mutable_updateavatar()->set_avatar(avatar);
        result = prepareChangeSetClocks(groupId, binDeviceId, changeSet, GROUP_SET_AVATAR, updateId, store, false);
        if (result < 0) {
            return result;
        }
    }
    if (!changeSet->has_updateburn()) {
        auto sec = static_cast<uint64_t>(Utilities::getJsonInt(group, GROUP_BURN_SEC, 0));
        int32_t mode = Utilities::getJsonInt(group, GROUP_BURN_MODE, 0);
        changeSet->mutable_updateburn()->set_burn_ttl_sec(sec);
        changeSet->mutable_updateburn()->set_burn_mode((GroupUpdateSetBurn_BurnMode)mode);
        result = prepareChangeSetClocks(groupId, binDeviceId, changeSet, GROUP_SET_BURN, updateId, store, false);
        if (result < 0) {
            return result;
        }
    }
    return SUCCESS;
}
コード例 #5
0
// This function removes an remove member from the group update
// Function assumes the change set is locked
static bool removeRmNameFromChangeSet(PtrChangeSet& changeSet, const string &name)
{
    if (!changeSet->has_updatermmember()) {
        return true;
    }
    GroupUpdateRmMember *updateRmMember = changeSet->mutable_updatermmember();
    int32_t numberNames = updateRmMember->rmmember_size();

    // Search for a name and remove it. Because repeated fields do not provide
    // a direct Remove(index) we first swap the found element with the last element
    // and then remove the last element.
    for (int32_t i = 0; i < numberNames; ++i) {
        if (name == updateRmMember->rmmember(i).user_id()) {
            updateRmMember->mutable_rmmember()->SwapElements(i, numberNames-1);
            updateRmMember->mutable_rmmember()->RemoveLast();
            break;
        }
    }
    return true;
}
コード例 #6
0
// Function checks for duplicates and ignores them, otherwise adds the name to the group update
// assumes the change set is locked
static bool addRemoveNameToChangeSet(PtrChangeSet& changeSet, const string &name)
{
    GroupUpdateRmMember *updateRmMember = changeSet->mutable_updatermmember();
    int32_t numberNames = updateRmMember->rmmember_size();
    // Check and silently ignore duplicate names
    for (int i = 0; i < numberNames; i++) {
        if (name == updateRmMember->rmmember(i).user_id()) {
            return true;
        }
    }
    Member *member = updateRmMember->add_rmmember();
    member->set_user_id(name);
    return true;
}
コード例 #7
0
static int32_t serializeChangeSet(PtrChangeSet& changeSet, cJSON *root, string *newAttributes)
{
    string serialized;
    if (!changeSet->SerializeToString(&serialized)) {
        return GENERIC_ERROR;
    }
    auto b64Size = static_cast<size_t>(serialized.size() * 2);
    unique_ptr<char[]> b64Buffer(new char[b64Size]);
    if (b64Encode(reinterpret_cast<const uint8_t *>(serialized.data()), serialized.size(), b64Buffer.get(), b64Size) == 0) {
        return GENERIC_ERROR;
    }
    cJSON_AddStringToObject(root, GROUP_CHANGE_SET, b64Buffer.get());
    CharUnique out(cJSON_PrintUnformatted(root));
    newAttributes->assign(out.get());
    return SUCCESS;
}
コード例 #8
0
static int32_t prepareChangeSetClocks(const string &groupId, const string &binDeviceId, PtrChangeSet& changeSet,
                                      GroupUpdateType type, const uint8_t *updateId, SQLiteStoreConv &store,
                                      bool updateClocks = true)
{
    LocalVClock lvc;
    VectorClock<string> vc;

    int32_t result = readLocalVectorClock(store, groupId, type, &lvc);
    if (result == SUCCESS) {        // we may not yet have a vector clock for this group update type, thus deserialize on SUCCESS only
        deserializeVectorClock(lvc.vclock(), &vc);
    }

    // In a first step read the local vector clock for this (group id, update type) tuple
    // increment the clock for our device.
    //
    // In the second step set this new clock to the appropriate update change set.
    if (updateClocks) {
        vc.incrementNodeClock(binDeviceId);
    }

    switch (type) {
        case GROUP_SET_NAME:
            changeSet->mutable_updatename()->set_update_id(updateId, UPDATE_ID_LENGTH);
            serializeVectorClock(vc, changeSet->mutable_updatename()->mutable_vclock());
            break;

        case GROUP_SET_AVATAR:
            changeSet->mutable_updateavatar()->set_update_id(updateId, UPDATE_ID_LENGTH);
            serializeVectorClock(vc, changeSet->mutable_updateavatar()->mutable_vclock());
            break;

        case GROUP_SET_BURN:
            changeSet->mutable_updateburn()->set_update_id(updateId, UPDATE_ID_LENGTH);
            serializeVectorClock(vc, changeSet->mutable_updateburn()->mutable_vclock());
            break;

        default:
            return ILLEGAL_ARGUMENT;

    }
    if (updateClocks) {
        // Now update and persist the local vector clock
        lvc.set_update_id(updateId, UPDATE_ID_LENGTH);
        serializeVectorClock(vc, lvc.mutable_vclock());
        return storeLocalVectorClock(store, groupId, type, lvc);
    }
    return SUCCESS;
}
コード例 #9
0
// Function checks for duplicates and ignores them, otherwise adds the name to the group update
// assumes the change set is locked
static bool addAddNameToChangeSet(PtrChangeSet& changeSet, const string &name, const string &changerId)
{

    GroupUpdateAddMember *updateAddMember = changeSet->mutable_updateaddmember();
    int32_t numberNames = updateAddMember->addmember_size();

    // Check and silently ignore duplicate names
    for (int i = 0; i < numberNames; i++) {
        if (name == updateAddMember->addmember(i).user_id()) {
            return true;
        }
    }
    Member *member = updateAddMember->add_addmember();
    member->set_user_id(name);

    if (!changerId.empty()) {
        updateAddMember->set_user_id(changerId);
    }

    return true;
}
コード例 #10
0
// This is a fairly complex test case. I runs thru a complete cycle:
// - add new members, set group data to create a change set
// - prepare the generic part of change set, update the database
// - prepare the device specific change set
//
// - add other new members, remove a member, change some group data
// - prepare the generic part of the new change set, update the database
// - prepare the device specific change set, this time it should also have data from the
//   previous change set because we don't ACK the data
TEST_F(ChangeSetTestsFixtureMembers, CreateChangeSetTests) {
    // Own user is already in the change set, added while creating the group,
    // has index 0 in add member update

    PtrChangeSet changeSet = getCurrentGroupChangeSet(groupId, *store);

    string binDeviceId;
    makeBinaryDeviceId(appInterface_1->getOwnDeviceId(), &binDeviceId);

    // At first add more members
    appInterface_1->addUser(groupId, memberId_1);
    appInterface_1->addUser(groupId, otherMemberId_1);

    // At this point we have a new group with three members: ownName, memberId_1, otherMemberId_1
    // in this order (alphabetically)

    // Set some group meta data
    ASSERT_EQ(SUCCESS, appInterface_1->setGroupName(groupId, &groupName_1));
    ASSERT_EQ(SUCCESS, appInterface_1->setGroupBurnTime(groupId, 500, 1));
    ASSERT_EQ(SUCCESS, appInterface_1->setGroupAvatar(groupId, &avatar_1));

    // Prepare the change set to apply updates and create non-device specific change set, check data
    ASSERT_EQ(SUCCESS, appInterface_1->prepareChangeSetSend(groupId));

    ASSERT_TRUE(changeSet->has_updatename());
    ASSERT_EQ(1, changeSet->updatename().vclock_size());
    ASSERT_EQ(1, changeSet->updatename().vclock(0).value());
    ASSERT_EQ(binDeviceId, changeSet->updatename().vclock(0).device_id());

    ASSERT_TRUE(changeSet->has_updateavatar());
    ASSERT_EQ(1, changeSet->updateavatar().vclock_size());
    ASSERT_EQ(1, changeSet->updateavatar().vclock(0).value());
    ASSERT_EQ(binDeviceId, changeSet->updateavatar().vclock(0).device_id());

    ASSERT_TRUE(changeSet->has_updateburn());
    ASSERT_EQ(1, changeSet->updateburn().vclock_size());
    ASSERT_EQ(1, changeSet->updateburn().vclock(0).value());
    ASSERT_EQ(binDeviceId, changeSet->updateburn().vclock(0).device_id());

    ASSERT_TRUE(changeSet->has_updateaddmember());
    ASSERT_EQ(3, changeSet->updateaddmember().addmember_size());

    // Preparing the change set also updates the database, adding group data and members
    ASSERT_TRUE(store->hasGroup(groupId, nullptr));

    int32_t result;
    shared_ptr<cJSON> group = store->listGroup(groupId, &result);
    ASSERT_FALSE(SQL_FAIL(result)) << store->getLastError();
    ASSERT_TRUE((bool)group);

    cJSON *root = group.get();
    ASSERT_EQ(groupId, string(Utilities::getJsonString(root, GROUP_ID, "")));
    ASSERT_EQ(avatar_1, string(Utilities::getJsonString(root, GROUP_AVATAR, "")));
    ASSERT_EQ(500, Utilities::getJsonInt(root, GROUP_BURN_SEC, -1));
    ASSERT_EQ(1, Utilities::getJsonInt(root, GROUP_BURN_MODE, -1));

    // List all members of a group, should return a list with size 3 and the correct data
    list<JsonUnique> members;
    result = store->getAllGroupMembers(groupId, members);
    ASSERT_FALSE(SQL_FAIL(result)) << store->getLastError();
    ASSERT_EQ(3, members.size());
    root = members.front().get();
    ASSERT_EQ(groupId, string(Utilities::getJsonString(root, GROUP_ID, "")));
    ASSERT_EQ(ownName, string(Utilities::getJsonString(root, MEMBER_ID, "")));

    members.pop_front();
    root = members.front().get();
    ASSERT_EQ(groupId, string(Utilities::getJsonString(root, GROUP_ID, "")));
    ASSERT_EQ(memberId_1, string(Utilities::getJsonString(root, MEMBER_ID, "")));

    members.pop_front();
    root = members.front().get();
    ASSERT_EQ(groupId, string(Utilities::getJsonString(root, GROUP_ID, "")));
    ASSERT_EQ(otherMemberId_1, string(Utilities::getJsonString(root, MEMBER_ID, "")));


    // Now we have a prepare change set, tested, database looks good
    // Create the device specific change set. Because we do not have another older change set
    // yet, the current 'old' change does not change. There is no data to collapse.

    string attributes;
    string newAttributes;
    appInterface_1->createChangeSetDevice(groupId, longDevId_2, attributes, &newAttributes);
    ASSERT_FALSE(newAttributes.empty());

//    cerr << "new attributes: " << newAttributes << endl;

    appInterface_1->groupUpdateSendDone(groupId);

    // Get the now pending change set, check if it has the expected data
    // Return no change set if no change set for a group id.
    PtrChangeSet pendingChangeSet = getPendingGroupChangeSet(groupId_1, *store);
    ASSERT_FALSE((bool)pendingChangeSet);

    pendingChangeSet = getPendingGroupChangeSet(groupId, *store);
    ASSERT_TRUE((bool)pendingChangeSet);

    ASSERT_TRUE(pendingChangeSet->has_updatename());
    ASSERT_EQ(1, pendingChangeSet->updatename().vclock_size());
    ASSERT_EQ(1, pendingChangeSet->updatename().vclock(0).value());
    ASSERT_EQ(binDeviceId, pendingChangeSet->updatename().vclock(0).device_id());

    ASSERT_TRUE(pendingChangeSet->has_updateavatar());
    ASSERT_EQ(1, pendingChangeSet->updateavatar().vclock_size());
    ASSERT_EQ(1, pendingChangeSet->updateavatar().vclock(0).value());
    ASSERT_EQ(binDeviceId, pendingChangeSet->updateavatar().vclock(0).device_id());

    ASSERT_TRUE(pendingChangeSet->has_updateburn());
    ASSERT_EQ(1, pendingChangeSet->updateburn().vclock_size());
    ASSERT_EQ(1, pendingChangeSet->updateburn().vclock(0).value());
    ASSERT_EQ(binDeviceId, pendingChangeSet->updateburn().vclock(0).device_id());

    ASSERT_TRUE(pendingChangeSet->has_updateaddmember());
    ASSERT_EQ(3, pendingChangeSet->updateaddmember().addmember_size());


    // ********************************************
    // OK, ready for the second part
    // ********************************************

    // At first one more member
    appInterface_1->addUser(groupId, otherMemberId_2);

    changeSet = getCurrentGroupChangeSet(groupId, *store);
    ASSERT_TRUE((bool)changeSet);
    ASSERT_TRUE(changeSet->has_updateaddmember());
    ASSERT_EQ(1, changeSet->updateaddmember().addmember_size());
    ASSERT_EQ(otherMemberId_2, changeSet->updateaddmember().addmember(0).user_id());

    // Now remove a known member (not myself)
    appInterface_1->removeUser(groupId, otherMemberId_1);
    ASSERT_TRUE(changeSet->has_updatermmember());
    ASSERT_EQ(1, changeSet->updatermmember().rmmember_size());
    ASSERT_EQ(otherMemberId_1, changeSet->updatermmember().rmmember(0).user_id());

    // At this point we have a new group with three members: ownName, memberId_1, otherMemberId_1, otherMemberId_2
    // in this order (alphabetically)

//    // Set some group meta data - don't set, done automatically by the prepare change set function
    // in case we have an add member
//    ASSERT_EQ(SUCCESS, appInterface_1->setGroupName(groupId, &groupName_2));
//    ASSERT_EQ(SUCCESS, appInterface_1->setGroupBurnTime(groupId, 600, 1));
//    ASSERT_EQ(SUCCESS, appInterface_1->setGroupAvatar(groupId, &avatar_2));
//
//    // Prepare the change set to apply updates and create non-device specific change set, check data
    ASSERT_EQ(SUCCESS, appInterface_1->prepareChangeSetSend(groupId));
//
//    // The vector clock now must have a value of 2
//    ASSERT_TRUE(changeSet->has_updatename());
//    ASSERT_EQ(1, changeSet->updatename().vclock_size());
//    ASSERT_EQ(2, changeSet->updatename().vclock(0).value());
//    ASSERT_EQ(binDeviceId, changeSet->updatename().vclock(0).device_id());
//
//    ASSERT_TRUE(changeSet->has_updateavatar());
//    ASSERT_EQ(1, changeSet->updateavatar().vclock_size());
//    ASSERT_EQ(2, changeSet->updateavatar().vclock(0).value());
//    ASSERT_EQ(binDeviceId, changeSet->updateavatar().vclock(0).device_id());
//
//    ASSERT_TRUE(changeSet->has_updateburn());
//    ASSERT_EQ(1, changeSet->updateburn().vclock_size());
//    ASSERT_EQ(2, changeSet->updateburn().vclock(0).value());
//    ASSERT_EQ(binDeviceId, changeSet->updateburn().vclock(0).device_id());
//
//    ASSERT_TRUE(changeSet->has_updateaddmember());
//    ASSERT_EQ(1, changeSet->updateaddmember().addmember_size());
//
//    ASSERT_TRUE(changeSet->has_updateaddmember());
//    ASSERT_EQ(1, changeSet->updatermmember().rmmember_size());
//
//    // Preparing the change set also updates the database, adding/removing group data and members
//    ASSERT_TRUE(store->hasGroup(groupId, nullptr));
//
//    group = store->listGroup(groupId, &result);
//    ASSERT_FALSE(SQL_FAIL(result)) << store->getLastError();
//    ASSERT_TRUE((bool)group);
//
//    root = group.get();
//    ASSERT_EQ(groupId, string(Utilities::getJsonString(root, GROUP_ID, "")));
//    ASSERT_EQ(avatar_2, string(Utilities::getJsonString(root, GROUP_AVATAR, "")));
//    ASSERT_EQ(600, Utilities::getJsonInt(root, GROUP_BURN_SEC, -1));
//    ASSERT_EQ(1, Utilities::getJsonInt(root, GROUP_BURN_MODE, -1));

    // List all members of a group, should return a list with size 3 and the correct data
    members.clear();
    result = store->getAllGroupMembers(groupId, members);
    ASSERT_FALSE(SQL_FAIL(result)) << store->getLastError();
    ASSERT_EQ(3, members.size());

    root = members.front().get();
    ASSERT_EQ(groupId, string(Utilities::getJsonString(root, GROUP_ID, "")));
    ASSERT_EQ(ownName, string(Utilities::getJsonString(root, MEMBER_ID, "")));

    members.pop_front();
    root = members.front().get();
    ASSERT_EQ(groupId, string(Utilities::getJsonString(root, GROUP_ID, "")));
    ASSERT_EQ(memberId_1, string(Utilities::getJsonString(root, MEMBER_ID, "")));

    members.pop_front();
    root = members.front().get();
    ASSERT_EQ(groupId, string(Utilities::getJsonString(root, GROUP_ID, "")));
    ASSERT_EQ(otherMemberId_2, string(Utilities::getJsonString(root, MEMBER_ID, "")));

    appInterface_1->createChangeSetDevice(groupId, longDevId_2, attributes, &newAttributes);
    ASSERT_FALSE(newAttributes.empty());

//    cerr << "new attributes: " << newAttributes << endl;

    // Finish creation of change set, manage pending change sets
    appInterface_1->groupUpdateSendDone(groupId);

    // This clears the cache, simulating a fresh start
    ASSERT_TRUE(removeGroupFromPendingChangeSet(groupId));

    // Get the pending change set, check if it has the expected data
    pendingChangeSet = getPendingGroupChangeSet(groupId, *store);
    ASSERT_TRUE((bool)pendingChangeSet);

    // Because we haven't changed the group's metadata the clocks still have value 1
    ASSERT_TRUE(pendingChangeSet->has_updatename());
    ASSERT_EQ(1, pendingChangeSet->updatename().vclock_size());
    ASSERT_EQ(1, pendingChangeSet->updatename().vclock(0).value());
    ASSERT_EQ(binDeviceId, pendingChangeSet->updatename().vclock(0).device_id());

    ASSERT_TRUE(pendingChangeSet->has_updateavatar());
    ASSERT_EQ(1, pendingChangeSet->updateavatar().vclock_size());
    ASSERT_EQ(1, pendingChangeSet->updateavatar().vclock(0).value());
    ASSERT_EQ(binDeviceId, pendingChangeSet->updateavatar().vclock(0).device_id());

    ASSERT_TRUE(pendingChangeSet->has_updateburn());
    ASSERT_EQ(1, pendingChangeSet->updateburn().vclock_size());
    ASSERT_EQ(1, pendingChangeSet->updateburn().vclock(0).value());
    ASSERT_EQ(binDeviceId, pendingChangeSet->updateburn().vclock(0).device_id());

    ASSERT_TRUE(pendingChangeSet->has_updateaddmember());
    ASSERT_EQ(4, pendingChangeSet->updateaddmember().addmember_size());

    ASSERT_TRUE(pendingChangeSet->has_updatermmember());
    ASSERT_EQ(1, pendingChangeSet->updatermmember().rmmember_size());

}
コード例 #11
0
TEST_F(ChangeSetTestsFixtureMembers, AddRemoveMemberTests) {

    // Own user is already in the change set, added while creating the group, has index 0

    PtrChangeSet changeSet = getCurrentGroupChangeSet(groupId, *store);

    // Own user is already in the change set, added while creating the group, has index 0
    // At first add a member, check data
    ASSERT_EQ(SUCCESS, appInterface_1->addUser(groupId, memberId_1));
    ASSERT_EQ(2, changeSet->updateaddmember().addmember_size());
    ASSERT_EQ(memberId_1, changeSet->updateaddmember().addmember(1).user_id());

    // add a second member
    ASSERT_EQ(SUCCESS, appInterface_1->addUser(groupId, otherMemberId_1));
    ASSERT_EQ(3, changeSet->updateaddmember().addmember_size());
    ASSERT_EQ(otherMemberId_1, changeSet->updateaddmember().addmember(2).user_id());

    // Now remove the first added member
    // expect that it is in remove update, and removed from add update thus it is down to 2
    ASSERT_EQ(SUCCESS, appInterface_1->removeUser(groupId, memberId_1));
    ASSERT_TRUE(changeSet->has_updatermmember());
    ASSERT_EQ(1, changeSet->updatermmember().rmmember_size());
    ASSERT_EQ(memberId_1, changeSet->updatermmember().rmmember(0).user_id());

    // check the add update data
    ASSERT_TRUE(changeSet->has_updateaddmember());
    ASSERT_EQ(2, changeSet->updateaddmember().addmember_size());
    ASSERT_EQ(otherMemberId_1, changeSet->updateaddmember().addmember(1).user_id());

    // Now remove another member
    ASSERT_EQ(SUCCESS, appInterface_1->removeUser(groupId, otherMemberId_2));
    ASSERT_TRUE(changeSet->has_updatermmember());
    ASSERT_EQ(2, changeSet->updatermmember().rmmember_size());
    ASSERT_EQ(otherMemberId_2, changeSet->updatermmember().rmmember(1).user_id());

    // now re-add the first member. It should be re-added to add update, removed from
    // remove update
    ASSERT_EQ(SUCCESS, appInterface_1->addUser(groupId, memberId_1));
    ASSERT_TRUE(changeSet->has_updateaddmember());
    ASSERT_EQ(3, changeSet->updateaddmember().addmember_size());
    ASSERT_EQ(memberId_1, changeSet->updateaddmember().addmember(2).user_id());

    // remove update down to one
    ASSERT_TRUE(changeSet->has_updatermmember());
    ASSERT_EQ(1, changeSet->updatermmember().rmmember_size());
    ASSERT_EQ(otherMemberId_2, changeSet->updatermmember().rmmember(0).user_id());
}
コード例 #12
0
TEST_F(ChangeSetTestsFixtureMembers, RemoveMemberTests) {

    PtrChangeSet changeSet = getCurrentGroupChangeSet(groupId, *store);
    ASSERT_TRUE((bool)changeSet);
    ASSERT_FALSE(changeSet->has_updatermmember());

    ASSERT_EQ(DATA_MISSING, appInterface_1->removeUser(Empty, Empty));
    ASSERT_EQ(DATA_MISSING, appInterface_1->removeUser(groupId, Empty));
    ASSERT_EQ(DATA_MISSING, appInterface_1->removeUser(Empty, otherMemberId_1));

    ASSERT_EQ(SUCCESS, appInterface_1->removeUser(groupId, otherMemberId_1));
    ASSERT_TRUE(changeSet->has_updatermmember());
    ASSERT_EQ(1, changeSet->updatermmember().rmmember_size());
    ASSERT_EQ(otherMemberId_1, changeSet->updatermmember().rmmember(0).user_id());

    ASSERT_EQ(SUCCESS, appInterface_1->removeUser(groupId, otherMemberId_2));
    ASSERT_TRUE(changeSet->has_updatermmember());
    ASSERT_EQ(2, changeSet->updatermmember().rmmember_size());
    ASSERT_EQ(otherMemberId_2, changeSet->updatermmember().rmmember(1).user_id());

    // removing a name a second time, ignore silently, no changes in change set
    ASSERT_EQ(SUCCESS, appInterface_1->removeUser(groupId, otherMemberId_2));
    ASSERT_TRUE(changeSet->has_updatermmember());
    ASSERT_EQ(2, changeSet->updatermmember().rmmember_size());
    ASSERT_EQ(otherMemberId_2, changeSet->updatermmember().rmmember(1).user_id());
}
コード例 #13
0
TEST_F(ChangeSetTestsFixtureMembers, AddMemberTests) {
    // Test for illegal parameters
    ASSERT_EQ(DATA_MISSING, appInterface_1->addUser(Empty, Empty));
    ASSERT_EQ(DATA_MISSING, appInterface_1->addUser(groupId, Empty));
    ASSERT_EQ(DATA_MISSING, appInterface_1->addUser(Empty, memberId_1));

    // Invite a user (addUser), own user is already in the change set, added while creating the group, has index 0
    PtrChangeSet changeSet = getCurrentGroupChangeSet(groupId, *store);
    ASSERT_TRUE((bool)changeSet);

    ASSERT_EQ(SUCCESS, appInterface_1->addUser(groupId, memberId_1));
    ASSERT_TRUE(changeSet->has_updateaddmember());
    ASSERT_EQ(2, changeSet->updateaddmember().addmember_size());
    ASSERT_EQ(memberId_1, changeSet->updateaddmember().addmember(1).user_id());

    ASSERT_EQ(SUCCESS, appInterface_1->addUser(groupId, otherMemberId_1));
    ASSERT_TRUE(changeSet->has_updateaddmember());
    ASSERT_EQ(3, changeSet->updateaddmember().addmember_size());
    ASSERT_EQ(otherMemberId_1, changeSet->updateaddmember().addmember(2).user_id());

    ASSERT_EQ(SUCCESS, appInterface_1->addUser(groupId, otherMemberId_2));
    ASSERT_TRUE(changeSet->has_updateaddmember());
    ASSERT_EQ(4, changeSet->updateaddmember().addmember_size());
    ASSERT_EQ(otherMemberId_2, changeSet->updateaddmember().addmember(3).user_id());

    // adding a name a second time, ignore silently, no changes in change set
    ASSERT_EQ(SUCCESS, appInterface_1->addUser(groupId, otherMemberId_2));
    ASSERT_TRUE(changeSet->has_updateaddmember());
    ASSERT_EQ(4, changeSet->updateaddmember().addmember_size());
    ASSERT_EQ(otherMemberId_2, changeSet->updateaddmember().addmember(3).user_id());
}
コード例 #14
0
TEST_F(ChangeSetTestsFixtureSimple, ExistingGroupTests) {
    int32_t result = store->insertGroup(groupId_1, groupName_1, appInterface_1->getOwnUser(), Empty, 0);
    ASSERT_FALSE(SQL_FAIL(result));

    PtrChangeSet changeSet = getCurrentGroupChangeSet(groupId_1, *store);
    ASSERT_TRUE((bool)changeSet);

    ASSERT_EQ(SUCCESS, appInterface_1->setGroupName(groupId_1, &groupName_1));
    ASSERT_TRUE(changeSet->has_updatename());
    ASSERT_EQ(groupName_1, changeSet->updatename().name());

    ASSERT_EQ(SUCCESS, appInterface_1->setGroupName(groupId_1, &groupName_2));
    ASSERT_TRUE(changeSet->has_updatename());
    ASSERT_EQ(groupName_2, changeSet->updatename().name());

    // Empty group name remove the name update
    ASSERT_EQ(SUCCESS, appInterface_1->setGroupName(groupId_1, nullptr));
    ASSERT_FALSE(changeSet->has_updatename());

    // Use some data to set avatar info
    ASSERT_EQ(SUCCESS, appInterface_1->setGroupAvatar(groupId_1, &groupName_2));
    ASSERT_TRUE(changeSet->has_updateavatar());
    ASSERT_EQ(groupName_2, changeSet->updateavatar().avatar());

    // change the data
    ASSERT_EQ(SUCCESS, appInterface_1->setGroupAvatar(groupId_1, &groupName_1));
    ASSERT_TRUE(changeSet->has_updateavatar());
    ASSERT_EQ(groupName_1, changeSet->updateavatar().avatar());

    // Empty avatar info
    ASSERT_EQ(SUCCESS, appInterface_1->setGroupAvatar(groupId_1, nullptr));
    ASSERT_FALSE(changeSet->has_updateavatar());

    // Burn time updates
    ASSERT_EQ(SUCCESS, appInterface_1->setGroupBurnTime(groupId_1, 500, 1));
    ASSERT_TRUE(changeSet->has_updateburn());
    ASSERT_EQ(500, changeSet->updateburn().burn_ttl_sec());
    ASSERT_EQ(1, changeSet->updateburn().burn_mode());

    ASSERT_EQ(SUCCESS, appInterface_1->setGroupBurnTime(groupId_1, 1000, 1));
    ASSERT_TRUE(changeSet->has_updateburn());
    ASSERT_EQ(1000, changeSet->updateburn().burn_ttl_sec());
    ASSERT_EQ(1, changeSet->updateburn().burn_mode());

    ASSERT_EQ(ILLEGAL_ARGUMENT, appInterface_1->setGroupBurnTime(groupId_1, 500, 0));
}
コード例 #15
0
int32_t AppInterfaceImpl::createChangeSetDevice(const string &groupId, const string &deviceId, const string &attributes, string *newAttributes)
{
    LOGGER(DEBUGGING, __func__, " -->");

    if (groupId.empty() || deviceId.empty()) {
        return DATA_MISSING;
    }

    // The attributes string has a serialized change set already, don't process and add the current change set
    // This may happen if ZINA sends an ACK set back to a device
    JsonUnique sharedRoot(!attributes.empty() ? cJSON_Parse(attributes.c_str()) : cJSON_CreateObject());
    cJSON* root = sharedRoot.get();

    if (Utilities::hasJsonKey(root, GROUP_CHANGE_SET)) {
        return SUCCESS;
    }
    unique_lock<mutex> lck(currentChangeSetLock);

    PtrChangeSet changeSet;

    string binDeviceId;
    makeBinaryDeviceId(deviceId, &binDeviceId);
    if (updateInProgress) {
        changeSet = getGroupChangeSet(groupId);
        if (!changeSet) {
            return GROUP_UPDATE_INCONSISTENT;
        }
        // Do we have any updates? If not, remove from current change set map and just return
        if (!changeSet->has_updatename() && !changeSet->has_updateavatar() && !changeSet->has_updateburn()
            && !changeSet->has_updateaddmember() && !changeSet->has_updatermmember() && !changeSet->has_burnmessage()) {
            removeGroupFromChangeSet(groupId);
            return SUCCESS;
        }
    }
    else {
        changeSet = getPendingGroupChangeSet(groupId, *store_);
        if (!changeSet) {
            return SUCCESS;
        }
        // Resend a change set only if a device has pending ACKs for this group.
        return store_->hasWaitAckGroupDevice(groupId, binDeviceId, nullptr) ?
               serializeChangeSet(changeSet, root, newAttributes) : SUCCESS;
    }

    string updateIdString(reinterpret_cast<const char*>(updateIdGlobal), UPDATE_ID_LENGTH);

    auto oldChangeSet = getPendingGroupChangeSet(groupId, *store_);
    if (oldChangeSet) {

        // Collapse older add/remove member group updates into the current one.
        // If the old change set has add new member _and_ the device has not ACK'd it, copy
        // the old member into current change set.
        // Thus if at least one device has not ACK'ed the previous changes then the new changes
        // get these changes as well. If all devices ACK'ed the previous changes, then the new
        // change set will not get the old changes.
        if (oldChangeSet->has_updateaddmember() &&
                store_->hasWaitAck(groupId, binDeviceId, oldChangeSet->updateaddmember().update_id(), GROUP_ADD_MEMBER,
                                   nullptr)) {
            // Use the own addAddName function: skips duplicate names, checks the remove member data
            const int32_t size = oldChangeSet->updateaddmember().addmember_size();
            for (int i = 0; i < size; i++) {
                addAddNameToChangeSet(changeSet, oldChangeSet->updateaddmember().addmember(i).user_id(), "");
            }
        }

        if (oldChangeSet->has_updatermmember() &&
                store_->hasWaitAck(groupId, binDeviceId, oldChangeSet->updatermmember().update_id(), GROUP_REMOVE_MEMBER,
                                   nullptr)) {
            // Use the own addRemoveName function: skips duplicate names, checks the add member data
            const int32_t size = oldChangeSet->updatermmember().rmmember_size();
            for (int i = 0; i < size; i++) {
                addRemoveNameToChangeSet(changeSet, oldChangeSet->updatermmember().rmmember(i).user_id());
            }
        }
    }

    // We may now have an add member update: may have added names from old change set, thus add
    // meta data if necessary.
    if (changeSet->has_updateaddmember()) {
        addMissingMetaData(changeSet, groupId, binDeviceId, updateIdGlobal, *store_);
    }

    int32_t result = serializeChangeSet(changeSet, root, newAttributes);
    if (result != SUCCESS) {
        errorCode_ = result;
        return result;
    }

    // Add WaitForAck records for the enw updates, remove old WaitForAck records for
    // attributes and data that's collapsed into the new change set.

    // Because we send a new group update we can remove older group updates from wait-for-ack.
    // The recent update overwrites older updates. ZINA ignores ACKs for the older updates.
    // Then store a new wait-for-ack record with the current update id.
    if (changeSet->has_updatename()) {
        store_->removeWaitAckWithType(groupId, binDeviceId, GROUP_SET_NAME);
        store_->insertWaitAck(groupId, binDeviceId, updateIdString, GROUP_SET_NAME);
    }
    if (changeSet->has_updateavatar()) {
        store_->removeWaitAckWithType(groupId, binDeviceId, GROUP_SET_AVATAR);
        store_->insertWaitAck(groupId, binDeviceId, updateIdString, GROUP_SET_AVATAR);
    }
    if (changeSet->has_updateburn()) {
        store_->removeWaitAckWithType(groupId, binDeviceId, GROUP_SET_BURN);
        store_->insertWaitAck(groupId, binDeviceId, updateIdString, GROUP_SET_BURN);
    }

    // Wait for ACK for each message burn, we don't collapse message burn changes because
    // this does not overwrite older burn message commands
    if (changeSet->has_burnmessage()) {
        store_->insertWaitAck(groupId, binDeviceId, updateIdString, GROUP_BURN_MESSSAGE);
    }

    // Add wait-for-ack records for add/remove group updates, remove old records.
    // Names are collapsed into new change set.
    if (changeSet->has_updateaddmember()) {
        store_->removeWaitAckWithType(groupId, binDeviceId, GROUP_ADD_MEMBER);
        store_->insertWaitAck(groupId, binDeviceId, updateIdString, GROUP_ADD_MEMBER);
    }
    if (changeSet->has_updatermmember()) {
        store_->removeWaitAckWithType(groupId, binDeviceId, GROUP_REMOVE_MEMBER);
        store_->insertWaitAck(groupId, binDeviceId, updateIdString, GROUP_REMOVE_MEMBER);
    }

    LOGGER(DEBUGGING, __func__, " <-- ");
    return result;
}