Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

+78
View File
@@ -0,0 +1,78 @@
#
# @RDE_HEADER@
# $Id$
# $HeadURL$
# $Author$
# Created by Christian Fröbel, SLSE, 2017-06-19
#
SET(SRCS_static
ava_test_server.h
ava_test_server.cpp
)
#
# Static lib just to hold the AvaTestServer as it is needed for the
# stand-alone executable as well as for the unit tests.
#
NGL_ADD_LIBRARY(WS AAR SV avaTestServer_static STATIC ${SRCS_static})
NGL_TARGET_LINK_LIBRARIES(avaTestServer_static
PUBLIC
Qt5::Core
Qt5::Network
)
SET(SRCS
ava_test_server_main.cpp
)
NGL_ADD_EXECUTABLE(WS AAR SV NO_TRANSLATION avaTestServer ${SRCS})
NGL_TARGET_LINK_LIBRARIES(avaTestServer
avaTestServer_static
Qt5::Core
)
IF(BUILD_TESTING)
SET(SRCS
main.cpp
ava_comm_double.h
parse_line_test.h
parse_line_test.cpp
ava_comm_test.h
ava_comm_test.cpp
ava_control_test.h
ava_control_test.cpp
av_helper_test.h
av_helper_test.cpp
asset_assignment_manager_test.h
asset_assignment_manager_test.cpp
audio_video_handler_impl_test.h
audio_video_handler_impl_test.cpp
choose_stream_test.h
choose_stream_test.cpp
avs_command_queue_test.h
avs_command_queue_test.cpp
ava_timeline_test.h
ava_timeline_test.cpp
rt_state_test.h
rt_state_test.cpp
list_response_collector_test.h
list_response_collector_test.cpp
)
IF(TARGET svcAvmCommon)
ADD_EXECUTABLE(avmTests ${SRCS})
TARGET_LINK_LIBRARIES(avmTests
Qt5::Core
Qt5::Network
Qt5::Test
svcAvmCommon
svcIsmCommon
avaTestServer_static
nglcoreapplication
NGLLogger
Boost::boost
)
ADD_TEST(NAME avmTests COMMAND $<TARGET_FILE:avmTests>)
ENDIF()
ENDIF()
@@ -0,0 +1,595 @@
/**
* @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"
@@ -0,0 +1,42 @@
/**
* @RDE_HEADER@
* AssetAssignmentManager
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, SLSE, 2017-12-01
*/
#pragma once
#include <QTest>
/**
* @author Christian Fröbel
*/
class AssetAssignmentManagerTest : public QObject
{
Q_OBJECT
private slots:
void initTestCase() {}
void cleanupTestCase() {}
void init() {}
void cleanup() {}
void testStartUnmountedUnassigned();
void testStartMountedAssigned();
void testRuntimeMountAssign();
void testRuntimeAssignMount();
void testStartMountedRuntimeAssign();
void testStartAssignedRuntimeMount();
void testRuntimeUnmountUnassign();
void testRuntimeUnassignUnmount();
void testRuntimeReassign();
void testShutdownMountedAssigned();
void testDoublePendingAssign();
};
@@ -0,0 +1,602 @@
/**
* @RDE_HEADER@
* AudioVideoHandlerImplTest
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, NLSE, 2018-01-09
*/
#include "audio_video_handler_impl_test.h"
#include <AudioVideoManagement/audio_video_handler_impl.h>
#include <AudioVideoManagement/asset.h>
#include <AudioVideoManagement/mounting_point.h>
#include <AudioVideoManagement/exercise.h>
#include <AudioVideoManagement/avm.hpp>
#include <InfrastructureManagement/ism.hpp>
#include <LtsUtilsCore/json_object.h>
#include <QtGlobal>
#include <boost/optional.hpp>
#include <map>
#include <string>
#include <vector>
#include <utility>
#include <memory>
using namespace AudioVideoManagement;
using namespace InfrastructureManagement;
using LtsUtils::JsonObject;
namespace
{
int streamCount(const Streams &streams)
{
if (streams.second)
return 2;
return 1;
}
class DbMock : public AudioVideoHandlerImpl::IDbProxy
{
public:
const Streams &findStreams(AssetId id) const override
{
auto found = m_cache.find(id);
Q_ASSERT(found != m_cache.end());
return found->second;
}
const Streams &findStreams(Asset *asset) override
{
Q_ASSERT(asset);
return findStreams(asset->data().id());
}
bool isEmpty() const { return m_cache.empty(); }
int deviceCount() const { return m_cache.size(); }
int streamCount() const
{
int count = 0;
for (const auto &streams: m_cache)
count += ::streamCount(streams.second);
return count;
}
void insert(Streams &streams) override
{
streams.first.id = ++m_nextId;
if (streams.second)
streams.second->id = ++m_nextId;
link(streams);
auto insertResult = m_cache.insert(std::make_pair(streams.assetId(), streams));
Q_ASSERT(insertResult.second);
}
/**
* We simply maintain the ids or give a new id to a possibly new entry.
*/
void update(Streams &newStreams) override
{
auto found = m_cache.find(newStreams.assetId());
Q_ASSERT(found != m_cache.end());
const Streams oldStreams = found->second;
newStreams.first.id = oldStreams.first.id;
if (newStreams.second)
{
if (oldStreams.second)
newStreams.second->id = oldStreams.second->id;
else
newStreams.second->id = ++m_nextId;
}
link(newStreams);
found->second = newStreams; // update cache
}
void remove(Asset *asset) override
{
Q_ASSERT(asset);
auto count = m_cache.erase(asset->data().id());
Q_ASSERT(count == 1);
}
private:
void link(Streams& streams)
{
if (!streams.second)
{
streams.first.link = 0;
return;
}
streams.first.link = streams.second->id;
streams.second->link = streams.first.id;
}
int m_nextId{0};
std::map<AssetId, Streams> m_cache;
};
class DtsSpy : public AudioVideoHandlerImpl::IDtsProxy
{
public:
void publish(const Stream &stream) override
{
m_published.push_back(stream);
}
void remove(uint64_t streamId) override
{
m_removed.push_back(streamId);
}
bool isEmpty() const { return m_published.empty() && m_removed.empty(); }
void reset()
{
m_published.clear();
m_removed.clear();
}
std::vector<Stream> m_published;
std::vector<uint64_t> m_removed;
};
Asset *createAsset(int id, MountingPointType mpType, const std::string &properties)
{
InstrumentationAsset data;
data.id(id);
data.name(QStringLiteral("ASSET_000%1").arg(id).toUtf8().constData());
data.hostname(QStringLiteral("host_000%1").arg(id).toUtf8().constData());
data.mountingPointTypeId(mpType);
data.properties(properties);
return new Asset(data);
}
MountingPoint *createMountingPoint(int id, MountingPointType mpType)
{
StructureElement data;
data.id(id);
data.name(QStringLiteral("MP_000%1").arg(id).toUtf8().constData());
data.displayName(QStringLiteral("display name %1").arg(id).toUtf8().constData());
data.structureElementTypeId(StructureElementType::MountingPoint);
data.mountingPointTypeId(mpType);
data.properties("{}");
return new MountingPoint(data);
}
void setProperties(Asset *asset, const std::string &newProperties)
{
Q_ASSERT(asset);
InstrumentationAsset data = asset->data();
data.properties(newProperties);
asset->update(data);
}
void setDisplayName(MountingPoint *mp, const std::string &newDisplayName)
{
Q_ASSERT(mp);
StructureElement data = mp->data();
data.displayName(newDisplayName);
mp->update(data);
}
void setRecording(MountingPoint *mp, bool on)
{
Q_ASSERT(mp);
StructureElement data = mp->data();
if (on)
data.properties(R"({"record": true})");
else
data.properties(R"({"record": false})");
mp->update(data);
}
void mount(Asset *asset, MountingPoint *mp)
{
Q_ASSERT(asset);
InstrumentationAsset data = asset->data();
data.assetStateId(InstrumentationAssetState::Mounted);
asset->update(data);
asset->mount(mp);
}
Database::AvmDeviceConfig dcEntry(int64_t id, const std::string &context, const std::string &type, const std::string &name, const std::string &controller, const std::string &definition, int32_t link)
{
Database::AvmDeviceConfig dc;
dc.id = id;
dc.context = QString::fromStdString(context);
dc.type = QString::fromStdString(type);
dc.name = QString::fromStdString(name);
dc.controller = QString::fromStdString(controller);
dc.definition = QString::fromStdString(definition);
dc.link = link;
return dc;
}
bool operator==(const Database::AvmDeviceConfig &lhs, const Database::AvmDeviceConfig &rhs)
{
if (lhs.id != rhs.id)
return false;
if (lhs.context != rhs.context)
return false;
if (lhs.type != rhs.type)
return false;
if (lhs.name != rhs.name)
return false;
if (lhs.controller != rhs.controller)
return false;
if (lhs.definition != rhs.definition)
return false;
if (lhs.link != rhs.link)
return false;
return true;
}
} // anonymous namespace
namespace AudioVideoManagement
{
AudioVideoHandlerImplTest::AudioVideoHandlerImplTest() = default;
AudioVideoHandlerImplTest::~AudioVideoHandlerImplTest() = default;
/**
* Create Assets and MountingPoints.
* They will be mounted already but left in the pool exercise.
*/
void AudioVideoHandlerImplTest::init()
{
m_nextAssetId = 0;
m_nextMpId = 0;
m_pool.reset(new Exercise(ExerciseContext::defaultContext()));
m_exercise.reset(new Exercise(ExerciseContext(42)));
m_indoorCam = createAsset(++m_nextAssetId, MountingPointType::IndoorCamera, R"({"type": "vapix"})");
m_outdoorCam = createAsset(++m_nextAssetId, MountingPointType::OutdoorCamera, R"({"type": "vapix"})");
m_announcementUnit = createAsset(++m_nextAssetId, MountingPointType::AnnouncementUnit, R"({"type": "vapix"})");
m_mpIndoor = createMountingPoint(++m_nextMpId, MountingPointType::IndoorCamera);
m_mpOutdoor = createMountingPoint(++m_nextMpId, MountingPointType::OutdoorCamera);
m_mpAnnouncementUnit = createMountingPoint(++m_nextMpId, MountingPointType::AnnouncementUnit);
mount(m_indoorCam, m_mpIndoor);
mount(m_outdoorCam, m_mpOutdoor);
mount(m_announcementUnit, m_mpAnnouncementUnit);
m_pool->add(std::unique_ptr<Asset>(m_indoorCam));
m_pool->add(std::unique_ptr<Asset>(m_outdoorCam));
m_pool->add(std::unique_ptr<Asset>(m_announcementUnit));
m_pool->add(std::unique_ptr<MountingPoint>(m_mpIndoor));
m_pool->add(std::unique_ptr<MountingPoint>(m_mpOutdoor));
m_pool->add(std::unique_ptr<MountingPoint>(m_mpAnnouncementUnit));
}
void AudioVideoHandlerImplTest::cleanup()
{
m_pool.reset();
m_exercise.reset();
}
/**
* Assign a camera to an Exercise that neither has microphone nor speaker.
*/
void AudioVideoHandlerImplTest::testAssignCameraVideo()
{
DbMock dbMock;
DtsSpy dtsSpy;
AudioVideoHandlerImpl handler(m_exercise.get(), &dbMock, &dtsSpy);
handler.onContextActivated();
QVERIFY(dbMock.isEmpty());
QVERIFY(dtsSpy.isEmpty());
m_pool->moveMountedTo(m_indoorCam, m_exercise.get(), boost::none);
QCOMPARE(dbMock.deviceCount(), 1);
QCOMPARE(dbMock.streamCount(), 1);
QCOMPARE(static_cast<int>(dtsSpy.m_published.size()), 1);
QVERIFY(dtsSpy.m_removed.empty());
// verify DB
const Streams &streams = dbMock.findStreams(m_indoorCam->data().id());
QVERIFY(!streams.second);
QVERIFY(streams.first == dcEntry(1, "ex_42", "AXIS_VIDEO_CHANNEL", "av_0001", "EXCAVS01", "url=rtsp://root:root@host_0001:554;mode=1;", 0));
// verify DDS
QCOMPARE(dtsSpy.m_published[0], Stream(1, "av_0001", StreamType::Video, m_mpIndoor->data().id()));
}
/**
* Assign a Camera to an Exercise that has a microphone.
*/
void AudioVideoHandlerImplTest::testAssignCameraVideoAudioIn()
{
testAssignCameraVideoAudio(R"({"type": "vapix", "hasMicrophone": true})", StreamType::AudioIn);
}
/**
* Assign a Camera to an Exercise that has a speaker.
*/
void AudioVideoHandlerImplTest::testAssignCameraVideoAudioOut()
{
testAssignCameraVideoAudio(R"({"type": "vapix", "hasSpeaker": true})", StreamType::AudioOut);
}
/**
* Assign a Camera to an Exercise that has both, microphone and speaker.
*/
void AudioVideoHandlerImplTest::testAssignCameraVideoAudioInOut()
{
testAssignCameraVideoAudio(R"({"type": "vapix", "hasMicrophone": true, "hasSpeaker": true})", StreamType::AudioInOut);
}
void AudioVideoHandlerImplTest::testAssignCameraVideoAudio(const std::string &camProperties, StreamType expectedStreamType)
{
DbMock dbMock;
DtsSpy dtsSpy;
AudioVideoHandlerImpl handler(m_exercise.get(), &dbMock, &dtsSpy);
// Change the Asset's properties before activating the context.
setProperties(m_outdoorCam, camProperties);
handler.onContextActivated();
m_pool->moveMountedTo(m_outdoorCam, m_exercise.get(), boost::none);
QCOMPARE(dbMock.deviceCount(), 1);
QCOMPARE(dbMock.streamCount(), 2);
QCOMPARE(static_cast<int>(dtsSpy.m_published.size()), 2);
QVERIFY(dtsSpy.m_removed.empty());
// verfiy DB
const Streams &streams = dbMock.findStreams(m_outdoorCam->data().id());
QVERIFY(streams.second);
QVERIFY(streams.first == dcEntry(1, "ex_42", "AXIS_VIDEO_CHANNEL", "av_0002", "EXCAVS01", "url=rtsp://root:root@host_0002:554;mode=1;", 2));
QVERIFY(*streams.second == dcEntry(2, "ex_42", "AXIS_AUDIO_CHANNEL", "av_0002", "EXCAVS01", "url=http://root:root@host_0002:80;mode=1;", 1));
// verify DDS
const Stream &videoStream = dtsSpy.m_published[0];
QCOMPARE(videoStream, Stream(1, "av_0002", StreamType::Video, m_mpOutdoor->data().id()));
const Stream &audioStream = dtsSpy.m_published[1];
QCOMPARE(audioStream, Stream(2, "av_0002", expectedStreamType, m_mpOutdoor->data().id()));
}
/**
* Assign an AnnouncementUnit to an exercise.
*/
void AudioVideoHandlerImplTest::testAssignAnnouncementUnit()
{
DbMock dbMock;
DtsSpy dtsSpy;
AudioVideoHandlerImpl handler(m_exercise.get(), &dbMock, &dtsSpy);
handler.onContextActivated();
m_pool->moveMountedTo(m_announcementUnit, m_exercise.get(), boost::none);
QCOMPARE(dbMock.deviceCount(), 1);
QCOMPARE(dbMock.streamCount(), 1);
QCOMPARE(static_cast<int>(dtsSpy.m_published.size()), 1);
QVERIFY(dtsSpy.m_removed.empty());
// verfiy DB
const Streams &streams = dbMock.findStreams(m_announcementUnit->data().id());
QVERIFY(!streams.second);
QVERIFY(streams.first == dcEntry(1, "ex_42", "AXIS_AUDIO_CHANNEL", "av_0003", "EXCAVS01", "url=http://root:root@host_0003:80;codec=aac;mode=1;", 0));
// verify DDS
QCOMPARE(dtsSpy.m_published[0], Stream(1, "av_0003", StreamType::AudioOut, m_mpAnnouncementUnit->data().id()));
}
/**
* In this test we assign Assets to the Exercise *before* the Exercise gets activated.
*/
void AudioVideoHandlerImplTest::testAssignBeforeActivation()
{
DbMock dbMock;
DtsSpy dtsSpy;
AudioVideoHandlerImpl handler(m_exercise.get(), &dbMock, &dtsSpy);
m_pool->moveMountedTo(m_indoorCam, m_exercise.get(), boost::none);
m_pool->moveMountedTo(m_outdoorCam, m_exercise.get(), boost::none);
m_pool->moveMountedTo(m_announcementUnit, m_exercise.get(), boost::none);
QVERIFY(dbMock.isEmpty());
QVERIFY(dtsSpy.isEmpty());
handler.onContextActivated();
QCOMPARE(dbMock.deviceCount(), 3);
QCOMPARE(dbMock.streamCount(), 3);
QCOMPARE(static_cast<int>(dtsSpy.m_published.size()), 3);
}
void AudioVideoHandlerImplTest::testUnassignCameraVideo()
{
DbMock dbMock;
DtsSpy dtsSpy;
AudioVideoHandlerImpl handler(m_exercise.get(), &dbMock, &dtsSpy);
m_pool->moveMountedTo(m_indoorCam, m_exercise.get(), boost::none);
handler.onContextActivated();
QCOMPARE(dbMock.deviceCount(), 1);
QCOMPARE(dbMock.streamCount(), 1);
QCOMPARE(static_cast<int>(dtsSpy.m_published.size()), 1);
QVERIFY(dtsSpy.m_removed.empty());
dtsSpy.reset();
m_exercise->moveMountedTo(m_indoorCam, m_pool.get(), boost::none);
QVERIFY(dbMock.isEmpty());
QVERIFY(dtsSpy.m_published.empty());
QCOMPARE(static_cast<int>(dtsSpy.m_removed.size()), 1);
QCOMPARE(static_cast<int>(dtsSpy.m_removed[0]), 1);
}
/**
* Update a Camera while it is assigned.
*/
void AudioVideoHandlerImplTest::testUpdateRemoveAssignedAsset()
{
DbMock dbMock;
DtsSpy dtsSpy;
AudioVideoHandlerImpl handler(m_exercise.get(), &dbMock, &dtsSpy);
m_pool->moveMountedTo(m_indoorCam, m_exercise.get(), boost::none);
handler.onContextActivated();
QCOMPARE(dbMock.deviceCount(), 1);
QCOMPARE(dbMock.streamCount(), 1);
QCOMPARE(static_cast<int>(dtsSpy.m_published.size()), 1);
QVERIFY(dtsSpy.m_removed.empty());
// add microphone to Camera => one additional audio stream
dtsSpy.reset();
setProperties(m_indoorCam, R"({"type": "vapix", "hasMicrophone": true})");
QCOMPARE(dbMock.deviceCount(), 1);
QCOMPARE(dbMock.streamCount(), 2);
QCOMPARE(static_cast<int>(dtsSpy.m_published.size()), 2); // one updated, one new
QVERIFY(dtsSpy.m_removed.empty());
// verify DB
Streams streams = dbMock.findStreams(m_indoorCam->data().id());
QVERIFY(streams.second);
QVERIFY(streams.first == dcEntry(1, "ex_42", "AXIS_VIDEO_CHANNEL", "av_0001", "EXCAVS01", "url=rtsp://root:root@host_0001:554;mode=1;", 2));
QVERIFY(*streams.second == dcEntry(2, "ex_42", "AXIS_AUDIO_CHANNEL", "av_0001", "EXCAVS01", "url=http://root:root@host_0001:80;mode=1;", 1));
// verify DDS
QCOMPARE(dtsSpy.m_published[0], Stream(1, "av_0001", StreamType::Video, m_mpIndoor->data().id()));
QCOMPARE(dtsSpy.m_published[1], Stream(2, "av_0001", StreamType::AudioIn, m_mpIndoor->data().id()));
// remove microphone again => removal of audio stream
dtsSpy.reset();
setProperties(m_indoorCam, R"({"type": "vapix", "hasMicrophone": false})");
QCOMPARE(dbMock.deviceCount(), 1);
QCOMPARE(dbMock.streamCount(), 1);
QCOMPARE(static_cast<int>(dtsSpy.m_published.size()), 1); // one updated
QCOMPARE(static_cast<int>(dtsSpy.m_removed.size()), 1);
// verify DB
streams = dbMock.findStreams(m_indoorCam->data().id());
QVERIFY(!streams.second);
QVERIFY(streams.first == dcEntry(1, "ex_42", "AXIS_VIDEO_CHANNEL", "av_0001", "EXCAVS01", "url=rtsp://root:root@host_0001:554;mode=1;", 0));
// verify DDS
QCOMPARE(dtsSpy.m_published[0], Stream(1, "av_0001", StreamType::Video, m_mpIndoor->data().id()));
QCOMPARE(static_cast<int>(dtsSpy.m_removed[0]), 2);
// unassign the Camera
dtsSpy.reset();
m_exercise->moveMountedTo(m_indoorCam, m_pool.get(), boost::none);
QCOMPARE(dbMock.deviceCount(), 0);
QVERIFY(dtsSpy.m_published.empty());
QCOMPARE(static_cast<int>(dtsSpy.m_removed.size()), 1);
// verify DDS
QCOMPARE(static_cast<int>(dtsSpy.m_removed[0]), 1);
}
/**
* Update the MountingPoint of an assigned Asset.
*/
void AudioVideoHandlerImplTest::testUpdateAssignedMountingPoint()
{
DbMock dbMock;
DtsSpy dtsSpy;
AudioVideoHandlerImpl handler(m_exercise.get(), &dbMock, &dtsSpy);
m_pool->moveMountedTo(m_indoorCam, m_exercise.get(), boost::none);
handler.onContextActivated();
QCOMPARE(dbMock.deviceCount(), 1);
QCOMPARE(dbMock.streamCount(), 1);
QCOMPARE(static_cast<int>(dtsSpy.m_published.size()), 1);
QVERIFY(dtsSpy.m_removed.empty());
// Change the displayName of the MountingPoint.
// We now distinguish between internal stream name and display name.
// Updating the displayName property of a MountingPoint does not change the internal stream name.
dtsSpy.reset();
setDisplayName(m_mpIndoor, "updated display name");
QCOMPARE(dbMock.deviceCount(), 1);
QCOMPARE(dbMock.streamCount(), 1);
QCOMPARE(static_cast<int>(dtsSpy.m_published.size()), 1);
QVERIFY(dtsSpy.m_removed.empty());
// verify DB
Streams streams = dbMock.findStreams(m_indoorCam->data().id());
QVERIFY(!streams.second);
QVERIFY(streams.first == dcEntry(1, "ex_42", "AXIS_VIDEO_CHANNEL", "av_0001", "EXCAVS01", "url=rtsp://root:root@host_0001:554;mode=1;", 0));
// verify DDS
QCOMPARE(dtsSpy.m_published[0], Stream(1, "av_0001", StreamType::Video, m_mpIndoor->data().id()));
// set recording-flag on MountingPoint
dtsSpy.reset();
setRecording(m_mpIndoor, true);
QCOMPARE(dbMock.deviceCount(), 1);
QCOMPARE(dbMock.streamCount(), 1);
QCOMPARE(static_cast<int>(dtsSpy.m_published.size()), 1);
QVERIFY(dtsSpy.m_removed.empty());
// verify DB
streams = dbMock.findStreams(m_indoorCam->data().id());
QVERIFY(!streams.second);
QVERIFY(streams.first == dcEntry(1, "ex_42", "AXIS_VIDEO_CHANNEL", "av_0001", "EXCAVS01", "url=rtsp://root:root@host_0001:554;mode=1;recording=ECRS@EXCAVS01;recordcontrol=prealarm;", 0));
// verify DDS
QCOMPARE(dtsSpy.m_published[0], Stream(1, "av_0001", StreamType::Video, m_mpIndoor->data().id())); // no change here
}
void AudioVideoHandlerImplTest::testDeviceDefinitionCameraVideo()
{
QCOMPARE(AudioVideoHandlerImpl::deviceDefinitionCameraVideo("host", JsonObject::fromJson(R"({"type": "vapix"})"), JsonObject::fromJson(R"({})")),
QStringLiteral("url=rtsp://root:root@host:554;mode=1;"));
QCOMPARE(AudioVideoHandlerImpl::deviceDefinitionCameraVideo("host", JsonObject::fromJson(R"({"type": "vapix", "username": "user", "password": "1234"})"), JsonObject::fromJson(R"({})")),
QStringLiteral("url=rtsp://user:1234@host:554;mode=1;"));
QCOMPARE(AudioVideoHandlerImpl::deviceDefinitionCameraVideo("host", JsonObject::fromJson(R"({"type": "vapix", "port": 123})"), JsonObject::fromJson(R"({})")),
QStringLiteral("url=rtsp://root:root@host:123;mode=1;"));
QCOMPARE(AudioVideoHandlerImpl::deviceDefinitionCameraVideo("host", JsonObject::fromJson(R"({"type": "vapix"})"), JsonObject::fromJson(R"({"record": true})")),
QStringLiteral("url=rtsp://root:root@host:554;mode=1;recording=ECRS@EXCAVS01;recordcontrol=prealarm;"));
}
void AudioVideoHandlerImplTest::testDeviceDefinitionCameraAudio()
{
QCOMPARE(AudioVideoHandlerImpl::deviceDefinitionCameraAudio("host", JsonObject::fromJson(R"({"type": "vapix"})"), JsonObject::fromJson(R"({})")),
QStringLiteral("url=http://root:root@host:80;mode=1;"));
QCOMPARE(AudioVideoHandlerImpl::deviceDefinitionCameraAudio("host", JsonObject::fromJson(R"({"type": "vapix", "username": "user", "password": "1234"})"), JsonObject::fromJson(R"({})")),
QStringLiteral("url=http://user:1234@host:80;mode=1;"));
QCOMPARE(AudioVideoHandlerImpl::deviceDefinitionCameraAudio("host", JsonObject::fromJson(R"({"type": "vapix", "audioPort": 88})"), JsonObject::fromJson(R"({})")),
QStringLiteral("url=http://root:root@host:88;mode=1;"));
QCOMPARE(AudioVideoHandlerImpl::deviceDefinitionCameraAudio("host", JsonObject::fromJson(R"({"type": "vapix"})"), JsonObject::fromJson(R"({"record": true})")),
QStringLiteral("url=http://root:root@host:80;mode=1;recording=ECRS@EXCAVS01;recordcontrol=prealarm;"));
}
void AudioVideoHandlerImplTest::testDeviceDefinitionAnnouncementUnit()
{
QCOMPARE(AudioVideoHandlerImpl::deviceDefinitionAnnouncementUnit("host", JsonObject::fromJson(R"({"type": "vapix"})"), JsonObject::fromJson(R"({})")),
QStringLiteral("url=http://root:root@host:80;codec=aac;mode=1;"));
QCOMPARE(AudioVideoHandlerImpl::deviceDefinitionAnnouncementUnit("host", JsonObject::fromJson(R"({"type": "vapix", "username": "user", "password": "1234"})"), JsonObject::fromJson(R"({})")),
QStringLiteral("url=http://user:1234@host:80;codec=aac;mode=1;"));
QCOMPARE(AudioVideoHandlerImpl::deviceDefinitionAnnouncementUnit("host", JsonObject::fromJson(R"({"type": "vapix", "audioPort": 88})"), JsonObject::fromJson(R"({})")),
QStringLiteral("url=http://root:root@host:88;codec=aac;mode=1;"));
QCOMPARE(AudioVideoHandlerImpl::deviceDefinitionAnnouncementUnit("host", JsonObject::fromJson(R"({"type": "vapix"})"), JsonObject::fromJson(R"({"record": true})")),
QStringLiteral("url=http://root:root@host:80;codec=aac;mode=1;recording=ECRS@EXCAVS01;"));
}
} // namespace AudioVideoManagement
@@ -0,0 +1,70 @@
/**
* @RDE_HEADER@
* AudioVideoHandlerImplTest
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, NLSE, 2018-01-09
*/
#pragma once
#include <AudioVideoManagement/avm_enums.hpp>
#include <QTest>
#include <memory>
namespace AudioVideoManagement
{
class Asset;
class MountingPoint;
class Exercise;
/**
* @author Christian Fröbel
*/
class AudioVideoHandlerImplTest : public QObject
{
Q_OBJECT
public:
AudioVideoHandlerImplTest();
~AudioVideoHandlerImplTest();
private slots:
void init();
void cleanup();
void testAssignCameraVideo();
void testAssignCameraVideoAudioIn();
void testAssignCameraVideoAudioOut();
void testAssignCameraVideoAudioInOut();
void testAssignAnnouncementUnit();
void testAssignBeforeActivation();
void testUnassignCameraVideo();
void testUpdateRemoveAssignedAsset();
void testUpdateAssignedMountingPoint();
void testDeviceDefinitionCameraVideo();
void testDeviceDefinitionCameraAudio();
void testDeviceDefinitionAnnouncementUnit();
private:
void testAssignCameraVideoAudio(const std::string &camProperties, StreamType expectedStreamType);
int m_nextAssetId{0};
int m_nextMpId{0};
std::unique_ptr<Exercise> m_pool;
std::unique_ptr<Exercise> m_exercise;
Asset *m_indoorCam{nullptr};
Asset *m_outdoorCam{nullptr};
Asset *m_announcementUnit{nullptr};
MountingPoint *m_mpIndoor{nullptr};
MountingPoint *m_mpOutdoor{nullptr};
MountingPoint *m_mpAnnouncementUnit{nullptr};
};
} // namespace AudioVideoManagement
@@ -0,0 +1,55 @@
/**
* @RDE_HEADER@
* unit tests for av helper functions
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, SLSE, 2017-06-23
*/
#include "av_helper_test.h"
#include <AudioVideoManagement/av_helper.h>
#include <QDateTime>
#include <QByteArray>
#include <string>
using namespace AudioVideoManagement;
void AvHelperTest::testCreateDuration()
{
AvaDuration duration = createDuration(QByteArray("1494829980.13"));
AvaDuration expected{149482998013};
QCOMPARE(duration, expected);
}
void AvHelperTest::testCreateDuration_withoutDot()
{
AvaDuration duration = createDuration(QByteArray("1494829980"));
AvaDuration expected{149482998000};
QCOMPARE(duration, expected);
}
void AvHelperTest::testDurationToString()
{
AvaDuration d{149482998013};
QString expected = QStringLiteral("1494829980.13");
QCOMPARE(toString(d), expected);
d = AvaDuration{149482998003};
expected = QStringLiteral("1494829980.03");
QCOMPARE(toString(d), expected);
}
void AvHelperTest::testIsStreamNameValid()
{
QVERIFY(isStreamNameValid(std::string("G3Krs")));
QVERIFY(isStreamNameValid(std::string("SR Ltg Krs")));
QVERIFY(isStreamNameValid(std::string("BtlFüKrs")));
QVERIFY(isStreamNameValid(std::string("GebVersBtl8")));
QVERIFY(isStreamNameValid(std::string("2.231")));
QVERIFY(!isStreamNameValid(std::string(" G3Krs")));
QVERIFY(!isStreamNameValid(std::string("G3Krs ")));
QVERIFY(!isStreamNameValid(std::string("tvc/01")));
QVERIFY(!isStreamNameValid(std::string("tvc\\01")));
}
@@ -0,0 +1,26 @@
/**
* @RDE_HEADER@
* unit tests for av helper functions
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, SLSE, 2017-06-23
*/
#pragma once
#include <QTest>
/**
* @author Christian Fröbel
*/
class AvHelperTest : public QObject
{
Q_OBJECT
private slots:
void testCreateDuration();
void testCreateDuration_withoutDot();
void testDurationToString();
void testIsStreamNameValid();
};
@@ -0,0 +1,70 @@
/**
* @RDE_HEADER@
* AvaCommDouble
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, NLE, 2018-12-19
*/
#pragma once
#include <AudioVideoManagement/iava_comm.h>
namespace AudioVideoManagement
{
/**
* All methods with empty implementation -- we only need the signals.
* @author Christian Fröbel
*/
class AvaCommDouble : public IAvaComm
{
public:
void forceDisconnect() override {}
void switchContext(int) override {}
void setUserProfile(const QString &, const QString &, AvaAudioPrio, AvaGuiStyle) override {}
void setTimezone(QChar) override {}
void setVideoDialogProfile(const QString &, AvaVisibility) override {}
void setVideoDialogGeometry(const QString &, const QRect &) override {}
void setVideoProfile(const QString &, const QString &) override {}
void setAnnouncementProfile(AvaSumChannel, const QString &, const QString &, const QString &) override {}
void getStreamList(AvaStreamType, AvaStreamSource) override {}
void chooseStream(const QString &, const QString &, bool ) override {}
void chooseStreamOverview(const QString &, const QStringList &, bool ) override {}
void releaseStream(const QString &, const QString &) override {}
void queryTimeline(const QString &, const boost::optional<AvaTimePoint> & = {}, const boost::optional<AvaTimePoint> & = {}) override {}
void setVolume(AvaVolume) override {}
void chooseImport(const QString &, const QString &, int ) override {}
void releaseImport(const QString &) override {}
void sendPreparedCommand(const QString &) override {}
void startStream(const QString &, const QString &, AvaSpeed) override {}
void stopStream(const QString &, const QString &) override {}
void pauseStream(const QString &, const QString &) override {}
void setStreamSpeed(const QString &, const QString &, AvaSpeed) override {}
void setAbsPosition(const QString &, const QString &, AvaTimePoint) override {}
void positionUpdate(const QString &, const QString &, std::chrono::milliseconds) override {}
void setPint(const QString &, const QString &, bool ) override {}
void getElokaList(const QString &) override {}
void chooseEloka(const QString &, const QString &, AvaElokaMode) override {}
void releaseEloka(const QString &) override {}
void queryEloka(AvaQueryElokaMode) override {}
void setScript(const QString &, const QString &, AvaScriptSource, const QVector<AvaScriptEntry> &) override {}
void releaseScript(const QString &, const QString &) override {}
void startScript(const QString &, const QString &) override {}
void stopScript(const QString &, const QString &) override {}
void pauseScript(const QString &, const QString &) override {}
void setAbsPositionScript(const QString &, const QString &, AvaTimePoint) override {}
void defineStreamNameMapping(const QString &, const QString &) override {}
void resetStreamNameMapping() override {}
void keepAlive() override {}
};
} // namespace AudioVideoManagement
@@ -0,0 +1,219 @@
/**
* @RDE_HEADER@
* AvaCommTest
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, SLSE, 2017-06-20
*/
#include "ava_comm_test.h"
#include "ava_test_server.h"
#include <AudioVideoManagement/ava_comm.h>
#include <AudioVideoManagement/ava_types.h>
#include <AudioVideoManagement/avs_dialog.h>
#include <QCoreApplication>
#include <QDateTime>
#include <QList>
#include <QVector>
#include <QByteArray>
#include <boost/optional.hpp>
using namespace AudioVideoManagement;
class AvaCommTest::SignalSpy : public QObject
{
Q_OBJECT
public:
SignalSpy(AvaComm *signalSource)
{
Q_ASSERT(signalSource);
connect(signalSource, &AvaComm::ok, this, &SignalSpy::onOk);
connect(signalSource, &AvaComm::connected, this, &SignalSpy::onConnected);
connect(signalSource, &AvaComm::streamListBeginReceived, this, &SignalSpy::onStreamListBeginReceived);
connect(signalSource, &AvaComm::timelineListBeginReceived, this, &SignalSpy::onTimelineListBeginReceived);
connect(signalSource, &AvaComm::elokaListBeginReceived, this, &SignalSpy::onElokaListBeginReceived);
connect(signalSource, &AvaComm::listValueReceived, this, &SignalSpy::onListValueReceived);
connect(signalSource, &AvaComm::endOfListReceived, this, &SignalSpy::onEndOfListReceived);
connect(signalSource, &AvaComm::streamStateReceived, this, &SignalSpy::onStreamStateReceived);
connect(signalSource, &AvaComm::streamInfoReceived, this, &SignalSpy::onStreamInfoReceived);
}
void reset()
{
m_connected = false;
m_ok = false;
m_streamListBegin = false;
m_timelineListBegin = false;
m_elokaListBegin = false;
m_endOfList = false;
m_listValues.clear();
m_streamState.reset();
m_streamInfo.reset();
}
public slots:
void onConnected() { m_connected = true; }
void onOk() { m_ok = true; }
void onStreamListBeginReceived() { m_streamListBegin = true; }
void onTimelineListBeginReceived() { m_timelineListBegin = true; }
void onElokaListBeginReceived() { m_elokaListBegin = true; }
void onListValueReceived(const QVector<QByteArray> &parts) { m_listValues.append(parts); }
void onEndOfListReceived() { m_endOfList = true; }
void onStreamStateReceived(const QString &dialogId, const QString &streamName, AvaStreamState streamState) { Q_UNUSED(dialogId); Q_UNUSED(streamName); m_streamState = streamState; }
void onStreamInfoReceived(const QString &dialogId, const QString &streamName, const AvaStreamInfo &streamInfo) { Q_UNUSED(dialogId); Q_UNUSED(streamName); m_streamInfo = streamInfo; }
public:
bool m_connected{false};
bool m_ok{false};
bool m_streamListBegin{false};
bool m_timelineListBegin{false};
bool m_elokaListBegin{false};
bool m_endOfList{false};
QVector<QVector<QByteArray>> m_listValues;
boost::optional<AvaStreamState> m_streamState;
boost::optional<AvaStreamInfo> m_streamInfo;
};
AvaCommTest::AvaCommTest() = default;
AvaCommTest::~AvaCommTest() = default;
void AvaCommTest::initTestCase()
{
m_fakeAvaServer.reset(new AvaTestServer);
}
void AvaCommTest::cleanupTestCase()
{
m_fakeAvaServer.reset();
}
void AvaCommTest::init()
{
m_avaComm.reset(new AvaComm(QStringLiteral("localhost"), 6370));
m_spy.reset(new SignalSpy(m_avaComm.get()));
m_avaComm->start();
}
void AvaCommTest::cleanup()
{
m_spy.reset();
m_avaComm.reset();
}
void AvaCommTest::testSwitchContext()
{
const int EXERCISE = 42;
QTRY_VERIFY(m_spy->m_connected);
m_avaComm->switchContext(EXERCISE);
QTRY_VERIFY(m_spy->m_ok);
}
void AvaCommTest::testSetUserProfile()
{
QTRY_VERIFY(m_spy->m_connected);
m_avaComm->setUserProfile(QStringLiteral("ctc"), QStringLiteral("en_US"), AvaAudioPrio(1), AvaGuiStyle::Light);
QTRY_VERIFY(m_spy->m_ok);
}
void AvaCommTest::testSetVideoDialogProfile()
{
// The real server needs a user profile before setVideoDialogProfile can be issued.
testSetUserProfile();
m_spy->reset();
m_avaComm->setVideoDialogProfile(AvsDialog::VIDEO_1, AvaVisibility::Hidden);
QTRY_VERIFY(m_spy->m_ok);
}
void AvaCommTest::testGetStreamList()
{
// The real server needs a context before getStreamList actually returns some streams.
testSwitchContext();
m_spy->reset();
m_avaComm->getStreamList(AvaStreamType::Video, AvaStreamSource::Server);
QTRY_VERIFY(m_spy->m_endOfList);
QVERIFY(m_spy->m_streamListBegin);
QCOMPARE(m_spy->m_listValues.size(), 5);
// sample check
const QVector<QByteArray> expected = {"2", "2", u8"Videöäü 50-5(S)"};
QCOMPARE(m_spy->m_listValues.at(1), expected);
}
void AvaCommTest::testChooseStream()
{
// The real server needs a context and a user profile before chooseStream can be issued.
login();
m_spy->reset();
const bool notMuted = false;
m_avaComm->chooseStream(AvsDialog::VIDEO_1, QStringLiteral("Video 52-1(S)"), notMuted);
QTRY_VERIFY(m_spy->m_ok);
QTRY_VERIFY(m_spy->m_streamState && *m_spy->m_streamState == AvaStreamState::Stop);
QTRY_VERIFY(m_spy->m_streamInfo);
const AvaStreamInfo& actual = *m_spy->m_streamInfo;
QCOMPARE(actual.start, AvaTimePoint(AvaDuration{125551541486}));
QCOMPARE(actual.end, AvaTimePoint(AvaDuration{125563407789}));
QCOMPARE(actual.duration, AvaDuration{11866303});
}
void AvaCommTest::testQueryTimeline()
{
// The real server needs a context and a user profile before queryTimeline can be issued.
login();
m_spy->reset();
m_avaComm->queryTimeline(QStringLiteral("Video 52-1(S)"));
QTRY_VERIFY(m_spy->m_ok);
QTRY_VERIFY(m_spy->m_endOfList);
QVERIFY(m_spy->m_timelineListBegin);
QCOMPARE(m_spy->m_listValues.size(), 2);
QVector<QVector<QByteArray>> expected = {{"1499415421.77-1499416659.03"}, {"1499417373.77-1499417842.06"}};
QCOMPARE(m_spy->m_listValues, expected);
}
void AvaCommTest::testGetElokaList()
{
// The real server needs a context before getStreamList actually returns some streams.
testSwitchContext();
m_spy->reset();
m_avaComm->getElokaList(QStringLiteral("TrSpFuKr12"));
QTRY_VERIFY(m_spy->m_ok);
QTRY_VERIFY(m_spy->m_endOfList);
QVERIFY(m_spy->m_elokaListBegin);
QCOMPARE(m_spy->m_listValues.size(), 2);
QVector<QVector<QByteArray>> expected = {{"ELOKA 1"}, {"Eloka 2"}};
QCOMPARE(m_spy->m_listValues, expected);
}
/**
* Login with context and user profile.
*/
void AvaCommTest::login()
{
const int EXERCISE = 42;
QTRY_VERIFY(m_spy->m_connected);
m_avaComm->switchContext(EXERCISE);
QTRY_VERIFY(m_spy->m_ok);
m_spy->reset();
m_avaComm->setUserProfile(QStringLiteral("ctc"), QStringLiteral("en_US"), AvaAudioPrio(1), AvaGuiStyle::Light);
QTRY_VERIFY(m_spy->m_ok);
}
#include "ava_comm_test.moc"
@@ -0,0 +1,56 @@
/**
* @RDE_HEADER@
* AvaComm
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, SLSE, 2017-06-20
*/
#pragma once
#include <QTest>
#include <memory>
namespace AudioVideoManagement
{
class AvaComm;
class AvaTestServer;
}
/**
* @author Christian Fröbel
*/
class AvaCommTest : public QObject
{
Q_OBJECT
public:
AvaCommTest();
~AvaCommTest();
private slots:
void initTestCase();
void cleanupTestCase();
void init();
void cleanup();
void testSwitchContext();
void testSetUserProfile();
void testSetVideoDialogProfile();
void testGetStreamList();
void testChooseStream();
void testQueryTimeline();
void testGetElokaList();
private:
void login();
class SignalSpy;
std::unique_ptr<AudioVideoManagement::AvaTestServer> m_fakeAvaServer;
std::unique_ptr<AudioVideoManagement::AvaComm> m_avaComm;
std::unique_ptr<SignalSpy> m_spy;
};
@@ -0,0 +1,264 @@
/**
* @RDE_HEADER@
* AvaControl
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, SLSE, 2017-06-29
*/
#include "ava_control_test.h"
#include "ava_test_server.h"
#include <AudioVideoManagement/ava_types.h>
#include <AudioVideoManagement/ava_comm.h>
#include <AudioVideoManagement/ava_control.h>
#include <AudioVideoManagement/stream_name_mapping.h>
#include <AudioVideoManagement/avs_dialog.h>
#include <AudioVideoManagement/list_response_collector.h>
#include <QVector>
#include <QtGlobal>
#include <QDateTime>
#include <QSignalSpy>
#include <boost/optional.hpp>
using namespace AudioVideoManagement;
AvaControlTest::AvaControlTest() = default;
AvaControlTest::~AvaControlTest() = default;
class AvaControlTest::SignalSpy : public QObject
{
Q_OBJECT
public:
SignalSpy(AvaControl *signalSource)
{
Q_ASSERT(signalSource);
connect(signalSource->comm(), &IAvaComm::connected, this, &SignalSpy::onConnected);
connect(signalSource, &AvaControl::chooseStreamResponse, this, &SignalSpy::onChooseStreamResponse);
connect(signalSource, &AvaControl::getStreamListResponse, this, &SignalSpy::onGetStreamListResponse);
connect(signalSource, &AvaControl::queryTimelineResponse, this, &SignalSpy::onQueryTimelineResponse);
connect(signalSource, &AvaControl::getElokaListResponse, this, &SignalSpy::onGetElokaListResponse);
connect(signalSource, &AvaControl::queryElokaResponse, this, &SignalSpy::onQueryElokaResponse);
connect(signalSource, &AvaControl::setScriptResponse, this, &SignalSpy::onSetScriptResponse);
}
void reset()
{
m_connected = false;
m_streamInfo.clear();
m_streamState.clear();
m_streamList.reset();
m_timeline.reset();
m_elokaList.reset();
m_queryElokaResponse.reset();
m_scriptInfo.clear();
m_scriptState.clear();
}
public slots:
void onConnected() { m_connected = true; }
void onChooseStreamResponse(const AvaStreamInfo &streamInfo, AvaStreamState streamState)
{
m_streamInfo.append(streamInfo);
m_streamState.append(streamState);
}
void onGetStreamListResponse(const QVector<AvaStream> &streams) { m_streamList = streams; }
void onQueryTimelineResponse(const AvaTimeline &timeline) { m_timeline = timeline; }
void onGetElokaListResponse(const QVector<QString> &elokaSignals) { m_elokaList = elokaSignals; }
void onQueryElokaResponse(const QVector<AvaEloka> &eloka) { m_queryElokaResponse = eloka; }
void onSetScriptResponse(const AvaStreamInfo &scriptInfo, AvaStreamState scriptState)
{
m_scriptInfo.append(scriptInfo);
m_scriptState.append(scriptState);
}
public:
bool m_connected{false};
QVector<AvaStreamInfo> m_streamInfo;
QVector<AvaStreamState> m_streamState;
boost::optional<QVector<AvaStream>> m_streamList;
boost::optional<AvaTimeline> m_timeline;
boost::optional<QVector<QString>> m_elokaList;
boost::optional<QVector<AvaEloka>> m_queryElokaResponse;
QVector<AvaStreamInfo> m_scriptInfo;
QVector<AvaStreamState> m_scriptState;
};
namespace
{
const QString &DIALOG()
{
return AvsDialog::VIDEO_1;
}
} // anonymous namespace
void AvaControlTest::initTestCase()
{
m_fakeAvaServer.reset(new AvaTestServer);
}
void AvaControlTest::cleanupTestCase()
{
m_fakeAvaServer.reset();
}
void AvaControlTest::init()
{
m_streamNameMapping.reset(new StreamNameMapping);
m_avaComm.reset(new AvaComm(QStringLiteral("localhost"), 6370));
m_avaCtrl.reset(new AvaControl(m_avaComm.get(), m_streamNameMapping.get(), std::chrono::seconds(3)));
m_spy.reset(new SignalSpy(m_avaCtrl.get()));
m_avaComm->start();
}
void AvaControlTest::cleanup()
{
m_spy.reset();
m_avaCtrl.reset();
m_avaComm.reset();
m_streamNameMapping.reset();
}
void AvaControlTest::testChooseStream()
{
QTRY_VERIFY(m_spy->m_connected);
m_avaCtrl->chooseStream(DIALOG(), QStringLiteral("Video 52-1(S)"));
QTRY_VERIFY(m_spy->m_streamInfo.size() == 1 && m_spy->m_streamState.size() == 1);
QCOMPARE(m_spy->m_streamState.front(), AvaStreamState::Stop);
QCOMPARE(m_spy->m_streamInfo.front().start, AvaTimePoint(AvaDuration{125551541486}));
QCOMPARE(m_spy->m_streamInfo.front().end, AvaTimePoint(AvaDuration{125563407789}));
QCOMPARE(m_spy->m_streamInfo.front().duration, AvaDuration{11866303});
}
/**
* A use case where the user quickly changes the selected stream
* before the response of the first chooseStream was received.
*/
void AvaControlTest::testChooseStreamCascade()
{
QTRY_VERIFY(m_spy->m_connected);
m_avaCtrl->chooseStream(DIALOG(), QStringLiteral("Video 52-1(S)"));
m_avaCtrl->chooseStream(DIALOG(), QStringLiteral("Video 3"));
QTRY_VERIFY(m_spy->m_streamInfo.size() == 2 && m_spy->m_streamState.size() == 2);
}
/**
* Test issuing several commands in quick succession.
* Such a sequence could be sent during application startup.
* AvaControl is responsible for serializing the commands:
* the next command must not be sent before the prior command finished.
*/
void AvaControlTest::testInitialSequence()
{
QTRY_VERIFY(m_spy->m_connected);
const int EXERCISE = 42;
m_avaCtrl->switchContext(EXERCISE);
m_avaCtrl->setUserProfile(QStringLiteral("ctc"), QStringLiteral("en_US"), AvaAudioPrio(1), AvaGuiStyle::Light);
m_avaCtrl->getStreamList(AvaStreamType::Video, AvaStreamSource::Server);
m_avaCtrl->chooseStream(DIALOG(), QStringLiteral("Video 52-1(S)"));
QTRY_VERIFY(m_spy->m_streamInfo.size() == 1 && m_spy->m_streamState.size() == 1);
}
void AvaControlTest::testGetStreamList()
{
QTRY_VERIFY(m_spy->m_connected);
m_avaCtrl->getStreamList(AvaStreamType::Video, AvaStreamSource::Server);
QTRY_VERIFY(m_spy->m_streamList);
// sample check
const AvaStream &sample = m_spy->m_streamList->at(1);
AvaStream expected{AvaStreamType::Video, AvaStreamSource::Server, QStringLiteral("Videöäü 50-5"), QStringLiteral("Videöäü 50-5")};
QCOMPARE(sample.type, expected.type);
QCOMPARE(sample.source, expected.source);
QCOMPARE(sample.internalName, expected.internalName);
}
void AvaControlTest::testQueryTimeline()
{
QTRY_VERIFY(m_spy->m_connected);
m_avaCtrl->queryTimeline(QStringLiteral("Video 52-1(S)"));
QTRY_VERIFY(m_spy->m_timeline);
AvaTimeline expected
({
{ AvaTimePoint(AvaDuration{149941542177}), AvaTimePoint(AvaDuration{149941665903}) },
{ AvaTimePoint(AvaDuration{149941737377}), AvaTimePoint(AvaDuration{149941784206}) }
});
QCOMPARE(*m_spy->m_timeline, expected);
}
void AvaControlTest::testGetElokaList()
{
QTRY_VERIFY(m_spy->m_connected);
m_avaCtrl->getElokaList(QStringLiteral("TrSpFuKr12"));
QTRY_VERIFY(m_spy->m_elokaList);
QVector<QString> expected = {QStringLiteral("ELOKA 1"), QStringLiteral("Eloka 2")};
QCOMPARE(*m_spy->m_elokaList, expected);
}
void AvaControlTest::testQueryEloka()
{
QTRY_VERIFY(m_spy->m_connected);
m_avaCtrl->queryEloka(AvaQueryElokaMode::Poll);
QTRY_VERIFY(m_spy->m_queryElokaResponse);
QVector<AvaEloka> expected = { {QStringLiteral("Eloka1"), QStringLiteral("Funk1"), 1, AvaTimePoint(AvaDuration{125551541400})} };
QCOMPARE(*m_spy->m_queryElokaResponse, expected);
}
void AvaControlTest::testQueryElokaStop()
{
QTRY_VERIFY(m_spy->m_connected);
QSignalSpy commandQueueEmptySpy(m_avaCtrl.get(), &AvaControl::commandQueueEmpty);
m_avaCtrl->queryEloka(AvaQueryElokaMode::Stop);
commandQueueEmptySpy.wait();
QVERIFY(!m_spy->m_queryElokaResponse);
}
/**
* When queryEloka is used in Push-Mode, we must handle asynchronous QueryElokaLists.
* This is what we simulate here.
*/
void AvaControlTest::testAsyncQueryElokaList()
{
QTRY_VERIFY(m_spy->m_connected);
boost::optional<QVector<AvaEloka>> queryElokaResponse;
auto doneCallback = [&queryElokaResponse] (QVector<AvaEloka> &&response) { queryElokaResponse = std::move(response); };
QueryElokaListCollector collector(m_avaComm.get(), doneCallback);
collector.start();
// We use Poll here as we want to control how many responses the AvaTestServer sends.
// Also, the Push-mode is not implemented in AvaTestServer.
m_avaCtrl->queryEloka(AvaQueryElokaMode::Poll);
QTRY_VERIFY(queryElokaResponse);
QVector<AvaEloka> expected = { {QStringLiteral("Eloka1"), QStringLiteral("Funk1"), 1, AvaTimePoint(AvaDuration{125551541400})} };
QCOMPARE(*queryElokaResponse, expected);
queryElokaResponse.reset();
collector.start();
m_avaCtrl->queryEloka(AvaQueryElokaMode::Poll);
QTRY_VERIFY(queryElokaResponse);
QCOMPARE(*queryElokaResponse, expected);
}
#include "ava_control_test.moc"
@@ -0,0 +1,60 @@
/**
* @RDE_HEADER@
* AvaControl
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, SLSE, 2017-06-29
*/
#pragma once
#include <QTest>
#include <memory>
namespace AudioVideoManagement
{
class AvaComm;
class AvaControl;
class AvaTestServer;
class StreamNameMapping;
}
/**
* @author Christian Fröbel
*/
class AvaControlTest : public QObject
{
Q_OBJECT
public:
AvaControlTest();
~AvaControlTest();
private slots:
void initTestCase();
void cleanupTestCase();
void init();
void cleanup();
void testChooseStream();
void testChooseStreamCascade();
void testInitialSequence();
void testGetStreamList();
void testQueryTimeline();
void testGetElokaList();
void testQueryEloka();
void testQueryElokaStop();
void testAsyncQueryElokaList();
private:
class SignalSpy;
std::unique_ptr<AudioVideoManagement::AvaTestServer> m_fakeAvaServer;
std::unique_ptr<AudioVideoManagement::StreamNameMapping> m_streamNameMapping;
std::unique_ptr<AudioVideoManagement::AvaComm> m_avaComm;
std::unique_ptr<AudioVideoManagement::AvaControl> m_avaCtrl;
std::unique_ptr<SignalSpy> m_spy;
};
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,609 @@
/**
* @RDE_HEADER@
* AvaTestServer
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, SLSE, 2017-06-19
*/
#pragma once
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <QtCore/QByteArray>
#include <QtCore/QObject>
#include <QtCore/QTimer>
#include <QtCore/QDateTime>
namespace AudioVideoManagement
{
/**
* Taken from ZW/ZWPR/ScrEdAV/ScrEdAVStream.{hpp,cpp}
*/
class ScrEdAVStream
{
public:
/**
* Get the stream category
* @return return the stream category type.
*/
int getCategory() const;
/**
* Get the stream duration.
* @return QDateTime object with the stream duration
*/
double getDuration() const;
/**
* get the stream end time.
* @return QDateTime object with the end time.
*/
QDateTime getEndTime() const;
/**
* get the stream name.
* @return QString with the name of the stream.
*/
QString getName() const;
/**
* get the stream's play time.
* @return QDateTime object with the actual stream play time.
*/
QDateTime getPlayTime() const;
/**
* get the signal pause handling flag.
* @return int with the signla pause handling state.
*/
int getSignalPauseHandling() const;
/**
* get the stream speed.
* @return int with the stream play speed.
*/
int getSpeed() const;
/**
* get the stream's start time.
* @return QDateTime object with the stream start time.
*/
QDateTime getStartTime() const;
/**
* get the stream play state.
* @return in with the stream play state.
*/
int getState() const;
/**
* get the stream ply mode.
* @return int with the stream play mode
*/
int getStreamPlayMode() const;
/**
* get the stream type.
* @return int with the stream type.
*/
int getType() const;
/**
* denotes the stream mute state.
* @return true when the stream is muted, false otherwise.
*/
bool isMute() const;
/**
* set the stream category, possible values are:
* @li 1 ->interface
* @li 2 ->server
* @li 4->local
*
* @param newVal
*/
void setCategory(int newVal);
/**
* set the stream's duration.
*
* @param newVal
*/
void setDuration(double newVal);
/**
* set the stream's end time.
*
* @param newVal
*/
void setEndTime(const QDateTime &newVal);
/**
* set the Mute state for the stream.
*
* @param newVal
*/
void setMute(bool newVal);
/**
* set the stream name
*
* @param newVal
*/
void setName(const QString &newVal);
/**
* set the stream's end time.
*
* @param newVal
*/
void setPlayTime(const QDateTime &newVal);
/**
* set the signal pause handling flag.
*
* @param newVal
*/
void setSignalPauseHandling(int newVal);
/**
* set the stream speed.
*
* @param newVal
*/
void setSpeed(int newVal);
/**
* set the stream's start time.
*
* @param newVal
*/
void setStartTime(const QDateTime &newVal);
/**
* set the Stream state.
*
* @param newVal
*/
void setState(int newVal);
/**
* set the stream play mode.
*
* @param newVal
*/
void setStreamPlayMode(int newVal);
/**
* set the stream type, 1 -> Audio, 2 -> Video
*
* @param newVal
*/
void setType(int newVal);
/**
* Set the volume factor
*/
void setVolumeFactor(double factor) { m_VolumeFactor = factor; }
/**
* Return the volume factor
*/
double getVolumeFactor() const { return m_VolumeFactor; }
double getVolume() const;
void setVolume(double volume);
private:
/**
* Contains the stream category, possible values are:
* @li 1 - interface
* @li 2 - server
* @li 4- local
*/
int m_Category{1};
/**
* Contains the stream duration.
*/
double m_Duration{0};
/**
* Contains the stream end time.
*/
QDateTime m_EndTime;
/**
* Contains the Mute state for the stream.
*/
bool m_Mute{false};
/**
* Contains the stream name
*/
QString m_Name;
/**
* Contains the stream's actual play position time.
*/
QDateTime m_PlayTime;
/**
* Contains the signal pause handling flag.
*/
int m_SignalPauseHandling{1};
/**
* Contains the stream speed.
*/
int m_Speed{100};
/**
* Contains the stream's start time.
*/
QDateTime m_StartTime;
/**
* Contains the Stream state.
*/
int m_State{0};
/**
* Contains the stream play mode.
*/
int m_StreamPlayMode{0};
/**
* Contains the stream type
* @li 1 - audio
* @li 2 - video
*/
int m_Type{0};
/**
* The volume factor for this script
*/
double m_VolumeFactor{50};
};
//======================================================================================================================
/**
* Taken from ZW/ZWPR/ScrEdAV/ScrEdAVScript.{hpp,cpp}
*/
class ScrEdAVScript
{
public:
/**
* Contains the script type. The type is one of the follwoing list:
* @li 1 = AUDIO
* @li 2 = VIDEO
* @li 3 = LOCAL AUDIO
* @li 4 = LOCAL VIDEO
* @return returns an int with the category type.
*/
int getCategory() const;
/**
* Gets the filesplitting parameter
* @return returns an int with the filesplitting parameter.
*/
int getFileSplitting() const;
/**
* get the name of the current played stream.
* @return returns a QString with the name of the current played stream.
*/
QString getCurrentPlayedStream() const;
/**
* get the name of export script name.
* @return returns a QString with the name of the export script name .
*/
QString getExportScriptName() const;
/**
* get the name of the export directory.
* @return returns a QString of the export directory.
*/
QString getExportDirectory() const;
/**
* Gets the scripts duration.
* @return returns a QDateTime object with the script duration.
*/
double getDuration() const;
/**
* gets the elapsed time in relation to the script start.
* @return returns a QDateTime with the elapsed time in relation to the script
* start.
*/
double getElapsedTime() const;
/**
* get the scripts end time.
* @return returns a QDateTime object with the scripts end time.
*/
QDateTime getEndTime() const;
/**
* gets the script's name.
* @return returns a QString with the scripts name.
*/
QString getName() const;
/**
* gets the scripts start time.
* @return returns a QDateTime object with the scripts start time.
*/
QDateTime getStartTime() const;
/**
* gets the scripts play state. Possible states are:
* @li 0 = STOP
* @li 1 = START
* @li 2 = PAUSE
* @li 3 = Datenpfad abgebaut
* @li 4 = Datenpfad darf nicht aufgebaut werden
* @return returns an int with the scripts play state.
*/
int getState() const;
/**
* Get the stream sequences.
* @return returns a QVector which contains ScrEdAVStream objects.
*/
QVector<ScrEdAVStream> getStreamSequences() const;
/**
* Denotes if the script is muted or not.
* @return true when the script is muted, false otherwise.
*/
bool isMute() const;
/**
* Contains the script type.
*
* @param newVal The new value for the script category. possible values are:
* @li 1 = AUDIO
* @li 2 = VIDEO
* @li 3 = LOCAL AUDIO
* @li 4 = LOCAL VIDEO
*/
void setCategory(int newVal);
/**
* Set the name of the current played stream.
*
* @param newVal The name of the current played stream.
*/
void setCurrentPlayedStream(const QString &newVal);
/**
* Set the scripts duration.
*
* @param newVal The new script duration value.
*/
void setDuration(double newVal);
/**
* Set the elapsed time in relation to the script start.
*
* @param newVal The new vakue for the elapsed time.
*/
void setElapsedTime(double newVal);
/**
* Set the scripts end time.
*
* @param newVal The new value for the end time.
*/
void setEndTime(const QDateTime &newVal);
/**
* set the script muted or not.
*
* @param newVal the new value for mute.
*/
void setMute(bool newVal);
/**
* Set the script's name.
*
* @param newVal The scripts new name.
*/
void setName(const QString &newVal);
/**
* Set the scripts start time.
*
* @param newVal The scripts new start time.
*/
void setStartTime(const QDateTime &newVal);
/**
* set the scripts play state.
*
* @param newVal set the new play state: Possible states are:
* @li 0 = STOP
* @li 1 = START
* @li 2 = PAUSE
* @li 3 = Datenpfad abgebaut
* @li 4 = Datenpfad darf nicht aufgebaut werden
*/
void setState(int newVal);
/**
* set the scripts play state.
*
* @param newVal set the new filesplitting value: Possible states are:
* @li 0 = Dateisplitting nur bei einem Wechsel des Stream-Typs
* @li 1 = Dateisplitting nach jeder Skriptzeile
*/
void setFileSplitting(int newVal);
/**
* set the stream sequences.
*
* @param newVal set a QVector with the new stream sequences.
*/
void setStreamSequences(const QVector<ScrEdAVStream> &newVal);
/**
* set the name of the export script.
*
* @param newVal name of the export script.
*/
void setExportScriptName(const QString &newVal);
/**
* set the directory for the export script
*
* @param newVal directory of the export script
*/
void setExportDirectory(const QString &newVal);
private:
/**
* Contains the script type. Possible values are:
* @li 1 = AUDIO
* @li 2 = VIDEO
* @li 3 = LOCAL AUDIO
* @li 4 = LOCAL VIDEO
*/
int m_Category{0};
/**
* Contains the name of the current played stream.
*/
QString m_CurrentPlayedStream;
/**
* Contains the scripts duration.
*/
double m_Duration{0};
/**
* Contains the elapsed time in relation to the script start.
*/
double m_ElapsedTime{0};
/**
* Contains the scripts end time.
*/
QDateTime m_EndTime;
/**
* Denotes if the script is muted or not.
*/
bool m_Mute{false};
/**
* Contains the script's name.
*/
QString m_Name;
/**
* Contains the scripts start time.
*/
QDateTime m_StartTime;
/**
* Contains the play state. Possible states are:
* @li 0 = STOP
* @li 1 = START
* @li 2 = PAUSE
* @li 3 = Datenpfad abgebaut
* @li 4 = Datenpfad darf nicht aufgebaut werden
*/
int m_State{0};
/**
* Contains the file splitting. Possible states are:
* @li 0 = Dateisplitting nur bei einem Wechsel des Stream-Typs
* @li 1 = Dateisplitting nach jeder Skriptzeile
*/
int m_FileSplitting{0};
/**
* Contains the stream sequences.
*/
QVector<ScrEdAVStream> m_StreamSequences;
/**
* Contains the export script name.
*/
QString m_ExportScriptName;
/**
* Contains the export directory name.
*/
QString m_ExportDirectory;
};
//======================================================================================================================
/**
* This class can be used in tests to replace AVS_Workstation.
* It mocks the server side of the RCP/AVA protocol.
*
* Taken and adapted from ZW/ZWPR/ScrEdAV/TestApp/ScrEdAVTestServer.{hpp,cpp}
*/
class AvaTestServer : public QObject
{
Q_OBJECT
public:
AvaTestServer();
~AvaTestServer() override;
private:
QTcpServer *m_Server;
QVector<QTcpSocket *> m_Conns;
QString m_SignalPause;
QDateTime m_Position;
float m_AbsScriptPos;
float m_RelScriptPos;
QString m_SelectedStream;
QString m_SelectedScript;
QString m_Outputdevice;
QTimer *m_PosUpdate;
QTimer *m_ScriptPosUpdate;
int m_PosUpdateTime;
int m_StreamState;
int m_ScriptState;
int m_StreamSpeed;
int m_PendingListReading;
bool m_ReversPlay;
bool m_Audiomute;
bool m_PlayMode;
QVector<ScrEdAVStream> m_Scripts;
ScrEdAVScript m_Script;
ScrEdAVStream m_SelStreamFromScript;
int m_StreamNbr;
QString qDateTimeToAVTime(const QDateTime &a_qDateTime);
QString floatToAVTime(float a_time);
float avTimeToFloat(const QByteArray &a_avTime);
QDateTime avTimeToQDateTime(const QString &a_avTime);
/** Called for every line received on a connection. */
void processLine(const QByteArray &line);
QList<QByteArray> parse(const QByteArray &a_byteArray, int a_i);
void sendData(const QString &data_);
void replyOk();
void replyBadArguments();
void replyBadCommand();
void replyCommandNotAllowed();
private slots:
void connectionIncoming();
void readAndAnswer();
void sendPosUpdate();
void sendScriptPosUpdate();
void closedConnection();
};
} // namespace AudioVideoManagement
@@ -0,0 +1,18 @@
/**
* @RDE_HEADER@
* Standalone Server for mocking AVA_Workstation (RCP/AVA).
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, SLSE, 2017-06-19
*/
#include "ava_test_server.h"
#include <QCoreApplication>
int main(int argc, char** argv)
{
QCoreApplication app(argc, argv);
AudioVideoManagement::AvaTestServer server;
return app.exec();
}
@@ -0,0 +1,92 @@
/**
* @RDE_HEADER@
* AvaTimelineTest
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, NLSE, 2018-03-28
*/
#include "ava_timeline_test.h"
#include <AudioVideoManagement/ava_types.h>
#include <initializer_list>
#include <utility>
#include <algorithm>
using namespace AudioVideoManagement;
namespace
{
/**
* Small helper so that we can easily craeate a AvaTimeline from a list of int-pairs.
*/
AvaTimeline makeTimeline(std::initializer_list<std::pair<int, int>> v)
{
AvaTimeline timeline;
std::for_each(v.begin(), v.end(), [&timeline] (const std::pair<int, int> &p) { timeline.append({AvaTimePoint(AvaDuration{p.first}), AvaTimePoint(AvaDuration{p.second})}); });
return timeline;
}
} // anonymous namespace
void AvaTimelineTest::testMergeEmpty()
{
const AvaTimeline timeline1 = makeTimeline({ {1, 2}, {3, 4} });
AvaTimeline timeline2 = timeline1;
AvaTimeline empty;
timeline2.merge(empty);
QCOMPARE(timeline2, timeline1);
empty.merge(timeline1);
QCOMPARE(empty, timeline1);
}
void AvaTimelineTest::testMergeSelf()
{
const AvaTimeline timeline1 = makeTimeline({ {1, 2}, {3, 4} });
AvaTimeline timeline2 = timeline1;
timeline2.merge(timeline2);
QCOMPARE(timeline2, timeline1);
}
void AvaTimelineTest::testMergeDisjunct()
{
AvaTimeline timeline1 = makeTimeline({ {1, 2} });
AvaTimeline timeline2 = makeTimeline({ {3, 4} });
timeline1.merge(timeline2);
AvaTimeline expected = makeTimeline({ {1, 2}, {3, 4}});
QCOMPARE(timeline1, expected);
}
void AvaTimelineTest::testMergeWithin()
{
AvaTimeline timeline1 = makeTimeline({ {1, 4} });
AvaTimeline timeline2 = makeTimeline({ {2, 3} });
timeline1.merge(timeline2);
AvaTimeline expected = makeTimeline({ {1, 4} });
QCOMPARE(timeline1, expected);
}
void AvaTimelineTest::testMergeOverlap()
{
AvaTimeline timeline1 = makeTimeline({ {1, 3} });
AvaTimeline timeline2 = makeTimeline({ {2, 4} });
timeline1.merge(timeline2);
AvaTimeline expected = makeTimeline({ {1, 4} });
QCOMPARE(timeline1, expected);
}
void AvaTimelineTest::testMergeEdgeOverlap()
{
AvaTimeline timeline1 = makeTimeline({ {1, 2} });
AvaTimeline timeline2 = makeTimeline({ {2, 3} });
timeline1.merge(timeline2);
AvaTimeline expected = makeTimeline({ {1, 3} });
QCOMPARE(timeline1, expected);
}
@@ -0,0 +1,28 @@
/**
* @RDE_HEADER@
* AvaTimelineTest
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, NLSE, 2018-03-28
*/
#pragma once
#include <QTest>
/**
* @author Christian Fröbel
*/
class AvaTimelineTest : public QObject
{
Q_OBJECT
private slots:
void testMergeEmpty();
void testMergeSelf();
void testMergeDisjunct();
void testMergeWithin();
void testMergeOverlap();
void testMergeEdgeOverlap();
};
@@ -0,0 +1,177 @@
/**
* @RDE_HEADER@
* AvsCommandQueueTest
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, NLSE, 2018-02-28
*/
#include "avs_command_queue_test.h"
#include <AudioVideoManagement/avs_command_queue.h>
#include <AudioVideoManagement/icommand.h>
#include <LtsUtilsCore/connection.h>
#include <memory>
#include <chrono>
using namespace AudioVideoManagement;
namespace
{
class ExecutionSpy
{
public:
void executedA(int value)
{
++m_execCountA;
m_lastValueA = value;
}
void executedB(int value)
{
++m_execCountB;
m_lastValueB = value;
}
int m_execCountA{0};
int m_lastValueA{0};
int m_execCountB{0};
int m_lastValueB{0};
};
class CommandA : public ICommand
{
public:
CommandA(ExecutionSpy *spy, int value)
: m_spy(spy)
, m_value(value)
{
Q_ASSERT(spy);
}
void send() override
{
m_spy->executedA(m_value);
}
QString debug() const override { return QLatin1String("CommandA"); }
bool nullifies(ICommand *other) const override
{
Q_ASSERT(other);
CommandA *other_ = dynamic_cast<CommandA*>(other);
return other_ != nullptr;
}
private:
ExecutionSpy *m_spy;
int m_value;
};
class CommandB : public ICommand
{
public:
CommandB(ExecutionSpy *spy, int value)
: m_spy(spy)
, m_value(value)
{
Q_ASSERT(spy);
}
void send() override
{
m_spy->executedB(m_value);
}
QString debug() const override { return QLatin1String("CommandB"); }
bool nullifies(ICommand *other) const override
{
Q_ASSERT(other);
CommandB *other_ = dynamic_cast<CommandB*>(other);
return other_ != nullptr;
}
private:
ExecutionSpy *m_spy;
int m_value;
};
} // anonymous namespace
void AvsCommandQueueTest::testReduceNeverDropCmdInExecution()
{
ExecutionSpy spy;
std::unique_ptr<CommandA> cmd1(new CommandA(&spy, 10));
std::unique_ptr<CommandA> cmd2(new CommandA(&spy, 20));
AvsCommandQueue queue(std::chrono::seconds(3));
queue.queue(std::move(cmd1), nullptr);
queue.queue(std::move(cmd2), nullptr); // Does not override cmd1 as cmd1 is being executed already and thus cannot be dropped anymore.
QCOMPARE(spy.m_execCountA, 1);
QCOMPARE(spy.m_lastValueA, 10);
queue.done();
QCOMPARE(spy.m_execCountA, 2);
QCOMPARE(spy.m_lastValueA, 20);
queue.done();
QVERIFY(queue.isEmpty());
}
void AvsCommandQueueTest::testReduce1()
{
ExecutionSpy spy;
std::unique_ptr<CommandA> cmd1(new CommandA(&spy, 10));
std::unique_ptr<CommandA> cmd2(new CommandA(&spy, 20));
std::unique_ptr<CommandA> cmd3(new CommandA(&spy, 30));
AvsCommandQueue queue(std::chrono::seconds(3));
queue.queue(std::move(cmd1), nullptr);
queue.queue(std::move(cmd2), nullptr);
queue.queue(std::move(cmd3), nullptr); // causes cmd2 to be dropped
// Only two commands complete as one was dropped.
queue.done();
queue.done();
QVERIFY(queue.isEmpty());
QCOMPARE(spy.m_execCountA, 2);
QCOMPARE(spy.m_lastValueA, 30);
}
void AvsCommandQueueTest::testReduce2()
{
ExecutionSpy spy;
std::unique_ptr<CommandA> cmdA1(new CommandA(&spy, 10));
std::unique_ptr<CommandA> cmdA2(new CommandA(&spy, 20));
std::unique_ptr<CommandA> cmdA3(new CommandA(&spy, 30));
std::unique_ptr<CommandB> cmdB1(new CommandB(&spy, 10));
std::unique_ptr<CommandB> cmdB2(new CommandB(&spy, 20));
std::unique_ptr<CommandB> cmdB3(new CommandB(&spy, 30));
AvsCommandQueue queue(std::chrono::seconds(3));
queue.queue(std::move(cmdA1), nullptr);
queue.queue(std::move(cmdB1), nullptr);
queue.queue(std::move(cmdA2), nullptr); // cmdA1 not dropped as it is being executed already
queue.queue(std::move(cmdB2), nullptr); // causes cmdB1 to be dropped
queue.queue(std::move(cmdA3), nullptr); // causes cmdA2 to be dropped
queue.queue(std::move(cmdB3), nullptr); // causes cmdB2 to be dropped
// Only three commands complete as one was dropped.
queue.done();
queue.done();
queue.done();
QVERIFY(queue.isEmpty());
QCOMPARE(spy.m_execCountA, 2);
QCOMPARE(spy.m_lastValueA, 30);
QCOMPARE(spy.m_execCountB, 1);
QCOMPARE(spy.m_lastValueB, 30);
}
@@ -0,0 +1,30 @@
/**
* @RDE_HEADER@
* AvsCommandQueueTest
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, NLSE, 2018-02-28
*/
#pragma once
#include <QTest>
/**
* @author Christian Fröbel
*/
class AvsCommandQueueTest : public QObject
{
Q_OBJECT
private slots:
void initTestCase() {}
void cleanupTestCase() {}
void init() {}
void cleanup() {}
void testReduceNeverDropCmdInExecution();
void testReduce1();
void testReduce2();
};
@@ -0,0 +1,179 @@
/**
* @RDE_HEADER@
* ChooseStreamTest
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, NLSE, 2018-01-26
*/
#include "choose_stream_test.h"
#include "ava_comm_double.h"
#include <AudioVideoManagement/AvsCommands/choose_stream.h>
#include <AudioVideoManagement/iava_comm.h>
#include <AudioVideoManagement/iava_command_done_observer.h>
using namespace AudioVideoManagement;
namespace
{
class DoneObserver : public IAvaCommandDoneObserver
{
public:
void done(ICommand *cmd) override { Q_UNUSED(cmd); m_done = true; }
void done(AvaCommands::ChooseStream *cmd) override { Q_UNUSED(cmd); m_done = true; }
void done(AvaCommands::GetStreamList *cmd) override { Q_UNUSED(cmd); m_done = true; }
void done(AvaCommands::QueryTimeline *cmd) override { Q_UNUSED(cmd); m_done = true; }
void done(AvaCommands::GetElokaList *cmd) override { Q_UNUSED(cmd); m_done = true; }
void done(AvaCommands::QueryEloka *cmd) override { Q_UNUSED(cmd); m_done = true; }
void done(AvaCommands::SetRtProfileBase *cmd) override { Q_UNUSED(cmd); m_done = true; }
void done(AvaCommands::SetAnnouncementProfile *cmd) override { Q_UNUSED(cmd); m_done = true; }
void done(AvaCommands::SetVideoProfile *cmd) override { Q_UNUSED(cmd); m_done = true; }
void done(AvaCommands::SetScript *cmd) override { Q_UNUSED(cmd); m_done = true; }
void done(AvaCommands::StartScript *cmd) override { Q_UNUSED(cmd); m_done = true; }
void failed(ICommand *cmd) override { Q_UNUSED(cmd); }
void alive(ICommand *cmd) override { Q_UNUSED(cmd); }
bool m_done{false};
};
} // anonymous namespace
/**
* Scenario when chooseStream is called when no stream is currently chosen.
* Sample comm protocol:
* chooseStream CAMERA_IN_AP_3 1 Video1
* 200 OK
* 220 "CAMERA_IN_AP_3" "Video1" 1516978925.00 1516978925.00 0.00 STREAMINFO
* 221 "CAMERA_IN_AP_3" "Video1" 1516978924.97 STREAMPOS
* 222 "CAMERA_IN_AP_3" "Video1" 100 STREAMSPEED
* 223 "CAMERA_IN_AP_3" "Video1" 0 STREAMSTATE
* 224 "CAMERA_IN_AP_3" "Video1" 1 0 STREAMMODE
* 225 "CAMERA_IN_AP_3" "Video1" 1 STREAMAUDIOMUTE
* 228 "CAMERA_IN_AP_3" "Video1" 1 STREAMVOLUMECHANGED
* 225 "CAMERA_IN_AP_3" "Video1" 1 STREAMAUDIOMUTE
* 223 "CAMERA_IN_AP_3" "Video1" 1 STREAMSTATE
*/
void ChooseStreamTest::testChooseFirstStream()
{
const QString dialog = QStringLiteral("Video1");
const QString stream = QStringLiteral("cam1");
AvaCommDouble avaCommDouble;
DoneObserver observer;
AvaCommands::ChooseStream chooseStream(&avaCommDouble, &observer, dialog, stream);
chooseStream.send();
emit avaCommDouble.ok();
emit avaCommDouble.streamInfoReceived(dialog, stream, AvaStreamInfo{AvaTimePoint{AvaDuration{151697892500}}, AvaTimePoint{AvaDuration{151697892500}}, AvaDuration{0}});
emit avaCommDouble.streamStateReceived(dialog, stream, AvaStreamState::Stop);
QVERIFY(!observer.m_done);
emit avaCommDouble.streamModeReceived(dialog, stream, true, false);
QVERIFY(observer.m_done);
}
/**
* Scenario when chooseStream is called when a stream is currenly chosen already.
* Sample comm protocol:
* chooseStream CAMERA_IN_AP_1 1 Video1
* 200 OK
* 221 "CAMERA_IN_AP_3" "Video1" 1516978962.33 STREAMPOS
* 223 "CAMERA_IN_AP_3" "Video1" 3 STREAMSTATE
* 223 "CAMERA_IN_AP_3" "Video1" 3 STREAMSTATE
* 220 "CAMERA_IN_AP_1" "Video1" 1516978962.00 1516978962.00 0.00 STREAMINFO
* 221 "CAMERA_IN_AP_1" "Video1" 1516978962.37 STREAMPOS
* 222 "CAMERA_IN_AP_1" "Video1" 100 STREAMSPEED
* 223 "CAMERA_IN_AP_1" "Video1" 0 STREAMSTATE
* 224 "CAMERA_IN_AP_1" "Video1" 1 0 STREAMMODE
* 225 "CAMERA_IN_AP_1" "Video1" 1 STREAMAUDIOMUTE
* 228 "CAMERA_IN_AP_1" "Video1" 1 STREAMVOLUMECHANGED
* 225 "CAMERA_IN_AP_1" "Video1" 1 STREAMAUDIOMUTE
* 223 "CAMERA_IN_AP_1" "Video1" 1 STREAMSTATE
*/
void ChooseStreamTest::testChooseSecondStream()
{
const QString dialog = QStringLiteral("Video1");
const QString oldStream = QStringLiteral("cam1");
const QString newStream = QStringLiteral("cam2");
AvaCommDouble avaCommDouble;
DoneObserver observer;
AvaCommands::ChooseStream chooseStream(&avaCommDouble, &observer, dialog, newStream);
chooseStream.send();
emit avaCommDouble.ok();
emit avaCommDouble.streamStateReceived(dialog, oldStream, AvaStreamState::NotPossible);
emit avaCommDouble.streamStateReceived(dialog, oldStream, AvaStreamState::NotPossible);
emit avaCommDouble.streamInfoReceived(dialog, newStream, AvaStreamInfo{AvaTimePoint{AvaDuration{151697896200}}, AvaTimePoint{AvaDuration{151697896200}}, AvaDuration{0}});
emit avaCommDouble.streamStateReceived(dialog, newStream, AvaStreamState::Stop);
QVERIFY(!observer.m_done);
emit avaCommDouble.streamModeReceived(dialog, newStream, true, false);
QVERIFY(observer.m_done);
}
/**
* Same as testChooseSecondStream but we choose the same stream.
*/
void ChooseStreamTest::testChooseSameStream()
{
const QString dialog = QStringLiteral("Video1");
const QString stream = QStringLiteral("cam1");
AvaCommDouble avaCommDouble;
DoneObserver observer;
AvaCommands::ChooseStream chooseStream(&avaCommDouble, &observer, dialog, stream);
chooseStream.send();
emit avaCommDouble.ok();
emit avaCommDouble.streamStateReceived(dialog, stream, AvaStreamState::NotPossible);
emit avaCommDouble.streamStateReceived(dialog, stream, AvaStreamState::NotPossible);
emit avaCommDouble.streamInfoReceived(dialog, stream, AvaStreamInfo{AvaTimePoint{AvaDuration{151697896200}}, AvaTimePoint{AvaDuration{151697896200}}, AvaDuration{0}});
emit avaCommDouble.streamStateReceived(dialog, stream, AvaStreamState::Stop);
QVERIFY(!observer.m_done);
emit avaCommDouble.streamModeReceived(dialog, stream, true, false);
QVERIFY(observer.m_done);
}
/**
* Almost the same as testChooseSecondStream() but this time there are two streams
* choosen already (using chooseStreamOverview).
* Sample comm protocol:
* chooseStream CAMERA_IN_AP_3 1 Video1
* 200 OK
* 221 "CAMERA_IN_AP_1" "Video1" 1516979047.87 STREAMPOS
* 221 "CAMERA_IN_AP_2" "Video1" 1516979047.87 STREAMPOS
* 223 "CAMERA_IN_AP_1" "Video1" 3 STREAMSTATE
* 223 "CAMERA_IN_AP_2" "Video1" 3 STREAMSTATE
* 223 "CAMERA_IN_AP_1" "Video1" 3 STREAMSTATE
* 223 "CAMERA_IN_AP_2" "Video1" 3 STREAMSTATE
* 220 "CAMERA_IN_AP_3" "Video1" 1516979048.00 1516979048.00 0.00 STREAMINFO
* 221 "CAMERA_IN_AP_3" "Video1" 1516979047.92 STREAMPOS
* 222 "CAMERA_IN_AP_3" "Video1" 100 STREAMSPEED
* 223 "CAMERA_IN_AP_3" "Video1" 0 STREAMSTATE
* 224 "CAMERA_IN_AP_3" "Video1" 1 0 STREAMMODE
* 225 "CAMERA_IN_AP_3" "Video1" 1 STREAMAUDIOMUTE
* 228 "CAMERA_IN_AP_3" "Video1" 1 STREAMVOLUMECHANGED
* 225 "CAMERA_IN_AP_3" "Video1" 1 STREAMAUDIOMUTE
* 223 "CAMERA_IN_AP_3" "Video1" 1 STREAMSTATE
*/
void ChooseStreamTest::testChooseAfterOverview()
{
const QString dialog = QStringLiteral("Video1");
const QString oldStream1 = QStringLiteral("cam1");
const QString oldStream2 = QStringLiteral("cam2");
const QString newStream = QStringLiteral("cam3");
AvaCommDouble avaCommDouble;
DoneObserver observer;
AvaCommands::ChooseStream chooseStream(&avaCommDouble, &observer, dialog, newStream);
chooseStream.send();
emit avaCommDouble.streamStateReceived(dialog, oldStream1, AvaStreamState::NotPossible);
emit avaCommDouble.streamStateReceived(dialog, oldStream2, AvaStreamState::NotPossible);
emit avaCommDouble.streamStateReceived(dialog, oldStream1, AvaStreamState::NotPossible);
emit avaCommDouble.streamStateReceived(dialog, oldStream2, AvaStreamState::NotPossible);
emit avaCommDouble.streamInfoReceived(dialog, newStream, AvaStreamInfo{AvaTimePoint{AvaDuration{151697904800}}, AvaTimePoint{AvaDuration{151697904800}}, AvaDuration{0}});
emit avaCommDouble.streamStateReceived(dialog, newStream, AvaStreamState::Stop);
QVERIFY(!observer.m_done);
emit avaCommDouble.streamModeReceived(dialog, newStream, true, false);
QVERIFY(observer.m_done);
}
@@ -0,0 +1,26 @@
/**
* @RDE_HEADER@
* ChooseStreamTest
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, NLSE, 2018-01-26
*/
#pragma once
#include <QTest>
/**
* @author Christian Fröbel
*/
class ChooseStreamTest : public QObject
{
Q_OBJECT
private slots:
void testChooseFirstStream();
void testChooseSecondStream();
void testChooseSameStream();
void testChooseAfterOverview();
};
@@ -0,0 +1,198 @@
/**
* @RDE_HEADER@
* ListResponseCollectorTest
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, NLE, 2018-12-18
*/
#include "list_response_collector_test.h"
#include "ava_comm_double.h"
#include <AudioVideoManagement/list_response_collector.h>
#include <AudioVideoManagement/ava_types.h>
#include <AudioVideoManagement/av_helper.h>
#include <QString>
#include <QVector>
using namespace AudioVideoManagement;
void ListResponseCollectorTest::test()
{
AvaCommDouble avaComm;
QVector<QString> invalidNames;
auto doneCallback = [&invalidNames] (QVector<QString> &&result) { invalidNames = std::move(result); };
InvalidNameListCollector collector(&avaComm, doneCallback);
// complete sequence -- but we didn't start yet
emit avaComm.invalidNameListBeginReceived();
emit avaComm.listValueReceived({"stream 1"});
emit avaComm.listValueReceived({"stream 2"});
emit avaComm.listValueReceived({"stream 3"});
emit avaComm.endOfListReceived();
QVERIFY(invalidNames.isEmpty());
collector.start();
emit avaComm.invalidNameListBeginReceived();
emit avaComm.listValueReceived({"stream 1"});
emit avaComm.listValueReceived({"stream 2"});
emit avaComm.listValueReceived({"stream 3"});
QVERIFY(invalidNames.isEmpty());
emit avaComm.endOfListReceived();
QVector<QString> expected{QString::fromUtf8("stream 1"), QString::fromUtf8("stream 2"), QString::fromUtf8("stream 3")};
QCOMPARE(invalidNames, expected);
}
void ListResponseCollectorTest::testOutOfOrder()
{
AvaCommDouble avaComm;
QVector<QString> invalidNames;
auto doneCallback = [&invalidNames] (QVector<QString> &&result) { invalidNames = std::move(result); };
InvalidNameListCollector collector(&avaComm, doneCallback);
collector.start();
// the following signals shall be ignored as the start of the (specific) list was not yet signalled
emit avaComm.elokaListBeginReceived();
emit avaComm.listValueReceived({"foo", "bar"});
emit avaComm.listValueReceived({"foo", "bar"});
emit avaComm.endOfListReceived();
QVERIFY(invalidNames.isEmpty());
emit avaComm.invalidNameListBeginReceived();
emit avaComm.listValueReceived({"stream 1"});
emit avaComm.listValueReceived({"stream 2"});
emit avaComm.listValueReceived({"stream 3"});
QVERIFY(invalidNames.isEmpty());
emit avaComm.endOfListReceived();
QVector<QString> expected{QString::fromUtf8("stream 1"), QString::fromUtf8("stream 2"), QString::fromUtf8("stream 3")};
QCOMPARE(invalidNames, expected);
}
void ListResponseCollectorTest::testIgnoreFollowingList()
{
AvaCommDouble avaComm;
QVector<QString> invalidNames;
auto doneCallback = [&invalidNames] (QVector<QString> &&result) { invalidNames = std::move(result); };
InvalidNameListCollector collector(&avaComm, doneCallback);
collector.start();
emit avaComm.invalidNameListBeginReceived();
emit avaComm.listValueReceived({"stream 1"});
emit avaComm.listValueReceived({"stream 2"});
emit avaComm.listValueReceived({"stream 3"});
emit avaComm.endOfListReceived();
QCOMPARE(invalidNames.size(), 3);
// the following list (of the same type) shall be ignored
invalidNames.clear();
emit avaComm.invalidNameListBeginReceived();
emit avaComm.listValueReceived({"another stream 1"});
emit avaComm.listValueReceived({"another stream 2"});
emit avaComm.endOfListReceived();
QVERIFY(invalidNames.isEmpty());
}
void ListResponseCollectorTest::testRestart()
{
AvaCommDouble avaComm;
QVector<QString> invalidNames;
auto doneCallback = [&invalidNames] (QVector<QString> &&result) { invalidNames = std::move(result); };
InvalidNameListCollector collector(&avaComm, doneCallback);
collector.start();
emit avaComm.invalidNameListBeginReceived();
emit avaComm.listValueReceived({"stream 1"});
emit avaComm.listValueReceived({"stream 2"});
emit avaComm.listValueReceived({"stream 3"});
emit avaComm.endOfListReceived();
QCOMPARE(invalidNames.size(), 3);
invalidNames.clear();
collector.start();
emit avaComm.invalidNameListBeginReceived();
emit avaComm.listValueReceived({"another stream 1"});
emit avaComm.listValueReceived({"another stream 2"});
emit avaComm.endOfListReceived();
QVector<QString> expected{QString::fromUtf8("another stream 1"), QString::fromUtf8("another stream 2")};
QCOMPARE(invalidNames, expected);
}
void ListResponseCollectorTest::testReset()
{
AvaCommDouble avaComm;
QVector<QString> invalidNames;
auto doneCallback = [&invalidNames] (QVector<QString> &&result) { invalidNames = std::move(result); };
InvalidNameListCollector collector(&avaComm, doneCallback);
collector.start();
emit avaComm.invalidNameListBeginReceived();
emit avaComm.listValueReceived({"stream 1"});
emit avaComm.listValueReceived({"stream 2"});
emit avaComm.listValueReceived({"stream 3"});
// restart the collector before the list is finished
collector.reset();
collector.start();
emit avaComm.invalidNameListBeginReceived();
emit avaComm.listValueReceived({"stream 1"});
emit avaComm.listValueReceived({"stream 2"});
emit avaComm.endOfListReceived();
QCOMPARE(invalidNames.size(), 2);
}
void ListResponseCollectorTest::testTimeline()
{
AvaCommDouble avaComm;
AvaTimeline timeline;
auto doneCallback = [&timeline] (AvaTimeline &&result) { timeline = std::move(result); };
auto aliveCallback = std::function<void()>{};
const QString streamName = QStringLiteral("CAM 1");
TimelineListCollector collector(&avaComm, streamName, doneCallback, aliveCallback);
collector.start();
emit avaComm.timelineListBeginReceived(QStringLiteral("CAM 1"));
emit avaComm.listValueReceived({"1.00-2.00"});
emit avaComm.listValueReceived({"3.00-4.00"});
emit avaComm.listValueReceived({"5.00-6.00"});
QVERIFY(timeline.isEmpty());
emit avaComm.endOfListReceived();
AvaTimeline expected({ {createTimePoint("1"), createTimePoint("2")}, {createTimePoint("3"), createTimePoint("4")}, {createTimePoint("5"), createTimePoint("6")} });
QCOMPARE(timeline, expected);
}
void ListResponseCollectorTest::testRestartTimeline()
{
AvaCommDouble avaComm;
AvaTimeline timeline;
auto doneCallback = [&timeline] (AvaTimeline &&result) { timeline = std::move(result); };
auto aliveCallback = std::function<void()>{};
const QString streamName = QStringLiteral("CAM 1");
TimelineListCollector collector(&avaComm, streamName, doneCallback, aliveCallback);
collector.start();
emit avaComm.timelineListBeginReceived(QStringLiteral("CAM 1"));
emit avaComm.listValueReceived({"1.00-2.00"});
emit avaComm.listValueReceived({"3.00-4.00"});
emit avaComm.listValueReceived({"5.00-6.00"});
emit avaComm.endOfListReceived();
QCOMPARE(timeline.parts().size(), 3);
timeline.clear();
emit avaComm.timelineListBeginReceived(QStringLiteral("CAM 1"));
emit avaComm.listValueReceived({"1.00-2.00"});
emit avaComm.listValueReceived({"3.00-4.00"});
emit avaComm.endOfListReceived();
QVERIFY(timeline.isEmpty()); // ignored as collector was not re-started yet
collector.start();
emit avaComm.timelineListBeginReceived(QStringLiteral("CAM 1"));
emit avaComm.listValueReceived({"1.00-2.00"});
emit avaComm.listValueReceived({"3.00-4.00"});
emit avaComm.endOfListReceived();
AvaTimeline expected({ {createTimePoint("1"), createTimePoint("2")}, {createTimePoint("3"), createTimePoint("4")} });
QCOMPARE(timeline, expected);
}
@@ -0,0 +1,29 @@
/**
* @RDE_HEADER@
* ListResponseCollectorTest
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, NLE, 2018-12-18
*/
#pragma once
#include <QTest>
/**
* @author Christian Fröbel
*/
class ListResponseCollectorTest : public QObject
{
Q_OBJECT
private slots:
void test();
void testOutOfOrder();
void testIgnoreFollowingList();
void testRestart();
void testReset();
void testTimeline();
void testRestartTimeline();
};
+151
View File
@@ -0,0 +1,151 @@
/**
* @RDE_HEADER@
* unit test main
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, SLSE, 2017-06-19
*/
#include "parse_line_test.h"
#include "ava_comm_test.h"
#include "ava_control_test.h"
#include "av_helper_test.h"
#include "asset_assignment_manager_test.h"
#include "audio_video_handler_impl_test.h"
#include "choose_stream_test.h"
#include "ava_timeline_test.h"
#include "avs_command_queue_test.h"
#include "rt_state_test.h"
#include "list_response_collector_test.h"
#include <nglapplication/nglcoreapplication.h>
#include <QtTest/QtTest>
#include <QCommandLineParser>
#include <QCommandLineOption>
#include <QStringList>
#include <map>
#include <functional>
#include <memory>
#include <algorithm>
#include <iterator>
#include <iostream>
using namespace AudioVideoManagement;
namespace
{
QStringList toStringList(int argc, char **argv)
{
QStringList options;
options.reserve(argc);
for (int i = 0; i < argc; ++i)
options.append(QString::fromUtf8(argv[i]));
return options;
}
QStringList removeParsedArgs(const QStringList &args)
{
QStringList reducedArgs = {args.front()};
auto separatorPos = std::find(args.begin(), args.end(), QStringLiteral("--"));
if (separatorPos == args.end())
return reducedArgs;
++separatorPos;
std::copy(separatorPos, args.end(), std::back_inserter(reducedArgs));
return reducedArgs;
}
/**
* Maps the name of the test class to an instance of it.
*/
using TestMap = std::map<QString, std::unique_ptr<QObject>>;
template<class Test>
void addTest(TestMap& tests)
{
std::unique_ptr<QObject> test(new Test);
auto result = tests.insert(std::make_pair(QString::fromUtf8(test->metaObject()->className()), std::move(test)));
Q_ASSERT(result.second);
}
/**
* Instantiate all test classes and return them.
* Every new test class needs to be added here.
*/
TestMap allTests()
{
TestMap tests;
addTest<ParseLineTest>(tests);
addTest<AvaCommTest>(tests);
addTest<AvaControlTest>(tests);
addTest<AvHelperTest>(tests);
addTest<AssetAssignmentManagerTest>(tests);
addTest<AudioVideoHandlerImplTest>(tests);
addTest<ChooseStreamTest>(tests);
addTest<AvsCommandQueueTest>(tests);
addTest<AvaTimelineTest>(tests);
addTest<RtStateTest>(tests);
addTest<ListResponseCollectorTest>(tests);
return tests;
}
int runTests(const QStringList &selectedTests, const TestMap &allTests, const QStringList &args)
{
int result = 0;
for (const QString &testName : selectedTests)
{
auto found = allTests.find(testName);
if (found == allTests.end())
{
std::cout << "Unknown test: " << testName.toUtf8().constData() << std::endl;
result = 1;
continue;
}
QObject* test = found->second.get();
result |= QTest::qExec(test, args);
}
return result;
}
int runAllTests(const TestMap &allTests, const QStringList &args)
{
QStringList allTestNames;
allTestNames.reserve(allTests.size());
for (const auto &test: allTests)
allTestNames.append(test.first);
return runTests(allTestNames, allTests, args);
}
} // anonymous namespace
int main(int argc, char **argv)
{
int fakeArgc = 1;
NGLCoreApplication app(fakeArgc, argv);
app.createLogger("AVM", "log4cxx_avmTests.props");
// Before passing the command line arguments to QTest::qExec() we do some parsing ourselves.
// The problem is that QTest conceptionally only allows one instance of a single test class.
// As we want to instantiate and test more than one test class, we offer a command line option
// to select one or more test classes to run: -t <TestClass>.
// Other options which shall be passed to QTest::qExec() must come after -t-options and
// an explicit '--' separator.
QCommandLineParser parser;
parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
QCommandLineOption selectTestOption(QStringList{QStringLiteral("t"), QStringLiteral("test")}, QStringLiteral("Test-class to run"), QStringLiteral("test"));
parser.addOption(selectTestOption);
QStringList commandLineArgs = toStringList(argc, argv);
(void) parser.parse(commandLineArgs);
QStringList reducedCommandLineArgs = removeParsedArgs(commandLineArgs);
if (parser.isSet(selectTestOption))
{
QStringList selectedTests = parser.values(selectTestOption.valueName());
return runTests(selectedTests, allTests(), reducedCommandLineArgs);
}
return runAllTests(allTests(), reducedCommandLineArgs);
}
@@ -0,0 +1,76 @@
/**
* @RDE_HEADER@
* ParseLineTest
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, SLSE, 2017-06-19
*/
#include "parse_line_test.h"
#include <AudioVideoManagement/av_socket.h>
using namespace AudioVideoManagement;
/**
* Special case that I could not cover with the data-driven approach.
*/
void ParseLineTest::testEmpty()
{
QByteArray line;
const auto parts = AVSocket::parseLine(line);
QCOMPARE(parts.size(), 0);
}
void ParseLineTest::test()
{
QFETCH(QByteArray, line);
QFETCH(QByteArray, gluedParts);
const auto expected = gluedParts.split(';');
const auto parts = AVSocket::parseLine(line);
QCOMPARE(parts.size(), expected.size());
for (int i = 0; i < expected.size(); ++i)
QCOMPARE(parts.at(i), expected.at(i));
}
void ParseLineTest::test_data()
{
QTest::addColumn<QByteArray>("line");
QTest::addColumn<QByteArray>("gluedParts");
QTest::newRow("")
<< QByteArray("{}")
<< QByteArray("");
QTest::newRow("")
<< QByteArray("218 Video1 \"\" VIDEODIALOGSTATE")
<< QByteArray("218;Video1;;VIDEODIALOGSTATE");
QTest::newRow("")
<< QByteArray("218 \"Video1\" \"\" VIDEODIALOGSTATE")
<< QByteArray("218;Video1;;VIDEODIALOGSTATE");
QTest::newRow("")
<< QByteArray("218 \"Video 1\" \"\" VIDEODIALOGSTATE")
<< QByteArray("218;Video 1;;VIDEODIALOGSTATE");
QTest::newRow("")
<< QByteArray("218 \"Video \\\"1\\\"\" \"\" VIDEODIALOGSTATE")
<< QByteArray("218;Video \"1\";;VIDEODIALOGSTATE");
QTest::newRow("")
<< QByteArray("setvideoprofile Video1 \"1 {bldg_56: {CAM_UBEICE0001} {CAM_UBEICE0002} {CAM_UBEICE0003} {CAM_UBEICE0004}}\"")
<< QByteArray("setvideoprofile;Video1;1 {bldg_56: {CAM_UBEICE0001} {CAM_UBEICE0002} {CAM_UBEICE0003} {CAM_UBEICE0004}}");
QTest::newRow("")
<< QByteArray("{hello \" world}")
<< QByteArray("hello \" world");
// Was that intended?
QTest::newRow("")
<< QByteArray("{")
<< QByteArray("");
QTest::newRow("")
<< QByteArray("\"")
<< QByteArray("");
// Oops, I guess that was not intended!
QTest::newRow("")
<< QByteArray("setvideoprofile Video1 {bldg_56: {CAM_UBEICE0001} {CAM_UBEICE0002} {CAM_UBEICE0003} {CAM_UBEICE0004}}")
<< QByteArray("setvideoprofile;Video1;bldg_56: {CAM_UBEICE0001;CAM_UBEICE0002;CAM_UBEICE0003;CAM_UBEICE0004;}");
}
@@ -0,0 +1,26 @@
/**
* @RDE_HEADER@
* ParseLineTest
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, SLSE, 2017-06-19
*/
#pragma once
#include <QTest>
/**
* @author Christian Fröbel
*/
class ParseLineTest : public QObject
{
Q_OBJECT
private slots:
void testEmpty();
void test();
void test_data();
};
@@ -0,0 +1,85 @@
/**
* @RDE_HEADER@
* RtStateTest
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, NLE, 2018-12-16
*/
#include "rt_state_test.h"
#include <AudioVideoManagement/rt_state.h>
#include <vector>
#include <string>
using namespace AudioVideoManagement;
namespace
{
QVector<RtState::RadioCircuit> radioCircuits()
{
return {{RadioSystem::Tvc, 1, "tvc 1"}, {RadioSystem::Tvc, 2, "tvc 2"}, {RadioSystem::Cnr, 1, "cnr 1"}};
}
} // anonymous namespace
void RtStateTest::testInitialState()
{
RtState rtState;
rtState.setRadioCircuits(radioCircuits());
QCOMPARE(rtState.toCommand(), std::string(R"(setRtProfile "Default" "15 0 {cnr 1} 15 0 {tvc 1} 15 0 {tvc 2} " "{tvc 1} {tvc 2} {cnr 1} {} {} {} {} {} " "1 1 1 1 1 1 1 1 " 1)"));
}
void RtStateTest::testVolume()
{
RtState rtState;
rtState.setRadioCircuits(radioCircuits());
rtState.setVolume("tvc 2", AvaVolume(5));
QCOMPARE(rtState.toCommand(), std::string(R"(setRtProfile "Default" "15 0 {cnr 1} 15 0 {tvc 1} 15 5 {tvc 2} " "{tvc 1} {tvc 2} {cnr 1} {} {} {} {} {} " "1 1 1 1 1 1 1 1 " 1)"));
}
void RtStateTest::testNewProfile()
{
RtState rtState;
rtState.setRadioCircuits(radioCircuits());
rtState.setVolume("tvc 2", AvaVolume(5));
rtState.setVolume("cnr 1", AvaVolume(-3));
rtState.setSelection(AvaRtPos(2), "tvc 2", AvaRtButtonState::Left);
rtState.setSelection(AvaRtPos(3), "cnr 1", AvaRtButtonState::Both);
rtState.setRadioCircuits({{RadioSystem::Tvc, 2, "tvc 2"}, {RadioSystem::Cnr, 1, "cnr 1"}, {RadioSystem::Cnr, 2, "cnr 2"}});
QCOMPARE(rtState.toCommand(), std::string(R"(setRtProfile "Default" "15 -3 {cnr 1} 15 0 {cnr 2} 15 5 {tvc 2} " "{tvc 2} {cnr 1} {cnr 2} {} {} {} {} {} " "2 4 1 1 1 1 1 1 " 1)"));
}
void RtStateTest::testSelection()
{
RtState rtState;
rtState.setRadioCircuits(radioCircuits());
rtState.setSelection(AvaRtPos(1), "", AvaRtButtonState::None); // de-select
rtState.setSelection(AvaRtPos(2), "", AvaRtButtonState::None); // de-select
rtState.setSelection(AvaRtPos(3), "cnr 1", AvaRtButtonState::Both);
rtState.setSelection(AvaRtPos(8), "tvc 1", AvaRtButtonState::Right);
QCOMPARE(rtState.toCommand(), std::string(R"(setRtProfile "Default" "15 0 {cnr 1} 15 0 {tvc 1} 15 0 {tvc 2} " "{} {} {cnr 1} {} {} {} {} {tvc 1} " "1 1 4 1 1 1 1 3 " 1)"));
}
void RtStateTest::testChangeName()
{
RtState rtState;
rtState.setRadioCircuits(radioCircuits());
rtState.changeName({RadioSystem::Tvc, 2, "TVC_2"});
QCOMPARE(rtState.toCommand(), std::string(R"(setRtProfile "Default" "15 0 {cnr 1} 15 0 {tvc 1} 15 0 {TVC_2} " "{tvc 1} {TVC_2} {cnr 1} {} {} {} {} {} " "1 1 1 1 1 1 1 1 " 1)"));
}
void RtStateTest::testSumChannel()
{
RtState rtState;
rtState.setRadioCircuits(radioCircuits());
rtState.setSumChannel(AvaSumChannel::Left);
QCOMPARE(rtState.toCommand(), std::string(R"(setRtProfile "Default" "15 0 {cnr 1} 15 0 {tvc 1} 15 0 {tvc 2} " "{tvc 1} {tvc 2} {cnr 1} {} {} {} {} {} " "1 1 1 1 1 1 1 1 " 2)"));
}
@@ -0,0 +1,28 @@
/**
* @RDE_HEADER@
* RtStateTest
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, NLE, 2018-12-16
*/
#pragma once
#include <QTest>
/**
* @author Christian Fröbel
*/
class RtStateTest : public QObject
{
Q_OBJECT
private slots:
void testInitialState();
void testVolume();
void testNewProfile();
void testSelection();
void testChangeName();
void testSumChannel();
};