// Copyright (c) 2020 Metrolab Technology S.A., Geneva, Switzerland (www.metrolab.com)
// See the included file LICENSE.txt for the licensing conditions.

//////////////////////////////////////////////////////////////////////////
/// \file
/// \brief Test code for THM1176 Instrument Manager: Device enumeration, connect

#include <regex>
#include <thread>
#include <chrono>
#include <iostream>

#include <QtTest>
#include <QSignalSpy>

#include "CTHM1176InstrumentManager.h"

#define THM1176_PROMPT_TIME         10      // s
#define THM1176_BOOT_N_SCAN_TIME	15000   // ms
#define THM1176_CONNECT_TIME		10000    // ms
#define THM1176_SCAN_INTERVAL		1000    // ms
#define THM1176_SILLY_RESOURCE_NAME	"Humpty_Dumpty_had_a_great_fall"

using namespace MTL;

static CTHM1176InstrumentManager	Manager;

//----------------------------------------------------------------------//
//	Utility functions													//
//----------------------------------------------------------------------//
static void PromptAndWait(std::string Prompt)
{
    std::string l_Space     = ("" == Prompt) ? "" : " ";
    std::string l_Message   = ">>> " + Prompt + l_Space + "(Will continue in " + std::to_string(THM1176_PROMPT_TIME) + " seconds) <<<";
    QWARN(" ");
    QWARN(l_Message.c_str());
    QThread::currentThread()->sleep(THM1176_PROMPT_TIME);
}

//----------------------------------------------------------------------//
//	Test class definition												//
//----------------------------------------------------------------------//
class THM1176IM_Test01_Connect: public QObject
{
    Q_OBJECT

public:
    THM1176IM_Test01_Connect();
    ~THM1176IM_Test01_Connect();

private slots:
    /// \brief Test case initialization.
    void initTestCase(void);

    /// \brief Test case cleanup.
    void cleanupTestCase(void);

    /// \brief Test NotifyInstrumentList.
    void TestNotifyInstrumentList(void);

    /// \brief Test SetCurrentInstrument.
    void TestSetCurrentInstrument(void);

};

//----------------------------------------------------------------------//
//	Test case constructor and destructor								//
//----------------------------------------------------------------------//
THM1176IM_Test01_Connect::THM1176IM_Test01_Connect()
{

}

THM1176IM_Test01_Connect::~THM1176IM_Test01_Connect()
{

}

//----------------------------------------------------------------------//
//	Test case initialization and cleanup								//
//----------------------------------------------------------------------//
void THM1176IM_Test01_Connect::initTestCase(void)
{
    Manager.Start();
}

void THM1176IM_Test01_Connect::cleanupTestCase(void)
{
    Manager.Stop();
}

//////////////////////////////////////////////////////////////////////////
/// \test   Test THM1176 Instrument Manager: NotifyInstrumentList
///
///	- Plug in 1 THM: should emit NotifyInstrumentList with 1 THM
///	- Plug in second THM: should emit NotifyInstrumentList with
///		2 THMs
///	- Unplug both THMs: should emit (depending on how fast they were
///		unplugged) 1x or 2x NotifyInstrumentList, ending with no THMs
void THM1176IM_Test01_Connect::TestNotifyInstrumentList(void)
{
    QSignalSpy		l_NotifyInstrumentListSpy(&Manager, SIGNAL(NotifyInstrumentList(CResourceList)));
    QSignalSpy		l_NotifyErrorListSpy(&Manager, SIGNAL(NotifyErrorList(CErrorList)));
    QList<QVariant>	l_Arguments;
    CResourceList	l_ResourceList;

    // Have the user plug in the first THM, and wait for it to boot.
    PromptAndWait("Please plug in the first THM");
    l_NotifyInstrumentListSpy.wait(THM1176_BOOT_N_SCAN_TIME);

    // Make sure the signal was emitted exactly one time, and that
    // there is exactly one resource.
    QCOMPARE(l_NotifyInstrumentListSpy.count(), 1);

    l_Arguments = l_NotifyInstrumentListSpy.takeFirst();
    l_ResourceList = qvariant_cast<CResourceList>(l_Arguments.at(0));

    QVERIFY(l_ResourceList.size() == 1);

    // Have the user plug in a second THM, and wait for it to boot.
    PromptAndWait("Please plug in the second THM");
    l_NotifyInstrumentListSpy.wait(THM1176_BOOT_N_SCAN_TIME);

    // Make sure the signal was emitted exactly one time, and that
    // there are exactly two resources.
    QCOMPARE(l_NotifyInstrumentListSpy.count(), 1);

    l_Arguments = l_NotifyInstrumentListSpy.takeFirst();
    l_ResourceList = qvariant_cast<CResourceList>(l_Arguments.at(0));
    QVERIFY(l_ResourceList.size() == 2);

    // Have the user unplug the THMs, and wait for one scan.
    PromptAndWait("Please unplug both THMs");
    l_NotifyInstrumentListSpy.wait(THM1176_SCAN_INTERVAL);

    // Make sure the signal was emitted once or twice, and that there are
    // no resources left.
    QVERIFY(l_NotifyInstrumentListSpy.count() == 1 || l_NotifyInstrumentListSpy.count() == 2);
    l_Arguments = l_NotifyInstrumentListSpy.takeFirst();
    if (l_NotifyInstrumentListSpy.count() == 1)
        l_Arguments = l_NotifyInstrumentListSpy.takeFirst();
    l_ResourceList = qvariant_cast<CResourceList>(l_Arguments.at(0));
    QVERIFY(l_ResourceList.size() == 0);

    // Make sure we got no errors.
    QCOMPARE(l_NotifyErrorListSpy.count(), 0);

} // THM1176IM_Test01_Connect::TestNotifyInstrumentList

