334 Zeilen
17 KiB
C++
334 Zeilen
17 KiB
C++
/**
|
|
* server/test/hardware/outputmap.cpp
|
|
*
|
|
* This file is part of the traintastic test suite.
|
|
*
|
|
* Copyright (C) 2024 Reinder Feenstra
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <catch2/catch_test_macros.hpp>
|
|
#include "../../src/core/method.tpp"
|
|
#include "../../src/core/objectproperty.tpp"
|
|
#include "../../src/world/world.hpp"
|
|
#include "../../src/hardware/interface/interfacelist.hpp"
|
|
#include "../../src/hardware/interface/ecosinterface.hpp"
|
|
#include "../../src/hardware/interface/loconetinterface.hpp"
|
|
#include "../../src/hardware/interface/xpressnetinterface.hpp"
|
|
#include "../../src/hardware/output/map/outputmappairoutputaction.hpp"
|
|
#include "../../src/hardware/output/map/outputmapecosstateoutputaction.hpp"
|
|
#include "../../src/board/board.hpp"
|
|
#include "../../src/board/boardlist.hpp"
|
|
#include "../../src/board/tile/rail/signal/signal3aspectrailtile.hpp"
|
|
#include "../../src/board/tile/rail/turnout/turnoutleft45railtile.hpp"
|
|
#include "../../src/board/tile/rail/turnout/turnout3wayrailtile.hpp"
|
|
|
|
TEST_CASE("OutputMap: Channel: Accessory -> ECoSObject -> Accessory", "[outputmap]")
|
|
{
|
|
// Create world:
|
|
auto world = World::create();
|
|
std::weak_ptr<World> worldWeak = world;
|
|
REQUIRE(worldWeak.lock()->interfaces->length == 0);
|
|
REQUIRE(worldWeak.lock()->boards->length == 0);
|
|
|
|
// Create interface:
|
|
std::weak_ptr<ECoSInterface> interfaceWeak = std::dynamic_pointer_cast<ECoSInterface>(world->interfaces->create(ECoSInterface::classId));
|
|
REQUIRE_FALSE(interfaceWeak.expired());
|
|
REQUIRE(worldWeak.lock()->interfaces->length == 1);
|
|
|
|
// Create board:
|
|
std::weak_ptr<Board> boardWeak = world->boards->create();
|
|
REQUIRE_FALSE(boardWeak.expired());
|
|
REQUIRE(worldWeak.lock()->boards->length == 1);
|
|
|
|
// Create signal:
|
|
REQUIRE(boardWeak.lock()->addTile(0, 0, TileRotate::Deg0, Signal3AspectRailTile::classId, false));
|
|
std::weak_ptr<Signal3AspectRailTile> signalWeak = std::dynamic_pointer_cast<Signal3AspectRailTile>(boardWeak.lock()->getTile({0, 0}));
|
|
std::weak_ptr<OutputMap> outputMapWeak = signalWeak.lock()->outputMap.value();
|
|
REQUIRE_FALSE(signalWeak.expired());
|
|
REQUIRE_FALSE(outputMapWeak.lock()->interface);
|
|
|
|
// Assign interface:
|
|
outputMapWeak.lock()->interface = interfaceWeak.lock();
|
|
REQUIRE(outputMapWeak.lock()->interface.value() == interfaceWeak.lock());
|
|
REQUIRE(isAccessory(outputMapWeak.lock()->channel.value()));
|
|
REQUIRE(outputMapWeak.lock()->addresses.size() == 1);
|
|
REQUIRE(outputMapWeak.lock()->ecosObject == 0);
|
|
for(const auto& item : outputMapWeak.lock()->items)
|
|
{
|
|
REQUIRE(item->outputActions.size() == 1);
|
|
REQUIRE(std::dynamic_pointer_cast<OutputMapPairOutputAction>(item->outputActions[0]));
|
|
}
|
|
|
|
// Add address:
|
|
outputMapWeak.lock()->addAddress();
|
|
REQUIRE(outputMapWeak.lock()->addresses.size() == 2);
|
|
REQUIRE(outputMapWeak.lock()->addresses[0] != outputMapWeak.lock()->addresses[1]);
|
|
for(const auto& item : outputMapWeak.lock()->items)
|
|
{
|
|
REQUIRE(item->outputActions.size() == 2);
|
|
REQUIRE(std::dynamic_pointer_cast<OutputMapPairOutputAction>(item->outputActions[0]));
|
|
REQUIRE(std::dynamic_pointer_cast<OutputMapPairOutputAction>(item->outputActions[1]));
|
|
}
|
|
|
|
// Change channel to ECoSObject:
|
|
outputMapWeak.lock()->channel = OutputChannel::ECoSObject;
|
|
REQUIRE(outputMapWeak.lock()->channel.value() == OutputChannel::ECoSObject);
|
|
REQUIRE(outputMapWeak.lock()->addresses.size() == 0);
|
|
REQUIRE(outputMapWeak.lock()->ecosObject == 0);
|
|
for(const auto& item : outputMapWeak.lock()->items)
|
|
{
|
|
REQUIRE(item->outputActions.empty());
|
|
}
|
|
|
|
// Select ECoS object:
|
|
outputMapWeak.lock()->ecosObject = 20000;
|
|
REQUIRE(outputMapWeak.lock()->ecosObject == 20000);
|
|
for(const auto& item : outputMapWeak.lock()->items)
|
|
{
|
|
REQUIRE(item->outputActions.size() == 1);
|
|
REQUIRE(std::dynamic_pointer_cast<OutputMapECoSStateOutputAction>(item->outputActions[0]));
|
|
}
|
|
|
|
// Change channel to AccessoryMotorola:
|
|
outputMapWeak.lock()->channel = OutputChannel::AccessoryMotorola;
|
|
REQUIRE(outputMapWeak.lock()->channel.value() == OutputChannel::AccessoryMotorola);
|
|
REQUIRE(outputMapWeak.lock()->addresses.size() == 1);
|
|
REQUIRE(outputMapWeak.lock()->ecosObject == 0);
|
|
for(const auto& item : outputMapWeak.lock()->items)
|
|
{
|
|
REQUIRE(item->outputActions.size() == 1);
|
|
REQUIRE(std::dynamic_pointer_cast<OutputMapPairOutputAction>(item->outputActions[0]));
|
|
}
|
|
|
|
// cleanup:
|
|
world.reset();
|
|
REQUIRE(worldWeak.expired());
|
|
REQUIRE(interfaceWeak.expired());
|
|
REQUIRE(boardWeak.expired());
|
|
REQUIRE(signalWeak.expired());
|
|
REQUIRE(outputMapWeak.expired());
|
|
}
|
|
|
|
TEST_CASE("OutputMap: Channel: preserve mapping", "[outputmap]")
|
|
{
|
|
// Create world:
|
|
auto world = World::create();
|
|
std::weak_ptr<World> worldWeak = world;
|
|
REQUIRE(worldWeak.lock()->interfaces->length == 0);
|
|
REQUIRE(worldWeak.lock()->boards->length == 0);
|
|
|
|
// Create interface:
|
|
std::weak_ptr<ECoSInterface> interfaceWeak = std::dynamic_pointer_cast<ECoSInterface>(world->interfaces->create(ECoSInterface::classId));
|
|
REQUIRE_FALSE(interfaceWeak.expired());
|
|
REQUIRE(worldWeak.lock()->interfaces->length == 1);
|
|
|
|
// Create board:
|
|
std::weak_ptr<Board> boardWeak = world->boards->create();
|
|
REQUIRE_FALSE(boardWeak.expired());
|
|
REQUIRE(worldWeak.lock()->boards->length == 1);
|
|
|
|
// Create turnout:
|
|
REQUIRE(boardWeak.lock()->addTile(0, 0, TileRotate::Deg0, TurnoutLeft45RailTile::classId, false));
|
|
std::weak_ptr<TurnoutLeft45RailTile> turnoutWeak = std::dynamic_pointer_cast<TurnoutLeft45RailTile>(boardWeak.lock()->getTile({0, 0}));
|
|
std::weak_ptr<OutputMap> outputMapWeak = turnoutWeak.lock()->outputMap.value();
|
|
REQUIRE_FALSE(turnoutWeak.expired());
|
|
REQUIRE_FALSE(outputMapWeak.lock()->interface);
|
|
|
|
// Assign interface:
|
|
outputMapWeak.lock()->interface = interfaceWeak.lock();
|
|
REQUIRE(outputMapWeak.lock()->interface.value() == interfaceWeak.lock());
|
|
REQUIRE(outputMapWeak.lock()->channel.value() == OutputChannel::AccessoryDCC);
|
|
REQUIRE(outputMapWeak.lock()->addresses.size() == 1);
|
|
REQUIRE(outputMapWeak.lock()->ecosObject == 0);
|
|
for(const auto& item : outputMapWeak.lock()->items)
|
|
{
|
|
REQUIRE(item->outputActions.size() == 1);
|
|
REQUIRE(std::dynamic_pointer_cast<OutputMapPairOutputAction>(item->outputActions[0]));
|
|
}
|
|
|
|
// Assign output actions:
|
|
std::weak_ptr<OutputMapPairOutputAction> outputActionWeakA = std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[0]->outputActions[0]);
|
|
std::weak_ptr<OutputMapPairOutputAction> outputActionWeakB = std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[1]->outputActions[0]);
|
|
outputActionWeakA.lock()->action = PairOutputAction::First;
|
|
outputActionWeakB.lock()->action = PairOutputAction::Second;
|
|
REQUIRE(outputActionWeakA.lock()->action.value() == PairOutputAction::First);
|
|
REQUIRE(outputActionWeakB.lock()->action.value() == PairOutputAction::Second);
|
|
|
|
// Change channel to AccessoryMotorola:
|
|
outputMapWeak.lock()->channel = OutputChannel::AccessoryMotorola;
|
|
REQUIRE(outputMapWeak.lock()->channel.value() == OutputChannel::AccessoryMotorola);
|
|
REQUIRE_FALSE(outputActionWeakA.expired());
|
|
REQUIRE_FALSE(outputActionWeakB.expired());
|
|
REQUIRE(outputActionWeakA.lock() == std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[0]->outputActions[0]));
|
|
REQUIRE(outputActionWeakB.lock() == std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[1]->outputActions[0]));
|
|
REQUIRE(outputActionWeakA.lock()->action.value() == PairOutputAction::First);
|
|
REQUIRE(outputActionWeakB.lock()->action.value() == PairOutputAction::Second);
|
|
REQUIRE(outputMapWeak.lock()->addresses.size() == 1);
|
|
REQUIRE(outputMapWeak.lock()->addresses[0] == 1);
|
|
REQUIRE(outputMapWeak.lock()->ecosObject == 0);
|
|
for(const auto& item : outputMapWeak.lock()->items)
|
|
{
|
|
REQUIRE(item->outputActions.size() == 1);
|
|
REQUIRE(std::dynamic_pointer_cast<OutputMapPairOutputAction>(item->outputActions[0]));
|
|
}
|
|
|
|
// cleanup:
|
|
world.reset();
|
|
REQUIRE(worldWeak.expired());
|
|
REQUIRE(interfaceWeak.expired());
|
|
REQUIRE(boardWeak.expired());
|
|
REQUIRE(turnoutWeak.expired());
|
|
REQUIRE(outputMapWeak.expired());
|
|
}
|
|
|
|
TEST_CASE("OutputMap: Change interface, preserve mapping", "[outputmap]")
|
|
{
|
|
// Create world:
|
|
auto world = World::create();
|
|
std::weak_ptr<World> worldWeak = world;
|
|
REQUIRE(worldWeak.lock()->interfaces->length == 0);
|
|
REQUIRE(worldWeak.lock()->boards->length == 0);
|
|
|
|
// Create interfaces:
|
|
std::weak_ptr<LocoNetInterface> loconetWeak = std::dynamic_pointer_cast<LocoNetInterface>(world->interfaces->create(LocoNetInterface::classId));
|
|
std::weak_ptr<XpressNetInterface> xpressnetWeak = std::dynamic_pointer_cast<XpressNetInterface>(world->interfaces->create(XpressNetInterface::classId));
|
|
std::weak_ptr<ECoSInterface> ecosWeak = std::dynamic_pointer_cast<ECoSInterface>(world->interfaces->create(ECoSInterface::classId));
|
|
REQUIRE_FALSE(loconetWeak.expired());
|
|
REQUIRE_FALSE(xpressnetWeak.expired());
|
|
REQUIRE_FALSE(ecosWeak.expired());
|
|
REQUIRE(worldWeak.lock()->interfaces->length == 3);
|
|
|
|
// Create board:
|
|
std::weak_ptr<Board> boardWeak = world->boards->create();
|
|
REQUIRE_FALSE(boardWeak.expired());
|
|
REQUIRE(worldWeak.lock()->boards->length == 1);
|
|
|
|
// Create turnout:
|
|
REQUIRE(boardWeak.lock()->addTile(0, 0, TileRotate::Deg0, Turnout3WayRailTile::classId, false));
|
|
std::weak_ptr<Turnout3WayRailTile> turnoutWeak = std::dynamic_pointer_cast<Turnout3WayRailTile>(boardWeak.lock()->getTile({0, 0}));
|
|
std::weak_ptr<OutputMap> outputMapWeak = turnoutWeak.lock()->outputMap.value();
|
|
REQUIRE_FALSE(turnoutWeak.expired());
|
|
REQUIRE_FALSE(outputMapWeak.lock()->interface);
|
|
|
|
// Assign interface:
|
|
outputMapWeak.lock()->interface = loconetWeak.lock();
|
|
REQUIRE(outputMapWeak.lock()->interface.value() == loconetWeak.lock());
|
|
REQUIRE(isAccessory(outputMapWeak.lock()->channel.value()));
|
|
REQUIRE(outputMapWeak.lock()->addresses.size() == 1);
|
|
REQUIRE(outputMapWeak.lock()->ecosObject == 0);
|
|
for(const auto& item : outputMapWeak.lock()->items)
|
|
{
|
|
REQUIRE(item->outputActions.size() == 1);
|
|
REQUIRE(std::dynamic_pointer_cast<OutputMapPairOutputAction>(item->outputActions[0]));
|
|
}
|
|
|
|
// Change address:
|
|
outputMapWeak.lock()->addresses.setInt64(0, 42);
|
|
REQUIRE(outputMapWeak.lock()->addresses[0] == 42);
|
|
|
|
// Add address:
|
|
outputMapWeak.lock()->addAddress();
|
|
REQUIRE(outputMapWeak.lock()->addresses.size() == 2);
|
|
REQUIRE(outputMapWeak.lock()->addresses[1] == 43);
|
|
for(const auto& item : outputMapWeak.lock()->items)
|
|
{
|
|
REQUIRE(item->outputActions.size() == 2);
|
|
REQUIRE(std::dynamic_pointer_cast<OutputMapPairOutputAction>(item->outputActions[0]));
|
|
REQUIRE(std::dynamic_pointer_cast<OutputMapPairOutputAction>(item->outputActions[1]));
|
|
}
|
|
|
|
// Assign output actions:
|
|
std::weak_ptr<OutputMapPairOutputAction> outputActionWeakA0 = std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[0]->outputActions[0]);
|
|
std::weak_ptr<OutputMapPairOutputAction> outputActionWeakA1 = std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[0]->outputActions[1]);
|
|
std::weak_ptr<OutputMapPairOutputAction> outputActionWeakB0 = std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[1]->outputActions[0]);
|
|
std::weak_ptr<OutputMapPairOutputAction> outputActionWeakB1 = std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[1]->outputActions[1]);
|
|
std::weak_ptr<OutputMapPairOutputAction> outputActionWeakC0 = std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[2]->outputActions[0]);
|
|
std::weak_ptr<OutputMapPairOutputAction> outputActionWeakC1 = std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[2]->outputActions[1]);
|
|
outputActionWeakA0.lock()->action = PairOutputAction::First;
|
|
outputActionWeakB0.lock()->action = PairOutputAction::Second;
|
|
outputActionWeakB1.lock()->action = PairOutputAction::First;
|
|
outputActionWeakC0.lock()->action = PairOutputAction::Second;
|
|
outputActionWeakC1.lock()->action = PairOutputAction::Second;
|
|
REQUIRE(outputActionWeakA0.lock()->action.value() == PairOutputAction::First);
|
|
REQUIRE(outputActionWeakA1.lock()->action.value() == PairOutputAction::None);
|
|
REQUIRE(outputActionWeakB0.lock()->action.value() == PairOutputAction::Second);
|
|
REQUIRE(outputActionWeakB1.lock()->action.value() == PairOutputAction::First);
|
|
REQUIRE(outputActionWeakC0.lock()->action.value() == PairOutputAction::Second);
|
|
REQUIRE(outputActionWeakC1.lock()->action.value() == PairOutputAction::Second);
|
|
|
|
// Change interface:
|
|
outputMapWeak.lock()->interface = xpressnetWeak.lock();
|
|
REQUIRE(outputMapWeak.lock()->interface.value() == xpressnetWeak.lock());
|
|
REQUIRE(isAccessory(outputMapWeak.lock()->channel.value()));
|
|
REQUIRE(outputMapWeak.lock()->addresses.size() == 2);
|
|
REQUIRE(outputMapWeak.lock()->addresses[0] == 42);
|
|
REQUIRE(outputMapWeak.lock()->addresses[1] == 43);
|
|
for(const auto& item : outputMapWeak.lock()->items)
|
|
{
|
|
REQUIRE(item->outputActions.size() == 2);
|
|
REQUIRE(std::dynamic_pointer_cast<OutputMapPairOutputAction>(item->outputActions[0]));
|
|
REQUIRE(std::dynamic_pointer_cast<OutputMapPairOutputAction>(item->outputActions[1]));
|
|
}
|
|
REQUIRE(outputActionWeakA0.lock() == std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[0]->outputActions[0]));
|
|
REQUIRE(outputActionWeakA1.lock() == std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[0]->outputActions[1]));
|
|
REQUIRE(outputActionWeakB0.lock() == std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[1]->outputActions[0]));
|
|
REQUIRE(outputActionWeakB1.lock() == std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[1]->outputActions[1]));
|
|
REQUIRE(outputActionWeakC0.lock() == std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[2]->outputActions[0]));
|
|
REQUIRE(outputActionWeakC1.lock() == std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[2]->outputActions[1]));
|
|
REQUIRE(outputActionWeakA0.lock()->action.value() == PairOutputAction::First);
|
|
REQUIRE(outputActionWeakA1.lock()->action.value() == PairOutputAction::None);
|
|
REQUIRE(outputActionWeakB0.lock()->action.value() == PairOutputAction::Second);
|
|
REQUIRE(outputActionWeakB1.lock()->action.value() == PairOutputAction::First);
|
|
REQUIRE(outputActionWeakC0.lock()->action.value() == PairOutputAction::Second);
|
|
REQUIRE(outputActionWeakC1.lock()->action.value() == PairOutputAction::Second);
|
|
|
|
// Change interface again:
|
|
outputMapWeak.lock()->interface = ecosWeak.lock();
|
|
REQUIRE(outputMapWeak.lock()->interface.value() == ecosWeak.lock());
|
|
REQUIRE(isAccessory(outputMapWeak.lock()->channel.value()));
|
|
REQUIRE(outputMapWeak.lock()->addresses.size() == 2);
|
|
REQUIRE(outputMapWeak.lock()->addresses[0] == 42);
|
|
REQUIRE(outputMapWeak.lock()->addresses[1] == 43);
|
|
for(const auto& item : outputMapWeak.lock()->items)
|
|
{
|
|
REQUIRE(item->outputActions.size() == 2);
|
|
REQUIRE(std::dynamic_pointer_cast<OutputMapPairOutputAction>(item->outputActions[0]));
|
|
REQUIRE(std::dynamic_pointer_cast<OutputMapPairOutputAction>(item->outputActions[1]));
|
|
}
|
|
REQUIRE(outputActionWeakA0.lock() == std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[0]->outputActions[0]));
|
|
REQUIRE(outputActionWeakA1.lock() == std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[0]->outputActions[1]));
|
|
REQUIRE(outputActionWeakB0.lock() == std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[1]->outputActions[0]));
|
|
REQUIRE(outputActionWeakB1.lock() == std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[1]->outputActions[1]));
|
|
REQUIRE(outputActionWeakC0.lock() == std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[2]->outputActions[0]));
|
|
REQUIRE(outputActionWeakC1.lock() == std::dynamic_pointer_cast<OutputMapPairOutputAction>(outputMapWeak.lock()->items[2]->outputActions[1]));
|
|
REQUIRE(outputActionWeakA0.lock()->action.value() == PairOutputAction::First);
|
|
REQUIRE(outputActionWeakA1.lock()->action.value() == PairOutputAction::None);
|
|
REQUIRE(outputActionWeakB0.lock()->action.value() == PairOutputAction::Second);
|
|
REQUIRE(outputActionWeakB1.lock()->action.value() == PairOutputAction::First);
|
|
REQUIRE(outputActionWeakC0.lock()->action.value() == PairOutputAction::Second);
|
|
REQUIRE(outputActionWeakC1.lock()->action.value() == PairOutputAction::Second);
|
|
|
|
// cleanup:
|
|
world.reset();
|
|
REQUIRE(worldWeak.expired());
|
|
REQUIRE(loconetWeak.expired());
|
|
REQUIRE(xpressnetWeak.expired());
|
|
REQUIRE(boardWeak.expired());
|
|
REQUIRE(turnoutWeak.expired());
|
|
REQUIRE(outputMapWeak.expired());
|
|
}
|