LPMuds.net

LPMuds.net Forums => Design Lab => Topic started by: Maeglin on November 26, 2017, 08:52:37 PM

Title: Testing in LPC
Post by: Maeglin on November 26, 2017, 08:52:37 PM
One of the (in my opinion) critical, but far too often overlooked aspects of software development lies in quality assurance - specifically, ensuring that that software you've written does what it's supposed to do and (even more importantly) when changes are made, things aren't inadvertently broken (ie: regression testing).

When I started rewriting the lib for RealmsMUD, one of the first things I did was implement a simple testing framework. Initially, I would run the tests manually whilst logged in, but it quickly became apparent that long-term, that was an untenable approach. I decided that it'd be nice to execute the tests outside of the running mud. Version 1 (which I'd suggest as a starting point for anyone who wants to try this) of this concept was to simply make use of preload_objects in the driver and call exit when this executed. Then, for any tests, I'd set up the init file with the list of everything I wanted to execute. The driver would load them (and in master.c, I changed the preload stuff to also call executeTests on anything that inherited my test fixture.). The one down side to this is that you will almost certainly have to give your driver's eval limit a hefty boost or it's going to puke if you have any meaningful number of tests in a single file.

I've since hacked the driver (well, my heavily hacked ldmud-3.2.17 driver) so that it can take a single file and evaluate it without having to fall to preload on master.c.

What I'm supplying should be looked at with the following caveat: I've only tested this on the ldmud 3.2.17 built in compat mode. Everything being done should be generic enough to work anywhere, but I make no promises.

Anyway, here's the test fixture:

Code: [Select]
//*****************************************************************************
// Copyright (c) 2017 - Allen Cummings, RealmsMUD, All rights reserved. See
//                      the accompanying LICENSE file for details.
//*****************************************************************************
#include <functionlist.h>

string Pass = "[  PASSED  ] ";
string Fail = "[  FAILED  ] ";

int CurrentTestPassed = 0;
int AnyFailure = 0;
string *ignoreList = ({ "__INIT", "Init", "Setup", "CleanUp" });

/////////////////////////////////////////////////////////////////////////////
public void Init()
{
}

/////////////////////////////////////////////////////////////////////////////
public void Setup()
{
}

/////////////////////////////////////////////////////////////////////////////
public void CleanUp()
{
}

/////////////////////////////////////////////////////////////////////////////
public int executeTests()
{
    Init();
    mixed *tests = functionlist(this_object(), RETURN_FUNCTION_NAME | NAME_INHERITED);
    tests -= ignoreList;

    debug_message(sprintf("\nTesting %s\n", file_name()));
    foreach(string test in tests)
    {
        Setup();
        CurrentTestPassed = 1;

        call_other(this_object(), test);
        debug_message(sprintf("%s %s\n", CurrentTestPassed ? Pass : Fail, test));
        CleanUp();
    }
    debug_message(sprintf("Test executed: %s -> %s\n", file_name(),
        AnyFailure ? Fail : Pass));

    return AnyFailure;
}

/////////////////////////////////////////////////////////////////////////////
public void validateExpect(mixed val1, mixed val2, string msg)
{
    if (!CurrentTestPassed)
    {
        AnyFailure = 1;
        debug_message(Fail + (stringp(msg) ? msg : "") + " -> Actual: " + val2 +
            ", Expected: " + val1 + "\n");
    }
}

/////////////////////////////////////////////////////////////////////////////
public int sortArray(mixed a, mixed b)
{
    string compA;
    string compB;

    if (mappingp(a) && mappingp(b))
    {
        compA = this_object()->convertDataToString(a);
        compB = this_object()->convertDataToString(b);
    }
    else
    {
        compA = to_string(a);
        compB = to_string(b);
    }

    return compA > compB;
}

/////////////////////////////////////////////////////////////////////////////
public string convertDataToString(mixed data)
{
    string ret = "";

    if (objectp(data))
    {
        ret += program_name(data);
    }
    else if (pointerp(data) && sizeof(data))
    {
        ret += "({ ";
        data = sort_array(data, "sortArray");
        foreach(mixed element in data)
        {
            ret += convertDataToString(element) + ", ";
        }
        ret += "})";
    }
    else if (mappingp(data))
    {
        ret += "([ ";
        mixed *indices = sort_array(m_indices(data), "sortArray");
        foreach(mixed index in indices)
        {
            ret += convertDataToString(index) + ": " + convertDataToString(data[index]) + ", ";
        }
        ret += "])";
    }
    else
    {
        ret += to_string(data);
    }
    return ret;
}

/////////////////////////////////////////////////////////////////////////////
public varargs void ExpectEq(mixed val1, mixed val2, string msg)
{
    string parsedVal1 = convertDataToString(val1);
    string parsedVal2 = convertDataToString(val2);

    CurrentTestPassed = parsedVal1 == parsedVal2;
    validateExpect(parsedVal1, parsedVal2, msg);
}

/////////////////////////////////////////////////////////////////////////////
public varargs void ExpectSubStringMatch(string val1, string val2, string msg)
{
    CurrentTestPassed = sizeof(regexp(({ val2 }), val1));
    validateExpect(val1, val2, msg);
}

/////////////////////////////////////////////////////////////////////////////
public varargs void ExpectNotEq(mixed val1, mixed val2, string msg)
{
    string parsedVal1 = convertDataToString(val1);
    string parsedVal2 = convertDataToString(val2);

    CurrentTestPassed = parsedVal1 != parsedVal2;
    validateExpect(parsedVal1, parsedVal2, msg);
}

/////////////////////////////////////////////////////////////////////////////
public varargs void ExpectTrue(mixed val1, string msg)
{
    CurrentTestPassed = val1;
    validateExpect("true", "false", msg);
}

/////////////////////////////////////////////////////////////////////////////
public varargs void ExpectFalse(mixed val1, string msg)
{
    CurrentTestPassed = !val1;
    validateExpect("false", "true", msg);
}

Building tests is pretty straightforward, but since it's caused some confusion with other wizzes on Realms, I figured I'd give an explanation of what you should do.

Here's a couple example tests. This first one exercises the quest module in the player:
Code: [Select]
//*****************************************************************************
// Copyright (c) 2017 - Allen Cummings, RealmsMUD, All rights reserved. See
//                      the accompanying LICENSE file for details.
//*****************************************************************************
inherit "/lib/tests/framework/testFixture.c";
#include "/lib/include/inventory.h"

object Quests;
object QuestItem;

/////////////////////////////////////////////////////////////////////////////
void Setup()
{
    Quests = clone_object("/lib/realizations/player");
    Quests->Name("Bob");

    QuestItem = clone_object("/lib/tests/support/quests/fakeQuestItem.c");
}