//////////////////////////////////////////////////////////////////////////
/// \test   Test THM1176 Instrument Manager: SetCurrentInstrument.
///
///	- Plug in 2 THMs: should emit NotifyInstrumentList 1x or 2x
///	- Emit SetCurrentInstrument to select THM 1:
///		should emit NotifyCurrentInstrument, NotifyIdentification,
///		NotifyRangeList, NotifyUnitsList, NotifyDivisorList,
///		NotifyAveragingParmBounds, NotifyTriggerParmBounds,
///		NotifyRangeParmBounds, NotifyAveragingParms, NotifyTriggerParms
///		NotifyOutputSelect, NotifySleepParm, NotifyUnits,
///		NotifyUseCalibration, NotifyRangeParms, NotifyCommFormat,
///		NotifyOperatingMode
///	- Emit SetCurrentInstrument to select THM 1 again:
///		should emit none of the above signals
///	- Emit SetCurrentInstrument for nonexistent THM:
///		should emit a NotifyErrorList. Nothing else.
///	- Emit SetCurrentInstrument to select THM 2:
///		should emit the above signals twice: first null, then non-null
///	- Unplug THM 2: should emit NotifyInstrumentList and
///		the above signals, all with empty lists
///	- Emit SetCurrentInstrument for unplugged THM:
///		should emit none of the above signals, plus
///		NotifyErrorList
///	- Emit SetCurrentInstrument to select THM 1:
///		should emit above signals
///	- Emit SetOperatingMode to diconnect THM:
///		should emit the above signals, all with empty lists
void THM1176IM_Test01_Connect::TestSetCurrentInstrument(void)
{
    // Create Signal Spies to retrieve instrument list, instrument info, parameters and possible errors.
    QSignalSpy	l_NotifyInstrumentListSpy(&Manager, SIGNAL(NotifyInstrumentList(CResourceList)));
    QSignalSpy	l_NotifyCurrentInstrumentSpy(&Manager, SIGNAL(NotifyCurrentInstrument(tResourceName)));

    QSignalSpy	l_NotifyIdentificationSpy(&Manager, SIGNAL(NotifyIdentification(sIdentifier)));
    QSignalSpy	l_NotifyRangeListSpy(&Manager, SIGNAL(NotifyRangeList(CFluxList)));
    QSignalSpy	l_NotifyUnitsListSpy(&Manager, SIGNAL(NotifyUnitsList(CTHM1176UnitsList)));
    QSignalSpy	l_NotifyDivisorListSpy(&Manager, SIGNAL(NotifyDivisorList(CDivisorList)));
    QSignalSpy	l_NotifyAveragingParmBoundsSpy(&Manager, SIGNAL(NotifyAveragingParmBounds(sAveraging<sBoundedParm>)));
    QSignalSpy	l_NotifyTriggerParmBoundsSpy(&Manager, SIGNAL(NotifyTriggerParmBounds(sInputTrigger<sBoundedParm>)));
    QSignalSpy	l_NotifyRangeParmBoundsSpy(&Manager, SIGNAL(NotifyRangeParmBounds(sRange<sBoundedParm>)));

    QSignalSpy	l_NotifyAveragingParmsSpy(&Manager, SIGNAL(NotifyAveragingParms(sAveraging<uParm>)));
    QSignalSpy	l_NotifyTriggerParmsSpy(&Manager, SIGNAL(NotifyTriggerParms(sInputTrigger<uParm>)));
    QSignalSpy	l_NotifyOutputSelectSpy(&Manager, SIGNAL(NotifyOutputSelect(sArbitraryMeasurements)));
    QSignalSpy	l_NotifySleepParmSpy(&Manager, SIGNAL(NotifySleepParm(bool)));
    QSignalSpy	l_NotifyUnitsSpy(&Manager, SIGNAL(NotifyUnits(eTHM1176Units)));
    QSignalSpy	l_NotifyRangeParmsSpy(&Manager, SIGNAL(NotifyRangeParms(sRange<uParm>)));
    QSignalSpy	l_NotifyCommFormatSpy(&Manager, SIGNAL(NotifyCommFormat(eCommunicationFormat)));

    QSignalSpy	l_NotifyOperatingModeSpy(&Manager, SIGNAL(NotifyOperatingMode(eTHM1176OperatingMode)));
    QSignalSpy	l_NotifyErrorListSpy(&Manager, SIGNAL(NotifyErrorList(CErrorList)));

    // Local variables.
    QList<QVariant>					l_Arguments;

    CResourceList					l_ResourceList;
    tResourceName					l_CurrentInstrument;
    tResourceName					l_FormerInstrument;
    sIdentifier						l_Identification;
    CFluxList						l_RangeList;
    CTHM1176UnitsList						l_UnitsList;
    CDivisorList					l_DivisorList;
    sAveraging<sBoundedParm>		l_AveragingBounds;
    sInputTrigger<sBoundedParm>		l_TriggerBounds;
    sRange<sBoundedParm>			l_RangeBounds;
    eTHM1176OperatingMode			l_CurrentOperatingMode;
    CErrorList						l_LatestErrors;

    sAveraging<uParm>				l_AveragingParms;
    sInputTrigger<uParm>			l_Trigger;
    sArbitraryMeasurements			l_OutputSelect;
    bool							l_SleepParm;
    eTHM1176Units							l_Units;
    bool							l_UseCalibration;
    sRange<uParm>					l_RangeParms;
    eCommunicationFormat			l_CommFormat;

    // Have the user plug in two THMs, and wait for them to boot.
    // Make sure the NotifyInstrumentList signal was emitted once or twice,
    // and that there are two resources in the end.
    PromptAndWait("Please plug in two THMs");
    for (int i = 0; i < 2; i++)
    {
        // Wait for a signal.
        l_NotifyInstrumentListSpy.wait(THM1176_BOOT_N_SCAN_TIME);

        // Possibly we got 2 signals; take the last one.
        QVERIFY(l_NotifyInstrumentListSpy.count() == 1 || l_NotifyInstrumentListSpy.count() == 2);
        l_Arguments = l_NotifyInstrumentListSpy.takeFirst();
        if (l_NotifyInstrumentListSpy.count() == 1)
            l_Arguments = l_NotifyInstrumentListSpy.takeFirst();
        l_ResourceList = qvariant_cast<CResourceList>(l_Arguments.at(0));

        // If we got 2 VISA resources on the first try, all is well.
        // Otherwise loop back to wait for the second THM to boot.
        if (l_ResourceList.size() == 2) break;
    }
    QCOMPARE(static_cast<int>(l_ResourceList.size()), 2);

    // Emit SetCurrentInstrument to select THM 1.
    // Make sure we got all the appropriate signals, with non-null results.
    Manager.SetCurrentInstrument(l_ResourceList[0]);

    // Wait for this instrument to become idle.
    l_NotifyOperatingModeSpy.wait(THM1176_CONNECT_TIME);
    QCOMPARE(l_NotifyOperatingModeSpy.count(), 1);
    l_Arguments = l_NotifyOperatingModeSpy.takeFirst();
    l_CurrentOperatingMode = qvariant_cast<eTHM1176OperatingMode>(l_Arguments.at(0));
    QCOMPARE(l_CurrentOperatingMode, kTHM1176Idle);

    // Retrieve current instrument.
    l_FormerInstrument = l_CurrentInstrument;
    QCOMPARE(l_NotifyCurrentInstrumentSpy.count(), 1);
    l_Arguments = l_NotifyCurrentInstrumentSpy.takeFirst();
    l_CurrentInstrument = qvariant_cast<tResourceName>(l_Arguments.at(0));
    QCOMPARE(l_CurrentInstrument, l_ResourceList[0]);

    // Retrieve instrument information and parameter bounds for this THM.
    QCOMPARE(l_NotifyIdentificationSpy.count(), 1);
    l_Arguments = l_NotifyIdentificationSpy.takeFirst();
    l_Identification = qvariant_cast<sIdentifier>(l_Arguments.at(0));
    QVERIFY(!l_Identification.Manufacturer.empty() && !l_Identification.Model.empty() && l_Identification.SerialNumber != 0);

    QCOMPARE(l_NotifyRangeListSpy.count(), 1);
    l_Arguments = l_NotifyRangeListSpy.takeFirst();
    l_RangeList = qvariant_cast<CFluxList>(l_Arguments.at(0));
    QVERIFY(!l_RangeList.empty());

    QCOMPARE(l_NotifyUnitsListSpy.count(), 1);
    l_Arguments		= l_NotifyUnitsListSpy.takeFirst();
    l_UnitsList		= qvariant_cast<CTHM1176UnitsList>(l_Arguments.at(0));
    QVERIFY(!l_UnitsList.empty());

    QCOMPARE(l_NotifyDivisorListSpy.count(), 1);
    l_Arguments		= l_NotifyDivisorListSpy.takeFirst();
    l_DivisorList	= qvariant_cast<CDivisorList>(l_Arguments.at(0));
    QVERIFY(!l_DivisorList.empty());

    QCOMPARE(l_NotifyAveragingParmBoundsSpy.count(), 1);
    l_Arguments = l_NotifyAveragingParmBoundsSpy.takeFirst();
    l_AveragingBounds = qvariant_cast<sAveraging<sBoundedParm>>(l_Arguments.at(0));
    QVERIFY(l_AveragingBounds.NoPoints.Val != 0 && l_AveragingBounds.NoPoints.Min != 0 &&
            l_AveragingBounds.NoPoints.Max != 0 && l_AveragingBounds.NoPoints.Def != 0);

    QCOMPARE(l_NotifyTriggerParmBoundsSpy.count(), 1);
    l_Arguments = l_NotifyTriggerParmBoundsSpy.takeFirst();
    l_TriggerBounds = qvariant_cast<sInputTrigger<sBoundedParm>>(l_Arguments.at(0));
    QVERIFY(l_TriggerBounds.Period_s.Val != 0 && l_TriggerBounds.Period_s.Min != 0 &&
            l_TriggerBounds.Period_s.Max != 0 && l_TriggerBounds.Period_s.Def != 0 &&
            l_TriggerBounds.Count.Val != 0 && l_TriggerBounds.Count.Min != 0 &&
            l_TriggerBounds.Count.Max != 0 && l_TriggerBounds.Count.Def != 0);

    QCOMPARE(l_NotifyRangeParmBoundsSpy.count(), 1);
    l_Arguments = l_NotifyRangeParmBoundsSpy.takeFirst();
    l_RangeBounds = qvariant_cast<sRange<sBoundedParm>>(l_Arguments.at(0));
    QVERIFY(l_RangeBounds.Range.Val != 0 && l_RangeBounds.Range.Min != 0 &&
            l_RangeBounds.Range.Max != 0 && l_RangeBounds.Range.Def != 0);

    // Retrieve the current parameters for this instrument.
    QCOMPARE(l_NotifyAveragingParmsSpy.count(), 1);
    l_Arguments = l_NotifyAveragingParmsSpy.takeFirst();
    l_AveragingParms = qvariant_cast<sAveraging<uParm>>(l_Arguments.at(0));
    QVERIFY(l_AveragingParms.NoPoints > 0);

    QCOMPARE(l_NotifyTriggerParmsSpy.count(), 1);
    l_Arguments = l_NotifyTriggerParmsSpy.takeFirst();
    l_Trigger = qvariant_cast<sInputTrigger<uParm>>(l_Arguments.at(0));
    QVERIFY(l_Trigger.Period_s > 0. && l_Trigger.Count > 0);

    QCOMPARE(l_NotifyOutputSelectSpy.count(), 1);
    l_Arguments = l_NotifyOutputSelectSpy.takeFirst();
    l_OutputSelect = qvariant_cast<sArbitraryMeasurements>(l_Arguments.at(0));

    QCOMPARE(l_NotifySleepParmSpy.count(), 1);
    l_Arguments = l_NotifySleepParmSpy.takeFirst();
    l_SleepParm = qvariant_cast<bool>(l_Arguments.at(0));

    QCOMPARE(l_NotifyUnitsSpy.count(), 1);
    l_Arguments = l_NotifyUnitsSpy.takeFirst();
    l_Units = qvariant_cast<eTHM1176Units>(l_Arguments.at(0));

    QCOMPARE(l_NotifyRangeParmsSpy.count(), 1);
    l_Arguments = l_NotifyRangeParmsSpy.takeFirst();
    l_RangeParms = qvariant_cast<sRange<uParm>>(l_Arguments.at(0));

    QCOMPARE(l_NotifyCommFormatSpy.count(), 1);
    l_Arguments = l_NotifyCommFormatSpy.takeFirst();
    l_CommFormat = qvariant_cast<eCommunicationFormat>(l_Arguments.at(0));

    // We expect no errors.
    QCOMPARE(l_NotifyErrorListSpy.count(), 0);

    std::cout << "- Connected to " << l_Identification.Model << ", S/N " << l_Identification.SerialNumber << std::endl;

    // Emit SetCurrentInstrument to select THM 1 again.
    // Make sure we get none of the signals.
    Manager.SetCurrentInstrument(l_ResourceList[0]);

    l_NotifyCurrentInstrumentSpy.wait(THM1176_CONNECT_TIME);
    QCOMPARE(l_NotifyCurrentInstrumentSpy.count(), 0);

    // Retrieve instrument information and parameter bounds for this THM.
    QCOMPARE(l_NotifyIdentificationSpy.count(), 0);
    QCOMPARE(l_NotifyRangeListSpy.count(), 0);
    QCOMPARE(l_NotifyUnitsListSpy.count(), 0);
    QCOMPARE(l_NotifyDivisorListSpy.count(), 0);
    QCOMPARE(l_NotifyAveragingParmBoundsSpy.count(), 0);
    QCOMPARE(l_NotifyTriggerParmBoundsSpy.count(), 0);
    QCOMPARE(l_NotifyRangeParmBoundsSpy.count(), 0);

    // Retrieve the current parameters for this instrument.
    QCOMPARE(l_NotifyAveragingParmsSpy.count(), 0);
    QCOMPARE(l_NotifyTriggerParmsSpy.count(), 0);
    QCOMPARE(l_NotifyOutputSelectSpy.count(), 0);
    QCOMPARE(l_NotifySleepParmSpy.count(), 0);
    QCOMPARE(l_NotifyUnitsSpy.count(), 0);
    QCOMPARE(l_NotifyRangeParmsSpy.count(), 0);
    QCOMPARE(l_NotifyCommFormatSpy.count(), 0);

    // Retrieve the operating mode and error list.
    QCOMPARE(l_NotifyOperatingModeSpy.count(), 0);
    QCOMPARE(l_NotifyErrorListSpy.count(), 0);

    std::cout << "- Still connected to " << l_Identification.Model << ", S/N " << l_Identification.SerialNumber << std::endl;

    // Emit SetCurrentInstrument for nonexistent THM.
    // Make sure we receive a NotifyErrorList; nothing else.
    Manager.SetCurrentInstrument(THM1176_SILLY_RESOURCE_NAME);

    l_FormerInstrument = l_CurrentInstrument;
    l_NotifyCurrentInstrumentSpy.wait(THM1176_CONNECT_TIME);
    QCOMPARE(l_NotifyCurrentInstrumentSpy.count(), 0);

    // Retrieve instrument information and parameter bounds for this THM.
    QCOMPARE(l_NotifyIdentificationSpy.count(), 0);
    QCOMPARE(l_NotifyRangeListSpy.count(), 0);
    QCOMPARE(l_NotifyUnitsListSpy.count(), 0);
    QCOMPARE(l_NotifyDivisorListSpy.count(), 0);
    QCOMPARE(l_NotifyAveragingParmBoundsSpy.count(), 0);
    QCOMPARE(l_NotifyTriggerParmBoundsSpy.count(), 0);
    QCOMPARE(l_NotifyRangeParmBoundsSpy.count(), 0);

    // Retrieve the current parameters for this instrument.
    QCOMPARE(l_NotifyAveragingParmsSpy.count(), 0);
    QCOMPARE(l_NotifyTriggerParmsSpy.count(), 0);
    QCOMPARE(l_NotifyOutputSelectSpy.count(), 0);
    QCOMPARE(l_NotifySleepParmSpy.count(), 0);
    QCOMPARE(l_NotifyUnitsSpy.count(), 0);
    QCOMPARE(l_NotifyRangeParmsSpy.count(), 0);
    QCOMPARE(l_NotifyCommFormatSpy.count(), 0);

    // Retrieve the operating mode and error list.
    QCOMPARE(l_NotifyOperatingModeSpy.count(), 0);

    // Retrieve the error list.
    QCOMPARE(l_NotifyErrorListSpy.count(), 1);
    l_Arguments = l_NotifyErrorListSpy.takeFirst();
    l_LatestErrors = qvariant_cast<CErrorList>(l_Arguments.at(0));
    QCOMPARE(static_cast<int>(l_LatestErrors.size()), 1);

    std::cout << "- Invalid connection request returned error: " << l_LatestErrors[0].Code << ", " <<
    l_LatestErrors[0].Description << ", " << l_LatestErrors[0].Context << std::endl;
    std::cout << "- Still connected to " << l_Identification.Model << ", S/N " << l_Identification.SerialNumber << std::endl;

    // Emit SetCurrentInstrument to select THM 2.
    Manager.SetCurrentInstrument(l_ResourceList[1]);

    // NotifyOperatingMode is emitted twice: once for the diconnect, once for the connect.
    // Otherwise, we should get all the appropriate signals twice, once with null and once with non-null results.
    l_NotifyOperatingModeSpy.wait(THM1176_CONNECT_TIME);
    QVERIFY(l_NotifyOperatingModeSpy.count() > 0);
    l_Arguments = l_NotifyOperatingModeSpy.takeFirst();
    l_CurrentOperatingMode = qvariant_cast<eTHM1176OperatingMode>(l_Arguments.at(0));
    QCOMPARE(l_CurrentOperatingMode, kTHM1176NotConnected);
    if (l_NotifyOperatingModeSpy.count() <= 0)
    {
        l_NotifyOperatingModeSpy.wait(THM1176_CONNECT_TIME);
        QCOMPARE(l_NotifyOperatingModeSpy.count(), 1);
    }
    l_Arguments = l_NotifyOperatingModeSpy.takeFirst();
    l_CurrentOperatingMode = qvariant_cast<eTHM1176OperatingMode>(l_Arguments.at(0));
    QCOMPARE(l_CurrentOperatingMode, kTHM1176Idle);

    // Retrieve the Current Instrument.
    l_FormerInstrument = l_CurrentInstrument;
    QCOMPARE(l_NotifyCurrentInstrumentSpy.count(), 2);
    l_Arguments = l_NotifyCurrentInstrumentSpy.takeFirst();
    l_CurrentInstrument = qvariant_cast<tResourceName>(l_Arguments.at(0));
    QVERIFY(l_CurrentInstrument.empty());
    l_Arguments = l_NotifyCurrentInstrumentSpy.takeFirst();
    l_CurrentInstrument = qvariant_cast<tResourceName>(l_Arguments.at(0));
    QVERIFY(l_CurrentInstrument == l_ResourceList[1]);

    // Retrieve instrument information and parameter bounds for this THM.
    QCOMPARE(l_NotifyIdentificationSpy.count(), 2);
    l_Arguments = l_NotifyIdentificationSpy.takeFirst();
    l_Identification = qvariant_cast<sIdentifier>(l_Arguments.at(0));
    QVERIFY(l_Identification.Manufacturer.empty() && l_Identification.Model.empty() && l_Identification.SerialNumber == 0);
    l_Arguments = l_NotifyIdentificationSpy.takeFirst();
    l_Identification = qvariant_cast<sIdentifier>(l_Arguments.at(0));
    QVERIFY(!l_Identification.Manufacturer.empty() && !l_Identification.Model.empty() && l_Identification.SerialNumber != 0);

    QCOMPARE(l_NotifyRangeListSpy.count(), 2);
    l_Arguments = l_NotifyRangeListSpy.takeFirst();
    l_RangeList = qvariant_cast<CFluxList>(l_Arguments.at(0));
    QVERIFY(l_RangeList.empty());
    l_Arguments = l_NotifyRangeListSpy.takeFirst();
    l_RangeList = qvariant_cast<CFluxList>(l_Arguments.at(0));
    QVERIFY(!l_RangeList.empty());

    QCOMPARE(l_NotifyUnitsListSpy.count(), 2);
    l_Arguments		= l_NotifyUnitsListSpy.takeFirst();
    l_UnitsList		= qvariant_cast<CTHM1176UnitsList>(l_Arguments.at(0));
    QVERIFY(l_UnitsList.empty());
    l_Arguments		= l_NotifyUnitsListSpy.takeFirst();
    l_UnitsList		= qvariant_cast<CTHM1176UnitsList>(l_Arguments.at(0));
    QVERIFY(!l_UnitsList.empty());

    QCOMPARE(l_NotifyDivisorListSpy.count(), 2);
    l_Arguments		= l_NotifyDivisorListSpy.takeFirst();
    l_DivisorList	= qvariant_cast<CDivisorList>(l_Arguments.at(0));
    QVERIFY(l_DivisorList.empty());
    l_Arguments		= l_NotifyDivisorListSpy.takeFirst();
    l_DivisorList	= qvariant_cast<CDivisorList>(l_Arguments.at(0));
    QVERIFY(!l_DivisorList.empty());

    QCOMPARE(l_NotifyAveragingParmBoundsSpy.count(), 2);
    l_Arguments = l_NotifyAveragingParmBoundsSpy.takeFirst();
    l_AveragingBounds = qvariant_cast<sAveraging<sBoundedParm>>(l_Arguments.at(0));
    QVERIFY(l_AveragingBounds.NoPoints.Val == 0 && l_AveragingBounds.NoPoints.Min == 0 &&
            l_AveragingBounds.NoPoints.Max == 0 && l_AveragingBounds.NoPoints.Def == 0);
    l_Arguments = l_NotifyAveragingParmBoundsSpy.takeFirst();
    l_AveragingBounds = qvariant_cast<sAveraging<sBoundedParm>>(l_Arguments.at(0));
    QVERIFY(l_AveragingBounds.NoPoints.Val != 0 && l_AveragingBounds.NoPoints.Min != 0 &&
            l_AveragingBounds.NoPoints.Max != 0 && l_AveragingBounds.NoPoints.Def != 0);

    QCOMPARE(l_NotifyTriggerParmBoundsSpy.count(), 2);
    l_Arguments = l_NotifyTriggerParmBoundsSpy.takeFirst();
    l_TriggerBounds = qvariant_cast<sInputTrigger<sBoundedParm>>(l_Arguments.at(0));
    QVERIFY(l_TriggerBounds.Period_s.Val == 0 && l_TriggerBounds.Period_s.Min == 0 &&
            l_TriggerBounds.Period_s.Max == 0 && l_TriggerBounds.Period_s.Def == 0 &&
            l_TriggerBounds.Count.Val == 0 && l_TriggerBounds.Count.Min == 0 &&
            l_TriggerBounds.Count.Max == 0 && l_TriggerBounds.Count.Def == 0);
    l_Arguments = l_NotifyTriggerParmBoundsSpy.takeFirst();
    l_TriggerBounds = qvariant_cast<sInputTrigger<sBoundedParm>>(l_Arguments.at(0));
    QVERIFY(l_TriggerBounds.Period_s.Val != 0 && l_TriggerBounds.Period_s.Min != 0 &&
            l_TriggerBounds.Period_s.Max != 0 && l_TriggerBounds.Period_s.Def != 0 &&
            l_TriggerBounds.Count.Val != 0 && l_TriggerBounds.Count.Min != 0 &&
            l_TriggerBounds.Count.Max != 0 && l_TriggerBounds.Count.Def != 0);

    QCOMPARE(l_NotifyRangeParmBoundsSpy.count(), 2);
    l_Arguments = l_NotifyRangeParmBoundsSpy.takeFirst();
    l_RangeBounds = qvariant_cast<sRange<sBoundedParm>>(l_Arguments.at(0));
    QVERIFY(l_RangeBounds.Range.Val == 0 && l_RangeBounds.Range.Min == 0 &&
            l_RangeBounds.Range.Max == 0 && l_RangeBounds.Range.Def == 0);
    l_Arguments = l_NotifyRangeParmBoundsSpy.takeFirst();
    l_RangeBounds = qvariant_cast<sRange<sBoundedParm>>(l_Arguments.at(0));
    QVERIFY(l_RangeBounds.Range.Val != 0 && l_RangeBounds.Range.Min != 0 &&
            l_RangeBounds.Range.Max != 0 && l_RangeBounds.Range.Def != 0);

    // Retrieve the current parameters for this instrument.
    QCOMPARE(l_NotifyAveragingParmsSpy.count(), 2);
    l_Arguments = l_NotifyAveragingParmsSpy.takeFirst();
    l_AveragingParms = qvariant_cast<sAveraging<uParm>>(l_Arguments.at(0));
    QVERIFY(l_AveragingParms.NoPoints == 0);
    l_Arguments = l_NotifyAveragingParmsSpy.takeFirst();
    l_AveragingParms = qvariant_cast<sAveraging<uParm>>(l_Arguments.at(0));
    QVERIFY(l_AveragingParms.NoPoints > 0);

    QCOMPARE(l_NotifyTriggerParmsSpy.count(), 2);
    l_Arguments = l_NotifyTriggerParmsSpy.takeFirst();
    l_Trigger = qvariant_cast<sInputTrigger<uParm>>(l_Arguments.at(0));
    QVERIFY(l_Trigger.Source == kInputTrigSrcImmediate && l_Trigger.Period_s == 0. && l_Trigger.Count == 0);
    l_Arguments = l_NotifyTriggerParmsSpy.takeFirst();
    l_Trigger = qvariant_cast<sInputTrigger<uParm>>(l_Arguments.at(0));
    QVERIFY(l_Trigger.Period_s > 0. && l_Trigger.Count > 0);

    QCOMPARE(l_NotifyOutputSelectSpy.count(), 2);
    l_Arguments = l_NotifyOutputSelectSpy.takeFirst();
    l_OutputSelect = qvariant_cast<sArbitraryMeasurements>(l_Arguments.at(0));
    QVERIFY(l_OutputSelect.Bx && l_OutputSelect.By && l_OutputSelect.Bz &&
            l_OutputSelect.Temperature && l_OutputSelect.Timestamp && l_OutputSelect.NoMeasurements == 1);
    l_Arguments = l_NotifyOutputSelectSpy.takeFirst();
    l_OutputSelect = qvariant_cast<sArbitraryMeasurements>(l_Arguments.at(0));

    QCOMPARE(l_NotifySleepParmSpy.count(), 2);
    l_Arguments = l_NotifySleepParmSpy.takeFirst();
    l_SleepParm = qvariant_cast<bool>(l_Arguments.at(0));
    QVERIFY(!l_SleepParm);
    l_Arguments = l_NotifySleepParmSpy.takeFirst();
    l_SleepParm = qvariant_cast<bool>(l_Arguments.at(0));

    QCOMPARE(l_NotifyUnitsSpy.count(), 2);
    l_Arguments = l_NotifyUnitsSpy.takeFirst();
    l_Units = qvariant_cast<eTHM1176Units>(l_Arguments.at(0));
    QVERIFY(l_Units == MTL::kT);
    l_Arguments = l_NotifyUnitsSpy.takeFirst();
    l_Units = qvariant_cast<eTHM1176Units>(l_Arguments.at(0));

    QCOMPARE(l_NotifyRangeParmsSpy.count(), 2);
    l_Arguments = l_NotifyRangeParmsSpy.takeFirst();
    l_RangeParms = qvariant_cast<sRange<uParm>>(l_Arguments.at(0));
    QVERIFY(!l_RangeParms.Auto && l_RangeParms.Range == 0.f);
    l_Arguments = l_NotifyRangeParmsSpy.takeFirst();
    l_RangeParms = qvariant_cast<sRange<uParm>>(l_Arguments.at(0));

    QCOMPARE(l_NotifyCommFormatSpy.count(), 2);
    l_Arguments = l_NotifyCommFormatSpy.takeFirst();
    l_CommFormat = qvariant_cast<eCommunicationFormat>(l_Arguments.at(0));
    QVERIFY(l_CommFormat == kComFormatAscii);
    l_Arguments = l_NotifyCommFormatSpy.takeFirst();
    l_CommFormat = qvariant_cast<eCommunicationFormat>(l_Arguments.at(0));

    // Retrieve the error list.
    QCOMPARE(l_NotifyErrorListSpy.count(), 0);

    std::cout << "- Connected to " << l_Identification.Model << ", S/N " << l_Identification.SerialNumber << std::endl;

    // Have the user unplug THM 2.
    // Make sure we get signals with empty lists, plus a NotifyErrorList.
    std::string l_Prompt = "Please unplug " + l_Identification.Model + ", S/N " + std::to_string(l_Identification.SerialNumber);
    PromptAndWait(l_Prompt);
    l_NotifyInstrumentListSpy.wait(THM1176_BOOT_N_SCAN_TIME);
    QVERIFY(l_NotifyInstrumentListSpy.count() == 1);
    l_Arguments = l_NotifyInstrumentListSpy.takeFirst();
    l_ResourceList = qvariant_cast<CResourceList>(l_Arguments.at(0));

    // The last signal that should arrive is NotifyCommFormat.
    l_NotifyCommFormatSpy.wait(THM1176_CONNECT_TIME);
    QCOMPARE(l_NotifyCommFormatSpy.count(), 1);
    l_Arguments = l_NotifyCommFormatSpy.takeFirst();
    l_CommFormat = qvariant_cast<eCommunicationFormat>(l_Arguments.at(0));
    QVERIFY(l_CommFormat == kComFormatAscii);

    // Retrieve the current instrument.
    l_FormerInstrument = l_CurrentInstrument;
    QCOMPARE(l_NotifyCurrentInstrumentSpy.count(), 1);
    l_Arguments = l_NotifyCurrentInstrumentSpy.takeFirst();
    l_CurrentInstrument = qvariant_cast<tResourceName>(l_Arguments.at(0));
    QVERIFY(l_CurrentInstrument.empty());

    // Retrieve instrument information and parameter bounds for this THM.
    QCOMPARE(l_NotifyIdentificationSpy.count(), 1);
    l_Arguments = l_NotifyIdentificationSpy.takeFirst();
    l_Identification = qvariant_cast<sIdentifier>(l_Arguments.at(0));
    QVERIFY(l_Identification.Manufacturer.empty() && l_Identification.Model.empty() && l_Identification.SerialNumber == 0);

    QCOMPARE(l_NotifyRangeListSpy.count(), 1);
    l_Arguments = l_NotifyRangeListSpy.takeFirst();
    l_RangeList = qvariant_cast<CFluxList>(l_Arguments.at(0));
    QVERIFY(l_RangeList.empty());

    QCOMPARE(l_NotifyUnitsListSpy.count(), 1);
    l_Arguments		= l_NotifyUnitsListSpy.takeFirst();
    l_UnitsList		= qvariant_cast<CTHM1176UnitsList>(l_Arguments.at(0));
    QVERIFY(l_UnitsList.empty());

    QCOMPARE(l_NotifyDivisorListSpy.count(), 1);
    l_Arguments		= l_NotifyDivisorListSpy.takeFirst();
    l_DivisorList	= qvariant_cast<CDivisorList>(l_Arguments.at(0));
    QVERIFY(l_DivisorList.empty());

    QCOMPARE(l_NotifyAveragingParmBoundsSpy.count(), 1);
    l_Arguments = l_NotifyAveragingParmBoundsSpy.takeFirst();
    l_AveragingBounds = qvariant_cast<sAveraging<sBoundedParm>>(l_Arguments.at(0));
    QVERIFY(l_AveragingBounds.NoPoints.Val == 0 && l_AveragingBounds.NoPoints.Min == 0 &&
            l_AveragingBounds.NoPoints.Max == 0 && l_AveragingBounds.NoPoints.Def == 0);

    QCOMPARE(l_NotifyTriggerParmBoundsSpy.count(), 1);
    l_Arguments = l_NotifyTriggerParmBoundsSpy.takeFirst();
    l_TriggerBounds = qvariant_cast<sInputTrigger<sBoundedParm>>(l_Arguments.at(0));
    QVERIFY(l_TriggerBounds.Period_s.Val == 0 && l_TriggerBounds.Period_s.Min == 0 &&
            l_TriggerBounds.Period_s.Max == 0 && l_TriggerBounds.Period_s.Def == 0 &&
            l_TriggerBounds.Count.Val == 0 && l_TriggerBounds.Count.Min == 0 &&
            l_TriggerBounds.Count.Max == 0 && l_TriggerBounds.Count.Def == 0);

    QCOMPARE(l_NotifyRangeParmBoundsSpy.count(), 1);
    l_Arguments = l_NotifyRangeParmBoundsSpy.takeFirst();
    l_RangeBounds = qvariant_cast<sRange<sBoundedParm>>(l_Arguments.at(0));
    QVERIFY(l_RangeBounds.Range.Val == 0 && l_RangeBounds.Range.Min == 0 &&
            l_RangeBounds.Range.Max == 0 && l_RangeBounds.Range.Def == 0);

    // Retrieve the current parameters for this instrument.
    QCOMPARE(l_NotifyAveragingParmsSpy.count(), 1);
    l_Arguments = l_NotifyAveragingParmsSpy.takeFirst();
    l_AveragingParms = qvariant_cast<sAveraging<uParm>>(l_Arguments.at(0));
    QVERIFY(l_AveragingParms.NoPoints == 0);

    QCOMPARE(l_NotifyTriggerParmsSpy.count(), 1);
    l_Arguments = l_NotifyTriggerParmsSpy.takeFirst();
    l_Trigger = qvariant_cast<sInputTrigger<uParm>>(l_Arguments.at(0));
    QVERIFY(l_Trigger.Period_s == 0. && l_Trigger.Count == 0);

    QCOMPARE(l_NotifyOutputSelectSpy.count(), 1);
    l_Arguments = l_NotifyOutputSelectSpy.takeFirst();
    l_OutputSelect = qvariant_cast<sArbitraryMeasurements>(l_Arguments.at(0));
    QVERIFY(l_OutputSelect.Bx && l_OutputSelect.By && l_OutputSelect.Bz &&
            l_OutputSelect.Temperature && l_OutputSelect.Timestamp &&
            l_OutputSelect.NoMeasurements == 1);

    QCOMPARE(l_NotifySleepParmSpy.count(), 1);
    l_Arguments = l_NotifySleepParmSpy.takeFirst();
    l_SleepParm = qvariant_cast<bool>(l_Arguments.at(0));
    QVERIFY(!l_SleepParm);

    QCOMPARE(l_NotifyUnitsSpy.count(), 1);
    l_Arguments = l_NotifyUnitsSpy.takeFirst();
    l_Units = qvariant_cast<eTHM1176Units>(l_Arguments.at(0));
    QVERIFY(l_Units == MTL::kT);

    QCOMPARE(l_NotifyRangeParmsSpy.count(), 1);
    l_Arguments = l_NotifyRangeParmsSpy.takeFirst();
    l_RangeParms = qvariant_cast<sRange<uParm>>(l_Arguments.at(0));
    QVERIFY(!l_RangeParms.Auto && l_RangeParms.Range == 0.f);

    // Retrieve the operating mode and error list.
    QCOMPARE(l_NotifyOperatingModeSpy.count(), 1);
    l_Arguments = l_NotifyOperatingModeSpy.takeFirst();
    l_CurrentOperatingMode = qvariant_cast<eTHM1176OperatingMode>(l_Arguments.at(0));
    QCOMPARE(l_CurrentOperatingMode, kTHM1176NotConnected);

    QCOMPARE(l_NotifyErrorListSpy.count(), 0);

    std::cout << "- No longer connected" << std::endl;

    // Emit SetCurrentInstrument for the now unplugged THM.
    // Make sure we get NotifyCurrentInstrument twice, once for the requested resource, once for no resource,
    // plus a NotifyErrorList.
    Manager.SetCurrentInstrument(l_FormerInstrument);

    l_NotifyCurrentInstrumentSpy.wait(THM1176_CONNECT_TIME);
    QCOMPARE(l_NotifyCurrentInstrumentSpy.count(), 0);

    // Retrieve instrument information and parameter bounds for this THM.
    QCOMPARE(l_NotifyIdentificationSpy.count(), 0);
    QCOMPARE(l_NotifyRangeListSpy.count(), 0);
    QCOMPARE(l_NotifyUnitsListSpy.count(), 0);
    QCOMPARE(l_NotifyDivisorListSpy.count(), 0);
    QCOMPARE(l_NotifyAveragingParmBoundsSpy.count(), 0);
    QCOMPARE(l_NotifyTriggerParmBoundsSpy.count(), 0);
    QCOMPARE(l_NotifyRangeParmBoundsSpy.count(), 0);

    // Retrieve the current parameters for this instrument.
    QCOMPARE(l_NotifyAveragingParmsSpy.count(), 0);
    QCOMPARE(l_NotifyTriggerParmsSpy.count(), 0);
    QCOMPARE(l_NotifyOutputSelectSpy.count(), 0);
    QCOMPARE(l_NotifySleepParmSpy.count(), 0);
    QCOMPARE(l_NotifyUnitsSpy.count(), 0);
    QCOMPARE(l_NotifyRangeParmsSpy.count(), 0);
    QCOMPARE(l_NotifyCommFormatSpy.count(), 0);

    // Retrieve the operating mode and error list.
    QCOMPARE(l_NotifyOperatingModeSpy.count(), 0);

    // Retrieve the error list.
    QCOMPARE(l_NotifyErrorListSpy.count(), 1);
    l_Arguments = l_NotifyErrorListSpy.takeFirst();
    l_LatestErrors = qvariant_cast<CErrorList>(l_Arguments.at(0));
    QCOMPARE(static_cast<int>(l_LatestErrors.size()), 1);

    std::cout << "- Invalid connection request returned error: " << l_LatestErrors[0].Code << ", " <<
    l_LatestErrors[0].Description << ", " << l_LatestErrors[0].Context << std::endl;
    std::cout << "- No longer connected" << std::endl;

    // Emit SetCurrentInstrument to select THM 1.
    // Make sure we got all the appropriate signals, with non-null results.
    Manager.SetCurrentInstrument(l_ResourceList[0]);

    // Wait for the operating mode to become Idle.
    l_NotifyOperatingModeSpy.wait(THM1176_CONNECT_TIME);
    QCOMPARE(l_NotifyOperatingModeSpy.count(), 1);
    l_Arguments = l_NotifyOperatingModeSpy.takeFirst();
    l_CurrentOperatingMode = qvariant_cast<eTHM1176OperatingMode>(l_Arguments.at(0));
    QCOMPARE(l_CurrentOperatingMode, kTHM1176Idle);

    // Retrieve the current instrument.
    l_FormerInstrument = l_CurrentInstrument;
    QCOMPARE(l_NotifyCurrentInstrumentSpy.count(), 1);
    l_Arguments = l_NotifyCurrentInstrumentSpy.takeFirst();
    l_CurrentInstrument = qvariant_cast<tResourceName>(l_Arguments.at(0));
    QCOMPARE(l_CurrentInstrument, l_ResourceList[0]);

    // Retrieve instrument information and parameter bounds for this THM.
    QCOMPARE(l_NotifyIdentificationSpy.count(), 1);
    l_Arguments = l_NotifyIdentificationSpy.takeFirst();
    l_Identification = qvariant_cast<sIdentifier>(l_Arguments.at(0));
    QVERIFY(!l_Identification.Manufacturer.empty() && !l_Identification.Model.empty() && l_Identification.SerialNumber != 0);

    QCOMPARE(l_NotifyRangeListSpy.count(), 1);
    l_Arguments = l_NotifyRangeListSpy.takeFirst();
    l_RangeList = qvariant_cast<CFluxList>(l_Arguments.at(0));
    QVERIFY(!l_RangeList.empty());

    QCOMPARE(l_NotifyUnitsListSpy.count(), 1);
    l_Arguments		= l_NotifyUnitsListSpy.takeFirst();
    l_UnitsList		= qvariant_cast<CTHM1176UnitsList>(l_Arguments.at(0));
    QVERIFY(!l_UnitsList.empty());

    QCOMPARE(l_NotifyDivisorListSpy.count(), 1);
    l_Arguments		= l_NotifyDivisorListSpy.takeFirst();
    l_DivisorList	= qvariant_cast<CDivisorList>(l_Arguments.at(0));
    QVERIFY(!l_DivisorList.empty());

    QCOMPARE(l_NotifyAveragingParmBoundsSpy.count(), 1);
    l_Arguments = l_NotifyAveragingParmBoundsSpy.takeFirst();
    l_AveragingBounds = qvariant_cast<sAveraging<sBoundedParm>>(l_Arguments.at(0));
    QVERIFY(l_AveragingBounds.NoPoints.Val != 0 && l_AveragingBounds.NoPoints.Min != 0 &&
            l_AveragingBounds.NoPoints.Max != 0 && l_AveragingBounds.NoPoints.Def != 0);

    QCOMPARE(l_NotifyTriggerParmBoundsSpy.count(), 1);
    l_Arguments = l_NotifyTriggerParmBoundsSpy.takeFirst();
    l_TriggerBounds = qvariant_cast<sInputTrigger<sBoundedParm>>(l_Arguments.at(0));
    QVERIFY(l_TriggerBounds.Period_s.Val != 0 && l_TriggerBounds.Period_s.Min != 0 &&
            l_TriggerBounds.Period_s.Max != 0 && l_TriggerBounds.Period_s.Def != 0 &&
            l_TriggerBounds.Count.Val != 0 && l_TriggerBounds.Count.Min != 0 &&
            l_TriggerBounds.Count.Max != 0 && l_TriggerBounds.Count.Def != 0);

    QCOMPARE(l_NotifyRangeParmBoundsSpy.count(), 1);
    l_Arguments = l_NotifyRangeParmBoundsSpy.takeFirst();
    l_RangeBounds = qvariant_cast<sRange<sBoundedParm>>(l_Arguments.at(0));
    QVERIFY(l_RangeBounds.Range.Val != 0 && l_RangeBounds.Range.Min != 0 &&
            l_RangeBounds.Range.Max != 0 && l_RangeBounds.Range.Def != 0);

    // Retrieve the current parameters for this instrument.
    QCOMPARE(l_NotifyAveragingParmsSpy.count(), 1);
    l_Arguments = l_NotifyAveragingParmsSpy.takeFirst();
    l_AveragingParms = qvariant_cast<sAveraging<uParm>>(l_Arguments.at(0));
    QVERIFY(l_AveragingParms.NoPoints > 0);

    QCOMPARE(l_NotifyTriggerParmsSpy.count(), 1);
    l_Arguments = l_NotifyTriggerParmsSpy.takeFirst();
    l_Trigger = qvariant_cast<sInputTrigger<uParm>>(l_Arguments.at(0));
    QVERIFY(l_Trigger.Period_s > 0. && l_Trigger.Count > 0);

    QCOMPARE(l_NotifyOutputSelectSpy.count(), 1);
    l_Arguments = l_NotifyOutputSelectSpy.takeFirst();
    l_OutputSelect = qvariant_cast<sArbitraryMeasurements>(l_Arguments.at(0));

    QCOMPARE(l_NotifySleepParmSpy.count(), 1);
    l_Arguments = l_NotifySleepParmSpy.takeFirst();
    l_SleepParm = qvariant_cast<bool>(l_Arguments.at(0));

    QCOMPARE(l_NotifyUnitsSpy.count(), 1);
    l_Arguments = l_NotifyUnitsSpy.takeFirst();
    l_Units = qvariant_cast<eTHM1176Units>(l_Arguments.at(0));

    QCOMPARE(l_NotifyRangeParmsSpy.count(), 1);
    l_Arguments = l_NotifyRangeParmsSpy.takeFirst();
    l_RangeParms = qvariant_cast<sRange<uParm>>(l_Arguments.at(0));

    QCOMPARE(l_NotifyCommFormatSpy.count(), 1);
    l_Arguments = l_NotifyCommFormatSpy.takeFirst();
    l_CommFormat = qvariant_cast<eCommunicationFormat>(l_Arguments.at(0));

    // Retrieve the error list.
    QCOMPARE(l_NotifyErrorListSpy.count(), 0);

    std::cout << "- Connected to " << l_Identification.Model << ", S/N " << l_Identification.SerialNumber << std::endl;

    //	- Emit SetOperatingMode to diconnect THM:
    // Make sure we get signals with empty lists.
    Manager.SetOperatingMode(kTHM1176NotConnected);

    // The last signal should be NotifyCommFormat.
    l_NotifyCommFormatSpy.wait(THM1176_CONNECT_TIME);
    QCOMPARE(l_NotifyCommFormatSpy.count(), 1);
    l_Arguments = l_NotifyCommFormatSpy.takeFirst();
    l_CommFormat = qvariant_cast<eCommunicationFormat>(l_Arguments.at(0));
    QVERIFY(l_CommFormat == kComFormatAscii);

    // Retrieve the current instrument.
    l_FormerInstrument = l_CurrentInstrument;
    QCOMPARE(l_NotifyCurrentInstrumentSpy.count(), 1);
    l_Arguments = l_NotifyCurrentInstrumentSpy.takeFirst();
    l_CurrentInstrument = qvariant_cast<tResourceName>(l_Arguments.at(0));
    QVERIFY(l_CurrentInstrument.empty());

    // Retrieve instrument information and parameter bounds for this THM.
    QCOMPARE(l_NotifyIdentificationSpy.count(), 1);
    l_Arguments = l_NotifyIdentificationSpy.takeFirst();
    l_Identification = qvariant_cast<sIdentifier>(l_Arguments.at(0));
    QVERIFY(l_Identification.Manufacturer.empty() && l_Identification.Model.empty() && l_Identification.SerialNumber == 0);

    QCOMPARE(l_NotifyRangeListSpy.count(), 1);
    l_Arguments = l_NotifyRangeListSpy.takeFirst();
    l_RangeList = qvariant_cast<CFluxList>(l_Arguments.at(0));
    QVERIFY(l_RangeList.empty());

    QCOMPARE(l_NotifyUnitsListSpy.count(), 1);
    l_Arguments		= l_NotifyUnitsListSpy.takeFirst();
    l_UnitsList		= qvariant_cast<CTHM1176UnitsList>(l_Arguments.at(0));
    QVERIFY(l_UnitsList.empty());

    QCOMPARE(l_NotifyDivisorListSpy.count(), 1);
    l_Arguments		= l_NotifyDivisorListSpy.takeFirst();
    l_DivisorList	= qvariant_cast<CDivisorList>(l_Arguments.at(0));
    QVERIFY(l_DivisorList.empty());

    QCOMPARE(l_NotifyAveragingParmBoundsSpy.count(), 1);
    l_Arguments = l_NotifyAveragingParmBoundsSpy.takeFirst();
    l_AveragingBounds = qvariant_cast<sAveraging<sBoundedParm>>(l_Arguments.at(0));
    QVERIFY(l_AveragingBounds.NoPoints.Val == 0 && l_AveragingBounds.NoPoints.Min == 0 &&
            l_AveragingBounds.NoPoints.Max == 0 && l_AveragingBounds.NoPoints.Def == 0);

    QCOMPARE(l_NotifyTriggerParmBoundsSpy.count(), 1);
    l_Arguments = l_NotifyTriggerParmBoundsSpy.takeFirst();
    l_TriggerBounds = qvariant_cast<sInputTrigger<sBoundedParm>>(l_Arguments.at(0));
    QVERIFY(l_TriggerBounds.Period_s.Val == 0 && l_TriggerBounds.Period_s.Min == 0 &&
            l_TriggerBounds.Period_s.Max == 0 && l_TriggerBounds.Period_s.Def == 0 &&
            l_TriggerBounds.Count.Val == 0 && l_TriggerBounds.Count.Min == 0 &&
            l_TriggerBounds.Count.Max == 0 && l_TriggerBounds.Count.Def == 0);

    QCOMPARE(l_NotifyRangeParmBoundsSpy.count(), 1);
    l_Arguments = l_NotifyRangeParmBoundsSpy.takeFirst();
    l_RangeBounds = qvariant_cast<sRange<sBoundedParm>>(l_Arguments.at(0));
    QVERIFY(l_RangeBounds.Range.Val == 0 && l_RangeBounds.Range.Min == 0 &&
            l_RangeBounds.Range.Max == 0 && l_RangeBounds.Range.Def == 0);

    // Retrieve the current parameters for this instrument.
    QCOMPARE(l_NotifyAveragingParmsSpy.count(), 1);
    l_Arguments = l_NotifyAveragingParmsSpy.takeFirst();
    l_AveragingParms = qvariant_cast<sAveraging<uParm>>(l_Arguments.at(0));
    QVERIFY(l_AveragingParms.NoPoints == 0);

    QCOMPARE(l_NotifyTriggerParmsSpy.count(), 1);
    l_Arguments = l_NotifyTriggerParmsSpy.takeFirst();
    l_Trigger = qvariant_cast<sInputTrigger<uParm>>(l_Arguments.at(0));
    QVERIFY(l_Trigger.Period_s == 0. && l_Trigger.Count == 0);

    QCOMPARE(l_NotifyOutputSelectSpy.count(), 1);
    l_Arguments = l_NotifyOutputSelectSpy.takeFirst();
    l_OutputSelect = qvariant_cast<sArbitraryMeasurements>(l_Arguments.at(0));
    QVERIFY(l_OutputSelect.Bx && l_OutputSelect.By && l_OutputSelect.Bz &&
            l_OutputSelect.Temperature && l_OutputSelect.Timestamp &&
            l_OutputSelect.NoMeasurements == 1);

    QCOMPARE(l_NotifySleepParmSpy.count(), 1);
    l_Arguments = l_NotifySleepParmSpy.takeFirst();
    l_SleepParm = qvariant_cast<bool>(l_Arguments.at(0));
    QVERIFY(!l_SleepParm);

    QCOMPARE(l_NotifyUnitsSpy.count(), 1);
    l_Arguments = l_NotifyUnitsSpy.takeFirst();
    l_Units = qvariant_cast<eTHM1176Units>(l_Arguments.at(0));
    QVERIFY(l_Units == MTL::kT);

    QCOMPARE(l_NotifyRangeParmsSpy.count(), 1);
    l_Arguments = l_NotifyRangeParmsSpy.takeFirst();
    l_RangeParms = qvariant_cast<sRange<uParm>>(l_Arguments.at(0));
    QVERIFY(!l_RangeParms.Auto && l_RangeParms.Range == 0.f);

    // Retrieve the operating mode and error list.
    QCOMPARE(l_NotifyOperatingModeSpy.count(), 1);
    l_Arguments = l_NotifyOperatingModeSpy.takeFirst();
    l_CurrentOperatingMode = qvariant_cast<eTHM1176OperatingMode>(l_Arguments.at(0));
    QCOMPARE(l_CurrentOperatingMode, kTHM1176NotConnected);

    QCOMPARE(l_NotifyErrorListSpy.count(), 0);

    std::cout << "- No longer connected" << std::endl;

} // THM1176IM_Test01_Connect::TestSetCurrentInstrument

//----------------------------------------------------------------------//
//	main()																//
//----------------------------------------------------------------------//
QTEST_MAIN(THM1176IM_Test01_Connect)

#include "THM1176IM_Test01_Connect.moc"
