#include "test.h"

#include <initializer_list>
#include <memory>
#include <string>
#include <sstream>
#include <vector>
#include <thread>

#include <errno.h>
#include <unistd.h>

#include <SecureString.h>
#include <Utils.h>

namespace
{

constexpr const char *FAKE_HOST = "SomeHostname";

constexpr int NUM_DB_PING_TRIALS = 10000;

std::vector<std::string> ExtractStringVector(Tango::DeviceData &dd)
{
    // requires cppTango main
    // BOOST_TEST_INFO("Device data: " << dd);
    std::vector<std::string> vec;
    BOOST_CHECK(dd >> vec);

    return vec;
}

template <typename T>
Tango::DeviceData MakeDeviceDataStringArray_impl(T &list)
{
    int idx = 0;
    Tango::DeviceData dd;
    auto *arr = new Tango::DevVarStringArray;
    dd << arr;

    arr->length(list.size());

    for(const auto &elem : list)
    {
        (*arr)[idx++] = Tango::string_dup(elem.c_str());
    }

    return dd;
}

Tango::DeviceData MakeDeviceDataStringArray(const std::vector<std::string> &vec)
{
    return MakeDeviceDataStringArray_impl(vec);
}

Tango::DeviceData MakeDeviceDataStringArray(std::initializer_list<std::string> list = {})
{
    return MakeDeviceDataStringArray_impl(list);
}

Tango::DeviceData MakeDeviceDataString(std::string str)
{
    Tango::DeviceData dd;
    dd << str;

    return dd;
}

void ExecuteInShell(const std::string &str)
{
    errno = 0;
    int ret = system(str.c_str());
    BOOST_TEST_INFO("system errno: " << errno << ", error string: " << strerror(errno));
    BOOST_REQUIRE(!ret);
}

} // namespace

class Fixture
{
  public:
    Fixture()
    {
        ExecuteInShell("./start_tdb.sh");

        for(int i = 0; i < NUM_DB_PING_TRIALS; i++)
        {
            try
            {
                dp = new Tango::DeviceProxy("sys/database/2");

                dp->ping();
            }
            catch(Tango::DevFailed &)
            {
                // Databaseds is not yet ready
                usleep(1e3);
                continue;
            }

            return;
        }

        BOOST_FAIL("The database could not be reached.");
    }

    ~Fixture()
    {
        ExecuteInShell("./stop_tdb.sh");
    }

    Tango::DeviceProxy *dp;
};

class AddAndExportDeviceFixture : public Fixture
{
  public:
    AddAndExportDeviceFixture()
    {
        // Executable name: Proc.py
        // Instance: myInst
        {
            auto dd = MakeDeviceDataStringArray({device_server_name, device_name, "myClass"});
            BOOST_CHECK_NO_THROW(dp->command_inout("DbAddDevice", dd));
        }

        {
            auto dd =
                MakeDeviceDataStringArray({device_server_name, "tango://myInst/mydomain/myfamily/mymember", "myClass"});
            BOOST_CHECK_NO_THROW(dp->command_inout("DbAddServer", dd));
        }

        {
            // using the maximum PID on 64-bit systems see
            // https://unix.stackexchange.com/questions/16883/what-is-the-maximum-value-of-the-process-id
            // and `man 5 proc`

            //                                    device name, CORBA IOR, host name,     pid,       process version
            auto dd = MakeDeviceDataStringArray({device_name, "SomeIOR", FAKE_HOST, "4194303", "123"});

            BOOST_CHECK_NO_THROW(dp->command_inout("DbExportDevice", dd));
        }
    }

    static std::string device_name;
    static std::string device_server_name;
    static std::string server_name;

    ~AddAndExportDeviceFixture() = default;
};

std::string AddAndExportDeviceFixture::device_name = "mydomain/myfamily/mymember";
std::string AddAndExportDeviceFixture::device_server_name = "Proc.py/myInst";
std::string AddAndExportDeviceFixture::server_name = "Proc.py";

BOOST_FIXTURE_TEST_SUITE(AllTests, Fixture)

BOOST_AUTO_TEST_CASE(ExportDeviceRequires5Args)
{
    auto dd = MakeDeviceDataStringArray();

    BOOST_CHECK_THROW(dp->command_inout("DbExportDevice", dd), Tango::DevFailed);
}