/////////////////////////////////////////////////////////////////////////////
void CleanUp()
{
    destruct(Quests);
}

/////////////////////////////////////////////////////////////////////////////
void QuestIsInProgressReturnsFalseWhenQuestIsInvalid()
{
    ExpectFalse(Quests->questIsInProgress("bad/quest.c"));
}

/////////////////////////////////////////////////////////////////////////////
void QuestIsInProgressReturnsFalseWhenQuestIsNotStarted()
{
    ExpectFalse(Quests->questIsInProgress("lib/tests/support/quests/fakeQuestItem.c"));
}

/////////////////////////////////////////////////////////////////////////////
void QuestIsInProgressReturnsTrueWhenQuestHasBeenStarted()
{
    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectTrue(Quests->questIsInProgress("lib/tests/support/quests/fakeQuestItem.c"));
}

/////////////////////////////////////////////////////////////////////////////
void QuestIsInProgressReturnsFalseWhenQuestHasBeenCompleted()
{
    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));

    // Progress the quest to completion
    ExpectTrue(QuestItem->receiveEvent(Quests, "meetTheKing"));
    ExpectTrue(QuestItem->receiveEvent(Quests, "ignoreTheKing"));

    ExpectFalse(Quests->questIsInProgress("lib/tests/support/quests/fakeQuestItem.c"));
}

/////////////////////////////////////////////////////////////////////////////
void QuestIsActiveReturnsFalseWhenQuestIsInvalid()
{
    ExpectFalse(Quests->questIsActive("bad/quest.c"));
}

/////////////////////////////////////////////////////////////////////////////
void QuestIsActiveReturnsFalseWhenQuestIsNotStarted()
{
    ExpectFalse(Quests->questIsActive("lib/tests/support/quests/fakeQuestItem.c"));
}

/////////////////////////////////////////////////////////////////////////////
void QuestIsActiveReturnsTrueWhenQuestHasBeenActivated()
{
    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectTrue(Quests->questIsActive("lib/tests/support/quests/fakeQuestItem.c"));
}

/////////////////////////////////////////////////////////////////////////////
void QuestIsActiveReturnsFalseWhenQuestHasBeenDeactivated()
{
    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectTrue(Quests->deactivateQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectFalse(Quests->questIsActive("lib/tests/support/quests/fakeQuestItem.c"));
}

/////////////////////////////////////////////////////////////////////////////
void QuestIsActiveReturnsTrueWhenQuestHasBeenReactivated()
{
    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectTrue(Quests->deactivateQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectFalse(Quests->questIsActive("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectTrue(Quests->activateQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectTrue(Quests->questIsActive("lib/tests/support/quests/fakeQuestItem.c"));
}

/////////////////////////////////////////////////////////////////////////////
void QuestIsActiveReturnsFalseWhenQuestHasBeenCompleted()
{
    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));

    // Progress the quest to completion
    ExpectTrue(QuestItem->receiveEvent(Quests, "meetTheKing"));
    ExpectTrue(QuestItem->receiveEvent(Quests, "ignoreTheKing"));

    ExpectFalse(Quests->questIsActive("lib/tests/support/quests/fakeQuestItem.c"));
}

/////////////////////////////////////////////////////////////////////////////
void QuestIsCompletedReturnsFalseWhenQuestIsInvalid()
{
    ExpectFalse(Quests->questIsCompleted("bad/quest.c"));
}

/////////////////////////////////////////////////////////////////////////////
void QuestIsCompletedReturnsFalseWhenQuestIsNotStarted()
{
    ExpectFalse(Quests->questIsCompleted("lib/tests/support/quests/fakeQuestItem.c"));
}

/////////////////////////////////////////////////////////////////////////////
void QuestIsCompletedReturnsFalseWhenQuestHasBeenStartedButNotCompleted()
{
    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectFalse(Quests->questIsCompleted("lib/tests/support/quests/fakeQuestItem.c"));
}

/////////////////////////////////////////////////////////////////////////////
void QuestIsCompletedReturnsTrueWhenQuestHasBeenCompleted()
{
    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));

    // Progress the quest to completion
    ExpectTrue(QuestItem->receiveEvent(Quests, "meetTheKing"));
    ExpectTrue(QuestItem->receiveEvent(Quests, "ignoreTheKing"));

    ExpectTrue(Quests->questIsCompleted("lib/tests/support/quests/fakeQuestItem.c"));
}

/////////////////////////////////////////////////////////////////////////////
void QuestStateReturnsFalseWhenQuestIsInvalid()
{
    ExpectFalse(Quests->questState("bad/quest.c"));
}

/////////////////////////////////////////////////////////////////////////////
void QuestStateReturnsFalseWhenQuestIsNotStarted()
{
    ExpectFalse(Quests->questState("lib/tests/support/quests/fakeQuestItem.c"));
}

/////////////////////////////////////////////////////////////////////////////
void QuestStateReturnsCorrectQuestState()
{
    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectEq("meet the king", Quests->questState("lib/tests/support/quests/fakeQuestItem.c"));

    ExpectTrue(QuestItem->receiveEvent(Quests, "meetTheKing"));
    ExpectEq("met the king", Quests->questState("lib/tests/support/quests/fakeQuestItem.c"));

    ExpectTrue(QuestItem->receiveEvent(Quests, "serveTheKing"));
    ExpectEq("serve the king", Quests->questState("lib/tests/support/quests/fakeQuestItem.c"));

    ExpectTrue(QuestItem->receiveEvent(Quests, "maybeNobodyWillNotice"));
    ExpectEq("king is dead", Quests->questState("lib/tests/support/quests/fakeQuestItem.c"));
}

