/** * @RDE_HEADER@ * AssetAssignmentManager * $Id$ * $HeadURL$ * $Author$ * Created by Christian Fröbel, SLSE, 2017-12-01 */ #include "asset_assignment_manager_test.h" #include #include #include #include #include #include #include #include #include #include #include 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 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 assignedAssets; std::vector unassignedAssets; private slots: void assetAssigned(Asset *asset) { assignedAssets.push_back(asset->data()); } void assetUnassigned(Asset *asset) { unassignedAssets.push_back(asset->data()); } }; template 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>; void fireRandomly(Notifications ¬ifications) { shuffle(notifications); std::for_each(notifications.begin(), notifications.end(), [] (const std::function &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"