BOOST_FIXTURE_TEST_CASE(PassedPIDIsParsedCorrectly, AddAndExportDeviceFixture)
{
    auto dd = MakeDeviceDataString(device_name);
    Tango::DeviceData reply;
    BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceInfo", dd));

    std::vector<std::string> vs;
    std::vector<Tango::DevLong> vl;
    BOOST_CHECK(reply.extract(vl, vs)); // exported, pid
    std::vector<Tango::DevLong> vl_ref = {1, 4194303};
    BOOST_CHECK_EQUAL_COLLECTIONS(vl.begin(), vl.end(), vl_ref.begin(), vl_ref.end());

    // replace volatile data
    vs[5] = "fakeDate";
    std::vector<std::string> vs_ref = {
        device_name, "SomeIOR", "123", device_server_name, FAKE_HOST, "fakeDate", "?", "myClass"};
    BOOST_CHECK_EQUAL_COLLECTIONS(vs.begin(), vs.end(), vs_ref.begin(), vs_ref.end());
}

BOOST_AUTO_TEST_CASE(AddDeviceWorks)
{
    {
        auto dd = MakeDeviceDataString("*");

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetServerList", dd));

        auto vec = ExtractStringVector(reply);
        BOOST_CHECK_EQUAL(vec.size(), 4);
    }

    // add device
    {
        auto dd = MakeDeviceDataStringArray({"TangoTest/mytest", "TangoTest/mytest/1", "TangoTest"});
        BOOST_CHECK_NO_THROW(dp->command_inout("DbAddServer", dd));
    }

    // check that it now exists
    {
        auto dd = MakeDeviceDataString("*");

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetServerList", dd));

        auto vec = ExtractStringVector(reply);
        BOOST_CHECK_EQUAL(vec.size(), 5);

        auto it = std::find(std::begin(vec), std::end(vec), "TangoTest/mytest");
        BOOST_CHECK(it != std::end(vec));
    }
}

BOOST_AUTO_TEST_CASE(DBInfoDoesNotThrow)
{
    Tango::DeviceData reply;
    BOOST_CHECK_NO_THROW(reply = dp->command_inout("DBInfo"));
    auto vec = ExtractStringVector(reply);
    BOOST_CHECK_GT(vec.size(), 0);
}

BOOST_FIXTURE_TEST_CASE(GetHostListWorks, AddAndExportDeviceFixture)
{
    // all
    {
        auto dd = MakeDeviceDataString("*");

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetHostList", dd));
        auto vec = ExtractStringVector(reply);

        BOOST_CHECK_EQUAL(vec.size(), 3);
        BOOST_CHECK_NE(std::find(std::begin(vec), std::end(vec), FAKE_HOST), std::end(vec));
        BOOST_CHECK_NE(std::find(std::begin(vec), std::end(vec), "nada"), std::end(vec));
    }

    // no matches
    {
        auto dd = MakeDeviceDataString("");

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetHostList", dd));
        auto vec = ExtractStringVector(reply);
        BOOST_CHECK(vec.empty());
    }

    // single match
    {
        auto dd = MakeDeviceDataString(FAKE_HOST);

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetHostList", dd));
        auto vec = ExtractStringVector(reply);
        BOOST_CHECK_EQUAL(vec.size(), 1);

        std::vector<std::string> ref = {FAKE_HOST};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }
}

BOOST_AUTO_TEST_CASE(DbPutPropertyWorks)
{
    const std::string objName{"globalObj"};
    const std::string propName{"propName"};
    const std::string propValue{"propValue"};

    // write free property
    {
        auto in = MakeDeviceDataStringArray({objName, "1", propName, "1", propValue});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbPutProperty", in));
    }

    // read it back
    {
        auto in = MakeDeviceDataStringArray({objName, propName});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetProperty", in));

        auto vec = ExtractStringVector(reply);
        std::vector<std::string> ref = {objName, "1", propName, "1", propValue};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }
}

BOOST_FIXTURE_TEST_CASE(DbGetAttributeAliasWorks, AddAndExportDeviceFixture)
{
    const std::string attrAlias{"someAlias"};
    const std::string attrTRL = device_name + "/attr";

    // no match
    {
        auto dd = MakeDeviceDataString(attrAlias);
        BOOST_CHECK_THROW(dp->command_inout("DbGetAttributeAlias", dd), Tango::DevFailed);
    }

    // put an alias first
    {
        auto dd = MakeDeviceDataStringArray({attrTRL, attrAlias});
        BOOST_CHECK_NO_THROW(dp->command_inout("DbPutAttributeAlias", dd));
    }

    // get alias match
    {
        auto dd = MakeDeviceDataString(attrAlias);

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetAttributeAlias", dd));
        std::string str;
        BOOST_CHECK(reply >> str);
        BOOST_CHECK_EQUAL(str, attrTRL);
    }
}