/////////////////////////////////////////////////////////////////////////////
void ActiveQuestsReturnsCorrectListOfQuests()
{
    ExpectEq(({}), Quests->activeQuests());

    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectEq(({ "lib/tests/support/quests/fakeQuestItem.c" }), Quests->activeQuests());

    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/mockQuest.c"));
    ExpectEq(({ "lib/tests/support/quests/fakeQuestItem.c", "lib/tests/support/quests/mockQuest.c" }),
        Quests->activeQuests());

    ExpectTrue(Quests->deactivateQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectEq(({ "lib/tests/support/quests/mockQuest.c" }), Quests->activeQuests());

    ExpectTrue(Quests->activateQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectEq(({ "lib/tests/support/quests/fakeQuestItem.c", "lib/tests/support/quests/mockQuest.c" }),
        Quests->activeQuests());
}

/////////////////////////////////////////////////////////////////////////////
void CompletedQuestsReturnsCorrectListOfQuests()
{
    ExpectEq(({}), Quests->activeQuests());

    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectEq(({ }), Quests->completedQuests());

    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/anotherQuest.c"));
    ExpectEq(({ "lib/tests/support/quests/anotherQuest.c" }), Quests->completedQuests());

    ExpectTrue(Quests->deactivateQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectEq(({ "lib/tests/support/quests/anotherQuest.c" }), Quests->completedQuests());

    ExpectTrue(QuestItem->receiveEvent(Quests, "meetTheKing"));
    ExpectTrue(QuestItem->receiveEvent(Quests, "ignoreTheKing"));
    ExpectEq(({ "lib/tests/support/quests/fakeQuestItem.c", "lib/tests/support/quests/anotherQuest.c" }),
        Quests->completedQuests());
}

/////////////////////////////////////////////////////////////////////////////
void QuestsInProgressReturnsCorrectListOfQuests()
{
    ExpectEq(({}), Quests->activeQuests());

    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectEq(({ "lib/tests/support/quests/fakeQuestItem.c" }), Quests->questsInProgress());

    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/mockQuest.c"));
    ExpectEq(({ "lib/tests/support/quests/fakeQuestItem.c", "lib/tests/support/quests/mockQuest.c" }),
        Quests->questsInProgress());

    ExpectTrue(Quests->deactivateQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectEq(({ "lib/tests/support/quests/fakeQuestItem.c", "lib/tests/support/quests/mockQuest.c" }),
        Quests->questsInProgress());

    ExpectTrue(QuestItem->receiveEvent(Quests, "meetTheKing"));
    ExpectTrue(QuestItem->receiveEvent(Quests, "ignoreTheKing"));
    ExpectEq(({ "lib/tests/support/quests/mockQuest.c" }), Quests->questsInProgress());
}

/////////////////////////////////////////////////////////////////////////////
void ActivateQuestReturnsFalseWhenQuestIsInvalid()
{
    ExpectFalse(Quests->activateQuest("bad/quest.c"));
    ExpectFalse(Quests->questIsActive("lib/tests/support/quests/fakeQuestItem.c"));
}

/////////////////////////////////////////////////////////////////////////////
void ActivateQuestReturnsFalseWhenQuestIsNotStarted()
{
    ExpectFalse(Quests->activateQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectFalse(Quests->questIsActive("lib/tests/support/quests/fakeQuestItem.c"));
}

/////////////////////////////////////////////////////////////////////////////
void ActivateQuestReturnsTrueWhenQuestHasBeenStarted()
{
    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectTrue(Quests->questIsActive("lib/tests/support/quests/fakeQuestItem.c"), "begun quest is active");
    ExpectTrue(Quests->deactivateQuest("lib/tests/support/quests/fakeQuestItem.c"), "quest deactivated");
    ExpectFalse(Quests->questIsActive("lib/tests/support/quests/fakeQuestItem.c", "deactivated quest returns not active"));
    ExpectTrue(Quests->activateQuest("lib/tests/support/quests/fakeQuestItem.c", "activate the quest"));
    ExpectTrue(Quests->questIsActive("lib/tests/support/quests/fakeQuestItem.c"), "re-activated quest is active");
}

/////////////////////////////////////////////////////////////////////////////
void ActivateQuestReturnsFalseWhenQuestHasBeenCompleted()
{
    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectTrue(QuestItem->receiveEvent(Quests, "meetTheKing"));
    ExpectTrue(QuestItem->receiveEvent(Quests, "ignoreTheKing"));

    ExpectFalse(Quests->activateQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectFalse(Quests->questIsActive("lib/tests/support/quests/fakeQuestItem.c"));
}

/////////////////////////////////////////////////////////////////////////////
void DeactivateQuestReturnsFalseWhenQuestIsInvalid()
{
    ExpectFalse(Quests->activateQuest("bad/quest.c"));
    ExpectFalse(Quests->deactivateQuest("lib/tests/support/quests/fakeQuestItem.c"));
}

/////////////////////////////////////////////////////////////////////////////
void DeactivateQuestReturnsFalseWhenQuestIsNotStarted()
{
    ExpectFalse(Quests->activateQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectFalse(Quests->deactivateQuest("lib/tests/support/quests/fakeQuestItem.c"));
}

/////////////////////////////////////////////////////////////////////////////
void DeactivateQuestReturnsTrueWhenQuestHasBeenStarted()
{
    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectTrue(Quests->questIsActive("lib/tests/support/quests/fakeQuestItem.c"), "begun quest is active");
    ExpectTrue(Quests->deactivateQuest("lib/tests/support/quests/fakeQuestItem.c"), "quest deactivated");
    ExpectFalse(Quests->questIsActive("lib/tests/support/quests/fakeQuestItem.c", "deactivated quest returns not active"));
}

/////////////////////////////////////////////////////////////////////////////
void DeactivateQuestReturnsFalseWhenQuestHasBeenCompleted()
{
    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectTrue(QuestItem->receiveEvent(Quests, "meetTheKing"));
    ExpectTrue(QuestItem->receiveEvent(Quests, "ignoreTheKing"));

    ExpectFalse(Quests->deactivateQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectFalse(Quests->questIsActive("lib/tests/support/quests/fakeQuestItem.c"));
}

/////////////////////////////////////////////////////////////////////////////
void BeginQuestFiresOnQuestStartedEvent()
{
    object events = clone_object("/lib/tests/support/events/questEventsSubscriber.c");
    Quests->registerEvent(events);
    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectEq(([ "onQuestStarted":"lib/tests/support/quests/fakeQuestItem.c"]),
        events->quests());
}

/////////////////////////////////////////////////////////////////////////////
void BeginQuestFiresProperEventsWhenInitialStateIsCompletionState()
{
    object events = clone_object("/lib/tests/support/events/questEventsSubscriber.c");
    Quests->registerEvent(events);
    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/anotherQuest.c"));
    ExpectEq((["onQuestStarted":"lib/tests/support/quests/anotherQuest.c",
        "onQuestCompleted": "lib/tests/support/quests/anotherQuest.c",
        "onQuestSucceeded": "lib/tests/support/quests/anotherQuest.c"]),
        events->quests());
}

/////////////////////////////////////////////////////////////////////////////
void AdvanceQuestStateFiresEachTimeStateAdvances()
{
    object events = clone_object("/lib/tests/support/events/questEventsSubscriber.c");
    Quests->registerEvent(events);

    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectFalse(member(events->quests(), "onQuestAdvancedState"));
    ExpectTrue(QuestItem->receiveEvent(Quests, "meetTheKing"));
    ExpectTrue(member(events->quests(), "onQuestAdvancedState"));

    events->clearEvents();
    ExpectFalse(member(events->quests(), "onQuestAdvancedState"));
    ExpectTrue(QuestItem->receiveEvent(Quests, "ignoreTheKing"));
    ExpectTrue(member(events->quests(), "onQuestAdvancedState"));
}

/////////////////////////////////////////////////////////////////////////////
void AdvanceQuestToFailStateFiresOnQuestCompletedAndOnQuestFailed()
{
    object events = clone_object("/lib/tests/support/events/questEventsSubscriber.c");
    Quests->registerEvent(events);

    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectFalse(member(events->quests(), "onQuestCompleted"));
    ExpectFalse(member(events->quests(), "onQuestFailed"));
    ExpectTrue(QuestItem->receiveEvent(Quests, "meetTheKing"));
    ExpectTrue(QuestItem->receiveEvent(Quests, "ignoreTheKing"));
    ExpectTrue(member(events->quests(), "onQuestCompleted"));
    ExpectTrue(member(events->quests(), "onQuestFailed"));
}

/////////////////////////////////////////////////////////////////////////////
void AdvanceQuestToFailStateDoesNotFireOnQuestSucceeded()
{
    object events = clone_object("/lib/tests/support/events/questEventsSubscriber.c");
    Quests->registerEvent(events);

    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectFalse(member(events->quests(), "onQuestSucceeded"));
    ExpectTrue(QuestItem->receiveEvent(Quests, "meetTheKing"));
    ExpectTrue(QuestItem->receiveEvent(Quests, "ignoreTheKing"));
    ExpectFalse(member(events->quests(), "onQuestSucceeded"));
}

/////////////////////////////////////////////////////////////////////////////
void AdvanceQuestToSuccessStateFiresOnQuestCompletedAndOnQuestSucceeded()
{
    object events = clone_object("/lib/tests/support/events/questEventsSubscriber.c");
    Quests->registerEvent(events);

    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectFalse(member(events->quests(), "onQuestCompleted"));
    ExpectFalse(member(events->quests(), "onQuestSucceeded"));
    ExpectTrue(QuestItem->receiveEvent(Quests, "meetTheKing"));
    ExpectTrue(QuestItem->receiveEvent(Quests, "serveTheKing"));
    ExpectTrue(QuestItem->receiveEvent(Quests, "hailToTheKing"));
    ExpectTrue(member(events->quests(), "onQuestCompleted"));
    ExpectTrue(member(events->quests(), "onQuestSucceeded"));
}

/////////////////////////////////////////////////////////////////////////////
void AdvanceQuestToSuccessStateDoesNotFireOnQuestFailed()
{
    object events = clone_object("/lib/tests/support/events/questEventsSubscriber.c");
    Quests->registerEvent(events);

    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectFalse(member(events->quests(), "onQuestFailed"));
    ExpectTrue(QuestItem->receiveEvent(Quests, "meetTheKing"));
    ExpectTrue(QuestItem->receiveEvent(Quests, "serveTheKing"));
    ExpectTrue(QuestItem->receiveEvent(Quests, "hailToTheKing"));
    ExpectFalse(member(events->quests(), "onQuestFailed"));
}

/////////////////////////////////////////////////////////////////////////////
void ActivateQuestFiresOnQuestActivatedWhenItSucceeds()
{
    object events = clone_object("/lib/tests/support/events/questEventsSubscriber.c");
    Quests->registerEvent(events);

    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectFalse(member(events->quests(), "onQuestActivated"));
    ExpectTrue(Quests->activateQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectTrue(member(events->quests(), "onQuestActivated"));
}

/////////////////////////////////////////////////////////////////////////////
void ActivateQuestDoesNotFireOnQuestActivatedWhenItFails()
{
    object events = clone_object("/lib/tests/support/events/questEventsSubscriber.c");
    Quests->registerEvent(events);

    ExpectFalse(member(events->quests(), "onQuestActivated"));
    ExpectFalse(Quests->activateQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectFalse(member(events->quests(), "onQuestActivated"));
}

/////////////////////////////////////////////////////////////////////////////
void DeactivateQuestFiresOnQuestActivatedWhenItSucceeds()
{
    object events = clone_object("/lib/tests/support/events/questEventsSubscriber.c");
    Quests->registerEvent(events);

    ExpectTrue(Quests->beginQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectFalse(member(events->quests(), "onQuestDeactivated"));
    ExpectTrue(Quests->deactivateQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectTrue(member(events->quests(), "onQuestDeactivated"));
}

/////////////////////////////////////////////////////////////////////////////
void DeactivateQuestDoesNotFireOnQuestActivatedWhenItFails()
{
    object events = clone_object("/lib/tests/support/events/questEventsSubscriber.c");
    Quests->registerEvent(events);

    ExpectFalse(member(events->quests(), "onQuestDeactivated"));
    ExpectFalse(Quests->deactivateQuest("lib/tests/support/quests/fakeQuestItem.c"));
    ExpectFalse(member(events->quests(), "onQuestDeactivated"));
}

EDIT: It appears that I'm too verbose and my post was concatenated... doh!
Title: Re: Testing in LPC
Post by: Maeglin on November 26, 2017, 08:59:43 PM
(continued)

This second one exercises the quest state machine. Note the last couple tests (QuestSucceededReturnsTrueWhenQuestCompletesAsSuccess, for example) as they use ancillary methods and fake objects that transition the quest to its various states:

Code: [Select]

//*****************************************************************************
// Copyright (c) 2017 - Allen Cummings, RealmsMUD, All rights reserved. See
//                      the accompanying LICENSE file for details.
//*****************************************************************************
inherit "/lib/tests/framework/testFixture.c";
#include "/lib/include/inventory.h"

object QuestItem;
object King;
object Quester;

/////////////////////////////////////////////////////////////////////////////
void SetUpQuestItem()
{
    QuestItem->testAddState("meet the king", "I've been asked to meet the king!");

    QuestItem->testAddState("met the king",
        "I met King Tantor the Unclean of Thisplace. He seems to like me.");
    QuestItem->testAddTransition("meet the king", "met the king", "meetTheKing",
        "/lib/tests/support/quests/testKingObject.c");

    QuestItem->testAddState("serve the king",
        "The king asked me - ME - to be his personal manservant. Yay me!");
    QuestItem->testAddTransition("met the king", "serve the king", "serveTheKing");

    QuestItem->testAddState("ignore the king",
        "I told the king to piss off. I have socks to fold.");
    QuestItem->testAddTransition("met the king", "ignore the king", "ignoreTheKing");
    QuestItem->testAddEntryAction("ignore the king", "killTheKing");
    QuestItem->testAddFinalState("ignore the king", "failure");

    QuestItem->testAddState("save the king",
        "Earl the Grey tried to kill the king but I gutted him like a fish.");
    QuestItem->testAddTransition("serve the king", "save the king", "hailToTheKing");
    QuestItem->testAddFinalState("save the king", "success");

    QuestItem->testAddState("king is dead",
        "I must lay off the sauce - and the wenches. King Tantor is dead because of my night of debauchery.");
    QuestItem->testAddTransition("serve the king", "king is dead", "maybeNobodyWillNotice");
    QuestItem->testAddFinalState("king is dead", "failure");

    King = clone_object("/lib/tests/support/quests/testKingObject.c");
    QuestItem->testRegisterQuestActor(King);
    King->SetQuestItem(QuestItem);
    King->SetQuester(Quester);

    object room = clone_object("/lib/environment/room.c");
    move_object(King, room);

    QuestItem->testSetInitialState("meet the king");
    Quester->StartQuest(QuestItem);
}

/////////////////////////////////////////////////////////////////////////////
void Init()
{
    ignoreList += ({ "SetUpQuestItem" });
}

/////////////////////////////////////////////////////////////////////////////
void Setup()
{
    Quester = clone_object("/lib/tests/support/services/combatWithMockServices.c");
    Quester->ToggleMockQuests();

    QuestItem = clone_object("/lib/tests/support/quests/testQuestItem.c");
}

/////////////////////////////////////////////////////////////////////////////
void CleanUp()
{
    destruct(King);
    destruct(QuestItem);
    destruct(Quester);
}

/////////////////////////////////////////////////////////////////////////////
void SetNameThrowsWhenNameNotValid()
{
    string err = catch (QuestItem->setName(3));
    ExpectEq("*ERROR - questItem: the name must be a string.", err);
}

/////////////////////////////////////////////////////////////////////////////
void SetNameSetsTheNameOfTheQuestItem()
{
    ExpectFalse(QuestItem->name());
    QuestItem->setName("Test");
    ExpectEq("Test", QuestItem->name());
}

/////////////////////////////////////////////////////////////////////////////
void SetDescriptionThrowsWhenNameNotValid()
{
    string err = catch (QuestItem->setDescription(3));
    ExpectEq("*ERROR - questItem: the description must be a string.", err);
}

/////////////////////////////////////////////////////////////////////////////
void SetDescriptionSetsTheNameOfTheQuestItem()
{
    ExpectEq("", QuestItem->description());
    QuestItem->setDescription("Test");
    ExpectEq("Test", QuestItem->description());
}
/////////////////////////////////////////////////////////////////////////////
void AddStateThrowsWhenStateNotValid()
{
    string err = catch (QuestItem->testAddState());
    ExpectEq("*ERROR - stateMachine: the state could not be added.", err);
}

/////////////////////////////////////////////////////////////////////////////
void AddStateThrowsWhenDescriptionNotValid()
{
    string err = catch (QuestItem->testAddState("blah"));
    ExpectEq("*ERROR - stateMachine: the state could not be added.", err);
}

/////////////////////////////////////////////////////////////////////////////
void AddStateSilentlySucceedsWhenStateIsValid()
{
    // If anything were to go amiss, this would throw.
    QuestItem->testAddState("meet the king", "I've been asked to meet the king!");
}

/////////////////////////////////////////////////////////////////////////////
void AddStateThrowsWhenAddingTheSameStateTwice()
{
    QuestItem->testAddState("meet the king", "I've been asked to meet the king!");
    string err = catch (QuestItem->testAddState("meet the king", "I've been asked to meet the king!"));
    ExpectEq("*ERROR - stateMachine: the 'meet the king' state has already been added.", err);
}

/////////////////////////////////////////////////////////////////////////////
void AddStateThrowsWhenAddingAnInvalidEntryEvent()
{
    string err = catch (QuestItem->testAddState("meet the king", "I've been asked to meet the king!", 25));
    ExpectEq("*ERROR - stateMachine: the entry event must be a string.", err);
}

/////////////////////////////////////////////////////////////////////////////
void AddStateSilentlySucceedsWhenEntryEventIsValid()
{
    // If anything were to go amiss, this would throw.
    QuestItem->testAddState("meet the king", "I've been asked to meet the king!", "someEvent");
}

/////////////////////////////////////////////////////////////////////////////
void AddStateThrowsWhenAddingAnInvalidFinalStateResult()
{
    string err = catch (QuestItem->testAddState("meet the king", "I've been asked to meet the king!", "killTheKing", "blah"));
    ExpectEq("*ERROR - stateMachine: the final state result must be 'success' or 'failure'.", err);
}

/////////////////////////////////////////////////////////////////////////////
void AddStateSilentlySucceedsWhenFinalStateResultIsSuccess()
{
    // If anything were to go amiss, this would throw.
    QuestItem->testAddState("meet the king", "I've been asked to meet the king!", "killTheKing", "success");
}

/////////////////////////////////////////////////////////////////////////////
void AddStateSilentlySucceedsWhenFinalStateResultIsFailure()
{
    // If anything were to go amiss, this would throw.
    QuestItem->testAddState("meet the king", "I've been asked to meet the king!", "killTheKing", "failure");
}
/////////////////////////////////////////////////////////////////////////////
void InitialStateThrowsWhenStateNotPresent()
{
    QuestItem->testAddState("meet the king", "I've been asked to meet the king!");
    string err = catch (QuestItem->testSetInitialState("blah"));
    ExpectEq("*ERROR - stateMachine: the initial state must have been added first.", err);
}

/////////////////////////////////////////////////////////////////////////////
void AddEntryActionThrowsWhenStateNotPresent()
{
    string err = catch (QuestItem->testAddEntryAction("blah", "things"));
    ExpectEq("*ERROR - stateMachine: an entry action can only be added if both the state exists and the method to call has been implemented on this object.", err);
}

/////////////////////////////////////////////////////////////////////////////
void AddEntryActionThrowsWhenAddingAnInvalidEntryAction()
{
    QuestItem->testAddState("meet the king", "I've been asked to meet the king!");

    string err = catch (QuestItem->testAddEntryAction("meet the king", "badMethod"));
    ExpectEq("*ERROR - stateMachine: an entry action can only be added if both the state exists and the method to call has been implemented on this object.", err);
}

/////////////////////////////////////////////////////////////////////////////
void AddEntryActionSilentlySucceedsWhenEverythingValidates()
{
    // If anything were to go amiss, this would throw.
    QuestItem->testAddState("meet the king", "I've been asked to meet the king!");
    QuestItem->testAddEntryAction("meet the king", "killTheKing");
}

/////////////////////////////////////////////////////////////////////////////
void AddExitActionThrowsWhenStateNotPresent()
{
    string err = catch (QuestItem->testAddExitAction("blah", "things"));
    ExpectEq("*ERROR - stateMachine: an exit action can only be added if both the state exists and the method to call has been implemented on this object.", err);
}

/////////////////////////////////////////////////////////////////////////////
void AddExitActionThrowsWhenAddingAnInvalidEntryAction()
{
    QuestItem->testAddState("meet the king", "I've been asked to meet the king!");

    string err = catch (QuestItem->testAddExitAction("meet the king", "badMethod"));
    ExpectEq("*ERROR - stateMachine: an exit action can only be added if both the state exists and the method to call has been implemented on this object.", err);
}

/////////////////////////////////////////////////////////////////////////////
void GetStateDescriptionReturnsDescriptionWhenStateExists()
{
    QuestItem->testAddState("meet the king", "I've been asked to meet the king!");
    ExpectEq("I've been asked to meet the king!", QuestItem->getStateDescription("meet the king"));
}

/////////////////////////////////////////////////////////////////////////////
void GetStateDescriptionReturnsNullWhenStateDoesNotExist()
{
    QuestItem->testAddState("meet the king", "I've been asked to meet the king!");
    ExpectFalse(QuestItem->getStateDescription("blah"));
}

/////////////////////////////////////////////////////////////////////////////
void AddExitActionSilentlySucceedsWhenEverythingValidates()
{
    // If anything were to go amiss, this would throw.
    QuestItem->testAddState("meet the king", "I've been asked to meet the king!");
    QuestItem->testAddExitAction("meet the king", "killTheKing");
}

/////////////////////////////////////////////////////////////////////////////
void SetInitialStateSetsTheInitialState()
{
    // If anything were to go amiss, this would throw.
    QuestItem->testAddState("meet the king", "I've been asked to meet the king!");
    QuestItem->testSetInitialState("meet the king");
    ExpectEq("meet the king", QuestItem->initialState());
}

/////////////////////////////////////////////////////////////////////////////
void AddTransitionThrowsWhenStatesDoNotExist()
{
    string err = catch (QuestItem->testAddTransition("a", "b", "someEvent"));
    ExpectEq("*ERROR - stateMachine: the transition could not be added.", err);
}

/////////////////////////////////////////////////////////////////////////////
void AddTransitionThrowsWhenFirstStateDoesNotExist()
{
    QuestItem->testAddState("b", "do b stuff");
    string err = catch (QuestItem->testAddTransition("a", "b", "someEvent"));
    ExpectEq("*ERROR - stateMachine: the transition could not be added.", err);
}

/////////////////////////////////////////////////////////////////////////////
void AddTransitionThrowsWhenSecondStateDoesNotExist()
{
    QuestItem->testAddState("a", "do a stuff");
    string err = catch (QuestItem->testAddTransition("a", "b", "someEvent"));
    ExpectEq("*ERROR - stateMachine: the transition could not be added.", err);
}

/////////////////////////////////////////////////////////////////////////////
void AddTransitionSilentlySucceedsWhenStatesValidate()
{
    QuestItem->testAddState("a", "do a stuff");
    QuestItem->testAddState("b", "do b stuff");
    QuestItem->testAddTransition("a", "b", "someEvent");
}

/////////////////////////////////////////////////////////////////////////////
void AddTransitionThrowsWhenEventHasAlreadyBeenAdded()
{
    QuestItem->testAddState("a", "do a stuff");
    QuestItem->testAddState("b", "do b stuff");
    QuestItem->testAddTransition("a", "b", "someEvent");

    QuestItem->testAddState("c", "do c stuff");
    string err = catch (QuestItem->testAddTransition("a", "c", "someEvent"));
    ExpectEq("*ERROR - stateMachine: a transition for that event already exists.", err);
}

/////////////////////////////////////////////////////////////////////////////
void AddTransitionThrowsWhenAnInvalidInitiatorIsPassed()
{
    QuestItem->testAddState("a", "do a stuff");
    QuestItem->testAddState("b", "do b stuff");

    string err = catch (QuestItem->testAddTransition("a", "b", "someEvent", "invalidInitiator"));
    ExpectEq("*ERROR - stateMachine: the transition initiator must be a valid program name.", err);
}

/////////////////////////////////////////////////////////////////////////////
void AddTransitionSilentlySucceedsWhenEverythingValidates()
{
    QuestItem->testAddState("a", "do a stuff");
    QuestItem->testAddState("b", "do b stuff");
    QuestItem->testAddTransition("a", "b", "someEvent", "/lib/realizations/living.c");
}

/////////////////////////////////////////////////////////////////////////////
void QuestStoryReturnsCorrectNarrativeForQuestStatesCompleted()
{
    SetUpQuestItem();
    ExpectEq("I've been asked to meet the king! I met King Tantor the Unclean of Thisplace. He seems to like me. The king asked me - ME - to be his personal manservant. Yay me! I told the king to piss off. I have socks to fold. [Failure]",
        QuestItem->questStory(({"meet the king", "met the king", "serve the king", "ignore the king"})));
}

/////////////////////////////////////////////////////////////////////////////
void CanTransitionQuestStates()
{
    SetUpQuestItem();

    ExpectEq("meet the king", Quester->questState(program_name(QuestItem)));

    King->DoMeetTheKingStuff();
    ExpectEq("met the king", Quester->questState(program_name(QuestItem)));
}

/////////////////////////////////////////////////////////////////////////////
void DoesNotTransitionIfNotInProperStateForEvent()
{
    SetUpQuestItem();

    ExpectEq("meet the king", Quester->questState(program_name(QuestItem)));

    King->ConfuseTheForcesOfEvil();
    ExpectEq("meet the king", Quester->questState(program_name(QuestItem)));
}

/////////////////////////////////////////////////////////////////////////////
void OnEnterEventsFireWhenTransitionOccurs()
{
    SetUpQuestItem();
    QuestItem->testAddEntryAction("met the king", "doEnterStuff");
    ExpectEq("meet the king", Quester->questState(program_name(QuestItem)));
    ExpectTrue(sizeof(QuestItem->actionList()) == 0);

    King->DoMeetTheKingStuff();
    ExpectEq("met the king", Quester->questState(program_name(QuestItem)));
    ExpectTrue(sizeof(QuestItem->actionList()) == 1);
    ExpectTrue(member(QuestItem->actionList(), "Enter stuff has been done.") == 0);
}

/////////////////////////////////////////////////////////////////////////////
void OnExitEventsFireWhenTransitionOccurs()
{
    SetUpQuestItem();
    QuestItem->testAddExitAction("meet the king", "doExitStuff");
    ExpectEq("meet the king", Quester->questState(program_name(QuestItem)));
    ExpectTrue(sizeof(QuestItem->actionList()) == 0);

    King->DoMeetTheKingStuff();
    ExpectEq("met the king", Quester->questState(program_name(QuestItem)));
    ExpectTrue(sizeof(QuestItem->actionList()) == 1);
    ExpectTrue(member(QuestItem->actionList(), "Exit stuff has been done.") == 0);
}

/////////////////////////////////////////////////////////////////////////////
void OnEnterAndExitEventsFireInCorrectOrder()
{
    SetUpQuestItem();
    QuestItem->testAddExitAction("meet the king", "doExitStuff");
    QuestItem->testAddEntryAction("met the king", "doEnterStuff");

    ExpectEq("meet the king", Quester->questState(program_name(QuestItem)));
    ExpectTrue(sizeof(QuestItem->actionList()) == 0);

    King->DoMeetTheKingStuff();
    ExpectEq("met the king", Quester->questState(program_name(QuestItem)));
    ExpectTrue(sizeof(QuestItem->actionList()) == 2);
    ExpectTrue(member(QuestItem->actionList(), "Exit stuff has been done.") == 0);
    ExpectTrue(member(QuestItem->actionList(), "Enter stuff has been done.") == 1);
}

/////////////////////////////////////////////////////////////////////////////
void OnEnterDoesNotFireEventWhenNoneSet()
{
    SetUpQuestItem();

    ExpectEq("meet the king", Quester->questState(program_name(QuestItem)));
    ExpectFalse(King->checkNotification());

    King->DoMeetTheKingStuff();
    ExpectEq("met the king", Quester->questState(program_name(QuestItem)));
    ExpectFalse(King->checkNotification());
}

/////////////////////////////////////////////////////////////////////////////
void OnEnterFiresEventWhenItHasBeenSet()
{
    QuestItem->testAddState("meet the king", "I've been asked to meet the king!");

    QuestItem->testAddState("met the king",
        "I met King Tantor the Unclean of Thisplace. He seems to like me.", "someEvent");
    QuestItem->testAddTransition("meet the king", "met the king", "meetTheKing");

    King = clone_object("/lib/tests/support/quests/testKingObject.c");
    object subscriber = clone_object("/lib/tests/support/quests/someEventHandler.c");
    King->registerEvent(subscriber);
    QuestItem->testRegisterQuestActor(King);
    King->SetQuestItem(QuestItem);
    King->SetQuester(Quester);

    object room = clone_object("/lib/environment/room.c");
    move_object(King, room);

    QuestItem->testSetInitialState("meet the king");
    Quester->StartQuest(QuestItem);
    ExpectTrue(QuestItem->beginQuest(Quester));

    ExpectEq("meet the king", Quester->questState(program_name(QuestItem)));
    ExpectFalse(subscriber->checkNotification());

    King->DoMeetTheKingStuff();
    ExpectEq("met the king", Quester->questState(program_name(QuestItem)));
    ExpectEq("someEvent", subscriber->checkNotification());
}

/////////////////////////////////////////////////////////////////////////////
void OnEnterFiresIfSetForInitialState()
{
    QuestItem->testAddState("meet the king", "I've been asked to meet the king!");
    QuestItem->testAddEntryAction("meet the king", "doEnterStuff");

    King = clone_object("/lib/tests/support/quests/testKingObject.c");
    QuestItem->testRegisterQuestActor(King);
    King->SetQuestItem(QuestItem);
    King->SetQuester(Quester);

    object room = clone_object("/lib/environment/room.c");
    move_object(King, room);

    QuestItem->testSetInitialState("meet the king");
    ExpectFalse(member(QuestItem->actionList(), "Enter stuff has been done.") == 0);
    Quester->StartQuest(QuestItem);

    ExpectTrue(QuestItem->beginQuest(Quester));
    ExpectEq("meet the king", Quester->questState(program_name(QuestItem)));
    ExpectTrue(member(QuestItem->actionList(), "Enter stuff has been done.") == 0);
}

/////////////////////////////////////////////////////////////////////////////
void EntryEventFiresIfSetForInitialState()
{
    QuestItem->testAddState("meet the king", "I've been asked to meet the king!", "someEvent");

    King = clone_object("/lib/tests/support/quests/testKingObject.c");
    object subscriber = clone_object("/lib/tests/support/quests/someEventHandler.c");
    King->registerEvent(subscriber);
    QuestItem->testRegisterQuestActor(King);
    King->SetQuestItem(QuestItem);
    King->SetQuester(Quester);

    object room = clone_object("/lib/environment/room.c");
    move_object(King, room);

    QuestItem->testSetInitialState("meet the king");
    ExpectFalse(subscriber->checkNotification());
    Quester->StartQuest(QuestItem);

    ExpectTrue(QuestItem->beginQuest(Quester));
    ExpectEq("meet the king", Quester->questState(program_name(QuestItem)));
    ExpectEq("someEvent", subscriber->checkNotification());
}

/////////////////////////////////////////////////////////////////////////////
void CanBeginQuestReturnsTrueIfNoPrerequisitesSet()
{
    SetUpQuestItem();

    ExpectTrue(QuestItem->canBeginQuest(Quester));
}

/////////////////////////////////////////////////////////////////////////////
void CanBeginQuestReturnsFalseIfPrerequisitesNotMet()
{
    SetUpQuestItem();
    QuestItem->testAddPrerequisite("long sword", (["type":"skill", "value" : 10]));

    ExpectFalse(QuestItem->canBeginQuest(Quester));
}

/////////////////////////////////////////////////////////////////////////////
void CanBeginQuestReturnsTrueIfPrerequisitesMet()
{
    SetUpQuestItem();
    QuestItem->testAddPrerequisite("long sword", (["type":"skill", "value" : 10]));
    Quester->Str(20);
    Quester->addSkillPoints(100);
    Quester->advanceSkill("long sword", 10);
    ExpectTrue(QuestItem->canBeginQuest(Quester));
}

/////////////////////////////////////////////////////////////////////////////
void BeginQuestReturnsFalseIfPrerequisitesNotMet()
{
    SetUpQuestItem();
    QuestItem->testAddPrerequisite("long sword", (["type":"skill", "value" : 10]));

    ExpectFalse(QuestItem->beginQuest(Quester));
}

/////////////////////////////////////////////////////////////////////////////
void BeginQuestReturnsTrueIfPrerequisitesMet()
{
    SetUpQuestItem();
    QuestItem->testAddPrerequisite("long sword", (["type":"skill", "value" : 10]));
    Quester->Str(20);
    Quester->addSkillPoints(100);
    Quester->advanceSkill("long sword", 10);
    ExpectTrue(QuestItem->beginQuest(Quester));
}

/////////////////////////////////////////////////////////////////////////////
void QuestInCompletionStateReturnsTrueWhenQuestCompletesAsFailure()
{
    SetUpQuestItem();

    ExpectFalse(QuestItem->questInCompletionState(Quester->questState(program_name(QuestItem))));

    King->DoMeetTheKingStuff();
    ExpectFalse(QuestItem->questInCompletionState(Quester->questState(program_name(QuestItem))));

    King->TimeToFoldSocks();
    ExpectEq("ignore the king", Quester->questState(program_name(QuestItem)));
    ExpectTrue(QuestItem->questInCompletionState(Quester->questState(program_name(QuestItem))));
}

/////////////////////////////////////////////////////////////////////////////
void QuestSucceededReturnsFalseWhenQuestCompletesAsFailure()
{
    SetUpQuestItem();

    ExpectFalse(QuestItem->questSucceeded(Quester));

    King->DoMeetTheKingStuff();
    King->TimeToFoldSocks();
    ExpectTrue(QuestItem->questInCompletionState(Quester->questState(program_name(QuestItem))));
    ExpectFalse(QuestItem->questSucceeded(Quester));
}

/////////////////////////////////////////////////////////////////////////////
void QuestInCompletionStateReturnsTrueWhenQuestCompletesAsSuccess()
{
    SetUpQuestItem();

    ExpectFalse(QuestItem->questInCompletionState(Quester->questState(program_name(QuestItem))));

    King->DoMeetTheKingStuff();
    ExpectFalse(QuestItem->questInCompletionState(Quester->questState(program_name(QuestItem))));

    King->SureIWillServe();
    ExpectFalse(QuestItem->questInCompletionState(Quester->questState(program_name(QuestItem))));

    King->ConfuseTheForcesOfEvil();
    ExpectTrue(QuestItem->questInCompletionState(Quester->questState(program_name(QuestItem))));
}

/////////////////////////////////////////////////////////////////////////////
void QuestSucceededReturnsTrueWhenQuestCompletesAsSuccess()
{
    SetUpQuestItem();

    ExpectFalse(QuestItem->questSucceeded(Quester));

    King->DoMeetTheKingStuff();
    ExpectFalse(QuestItem->questSucceeded(Quester));

    King->SureIWillServe();
    ExpectFalse(QuestItem->questSucceeded(Quester));

    King->ConfuseTheForcesOfEvil();
    ExpectTrue(QuestItem->questSucceeded(Quester));
}

Execution, then, will show the test executed and whether or not it passed. If it fails, it will report how it failed:

Code: [Select]
Compiling file: /lib/tests/items/shopSelectorTest.c
Testing lib/tests/items/shopSelectorTest
[  PASSED  ]  TopLevelMenuDisplaysCorrectly
[  PASSED  ]  DescriptionsAreDisplayed
[  PASSED  ]  SelectingPurchaseDisplaysBuyMenu
[  PASSED  ]  SelectingSellItemsDisplaysSellMenu
[  PASSED  ]  SelectingExitExitsSelector
Test executed: lib/tests/items/shopSelectorTest -> [  PASSED  ]
 -> built in 57 ms
Compiling file: /lib/tests/items/buyItemSelectorTest.c
Testing lib/tests/items/buyItemSelectorTest
[  PASSED  ]  TopLevelMenuDisplaysCorrectly
[  PASSED  ]  SelectingExitExitsTheMenu
[  PASSED  ]  DescribeItemDisplaysCorrectMessage
[  FAILED  ]  -> Actual: Buy Items - Select an item to buy:
-=-=-=-=-=-=-= Name =-=-=-=-=-=-=- Cost -=-=-= Item Details =-=-=-=-=-=-=-=-=-
[1]  - Bastard sword                650    Attack:  5, Damage: 12, Defense:  3
[2]  - Bastard sword of Good       5650    Attack:  5, Damage: 12, Defense:  3
[3]  - Broad sword of Good        20824    Attack:  4, Damage: 10, Defense:  5
[4]  - Broad sword                  500    Attack:  4, Damage: 10, Defense:  4
[5]  - Broad sword                 5500    Attack:  4, Damage: 10, Defense:  4
[6]  - Claymore                     750    Attack:  5, Damage: 16, Defense:  4
[7]  - Claymore of Energy         13674    Attack:  7, Damage: 18, Defense:  6
[8]  - Great sword of Energy      70312    Attack: 12, Damage: 22, Defense: 11
[9]  - Harpe of Poison            13375    Attack: 10, Damage: 14, Defense:  5
[10] - Katana of Air               3189    Attack: 11, Damage: 11, Defense:  3
[11] - Long sword                   450    Attack:  5, Damage: 10, Defense:  2
[12] - Long sword of Energy       12950    Attack:  5, Damage: 10, Defense:  2
[13] - Machete of Evil            12825    Attack:  4, Damage:  5, Defense:  2
[14] - Machete of Water           17825    Attack:  4, Damage:  5, Defense:  2
[15] - Sabre of Undead            26124    Attack:  7, Damage: 10, Defense:  6
[16] - Sabre of Acid              70724    Attack: 11, Damage: 14, Defense: 10
[17] - Scimitar of Water          17914    Attack:  6, Damage:  9, Defense:  2
[18] - Scimitar of Earth          34574    Attack:  7, Damage: 10, Defense:  4
[19] - Short sword                  350    Attack:  5, Damage:  6, Defense:  3
[20] - Short sword of Air          2839    Attack:  4, Damage:  5, Defense:  3
[21] - Return to previous menu
You must select a number from 1 to 21.
For details on a given choice, type 'describe X' (or '? X') where
X is the option about which you would like further details.
, Expected: You must select a number from 1 to 21
[  FAILED  ]  SelectSubMenuDisplaysBuyList
[  PASSED  ]  DescribeShowsItemDetails
[  PASSED  ]  PurchaseWithInsufficientFundsFails
[  PASSED  ]  PurchaseWithSufficientFundsSubtractsMoneyAndAddsItemToPlayerInventory
[  PASSED  ]  PurchaseOfPermanentItemDoesNotRemoveFromStore
[  PASSED  ]  PurchaseOfNonPermanentItemRemovesItFromStore
Test executed: lib/tests/items/buyItemSelectorTest -> [  FAILED  ]
 -> built in 527 ms
Title: Re: Testing in LPC
Post by: silenus on December 04, 2017, 07:59:03 AM
I think I could see this being used for mudlib code. For realms and domain code I think it might be a bit harder to teach content coders to do this properly but it's still a good idea. In the past I have noticed that a many content coders simply don't have much of a technical background so they might need quite a bit of help in implementing proper testing. Neat idea though.