仅当所有测试同时执行时,UnitTest 才会失败

UnitTest failing only when all tests get executed at once

我目前在 VS2013 中 运行 Microsoft C++ UnitTest 时遇到一个奇怪的问题。

每次我在 IDE 中执行 Run All 测试时,测试都会失败,但是当我在特定的测试中使用 Run Selected Tests 时,它会成功。

我测试的是我自己的 BinarySearchTree class 的用法。如果失败,将返回异常代码 C0000005(这可能是由于指针的内部链接错误)。

那么这可能是什么原因呢?

额外

最后一段代码的测试失败了,我在其中生成随机 ID 字符串,将它们单独添加到我的 BST 并随机删除它们,直到整个 BST 再次为空。

BinarySearchTree clientList;
Client client;

// test random adding & deletion
std::vector<std::string> idAddList;
for (unsigned int i = 0; i < 100; i++)
    idAddList.push_back(randomID(20));

std::vector<std::string> idRemoveList;
unsigned int id;
while (idAddList.size() > 0)
{
    // pick random id from list and delete it
    id = rand() % idAddList.size();
    client.uniqueID = idAddList.at(id);
    clientList.add(client);
    if (clientList.lastICLResult() == iList::ICLResult::OK)
        idRemoveList.push_back(idAddList.at(id));
    idAddList.erase(idAddList.begin() + id);
}

    while (idRemoveList.size() > 0)
{
    // pick random client from list and delete it
    id = rand() % idRemoveList.size();
    clientList.remove(idRemoveList.at(id));
    if (clientList.lastICLResult() == iList::ICLResult::OK)
        idRemoveList.erase(idRemoveList.begin() + id);
}

Assert::AreEqual<unsigned int>(0, clientList.size());

这里是 randomID() 函数的代码:

std::string randomID(const unsigned int maxLength)
{
    static const char alphanum[] =
        "0123456789"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz"
        "öÖäÄüÜ"
        "!=[]$%&";

    std::string result;
    for (unsigned int i = 0; i < maxLength; i++)
        result += alphanum[rand() % (sizeof(alphanum - 1))];

    return result;
}

与此问题相关的 class-specific 代码是 removeMatch() 函数和相应的 Node class。 BSTClient class 作为其 add() 函数的数据 object,并且仅在其 ID 字符串不在 BST。内部还有函数 fetchMinNode()fetchMaxNode(),它们在 BST 中检索指向 left-most 或 right-most 节点的指针。 main/root 节点称为 m_rootNode,如果 BST 为空,则为该节点分配 nullptr

removeMatch() 函数的情况下,我总是去要删除的节点的右边 child node/subtree,并且 - 如果可能的话 - 重新分配内部节点树具体地点。

void iList::removeMatch(Node* toBeDeletedNode)
{
    if (toBeDeletedNode)
    {
        // IN CASE GIVEN NODE IS THE ROOT NODE
        if (!toBeDeletedNode->parent)
        {
            // Case 1: root node is the only node in the BST
            if (!toBeDeletedNode->left && !toBeDeletedNode->right)
                handleRootLeaf(toBeDeletedNode);

            // Case 2: either both child nodes are available or just the right
            //         child node is available
            else if ((toBeDeletedNode->left && toBeDeletedNode->right) || toBeDeletedNode->right)
                handleRootRSubtree(toBeDeletedNode);

            // Case 3: only left child node is present
            else
                handleRootLSubtree(toBeDeletedNode);
        }

        // IN CASE GIVEN NODE IS ANY CHILD NODE
        else
        {
            // Case 1: if given node is a leaf node
            if (!toBeDeletedNode->left && !toBeDeletedNode->right)
                handleChildLeaf(toBeDeletedNode);

            // Case 2: if given node has either both child nodes or just a right child node
            else if ((toBeDeletedNode->left && toBeDeletedNode->right) || toBeDeletedNode->right)
                handleChildRSubtree(toBeDeletedNode);

            // Case 3: if given node has only a left child node
            else
                handleChildLSubtree(toBeDeletedNode);
        }

        m_elementCount--;
    }
}

这里是相应的私有函数:

[私人]handleRootLSubtree()

void handleRootLSubtree(Node* toBeDeletedNode)
{
    // fetch max node in left subtree
    Node* maxNode = fetchMaxNode(toBeDeletedNode->left);

    // in case left child node is the max node
    if (maxNode->parent != toBeDeletedNode)
    {
        if (maxNode->left)
        {
            maxNode->left->parent = maxNode->parent;
            maxNode->parent->right = maxNode->left;
        }
        else
            maxNode->parent->right = nullptr;

        maxNode->left = toBeDeletedNode->left;
        maxNode->parent->parent = maxNode;
    }

    maxNode->parent = nullptr;

    // cut linkage
    toBeDeletedNode->left = nullptr;
    delete toBeDeletedNode;
    m_rootNode = maxNode;
}