BOOST_FIXTURE_TEST_CASE(DbGetAttributeAlias2Works, AddAndExportDeviceFixture)
{
    const std::string attrAlias{"someAlias"};
    const std::string attrTRL = device_name + "/attr";

    // no match
    {
        auto dd = MakeDeviceDataString(attrTRL);
        BOOST_CHECK_THROW(dp->command_inout("DbGetAttributeAlias2", dd), Tango::DevFailed);
    }

    // put an alias first
    {
        auto dd = MakeDeviceDataStringArray({attrTRL, attrAlias});
        BOOST_CHECK_NO_THROW(dp->command_inout("DbPutAttributeAlias", dd));
    }

    // get alias match
    {
        auto dd = MakeDeviceDataString(attrTRL);

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetAttributeAlias2", dd));
        std::string str;
        BOOST_CHECK(reply >> str);
        BOOST_CHECK_EQUAL(str, attrAlias);
    }
}

BOOST_FIXTURE_TEST_CASE(DbGetAliasAttributeWorks, AddAndExportDeviceFixture)
{
    const std::string attrAlias{"someAlias"};
    const std::string attrTRL = device_name + "/attr";

    // no match
    {
        auto dd = MakeDeviceDataString(attrAlias);
        BOOST_CHECK_THROW(dp->command_inout("DbGetAliasAttribute", dd), Tango::DevFailed);
    }

    // put an alias first
    {
        auto dd = MakeDeviceDataStringArray({attrTRL, attrAlias});
        BOOST_CHECK_NO_THROW(dp->command_inout("DbPutAttributeAlias", dd));
    }

    // get alias match
    {
        auto dd = MakeDeviceDataString(attrAlias);

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetAliasAttribute", dd));
        std::string str;
        BOOST_CHECK(reply >> str);
        BOOST_CHECK_EQUAL(str, attrTRL);
    }
}

BOOST_FIXTURE_TEST_CASE(DbGetInstanceNameListWorks, AddAndExportDeviceFixture)
{
    auto dd = MakeDeviceDataString(server_name);
    Tango::DeviceData reply;
    BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetInstanceNameList", dd));
    auto vec = ExtractStringVector(reply);
    std::vector<std::string> ref = {"myInst"};
    BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
}

BOOST_AUTO_TEST_CASE(ReadStoredProcedureAttr)
{
    auto device = std::make_unique<Tango::DeviceProxy>("sys/database/2");
    auto attr = device->read_attribute("StoredProcedureRelease");
    std::string version;
    BOOST_CHECK(attr >> version);
    BOOST_CHECK_EQUAL(version, "release 1.13");
}

BOOST_AUTO_TEST_CASE(ReadDbInfo)
{
    Tango::DeviceData reply;
    BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbInfo"));
    auto vec = ExtractStringVector(reply);

    // Element number [0] = TANGO Database sys/database/2
    // Element number [1] =
    // Element number [2] = Running since 2025-08-22 21:46:01
    // Element number [3] =
    // Element number [4] = Devices defined  = 8
    // Element number [5] = Devices exported  = 2
    // Element number [6] = Device servers defined  = 4
    // Element number [7] = Device servers exported  = 1
    // Element number [8] =
    // Element number [9] = Device properties defined  = 0 [History lgth = 0]
    // Element number [10] = Class properties defined  = 67 [History lgth = 0]
    // Element number [11] = Device attribute properties defined  = 0 [History lgth = 0]
    // Element number [12] = Class attribute properties defined  = 0 [History lgth = 0]
    // Element number [13] = Object properties defined  = 0 [History lgth = 0]

    // set the third element with the start time to something fixed"
    vec[2] = "Running since XXX";

    std::vector<std::string> ref;
    ref.resize(14);
    ref[0] = "TANGO Database sys/database/2";
    ref[1] = " ";
    ref[2] = "Running since XXX";
    ref[3] = " ";
    ref[4] = "Devices defined  = 8";
    ref[5] = "Devices exported  = 2";
    ref[6] = "Device servers defined  = 4";
    ref[7] = "Device servers exported  = 1";
    ref[8] = " ";
    ref[9] = "Device properties defined  = 0 [History lgth = 0]";
    ref[10] = "Class properties defined  = 67 [History lgth = 0]";
    ref[11] = "Device attribute properties defined  = 0 [History lgth = 0]";
    ref[12] = "Class attribute properties defined  = 0 [History lgth = 0]";
    ref[13] = "Object properties defined  = 0 [History lgth = 0]";

    BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
}

