Files
qt5-legatus-legacy/Lts/Services/AudioVideoManagement/UnitTests/asset_assignment_manager_test.cpp
T
2026-08-13 18:33:10 +02:00

596 lines
22 KiB
C++
Executable File

/**
* @RDE_HEADER@
* AssetAssignmentManager
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, SLSE, 2017-12-01
*/
#include "asset_assignment_manager_test.h"
#include <AudioVideoManagement/asset_assignment_manager.h>
#include <AudioVideoManagement/exercise.h>
#include <AudioVideoManagement/asset.h>
#include <AudioVideoManagement/mounting_point.h>
#include <InfrastructureManagement/ism.hpp>
#include <LtsUtilsCore/data_modification_enum.h>
#include <QString>
#include <QSignalSpy>
#include <vector>
#include <functional>
#include <random>
using namespace AudioVideoManagement;
using namespace InfrastructureManagement;
namespace
{
const int EXERCISE_42 = 42;
const int EXERCISE_35 = 35;
MainStructureElement createBuilding()
{
static int mseId = 0;
static int seId = 0;
MainStructureElement building;
building.id(++mseId);
building.name("Building");
building.structureElementId(++seId);
building.properties("{}");
StructureElement mountingPoint;
mountingPoint.id(++seId);
mountingPoint.name("MP_OUT0001");
mountingPoint.displayName("Outdoor Camera 1");
mountingPoint.mainStructureElementId(building.id());
mountingPoint.structureElementTypeId(StructureElementType::MountingPoint);
mountingPoint.mountingPointTypeId(MountingPointType::OutdoorCamera);
mountingPoint.properties("{}");
building.elements().push_back(mountingPoint);
return building;
}
InstrumentationAsset createAsset()
{
static int id = 0;
InstrumentationAsset asset;
asset.id(++id);
asset.name(QStringLiteral("CAM_UBEOCE000%1").arg(id).toUtf8().constData());
asset.mountingPointTypeId(MountingPointType::OutdoorCamera);
return asset;
}
MountingPointAsset createMount(InstrumentationAsset &asset, const StructureElement &mountingPoint)
{
static int id = 0;
MountingPointAsset mount;
mount.id(++id);
mount.assetId(asset.id());
mount.structureElementId(mountingPoint.id());
asset.assetStateId(InstrumentationAssetState::Mounted);
return mount;
}
void unmount(InstrumentationAsset &asset)
{
asset.assetStateId(InstrumentationAssetState::Unmounted);
}
void assign(MainStructureElement &building, int exerciseId)
{
building.properties(QStringLiteral("{\"available_in_exercise\": [%1]}").arg(exerciseId).toUtf8().constData());
}
void unassign(MainStructureElement &building)
{
building.properties("{}");
}
void assign(InstrumentationAsset &asset, int exerciseId)
{
Q_ASSERT(asset.assetStateId() == InstrumentationAssetState::Mounted);
std::vector<int> exercises{exerciseId};
asset.availableInExercise(exercises);
}
void unassign(InstrumentationAsset &asset)
{
Q_ASSERT(asset.assetStateId() == InstrumentationAssetState::Mounted);
asset.availableInExercise().clear();
}
class AssignmentSpy : public QObject
{
Q_OBJECT
public:
AssignmentSpy(Exercise *exercise)
{
connect(exercise, &Exercise::assetAssigned, this, &AssignmentSpy::assetAssigned);
connect(exercise, &Exercise::assetUnassigned, this, &AssignmentSpy::assetUnassigned);
}
bool isEmpty() const { return assignedAssets.empty() && unassignedAssets.empty(); }
void clear()
{
assignedAssets.clear();
unassignedAssets.clear();
}
std::vector<InstrumentationAsset> assignedAssets;
std::vector<InstrumentationAsset> unassignedAssets;
private slots:
void assetAssigned(Asset *asset)
{
assignedAssets.push_back(asset->data());
}
void assetUnassigned(Asset *asset)
{
unassignedAssets.push_back(asset->data());
}
};
template<class Container>
void shuffle(Container &c)
{
static std::random_device randomDevice;
static std::default_random_engine randomEngine(randomDevice());
std::shuffle(c.begin(), c.end(), randomEngine);
}
using Notifications = std::vector<std::function<void()>>;
void fireRandomly(Notifications &notifications)
{
shuffle(notifications);
std::for_each(notifications.begin(), notifications.end(), [] (const std::function<void()> &f) { f(); });
notifications.clear();
}
} // anonymous namespace
/**
* Scenario where the application starts with the Asset unmounted and unassigned.
*/
void AssetAssignmentManagerTest::testStartUnmountedUnassigned()
{
AssetAssignmentManager mgr;
Exercise *exercise = mgr.exercise(EXERCISE_42);
AssignmentSpy spy(exercise);
Notifications notifications;
MainStructureElement building = createBuilding();
InstrumentationAsset asset1 = createAsset();
InstrumentationAsset asset2 = createAsset();
notifications.push_back(std::bind(&AssetAssignmentManager::onMainStructureElementChanged, &mgr, building, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset1, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset2, DataModification::eAdded));
fireRandomly(notifications);
QVERIFY(!mgr.hasPending());
QVERIFY(spy.isEmpty());
}
/**
* Scenario where the application starts with the Asset mounted and assigned.
*/
void AssetAssignmentManagerTest::testStartMountedAssigned()
{
AssetAssignmentManager mgr;
Exercise *exercise = mgr.exercise(EXERCISE_42);
AssignmentSpy spy(exercise);
Notifications notifications;
MainStructureElement building = createBuilding();
InstrumentationAsset asset1 = createAsset();
InstrumentationAsset asset2 = createAsset();
MountingPointAsset mount = createMount(asset1, building.elements()[0]);
assign(building, EXERCISE_42);
assign(asset1, EXERCISE_42);
notifications.push_back(std::bind(&AssetAssignmentManager::onMainStructureElementChanged, &mgr, building, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset1, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset2, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onMountingChanged, &mgr, mount, DataModification::eAdded));
fireRandomly(notifications);
QVERIFY(!mgr.hasPending());
QVERIFY(spy.assignedAssets.size() == 1);
QVERIFY(spy.unassignedAssets.empty());
QCOMPARE(spy.assignedAssets[0], asset1);
}
/**
* Scenario where at runtime the Asset is first mounted, then the Building is assigned.
*/
void AssetAssignmentManagerTest::testRuntimeMountAssign()
{
AssetAssignmentManager mgr;
Exercise *exercise = mgr.exercise(EXERCISE_42);
AssignmentSpy spy(exercise);
Notifications notifications;
// create building unassigned, assets unmounted
MainStructureElement building = createBuilding();
InstrumentationAsset asset1 = createAsset();
InstrumentationAsset asset2 = createAsset();
notifications.push_back(std::bind(&AssetAssignmentManager::onMainStructureElementChanged, &mgr, building, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset1, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset2, DataModification::eAdded));
fireRandomly(notifications);
QVERIFY(!mgr.hasPending());
QVERIFY(spy.isEmpty());
// create the mount, building still unassigned
MountingPointAsset mount = createMount(asset1, building.elements()[0]);
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset1, DataModification::eModified));
notifications.push_back(std::bind(&AssetAssignmentManager::onMountingChanged, &mgr, mount, DataModification::eAdded));
fireRandomly(notifications);
QVERIFY(!mgr.hasPending());
QVERIFY(spy.isEmpty());
// assign the building and the asset as it is mounted
assign(building, EXERCISE_42);
assign(asset1, EXERCISE_42);
notifications.push_back(std::bind(&AssetAssignmentManager::onMainStructureElementChanged, &mgr, building, DataModification::eModified));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset1, DataModification::eModified));
fireRandomly(notifications);
QVERIFY(!mgr.hasPending());
QVERIFY(spy.assignedAssets.size() == 1);
QVERIFY(spy.unassignedAssets.empty());
QCOMPARE(spy.assignedAssets[0], asset1);
}
/**
* Scenario where at runtime the Building is first assigned, then the Asset is mounted.
*/
void AssetAssignmentManagerTest::testRuntimeAssignMount()
{
AssetAssignmentManager mgr;
Exercise *exercise = mgr.exercise(EXERCISE_42);
AssignmentSpy spy(exercise);
Notifications notifications;
// create building unassigned, assets unmounted
MainStructureElement building = createBuilding();
InstrumentationAsset asset1 = createAsset();
InstrumentationAsset asset2 = createAsset();
notifications.push_back(std::bind(&AssetAssignmentManager::onMainStructureElementChanged, &mgr, building, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset1, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset2, DataModification::eAdded));
fireRandomly(notifications);
QVERIFY(!mgr.hasPending());
QVERIFY(spy.isEmpty());
// assign the building, but not yet the asset as it is not mounted
assign(building, EXERCISE_42);
mgr.onMainStructureElementChanged(building, DataModification::eModified);
QVERIFY(!mgr.hasPending());
QVERIFY(spy.isEmpty());
// mount the asset and assign it as the building is assigned already
MountingPointAsset mount = createMount(asset1, building.elements()[0]);
assign(asset1, EXERCISE_42);
notifications.push_back(std::bind(&AssetAssignmentManager::onMountingChanged, &mgr, mount, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset1, DataModification::eModified));
fireRandomly(notifications);
QVERIFY(!mgr.hasPending());
QVERIFY(spy.assignedAssets.size() == 1);
QVERIFY(spy.unassignedAssets.empty());
QCOMPARE(spy.assignedAssets[0], asset1);
}
/**
* Scenario where the Asset is already mounted at startup and then the Building will be assigned at runtime.
*/
void AssetAssignmentManagerTest::testStartMountedRuntimeAssign()
{
AssetAssignmentManager mgr;
Exercise *exercise = mgr.exercise(EXERCISE_42);
AssignmentSpy spy(exercise);
Notifications notifications;
// create building unassigned and asset1 mounted
MainStructureElement building = createBuilding();
InstrumentationAsset asset1 = createAsset();
InstrumentationAsset asset2 = createAsset();
MountingPointAsset mount = createMount(asset1, building.elements()[0]);
notifications.push_back(std::bind(&AssetAssignmentManager::onMainStructureElementChanged, &mgr, building, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset1, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset2, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onMountingChanged, &mgr, mount, DataModification::eAdded));
fireRandomly(notifications);
QVERIFY(!mgr.hasPending());
QVERIFY(spy.isEmpty());
// assign building and asset1 as it is mounted
assign(building, EXERCISE_42);
assign(asset1, EXERCISE_42);
notifications.push_back(std::bind(&AssetAssignmentManager::onMainStructureElementChanged, &mgr, building, DataModification::eModified));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset1, DataModification::eModified));
fireRandomly(notifications);
QVERIFY(!mgr.hasPending());
QVERIFY(spy.assignedAssets.size() == 1);
QVERIFY(spy.unassignedAssets.empty());
QCOMPARE(spy.assignedAssets[0], asset1);
}
/**
* Scenario where the Building is already assigned at startup and the Asset will be mounted at runtime.
*/
void AssetAssignmentManagerTest::testStartAssignedRuntimeMount()
{
AssetAssignmentManager mgr;
Exercise *exercise = mgr.exercise(EXERCISE_42);
AssignmentSpy spy(exercise);
Notifications notifications;
// create building assigned, assets unmounted
MainStructureElement building = createBuilding();
assign(building, EXERCISE_42);
InstrumentationAsset asset1 = createAsset();
InstrumentationAsset asset2 = createAsset();
notifications.push_back(std::bind(&AssetAssignmentManager::onMainStructureElementChanged, &mgr, building, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset1, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset2, DataModification::eAdded));
fireRandomly(notifications);
QVERIFY(!mgr.hasPending());
QVERIFY(spy.isEmpty());
// mount asset1 and assign it as the building is assigned already
MountingPointAsset mount = createMount(asset1, building.elements()[0]);
assign(asset1, EXERCISE_42);
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset1, DataModification::eModified));
notifications.push_back(std::bind(&AssetAssignmentManager::onMountingChanged, &mgr, mount, DataModification::eAdded));
fireRandomly(notifications);
QVERIFY(!mgr.hasPending());
QVERIFY(spy.assignedAssets.size() == 1);
QVERIFY(spy.unassignedAssets.empty());
QCOMPARE(spy.assignedAssets[0], asset1);
}
/**
* Scenario where the Asset is first umounted at runtime, then the Building is unassigned.
*/
void AssetAssignmentManagerTest::testRuntimeUnmountUnassign()
{
AssetAssignmentManager mgr;
Exercise *exercise = mgr.exercise(EXERCISE_42);
AssignmentSpy spy(exercise);
Notifications notifications;
MainStructureElement building = createBuilding();
InstrumentationAsset asset1 = createAsset();
InstrumentationAsset asset2 = createAsset();
MountingPointAsset mount = createMount(asset1, building.elements()[0]);
assign(building, EXERCISE_42);
assign(asset1, EXERCISE_42);
notifications.push_back(std::bind(&AssetAssignmentManager::onMainStructureElementChanged, &mgr, building, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset1, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset2, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onMountingChanged, &mgr, mount, DataModification::eAdded));
fireRandomly(notifications);
spy.clear();
// unmount asset1 and also unassign it
InstrumentationAsset assignedAsset = asset1;
unassign(assignedAsset);
unmount(assignedAsset);
notifications.push_back(std::bind(&AssetAssignmentManager::onMountingChanged, &mgr, mount, DataModification::eRemoved));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, assignedAsset, DataModification::eModified));
fireRandomly(notifications);
QVERIFY(!mgr.hasPending());
QVERIFY(spy.assignedAssets.empty());
QVERIFY(spy.unassignedAssets.size() == 1);
QCOMPARE(spy.unassignedAssets[0], asset1);
spy.clear();
// unassign the building
unassign(building);
mgr.onMainStructureElementChanged(building, DataModification::eModified);
QVERIFY(!mgr.hasPending());
QVERIFY(spy.isEmpty());
}
/**
* Scenario where the Building is first unassigned at runtime, then the Asset is unmounted.
*/
void AssetAssignmentManagerTest::testRuntimeUnassignUnmount()
{
AssetAssignmentManager mgr;
Exercise *exercise = mgr.exercise(EXERCISE_42);
AssignmentSpy spy(exercise);
Notifications notifications;
MainStructureElement building = createBuilding();
InstrumentationAsset asset1 = createAsset();
InstrumentationAsset asset2 = createAsset();
MountingPointAsset mount = createMount(asset1, building.elements()[0]);
assign(building, EXERCISE_42);
assign(asset1, EXERCISE_42);
notifications.push_back(std::bind(&AssetAssignmentManager::onMainStructureElementChanged, &mgr, building, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset1, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset2, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onMountingChanged, &mgr, mount, DataModification::eAdded));
fireRandomly(notifications);
spy.clear();
// unassign the building and also asset1
InstrumentationAsset unassignedAsset = asset1;
unassign(building);
unassign(unassignedAsset);
notifications.push_back(std::bind(&AssetAssignmentManager::onMainStructureElementChanged, &mgr, building, DataModification::eModified));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, unassignedAsset, DataModification::eModified));
fireRandomly(notifications);
QVERIFY(!mgr.hasPending());
QVERIFY(spy.assignedAssets.empty());
QVERIFY(spy.unassignedAssets.size() == 1);
QCOMPARE(spy.unassignedAssets[0], asset1);
spy.clear();
// unmount the asset
unmount(asset1);
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset1, DataModification::eModified));
notifications.push_back(std::bind(&AssetAssignmentManager::onMountingChanged, &mgr, mount, DataModification::eRemoved));
QVERIFY(!mgr.hasPending());
QVERIFY(spy.isEmpty());
}
/**
* Scenario where the MountingPoint (i.e. the Building) will be directly reassigned at runtime
* while the Assets stays mounted.
*/
void AssetAssignmentManagerTest::testRuntimeReassign()
{
AssetAssignmentManager mgr;
Notifications notifications;
MainStructureElement building = createBuilding();
InstrumentationAsset asset1 = createAsset();
InstrumentationAsset asset2 = createAsset();
MountingPointAsset mount = createMount(asset1, building.elements()[0]);
assign(building, EXERCISE_42);
assign(asset1, EXERCISE_42);
notifications.push_back(std::bind(&AssetAssignmentManager::onMainStructureElementChanged, &mgr, building, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset1, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset2, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onMountingChanged, &mgr, mount, DataModification::eAdded));
fireRandomly(notifications);
AssignmentSpy spy42(mgr.exercise(EXERCISE_42));
AssignmentSpy spy35(mgr.exercise(EXERCISE_35));
InstrumentationAsset reassignedAsset = asset1;
assign(building, EXERCISE_35);
assign(reassignedAsset, EXERCISE_35);
notifications.push_back(std::bind(&AssetAssignmentManager::onMainStructureElementChanged, &mgr, building, DataModification::eModified));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, reassignedAsset, DataModification::eModified));
fireRandomly(notifications);
QVERIFY(!mgr.hasPending());
QVERIFY(spy42.assignedAssets.empty());
QVERIFY(spy42.unassignedAssets.size() == 1);
QCOMPARE(spy42.unassignedAssets[0], asset1);
QVERIFY(spy35.assignedAssets.size() == 1);
QVERIFY(spy35.unassignedAssets.empty());
QCOMPARE(spy35.assignedAssets[0], reassignedAsset);
}
/**
* Scenario that happens during shutdown of the services when an Asset is mounted and anssigned.
* All Entities are then simply removed from DDS.
*/
void AssetAssignmentManagerTest::testShutdownMountedAssigned()
{
AssetAssignmentManager mgr;
Exercise *exercise = mgr.exercise(EXERCISE_42);
AssignmentSpy spy(exercise);
Notifications notifications;
MainStructureElement building = createBuilding();
InstrumentationAsset asset1 = createAsset();
InstrumentationAsset asset2 = createAsset();
MountingPointAsset mount = createMount(asset1, building.elements()[0]);
assign(building, EXERCISE_42);
assign(asset1, EXERCISE_42);
notifications.push_back(std::bind(&AssetAssignmentManager::onMainStructureElementChanged, &mgr, building, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset1, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset2, DataModification::eAdded));
notifications.push_back(std::bind(&AssetAssignmentManager::onMountingChanged, &mgr, mount, DataModification::eAdded));
fireRandomly(notifications);
spy.clear();
// remove all entities
notifications.push_back(std::bind(&AssetAssignmentManager::onMainStructureElementChanged, &mgr, building, DataModification::eRemoved));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset1, DataModification::eRemoved));
notifications.push_back(std::bind(&AssetAssignmentManager::onAssetChanged, &mgr, asset2, DataModification::eRemoved));
notifications.push_back(std::bind(&AssetAssignmentManager::onMountingChanged, &mgr, mount, DataModification::eRemoved));
fireRandomly(notifications);
QVERIFY(!mgr.hasPending());
QVERIFY(spy.assignedAssets.empty());
QVERIFY(spy.unassignedAssets.size() == 1);
QCOMPARE(spy.unassignedAssets[0], asset1);
}
/**
* Special case where an Asset will be added (alreay assigned) *and* modified (still assigned)
* before the MountingPointAsset will be published.
* In that case there would be two pending assigns.
*/
void AssetAssignmentManagerTest::testDoublePendingAssign()
{
AssetAssignmentManager mgr;
Exercise *exercise = mgr.exercise(EXERCISE_42);
AssignmentSpy spy(exercise);
Notifications notifications;
MainStructureElement building = createBuilding();
InstrumentationAsset asset1 = createAsset();
InstrumentationAsset asset2 = createAsset();
MountingPointAsset mount = createMount(asset1, building.elements()[0]);
assign(building, EXERCISE_42);
assign(asset1, EXERCISE_42);
mgr.onMainStructureElementChanged(building, DataModification::eAdded);
mgr.onAssetChanged(asset1, DataModification::eAdded);
mgr.onAssetChanged(asset2, DataModification::eAdded);
mgr.onAssetChanged(asset1, DataModification::eModified);
mgr.onMountingChanged(mount, DataModification::eAdded);
QVERIFY(!mgr.hasPending());
QVERIFY(spy.assignedAssets.size() == 1);
QVERIFY(spy.unassignedAssets.empty());
QCOMPARE(spy.assignedAssets[0], asset1);
}
#include "asset_assignment_manager_test.moc"