[私人]handleRootRSubtree()

void handleRootRSubtree(Node* toBeDeletedNode)
{
    // fetch min node in right subtree
    Node* minNode = fetchMinNode(toBeDeletedNode->right);

    // in case right child node isn't the min node
    if (minNode->parent != toBeDeletedNode)
    {
        if (minNode->right)
        {
            minNode->right->parent = minNode->parent;
            minNode->parent->left = minNode->right;
        }
        else
            minNode->parent->left = nullptr;

        minNode->right = toBeDeletedNode->right;
        toBeDeletedNode->right->parent = minNode;
    }

    minNode->left = toBeDeletedNode->left;
    minNode->parent = nullptr;

    if (toBeDeletedNode->left)
        toBeDeletedNode->left->parent = minNode;

    // cut linkage
    toBeDeletedNode->left = nullptr;
    toBeDeletedNode->right = nullptr;
    delete toBeDeletedNode;
    m_rootNode = minNode;
}

[私人] handleRootLeaf()

void handleRootLeaf(Node* toBeDeletedNode)
{
    delete m_rootNode;
    m_rootNode = nullptr;
}

[私人]handleChildLSubtree()

void handleChildLSubtree(Node* toBeDeletedNode)
{
    // fetch max node in left subtree
    Node* maxNode = fetchMaxNode(toBeDeletedNode->left);

    // in case left child node is the max node
    if (maxNode->parent == toBeDeletedNode)
        maxNode->parent = toBeDeletedNode->parent;
    else
    {
        if (maxNode->left)
        {
            maxNode->left->parent = maxNode->parent;
            maxNode->parent->right = maxNode->left;
        }
        else
            maxNode->parent->right = nullptr;

        maxNode->left = toBeDeletedNode->left;
        toBeDeletedNode->left->parent = maxNode;
        maxNode->parent = toBeDeletedNode->parent;
    }

    if (toBeDeletedNode->parent->left
        && toBeDeletedNode->parent->left == toBeDeletedNode)
        toBeDeletedNode->parent->left = maxNode;
    else
        toBeDeletedNode->parent->right = maxNode;

    // cut linkage
    toBeDeletedNode->left = nullptr;
    toBeDeletedNode->right = nullptr;
    delete toBeDeletedNode;
}

[私人]handleChildRSubtree()

void handleChildRSubtree(Node* toBeDeletedNode)
{
    // fetch min node in right subtree
    Node* minNode = fetchMinNode(toBeDeletedNode->right);

    // in case right child node is the min node
    if (minNode->parent == toBeDeletedNode)
    {
        if (toBeDeletedNode->left)
        {
            minNode->left = toBeDeletedNode->left;
            toBeDeletedNode->left->parent = minNode;
        }
        minNode->parent = toBeDeletedNode->parent;
    }
    else
    {
        if (minNode->right)
        {
            minNode->right->parent = minNode->parent;
            minNode->parent->left = minNode->right;
        }
        else
            minNode->parent->left = nullptr;

        minNode->left = toBeDeletedNode->left;
        if (toBeDeletedNode->left)
            toBeDeletedNode->left->parent = minNode;
        minNode->right = toBeDeletedNode->right;
        toBeDeletedNode->right->parent = minNode;
        minNode->parent = toBeDeletedNode->parent;
    }

    if (toBeDeletedNode->parent->left
        && toBeDeletedNode->parent->left == toBeDeletedNode)
        toBeDeletedNode->parent->left = minNode;
    else
        toBeDeletedNode->parent->right = minNode;

    // cut linkage
    toBeDeletedNode->left = nullptr;
    toBeDeletedNode->right = nullptr;
    delete toBeDeletedNode;
}

[私人]handleChildLeaf()

void handleChildLeaf(Node* toBeDeletedNode)
{
    if (toBeDeletedNode->parent->left
        && toBeDeletedNode->parent->left == toBeDeletedNode)
        toBeDeletedNode->parent->left = nullptr;
    else
        toBeDeletedNode->parent->right = nullptr;
    delete toBeDeletedNode;
}

Node class:

struct Node
{
    Node() : client(nullptr), parent(nullptr), left(nullptr), right(nullptr) {}
    ~Node();
    Client* client;
    Node*   parent;
    Node*   left;
    Node*   right;
};

错误原因在于handleRootLSubtree()函数:

maxNode->parent->parent = maxNode;

必须替换为

toBeDeletedNode->left->parent = maxNode;