BOOST_FIXTURE_TEST_CASE(DbGetClassAttributeListWorks, AddAndExportDeviceFixture)
{
    // no match, but asking for all
    {
        auto dd = MakeDeviceDataStringArray({"myClass", "*"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetClassAttributeList", dd));
        auto vec = ExtractStringVector(reply);
        BOOST_CHECK(vec.empty());
    }

    // insert some class properties
    {
        auto dd = MakeDeviceDataStringArray({"myClass",
                                             "2",
                                             "attr1",
                                             "2",
                                             "prop1",
                                             "val1",
                                             "prop2",
                                             "val2",
                                             "attr2",
                                             "2",
                                             "prop3",
                                             "val3",
                                             "prop4",
                                             "val5"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbPutClassAttributeProperty", dd));
    }

    // match with wildcard
    {
        auto dd = MakeDeviceDataStringArray({"myClass", "att*"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetClassAttributeList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {"attr1", "attr2"};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }

    // match with plain string
    {
        auto dd = MakeDeviceDataStringArray({"myClass", "attr1"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetClassAttributeList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {"attr1"};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }
}

BOOST_FIXTURE_TEST_CASE(DbGetClassPipeListWorks, AddAndExportDeviceFixture)
{
    // no match, but asking for all
    {
        auto dd = MakeDeviceDataStringArray({"myClass", "*"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetClassPipeList", dd));
        auto vec = ExtractStringVector(reply);
        BOOST_CHECK(vec.empty());
    }

    // insert some class properties
    {
        auto dd = MakeDeviceDataStringArray({"myClass",
                                             "2",
                                             "pipe1",
                                             "2",
                                             "prop1",
                                             "1",
                                             "val1",
                                             "prop2",
                                             "1",
                                             "val2",
                                             "pipe2",
                                             "2",
                                             "prop3",
                                             "1",
                                             "val3",
                                             "prop4",
                                             "1",
                                             "val5"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbPutClassPipeProperty", dd));
    }

    // match with wildcard
    {
        auto dd = MakeDeviceDataStringArray({"myClass", "pipe*"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetClassPipeList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {"pipe1", "pipe2"};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }

    // match with plain string
    {
        auto dd = MakeDeviceDataStringArray({"myClass", "pipe1"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetClassPipeList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {"pipe1"};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }
}

BOOST_AUTO_TEST_CASE(SecureStringWorks)
{
    // works with empty string
    {
        std::string str;
        SecureString sec(str);

        BOOST_CHECK(str.empty());
        BOOST_CHECK_EQUAL(strcmp(sec.c_str(), ""), 0);
        BOOST_CHECK(sec.empty());
    }

    // works with string of length 1
    {
        std::string str{"a"};
        SecureString sec(str);

        BOOST_CHECK(str.empty());
        BOOST_CHECK_EQUAL(strcmp(sec.c_str(), "a"), 0);
        BOOST_CHECK(!sec.empty());
    }

    // works with real string
    {
        std::string str{"secret"};
        SecureString sec(str);

        BOOST_CHECK(str.empty());
        BOOST_CHECK_EQUAL(strcmp(sec.c_str(), "secret"), 0);
        BOOST_CHECK(!sec.empty());
    }
}

BOOST_FIXTURE_TEST_CASE(DbGetDeviceDomainListWorks, AddAndExportDeviceFixture)
{
    // match with full device name
    {
        auto dd = MakeDeviceDataString({"sys/database/2"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceDomainList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {"sys"};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }

    // match with wildcard
    {
        auto dd = MakeDeviceDataString({"sys*"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceDomainList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {"sys"};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }

    // no match with partial name
    {
        auto dd = MakeDeviceDataString({"sys"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceDomainList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }
}

BOOST_FIXTURE_TEST_CASE(DbGetDeviceFamilyListWorks, AddAndExportDeviceFixture)
{
    // match with full device name
    {
        auto dd = MakeDeviceDataString({"sys/database/2"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceFamilyList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {"database"};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }

    // match with wildcard
    {
        auto dd = MakeDeviceDataString({"*database/*"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceFamilyList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {"database"};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }

    // no match with partial name
    {
        auto dd = MakeDeviceDataString({"database"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceFamilyList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }
}

BOOST_FIXTURE_TEST_CASE(DbGetDeviceMemberListWorks, AddAndExportDeviceFixture)
{
    // match with full device name
    {
        auto dd = MakeDeviceDataString({"sys/database/2"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceMemberList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {"2"};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }

    // match with wildcard
    {
        auto dd = MakeDeviceDataString({"*2"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceMemberList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {"2"};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }

    // no match with partial name
    {
        auto dd = MakeDeviceDataString({"2"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceMemberList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }
}

#if TANGO_VERSION >= TANGO_MAKE_VERSION(10, 3, 0)

BOOST_AUTO_TEST_CASE(EventsWork)
{
    int event_id;
    BOOST_CHECK_NO_THROW(event_id = dp->subscribe_event("state", Tango::ATTR_CONF_EVENT, 10));

    std::this_thread::sleep_for(std::chrono::seconds(2 * Tango::EVENT_HEARTBEAT_PERIOD));

    Tango::AttrConfEventDataList events;
    dp->get_events(event_id, events);

    BOOST_CHECK(!events.empty());

    // with the fix in TangoDatabase we don't have errors
    for(auto &elem : events)
    {
        BOOST_CHECK(!elem->err);
    }

    auto adm = std::unique_ptr<Tango::DeviceProxy>(dp->get_adm_device());
    Tango::DeviceData reply;
    BOOST_CHECK_NO_THROW(reply = adm->command_inout("QueryEventSystem"));

    // and with the fix here we do use the database
    // Reference:
    // {"version":1,"server":{"event_counters":{"tango://thomas-kleintop:10000/sys/database/2/state.attr_conf":1},"perf":null},"client":null}
    std::string str;
    BOOST_CHECK(reply >> str);
    BOOST_TEST_INFO(str);
    BOOST_CHECK_NE(str.find("sys/database/2/state.attr_conf"), std::string::npos);
}

#else

BOOST_AUTO_TEST_CASE(EventsWork)
{
    BOOST_CHECK(true);
}

#endif

BOOST_FIXTURE_TEST_CASE(DbGetDeviceAttributeProperty2Works, AddAndExportDeviceFixture)
{
    const std::vector<std::string> input = {device_name,
                                            "2", // #attributes
                                            "attr0",
                                            "3", // #properties of first attribute
                                            "attr0_prop0",
                                            "1", // # property values: 1 for scalar, > 1 for arrays
                                            "attr0_prop0_scalar_val0",
                                            "attr0_prop1",
                                            "4",
                                            "attr0_prop1_array_val0",
                                            "attr0_prop1_array_val1",
                                            "attr0_prop1_array_val2",
                                            "attr0_prop1_array_val3",
                                            "attr0_prop2",
                                            "1",
                                            "attr0_prop2_scalar_val0",
                                            "attr1",
                                            "1",
                                            "attr1_prop0",
                                            "1",
                                            "attr1_prop0_scalar_val0"};
    // insert some attribute properties
    {
        auto dd = MakeDeviceDataStringArray(input);

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbPutDeviceAttributeProperty2", dd));
    }

    // all attributes
    {
        auto dd = MakeDeviceDataStringArray({device_name, "attr0", "attr1"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceAttributeProperty2", dd));
        auto vec = ExtractStringVector(reply);

        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(input), std::end(input));
    }

    // only second attribute
    {
        auto dd = MakeDeviceDataStringArray({device_name, "attr1"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceAttributeProperty2", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {device_name, "1", "attr1", "1", "attr1_prop0", "1", "attr1_prop0_scalar_val0"};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }
}

BOOST_FIXTURE_TEST_CASE(DbPutDeviceAttributeProperty2Works, AddAndExportDeviceFixture)
{
    const std::vector<std::string> input = {device_name,
                                            "2", // #attributes
                                            "attr0",
                                            "3", // #properties of first attribute
                                            "attr0_prop0",
                                            "1", // #property values: 1 for scalar, > 1 for arrays
                                            "attr0_prop0_scalar_val0",
                                            "attr0_prop1",
                                            "4",
                                            "attr0_prop1_array_val0",
                                            "attr0_prop1_array_val1",
                                            "attr0_prop1_array_val2",
                                            "attr0_prop1_array_val3",
                                            "attr0_prop2",
                                            "1",
                                            "attr0_prop2_scalar_val0",
                                            "attr1",
                                            "1",
                                            "attr1_prop0",
                                            "1",
                                            "attr1_prop0_scalar_val0"};
    // insert some attribute properties
    {
        auto dd = MakeDeviceDataStringArray(input);

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbPutDeviceAttributeProperty2", dd));
    }

    // all attributes
    {
        auto dd = MakeDeviceDataStringArray({device_name, "attr0", "attr1"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceAttributeProperty2", dd));
        auto vec = ExtractStringVector(reply);

        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(input), std::end(input));
    }

    // overwrite existing property attr0_prop1
    {
        const std::vector<std::string> overwrite = {device_name,
                                                    "1", // #attributes
                                                    "attr0",
                                                    "1", // #properties of first attribute
                                                    "attr0_prop1",
                                                    "4",
                                                    "attr0_prop1_array_val0_2",
                                                    "attr0_prop1_array_val1_2",
                                                    "attr0_prop1_array_val2_2",
                                                    "attr0_prop1_array_val3_2"};

        auto dd = MakeDeviceDataStringArray(overwrite);

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbPutDeviceAttributeProperty2", dd));
    }

    // and now query its history
    {
        auto dd = MakeDeviceDataStringArray({device_name, "attr0", "attr0_prop1"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceAttributePropertyHist", dd));
        auto vec = ExtractStringVector(reply);

        // replace timestamps like 2025-11-02 16:04:50
        vec[2] = "time0";
        vec[10] = "time1";

        std::vector<std::string> ref = {"attr0",
                                        "attr0_prop1",
                                        "time0",
                                        "4",
                                        "attr0_prop1_array_val0",
                                        "attr0_prop1_array_val1",
                                        "attr0_prop1_array_val2",
                                        "attr0_prop1_array_val3",
                                        "attr0",
                                        "attr0_prop1",
                                        "time1",
                                        "4",
                                        "attr0_prop1_array_val0_2",
                                        "attr0_prop1_array_val1_2",
                                        "attr0_prop1_array_val2_2",
                                        "attr0_prop1_array_val3_2"};

        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }
}

BOOST_FIXTURE_TEST_CASE(DbPutDeviceAttributeProperty2MemorizedWorks, AddAndExportDeviceFixture)
{
    const std::vector<std::string> input = {device_name,
                                            "1", // #attributes
                                            "attr0",
                                            "1", // #properties of first attribute
                                            "__value",
                                            "1",
                                            "mem_att_value"};
    {
        auto dd = MakeDeviceDataStringArray(input);

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbPutDeviceAttributeProperty2", dd));
    }

    // read it back
    {
        auto dd = MakeDeviceDataStringArray({device_name, "attr0"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceAttributeProperty2", dd));
        auto vec = ExtractStringVector(reply);

        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(input), std::end(input));
    }

    // but no history
    {
        auto dd = MakeDeviceDataStringArray({device_name, "attr0", "__value"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceAttributePropertyHist", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }
}

BOOST_FIXTURE_TEST_CASE(DbGetDeviceAttributeListWorks, AddAndExportDeviceFixture)
{
    const std::vector<std::string> input = {device_name,
                                            "2", // #attributes
                                            "attr0",
                                            "3", // #properties of first attribute
                                            "attr0_prop0",
                                            "1", // # property values: 1 for scalar, > 1 for arrays
                                            "attr0_prop0_scalar_val0",
                                            "attr0_prop1",
                                            "4",
                                            "attr0_prop1_array_val0",
                                            "attr0_prop1_array_val1",
                                            "attr0_prop1_array_val2",
                                            "attr0_prop1_array_val3",
                                            "attr0_prop2",
                                            "1",
                                            "attr0_prop2_scalar_val0",
                                            "attr1",
                                            "1",
                                            "attr1_prop0",
                                            "1",
                                            "attr1_prop0_scalar_val0"};
    // insert some attribute properties
    {
        auto dd = MakeDeviceDataStringArray(input);

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbPutDeviceAttributeProperty2", dd));
    }

    // no match
    {
        auto dd = MakeDeviceDataStringArray({device_name, "I_DONT_EXIST"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceAttributeList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }

    // match with wildcard
    {
        auto dd = MakeDeviceDataStringArray({device_name, "attr*"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceAttributeList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {"attr0", "attr1"};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }

    // match with literal name
    {
        auto dd = MakeDeviceDataStringArray({device_name, "attr1"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceAttributeList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {"attr1"};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }
}

BOOST_FIXTURE_TEST_CASE(DbGetDevicePipePropertyWorks, AddAndExportDeviceFixture)
{
    const std::vector<std::string> input({device_name,
                                          "2", // #pipes
                                          "pipe0",
                                          "3", // #properties of first pipe
                                          "pipe0_prop0",
                                          "1", // # property values: 1 for scalar, > 1 for arrays
                                          "pipe0_prop0_scalar_val0",
                                          "pipe0_prop1",
                                          "4",
                                          "pipe0_prop1_array_val0",
                                          "pipe0_prop1_array_val1",
                                          "pipe0_prop1_array_val2",
                                          "pipe0_prop1_array_val3",
                                          "pipe0_prop2",
                                          "1",
                                          "pipe0_prop2_scalar_val0",
                                          "pipe1",
                                          "1",
                                          "pipe1_prop0",
                                          "1",
                                          "pipe1_prop0_scalar_val0"});

    // insert some class properties
    {
        auto dd = MakeDeviceDataStringArray(input);
        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbPutDevicePipeProperty", dd));
    }

    // all pipes
    {
        auto dd = MakeDeviceDataStringArray({device_name, "pipe0", "pipe1"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDevicePipeProperty", dd));
        auto vec = ExtractStringVector(reply);

        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(input), std::end(input));
    }

    // only second pipe
    {
        auto dd = MakeDeviceDataStringArray({device_name, "pipe1"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDevicePipeProperty", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {device_name, "1", "pipe1", "1", "pipe1_prop0", "1", "pipe1_prop0_scalar_val0"};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }
}

BOOST_FIXTURE_TEST_CASE(DbGetDevicePropertyWorks, AddAndExportDeviceFixture)
{
    const std::vector<std::string> input = {device_name,
                                            "2", // # properties
                                            "prop0",
                                            "1", // # property values: 1 for scalar, > 1 for arrays
                                            "prop0_scalar_val0",
                                            "prop1",
                                            "4",
                                            "prop1_array_val0",
                                            "prop1_array_val1",
                                            "prop1_array_val2",
                                            "prop1_array_val3"};
    // insert some device properties
    {
        auto dd = MakeDeviceDataStringArray(input);
        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbPutDeviceProperty", dd));
    }

    {
        auto dd = MakeDeviceDataStringArray({device_name, "prop0", "prop1"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceProperty", dd));
        auto vec = ExtractStringVector(reply);

        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(input), std::end(input));
    }
}

BOOST_FIXTURE_TEST_CASE(DbGetDevicePropertyListWorks, AddAndExportDeviceFixture)
{
    const std::vector<std::string> input = {device_name,
                                            "2", // # properties
                                            "prop0",
                                            "1", // # property values: 1 for scalar, > 1 for arrays
                                            "prop0_scalar_val0",
                                            "prop1",
                                            "4",
                                            "prop1_array_val0",
                                            "prop1_array_val1",
                                            "prop1_array_val2",
                                            "prop1_array_val3"};
    // insert some device properties
    {
        auto dd = MakeDeviceDataStringArray(input);
        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbPutDeviceProperty", dd));
    }

    // no match
    {
        auto dd = MakeDeviceDataStringArray({device_name, "I_DONT_EXIST"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDevicePropertyList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }

    // match with wildcard
    {
        auto dd = MakeDeviceDataStringArray({device_name, "prop*"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDevicePropertyList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {"prop0", "prop1"};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }

    // match with literal name
    {
        auto dd = MakeDeviceDataStringArray({device_name, "prop1"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDevicePropertyList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {"prop1"};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }
}

BOOST_FIXTURE_TEST_CASE(DbGetClassListWorks, AddAndExportDeviceFixture)
{
    // it is already prefilled from our fixture

    // match with full device name
    {
        auto dd = MakeDeviceDataString({"myClass"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetClassList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {"myClass"};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }

    // match with wildcard
    {
        auto dd = MakeDeviceDataString({"my*"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetClassList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {"myClass"};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }

    // no match with partial name
    {
        auto dd = MakeDeviceDataString({"my"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetClassList", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }
}

// DbGetDeviceAttributeProperty can only handle scalar properties, whereas DbGetDeviceAttributeProperty2 can
// also handle arrays as property values.
BOOST_FIXTURE_TEST_CASE(DbGetDeviceAttributePropertyWorks, AddAndExportDeviceFixture)
{
    const std::vector<std::string> input = {device_name,
                                            "2", // #attributes
                                            "attr0",
                                            "3", // #properties of attr0 in name, value pairs
                                            "attr0_prop0",
                                            "attr0_prop0_val",
                                            "attr0_prop1",
                                            "attr0_prop1_val",
                                            "attr0_prop2",
                                            "attr0_prop2_val",
                                            "attr1",
                                            "1", // #properties of attr1
                                            "attr1_prop0",
                                            "attr1_prop0_val"};

    // insert some attribute properties
    {
        auto dd = MakeDeviceDataStringArray(input);
        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbPutDeviceAttributeProperty", dd));
    }

    {
        auto dd = MakeDeviceDataStringArray({device_name, "attr0", "attr1"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceAttributeProperty", dd));
        auto vec = ExtractStringVector(reply);

        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(input), std::end(input));
    }

    // only second attribute
    {
        auto dd = MakeDeviceDataStringArray({device_name, "attr1"});

        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceAttributeProperty", dd));
        auto vec = ExtractStringVector(reply);

        std::vector<std::string> ref = {device_name, "1", "attr1", "1", "attr1_prop0", "attr1_prop0_val"};
        BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(vec), std::end(vec), std::begin(ref), std::end(ref));
    }
}

BOOST_DATA_TEST_CASE_F(AddAndExportDeviceFixture,
                       DbDeleteDeviceAndServerWorks,
                       data::make({"DbDeleteDevice", "DbDeleteServer"}) ^
                           data::make({AddAndExportDeviceFixture::device_name,
                                       AddAndExportDeviceFixture::device_server_name}),
                       command,
                       param)
{
    const std::string attrAlias{"someAlias"};

    // Deleting a device deletes entries from the following tables
    //
    // device table -> DbGetClassList
    // property_device table -> DbGetDeviceProperty
    // property_attribute_device table -> DbGetDeviceAttributeProperty
    // property_pipe_device table -> DbGetDevicePipeProperty
    // attribute_alias table -> DbGetAttributeAlias
    //
    // so we need to put entries first and check that we have some

    // we already have a class entry due to our fixture
    {
        auto dd = MakeDeviceDataString({"myClass"});
        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetClassList", dd));
        auto vec = ExtractStringVector(reply);
        BOOST_CHECK(!vec.empty());
    }

    // device property
    {
        const std::vector<std::string> input = {device_name,
                                                "1", // # properties
                                                "prop0",
                                                "1", // # property values: 1 for scalar, > 1 for arrays
                                                "prop0_scalar_val0"};

        {
            auto dd = MakeDeviceDataStringArray(input);
            Tango::DeviceData reply;
            BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbPutDeviceProperty", dd));
        }

        {
            auto dd = MakeDeviceDataStringArray({device_name, "*"});
            Tango::DeviceData reply;
            BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDevicePropertyList", dd));
            auto vec = ExtractStringVector(reply);
            BOOST_CHECK(!vec.empty());
        }
    }

    // attribute property
    {
        const std::vector<std::string> input = {device_name,
                                                "1", // #attributes
                                                "attr0",
                                                "1", // #properties of attr0 in name, value pairs
                                                "attr0_prop0",
                                                "attr0_prop0_val"};

        {
            auto dd = MakeDeviceDataStringArray(input);
            Tango::DeviceData reply;
            BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbPutDeviceAttributeProperty", dd));
        }

        {
            auto dd = MakeDeviceDataStringArray({device_name, "*"});
            Tango::DeviceData reply;
            BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceAttributeList", dd));
            auto vec = ExtractStringVector(reply);
            BOOST_CHECK(!vec.empty());
        }
    }

    // device pipe property
    {
        const std::vector<std::string> input{device_name,
                                             "1", // #pipes
                                             "pipe0",
                                             "1", // #properties of first pipe
                                             "pipe0_prop0",
                                             "1", // #property values: 1 for scalar, > 1 for arrays
                                             "pipe0_prop0_scalar_val0"};

        {
            auto dd = MakeDeviceDataStringArray(input);
            Tango::DeviceData reply;
            BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbPutDevicePipeProperty", dd));
        }

        {
            auto dd = MakeDeviceDataStringArray({device_name, "pipe0"});
            Tango::DeviceData reply;
            BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDevicePipeProperty", dd));
            auto vec = ExtractStringVector(reply);
            BOOST_CHECK(!vec.empty());
        }
    }

    // attribute alias
    {
        const std::string attrTRL = device_name + "/attr";

        {
            auto dd = MakeDeviceDataStringArray({attrTRL, attrAlias});
            BOOST_CHECK_NO_THROW(dp->command_inout("DbPutAttributeAlias", dd));
        }

        {
            auto dd = MakeDeviceDataString(attrAlias);
            Tango::DeviceData reply;
            BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetAttributeAlias", dd));
            std::string str;
            BOOST_CHECK(reply >> str);
            BOOST_CHECK(!str.empty());
        }
    }

    // delete the device or device server process
    {
        auto dd = MakeDeviceDataString(param);
        BOOST_CHECK_NO_THROW(dp->command_inout(command, dd));
    }

    // and now we should not have entries for this class anymore
    {
        auto dd = MakeDeviceDataString("myClass");
        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetClassList", dd));
        auto vec = ExtractStringVector(reply);
        BOOST_CHECK(vec.empty());
    }

    // device property
    {
        auto dd = MakeDeviceDataStringArray({device_name, "*"});
        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDevicePropertyList", dd));
        auto vec = ExtractStringVector(reply);
        BOOST_CHECK(vec.empty());
    }

    // attribute property
    {
        auto dd = MakeDeviceDataStringArray({device_name, "*"});
        Tango::DeviceData reply;
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDeviceAttributeList", dd));
        auto vec = ExtractStringVector(reply);
        BOOST_CHECK(vec.empty());
    }

    // device pipe property
    {
        auto dd = MakeDeviceDataStringArray({device_name, "pipe0"});
        Tango::DeviceData reply;
        // there is no DbGetDevicePipePropertyList
        // so we have to cope with incomplete output
        BOOST_CHECK_NO_THROW(reply = dp->command_inout("DbGetDevicePipeProperty", dd));
        auto vec = ExtractStringVector(reply);
        std::vector<std::string> ref = {device_name, "1", "pipe0", "0"};
        BOOST_CHECK_EQUAL_COLLECTIONS(vec.begin(), vec.end(), ref.begin(), ref.end());
    }

    // attribute alias
    {
        auto dd = MakeDeviceDataString(attrAlias);
        Tango::DeviceData reply;
        BOOST_CHECK_THROW(reply = dp->command_inout("DbGetAttributeAlias", dd), Tango::DevFailed);
    }
}

BOOST_AUTO_TEST_SUITE_END() // AllTests
