FlightPlans: fix save/load of restrictions, deleting legs from Nasal
- Fix Leg ownership to avoid segfaults when deleting them via Nasal - Modify FP save/load code so restrictions and holds are correctly saved and restored Extend the tests to cover these cases.
This commit is contained in:
parent
de172de5b3
commit
3f14d53286
10 changed files with 326 additions and 96 deletions
|
@ -75,6 +75,9 @@ const string_list static_icaoFlightTypeCode = {
|
|||
|
||||
namespace flightgear {
|
||||
|
||||
// implemented in route.cxx
|
||||
const char* restrictionToString(RouteRestriction aRestrict);
|
||||
|
||||
typedef std::vector<FlightPlan::DelegateFactory*> FPDelegateFactoryVec;
|
||||
static FPDelegateFactoryVec static_delegateFactories;
|
||||
|
||||
|
@ -109,11 +112,6 @@ FlightPlan::~FlightPlan()
|
|||
delete d;
|
||||
}
|
||||
}
|
||||
|
||||
// delete legs
|
||||
for (auto l : _legs) {
|
||||
delete l;
|
||||
}
|
||||
}
|
||||
|
||||
FlightPlan* FlightPlan::clone(const string& newIdent) const
|
||||
|
@ -160,7 +158,7 @@ string FlightPlan::ident() const
|
|||
return _ident;
|
||||
}
|
||||
|
||||
FlightPlan::Leg* FlightPlan::insertWayptAtIndex(Waypt* aWpt, int aIndex)
|
||||
FlightPlan::LegRef FlightPlan::insertWayptAtIndex(Waypt* aWpt, int aIndex)
|
||||
{
|
||||
if (!aWpt) {
|
||||
return nullptr;
|
||||
|
@ -189,9 +187,7 @@ void FlightPlan::insertWayptsAtIndex(const WayptVec& wps, int aIndex)
|
|||
index = _legs.size();
|
||||
}
|
||||
|
||||
LegVec::iterator it = _legs.begin();
|
||||
it += index;
|
||||
|
||||
auto it = _legs.begin() + index;
|
||||
int endIndex = index + wps.size() - 1;
|
||||
if (_currentIndex >= endIndex) {
|
||||
_currentIndex += wps.size();
|
||||
|
@ -199,7 +195,7 @@ void FlightPlan::insertWayptsAtIndex(const WayptVec& wps, int aIndex)
|
|||
|
||||
LegVec newLegs;
|
||||
for (WayptRef wp : wps) {
|
||||
newLegs.push_back(new Leg(this, wp));
|
||||
newLegs.push_back(LegRef{new Leg(this, wp)});
|
||||
}
|
||||
|
||||
lockDelegates();
|
||||
|
@ -223,12 +219,11 @@ void FlightPlan::deleteIndex(int aIndex)
|
|||
lockDelegates();
|
||||
_waypointsChanged = true;
|
||||
|
||||
LegVec::iterator it = _legs.begin();
|
||||
it += index;
|
||||
Leg* l = *it;
|
||||
auto it = _legs.begin() + index;
|
||||
LegRef l = *it;
|
||||
_legs.erase(it);
|
||||
delete l;
|
||||
|
||||
l->_parent = nullptr; // orphan the leg so it's clear from Nasal
|
||||
|
||||
if (_currentIndex == index) {
|
||||
// current waypoint was removed
|
||||
_currentWaypointChanged = true;
|
||||
|
@ -255,43 +250,18 @@ void FlightPlan::clear()
|
|||
_cruiseDataChanged = true;
|
||||
|
||||
_currentIndex = -1;
|
||||
for (Leg* l : _legs) {
|
||||
delete l;
|
||||
}
|
||||
_legs.clear();
|
||||
|
||||
notifyCleared();
|
||||
unlockDelegates();
|
||||
}
|
||||
|
||||
class RemoveWithFlag
|
||||
{
|
||||
public:
|
||||
RemoveWithFlag(WayptFlag f) : flag(f), delCount(0) { }
|
||||
|
||||
int numDeleted() const { return delCount; }
|
||||
|
||||
bool operator()(FlightPlan::Leg* leg) const
|
||||
{
|
||||
if (leg->waypoint()->flag(flag)) {
|
||||
delete leg;
|
||||
++delCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
WayptFlag flag;
|
||||
mutable int delCount;
|
||||
};
|
||||
|
||||
int FlightPlan::clearWayptsWithFlag(WayptFlag flag)
|
||||
{
|
||||
int count = 0;
|
||||
// first pass, fix up currentIndex
|
||||
for (int i=0; i<_currentIndex; ++i) {
|
||||
Leg* l = _legs[i];
|
||||
const auto& l = _legs.at(i);
|
||||
if (l->waypoint()->flag(flag)) {
|
||||
++count;
|
||||
}
|
||||
|
@ -311,13 +281,21 @@ int FlightPlan::clearWayptsWithFlag(WayptFlag flag)
|
|||
// and let the use re-activate.
|
||||
// http://code.google.com/p/flightgear-bugs/issues/detail?id=1134
|
||||
if (currentIsBeingCleared) {
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "currentIsBeingCleared:" << currentIsBeingCleared);
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "FlightPlan::clearWayptsWithFlag: currentIsBeingCleared:" << currentIsBeingCleared);
|
||||
_currentIndex = -1;
|
||||
}
|
||||
|
||||
// now delete and remove
|
||||
RemoveWithFlag rf(flag);
|
||||
auto it = std::remove_if(_legs.begin(), _legs.end(), rf);
|
||||
int numDeleted = 0;
|
||||
auto it = std::remove_if(_legs.begin(), _legs.end(),
|
||||
[flag, &numDeleted](const LegRef& leg)
|
||||
{
|
||||
if (leg->waypoint()->flag(flag)) {
|
||||
++numDeleted;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (it == _legs.end()) {
|
||||
return 0; // nothing was cleared, don't fire the delegate
|
||||
}
|
||||
|
@ -335,7 +313,7 @@ int FlightPlan::clearWayptsWithFlag(WayptFlag flag)
|
|||
}
|
||||
|
||||
unlockDelegates();
|
||||
return rf.numDeleted();
|
||||
return numDeleted;
|
||||
}
|
||||
|
||||
bool FlightPlan::isActive() const
|
||||
|
@ -407,14 +385,14 @@ int FlightPlan::findWayptIndex(const FGPositionedRef aPos) const
|
|||
return -1;
|
||||
}
|
||||
|
||||
FlightPlan::Leg* FlightPlan::currentLeg() const
|
||||
FlightPlan::LegRef FlightPlan::currentLeg() const
|
||||
{
|
||||
if ((_currentIndex < 0) || (_currentIndex >= numLegs()))
|
||||
return nullptr;
|
||||
return legAtIndex(_currentIndex);
|
||||
}
|
||||
|
||||
FlightPlan::Leg* FlightPlan::previousLeg() const
|
||||
FlightPlan::LegRef FlightPlan::previousLeg() const
|
||||
{
|
||||
if (_currentIndex <= 0) {
|
||||
return nullptr;
|
||||
|
@ -423,7 +401,7 @@ FlightPlan::Leg* FlightPlan::previousLeg() const
|
|||
return legAtIndex(_currentIndex - 1);
|
||||
}
|
||||
|
||||
FlightPlan::Leg* FlightPlan::nextLeg() const
|
||||
FlightPlan::LegRef FlightPlan::nextLeg() const
|
||||
{
|
||||
if ((_currentIndex < 0) || ((_currentIndex + 1) >= numLegs())) {
|
||||
return nullptr;
|
||||
|
@ -432,19 +410,19 @@ FlightPlan::Leg* FlightPlan::nextLeg() const
|
|||
return legAtIndex(_currentIndex + 1);
|
||||
}
|
||||
|
||||
FlightPlan::Leg* FlightPlan::legAtIndex(int index) const
|
||||
FlightPlan::LegRef FlightPlan::legAtIndex(int index) const
|
||||
{
|
||||
if ((index < 0) || (index >= numLegs())) {
|
||||
throw sg_range_exception("index out of range", "FlightPlan::legAtIndex");
|
||||
}
|
||||
|
||||
return _legs[index];
|
||||
return _legs.at(index);
|
||||
}
|
||||
|
||||
int FlightPlan::findLegIndex(const Leg *l) const
|
||||
int FlightPlan::findLegIndex(const Leg* l) const
|
||||
{
|
||||
for (unsigned int i=0; i<_legs.size(); ++i) {
|
||||
if (_legs[i] == l) {
|
||||
if (_legs.at(i).get() == l) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
@ -778,8 +756,11 @@ void FlightPlan::saveToProperties(SGPropertyNode* d) const
|
|||
// route nodes
|
||||
SGPropertyNode* routeNode = d->getChild("route", 0, true);
|
||||
for (unsigned int i=0; i<_legs.size(); ++i) {
|
||||
Waypt* wpt = _legs[i]->waypoint();
|
||||
wpt->saveAsNode(routeNode->getChild("wp", i, true));
|
||||
auto leg = _legs.at(i);
|
||||
Waypt* wpt = leg->waypoint();
|
||||
auto legNode = routeNode->getChild("wp", i, true);
|
||||
wpt->saveAsNode(legNode);
|
||||
leg->writeToProperties(legNode);
|
||||
} // of waypoint iteration
|
||||
}
|
||||
|
||||
|
@ -1119,7 +1100,20 @@ void FlightPlan::loadVersion2XMLRoute(SGPropertyNode_ptr routeData)
|
|||
SGPropertyNode_ptr routeNode = routeData->getChild("route", 0);
|
||||
if (routeNode.valid()) {
|
||||
for (auto wpNode : routeNode->getChildren("wp")) {
|
||||
Leg* l = new Leg{this, Waypt::createFromProperties(this, wpNode)};
|
||||
auto wp = Waypt::createFromProperties(this, wpNode);
|
||||
LegRef l = new Leg{this, wp};
|
||||
// sync leg restrictions with waypoint ones
|
||||
if (wp->speedRestriction() != RESTRICT_NONE) {
|
||||
l->setSpeed(wp->speedRestriction(), wp->speed());
|
||||
}
|
||||
|
||||
if (wp->altitudeRestriction() != RESTRICT_NONE) {
|
||||
l->setAltitude(wp->altitudeRestriction(), wp->altitudeFt());
|
||||
}
|
||||
|
||||
if (wpNode->hasChild("hold-count")) {
|
||||
l->setHoldCount(wpNode->getIntValue("hold-count"));
|
||||
}
|
||||
_legs.push_back(l);
|
||||
} // of route iteration
|
||||
}
|
||||
|
@ -1135,7 +1129,7 @@ void FlightPlan::loadVersion1XMLRoute(SGPropertyNode_ptr routeData)
|
|||
SGPropertyNode_ptr routeNode = routeData->getChild("route", 0);
|
||||
for (int i=0; i<routeNode->nChildren(); ++i) {
|
||||
SGPropertyNode_ptr wpNode = routeNode->getChild("wp", i);
|
||||
Leg* l = new Leg(this, parseVersion1XMLWaypt(wpNode));
|
||||
LegRef l = new Leg(this, parseVersion1XMLWaypt(wpNode));
|
||||
_legs.push_back(l);
|
||||
} // of route iteration
|
||||
_waypointsChanged = true;
|
||||
|
@ -1218,7 +1212,7 @@ bool FlightPlan::loadPlainTextFormat(const SGPath& path)
|
|||
throw sg_io_exception("Failed to create waypoint from line '" + line + "'.");
|
||||
}
|
||||
|
||||
_legs.push_back(new Leg(this, w));
|
||||
_legs.push_back(LegRef{new Leg(this, w)});
|
||||
} // of line iteration
|
||||
} catch (sg_exception& e) {
|
||||
SG_LOG(SG_NAVAID, SG_ALERT, "Failed to load route from: '" << path << "'. " << e.getMessage());
|
||||
|
@ -1425,26 +1419,23 @@ void FlightPlan::activate()
|
|||
_currentIndex = 0;
|
||||
_currentWaypointChanged = true;
|
||||
|
||||
for (unsigned int i=0; i < _legs.size(); ) {
|
||||
for (unsigned int i=1; i < _legs.size(); ) {
|
||||
if (_legs[i]->waypoint()->type() == "via") {
|
||||
WayptRef preceeding = _legs[i - 1]->waypoint();
|
||||
Via* via = static_cast<Via*>(_legs[i]->waypoint());
|
||||
WayptVec wps = via->expandToWaypoints(preceeding);
|
||||
|
||||
// delete the VIA leg
|
||||
LegVec::iterator it = _legs.begin();
|
||||
it += i;
|
||||
Leg* l = *it;
|
||||
auto it = _legs.begin() + i;
|
||||
LegRef l = *it;
|
||||
_legs.erase(it);
|
||||
delete l;
|
||||
|
||||
// create new legs and insert
|
||||
it = _legs.begin();
|
||||
it += i;
|
||||
it = _legs.begin() + i;
|
||||
|
||||
LegVec newLegs;
|
||||
for (WayptRef wp : wps) {
|
||||
newLegs.push_back(new Leg(this, wp));
|
||||
newLegs.push_back(LegRef{new Leg(this, wp)});
|
||||
}
|
||||
|
||||
_waypointsChanged = true;
|
||||
|
@ -1630,6 +1621,28 @@ int FlightPlan::Leg::holdCount() const
|
|||
return _holdCount;
|
||||
}
|
||||
|
||||
void FlightPlan::Leg::writeToProperties(SGPropertyNode* aProp) const
|
||||
{
|
||||
if (_speedRestrict != RESTRICT_NONE) {
|
||||
aProp->setStringValue("speed-restrict", restrictionToString(_speedRestrict));
|
||||
if (_speedRestrict == SPEED_RESTRICT_MACH) {
|
||||
aProp->setDoubleValue("speed", speedMach());
|
||||
} else {
|
||||
aProp->setDoubleValue("speed", _speed);
|
||||
}
|
||||
}
|
||||
|
||||
if (_altRestrict != RESTRICT_NONE) {
|
||||
aProp->setStringValue("alt-restrict", restrictionToString(_altRestrict));
|
||||
aProp->setDoubleValue("altitude-ft", _altitudeFt);
|
||||
}
|
||||
|
||||
if (_holdCount > 0) {
|
||||
aProp->setDoubleValue("hold-count", _holdCount);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FlightPlan::rebuildLegData()
|
||||
{
|
||||
_totalDistance = 0.0;
|
||||
|
|
|
@ -80,7 +80,7 @@ public:
|
|||
/**
|
||||
* flight-plan leg encapsulation
|
||||
*/
|
||||
class Leg
|
||||
class Leg : public SGReferenced
|
||||
{
|
||||
public:
|
||||
FlightPlan* owner() const
|
||||
|
@ -138,6 +138,8 @@ public:
|
|||
|
||||
Leg* cloneFor(FlightPlan* owner) const;
|
||||
|
||||
void writeToProperties(SGPropertyNode* node) const;
|
||||
|
||||
const FlightPlan* _parent;
|
||||
RouteRestriction _speedRestrict = RESTRICT_NONE,
|
||||
_altRestrict = RESTRICT_NONE;
|
||||
|
@ -157,6 +159,8 @@ public:
|
|||
/// total distance of this leg from departure point
|
||||
mutable double _distanceAlongPath = 11.0;
|
||||
};
|
||||
|
||||
using LegRef = SGSharedPtr<Leg>;
|
||||
|
||||
class Delegate
|
||||
{
|
||||
|
@ -191,7 +195,7 @@ public:
|
|||
bool _deleteWithPlan = false;
|
||||
};
|
||||
|
||||
Leg* insertWayptAtIndex(Waypt* aWpt, int aIndex);
|
||||
LegRef insertWayptAtIndex(Waypt* aWpt, int aIndex);
|
||||
void insertWayptsAtIndex(const WayptVec& wps, int aIndex);
|
||||
|
||||
void deleteIndex(int index);
|
||||
|
@ -211,15 +215,14 @@ public:
|
|||
|
||||
bool isActive() const;
|
||||
|
||||
Leg* currentLeg() const;
|
||||
Leg* nextLeg() const;
|
||||
Leg* previousLeg() const;
|
||||
LegRef currentLeg() const;
|
||||
LegRef nextLeg() const;
|
||||
LegRef previousLeg() const;
|
||||
|
||||
int numLegs() const
|
||||
{ return static_cast<int>(_legs.size()); }
|
||||
|
||||
Leg* legAtIndex(int index) const;
|
||||
int findLegIndex(const Leg* l) const;
|
||||
LegRef legAtIndex(int index) const;
|
||||
|
||||
int findWayptIndex(const SGGeod& aPos) const;
|
||||
int findWayptIndex(const FGPositionedRef aPos) const;
|
||||
|
@ -373,6 +376,8 @@ public:
|
|||
private:
|
||||
friend class Leg;
|
||||
|
||||
int findLegIndex(const Leg* l) const;
|
||||
|
||||
void lockDelegates();
|
||||
void unlockDelegates();
|
||||
|
||||
|
@ -426,7 +431,7 @@ private:
|
|||
double _totalDistance;
|
||||
void rebuildLegData();
|
||||
|
||||
typedef std::vector<Leg*> LegVec;
|
||||
using LegVec = std::vector<LegRef>;
|
||||
LegVec _legs;
|
||||
|
||||
std::vector<Delegate*> _delegates;
|
||||
|
|
|
@ -175,7 +175,7 @@ static RouteRestriction restrictionFromString(const char* aStr)
|
|||
"Route restrictFromString");
|
||||
}
|
||||
|
||||
static const char* restrictionToString(RouteRestriction aRestrict)
|
||||
const char* restrictionToString(RouteRestriction aRestrict)
|
||||
{
|
||||
switch (aRestrict) {
|
||||
case RESTRICT_AT: return "at";
|
||||
|
|
|
@ -235,7 +235,9 @@ static void wayptGhostDestroy(void* g)
|
|||
|
||||
static void legGhostDestroy(void* g)
|
||||
{
|
||||
// nothing for now
|
||||
FlightPlan::Leg* leg = (FlightPlan::Leg*) g;
|
||||
if (!FlightPlan::Leg::put(leg)) // unref
|
||||
delete leg;
|
||||
}
|
||||
|
||||
|
||||
|
@ -382,6 +384,7 @@ naRef ghostForLeg(naContext c, const FlightPlan::Leg* leg)
|
|||
return naNil();
|
||||
}
|
||||
|
||||
FlightPlan::Leg::get(leg); // take a ref
|
||||
return naNewGhost2(c, &FPLegGhostType, (void*) leg);
|
||||
}
|
||||
|
||||
|
@ -3143,7 +3146,7 @@ static naRef f_leg_setAltitude(naContext c, naRef me, int argc, naRef* args)
|
|||
static naRef f_leg_path(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
FlightPlan::Leg* leg = fpLegGhost(me);
|
||||
if (!leg) {
|
||||
if (!leg || !leg->owner()) {
|
||||
naRuntimeError(c, "leg.setAltitude called on non-flightplan-leg object");
|
||||
}
|
||||
|
||||
|
@ -3165,7 +3168,7 @@ static naRef f_leg_path(naContext c, naRef me, int argc, naRef* args)
|
|||
static naRef f_leg_courseAndDistanceFrom(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
FlightPlan::Leg* leg = fpLegGhost(me);
|
||||
if (!leg) {
|
||||
if (!leg || !leg->owner()) {
|
||||
naRuntimeError(c, "leg.courseAndDistanceFrom called on non-flightplan-leg object");
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ set(TESTSUITE_SOURCES
|
|||
${TESTSUITE_SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_flightplan.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_fpNasal.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_navaids2.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_aircraftPerformance.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_routeManager.cxx
|
||||
|
@ -11,6 +12,7 @@ set(TESTSUITE_SOURCES
|
|||
set(TESTSUITE_HEADERS
|
||||
${TESTSUITE_HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_flightplan.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_fpNasal.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_aircraftPerformance.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_routeManager.hxx
|
||||
PARENT_SCOPE
|
||||
|
|
|
@ -21,9 +21,11 @@
|
|||
#include "test_navaids2.hxx"
|
||||
#include "test_aircraftPerformance.hxx"
|
||||
#include "test_routeManager.hxx"
|
||||
#include "test_fpNasal.hxx"
|
||||
|
||||
// Set up the unit tests.
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(FlightplanTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(FPNasalTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(NavaidsTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(AircraftPerformanceTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(RouteManagerTests, "Unit tests");
|
||||
|
|
|
@ -25,12 +25,9 @@ void FlightplanTests::setUp()
|
|||
FGTestApi::setUp::initTestGlobals("flightplan");
|
||||
FGTestApi::setUp::initNavDataCache();
|
||||
|
||||
globals->get_subsystem_mgr()->init();
|
||||
|
||||
FGTestApi::setUp::initStandardNasal();
|
||||
|
||||
globals->get_subsystem_mgr()->postinit();
|
||||
globals->get_subsystem_mgr()->bind();
|
||||
globals->get_subsystem_mgr()->init();
|
||||
globals->get_subsystem_mgr()->postinit();
|
||||
}
|
||||
|
||||
|
||||
|
@ -61,7 +58,7 @@ void FlightplanTests::testBasic()
|
|||
CPPUNIT_ASSERT(fp1->destinationAirport()->ident() == "EHAM");
|
||||
CPPUNIT_ASSERT(fp1->destinationRunway()->ident() == "24");
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(fp1->numLegs(), 5);
|
||||
CPPUNIT_ASSERT_EQUAL(5, fp1->numLegs());
|
||||
|
||||
CPPUNIT_ASSERT(fp1->legAtIndex(0)->waypoint()->source()->ident() == "23L");
|
||||
|
||||
|
@ -328,15 +325,72 @@ void FlightplanTests::testBug1814()
|
|||
CPPUNIT_ASSERT_DOUBLES_EQUAL(101, f->legAtIndex(2)->distanceNm(), 0.5);
|
||||
}
|
||||
|
||||
void FlightplanTests::testSegfaultWaypointGhost() {
|
||||
// checking for a segfault here, no segfault indicates success. A runtime error in the log is acceptable here.
|
||||
bool ok = FGTestApi::executeNasal(R"(
|
||||
var fp = createFlightplan();
|
||||
fp.departure = airportinfo("BIKF");
|
||||
fp.destination = airportinfo("EGLL");
|
||||
var wp = fp.getWP(1);
|
||||
fp.deleteWP(1);
|
||||
print(wp.wp_name);
|
||||
)");
|
||||
void FlightplanTests::testLoadSaveMachRestriction()
|
||||
{
|
||||
const std::string fpXML = R"(<?xml version="1.0" encoding="UTF-8"?>
|
||||
<PropertyList>
|
||||
<version type="int">2</version>
|
||||
<departure>
|
||||
<airport type="string">SAWG</airport>
|
||||
<runway type="string">25</runway>
|
||||
</departure>
|
||||
<destination>
|
||||
<airport type="string">SUMU</airport>
|
||||
</destination>
|
||||
<route>
|
||||
<wp n="0">
|
||||
<type type="string">navaid</type>
|
||||
<ident type="string">PUGLI</ident>
|
||||
<lon type="double">-60.552200</lon>
|
||||
<lat type="double">-40.490000</lat>
|
||||
</wp>
|
||||
<wp n="1">
|
||||
<type type="string">basic</type>
|
||||
<alt-restrict type="string">at</alt-restrict>
|
||||
<altitude-ft type="double">36000</altitude-ft>
|
||||
<speed-restrict type="string">mach</speed-restrict>
|
||||
<speed type="double">1.24</speed>
|
||||
<ident type="string">SV002</ident>
|
||||
<lon type="double">-115.50531</lon>
|
||||
<lat type="double">37.89523</lat>
|
||||
</wp>
|
||||
<wp n="2">
|
||||
<type type="string">navaid</type>
|
||||
<ident type="string">SIGUL</ident>
|
||||
<lon type="double">-60.552200</lon>
|
||||
<lat type="double">-40.490000</lat>
|
||||
</wp>
|
||||
</route>
|
||||
</PropertyList>
|
||||
)";
|
||||
|
||||
std::istringstream stream(fpXML);
|
||||
FlightPlanRef f = new FlightPlan;
|
||||
bool ok = f->load(stream);
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
auto leg = f->legAtIndex(1);
|
||||
CPPUNIT_ASSERT_EQUAL(SPEED_RESTRICT_MACH, leg->speedRestriction());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.24, leg->speedMach(), 0.01);
|
||||
|
||||
auto firstLeg = f->legAtIndex(0);
|
||||
firstLeg->setSpeed(SPEED_RESTRICT_MACH, 1.56);
|
||||
|
||||
// upgrade to a hold and set the count
|
||||
f->legAtIndex(2)->setHoldCount(8);
|
||||
|
||||
// round trip through XML to check :)
|
||||
std::ostringstream ss;
|
||||
f->save(ss);
|
||||
|
||||
std::istringstream iss(ss.str());
|
||||
FlightPlanRef f2 = new FlightPlan;
|
||||
ok = f2->load(iss);
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
auto leg3 = f2->legAtIndex(0);
|
||||
CPPUNIT_ASSERT_EQUAL(SPEED_RESTRICT_MACH, leg3->speedRestriction());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.56, leg3->speedMach(), 0.01);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(8, f2->legAtIndex(2)->holdCount());
|
||||
}
|
||||
|
|
|
@ -38,10 +38,10 @@ class FlightplanTests : public CppUnit::TestFixture
|
|||
CPPUNIT_TEST(testBasicAirways);
|
||||
CPPUNIT_TEST(testAirwayNetworkRoute);
|
||||
CPPUNIT_TEST(testBug1814);
|
||||
CPPUNIT_TEST(testSegfaultWaypointGhost);
|
||||
CPPUNIT_TEST(testRoutPathWpt0Midflight);
|
||||
CPPUNIT_TEST(testRoutePathVec);
|
||||
|
||||
CPPUNIT_TEST(testLoadSaveMachRestriction);
|
||||
|
||||
// CPPUNIT_TEST(testParseICAORoute);
|
||||
// CPPUNIT_TEST(testParseICANLowLevelRoute);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
@ -63,9 +63,9 @@ public:
|
|||
void testParseICAORoute();
|
||||
void testParseICANLowLevelRoute();
|
||||
void testBug1814();
|
||||
void testSegfaultWaypointGhost();
|
||||
void testRoutPathWpt0Midflight();
|
||||
void testRoutePathVec();
|
||||
void testLoadSaveMachRestriction();
|
||||
};
|
||||
|
||||
#endif // FG_FLIGHTPLAN_UNIT_TESTS_HXX
|
||||
|
|
105
test_suite/unit_tests/Navaids/test_fpNasal.cxx
Normal file
105
test_suite/unit_tests/Navaids/test_fpNasal.cxx
Normal file
|
@ -0,0 +1,105 @@
|
|||
#include "test_fpNasal.hxx"
|
||||
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
#include "test_suite/FGTestApi/NavDataCache.hxx"
|
||||
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
|
||||
#include <Navaids/FlightPlan.hxx>
|
||||
#include <Navaids/routePath.hxx>
|
||||
#include <Navaids/NavDataCache.hxx>
|
||||
#include <Navaids/waypoint.hxx>
|
||||
#include <Navaids/navlist.hxx>
|
||||
#include <Navaids/navrecord.hxx>
|
||||
#include <Navaids/airways.hxx>
|
||||
#include <Navaids/fix.hxx>
|
||||
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Autopilot/route_mgr.hxx>
|
||||
|
||||
using namespace flightgear;
|
||||
|
||||
|
||||
// Set up function for each test.
|
||||
void FPNasalTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("flightplan");
|
||||
FGTestApi::setUp::initNavDataCache();
|
||||
|
||||
// flightplan() acces needs the route manager
|
||||
globals->add_new_subsystem<FGRouteMgr>();
|
||||
|
||||
globals->get_subsystem_mgr()->bind();
|
||||
globals->get_subsystem_mgr()->init();
|
||||
|
||||
FGTestApi::setUp::initStandardNasal();
|
||||
globals->get_subsystem_mgr()->postinit();
|
||||
}
|
||||
|
||||
|
||||
// Clean up after each test.
|
||||
void FPNasalTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
static FlightPlanRef makeTestFP(const std::string& depICAO, const std::string& depRunway,
|
||||
const std::string& destICAO, const std::string& destRunway,
|
||||
const std::string& waypoints)
|
||||
{
|
||||
FlightPlanRef f = new FlightPlan;
|
||||
FGTestApi::setUp::populateFPWithNasal(f, depICAO, depRunway, destICAO, destRunway, waypoints);
|
||||
return f;
|
||||
}
|
||||
|
||||
void FPNasalTests::testBasic()
|
||||
{
|
||||
|
||||
FlightPlanRef fp1 = makeTestFP("EGCC", "23L", "EHAM", "24",
|
||||
"TNT CLN");
|
||||
fp1->setIdent("testplan");
|
||||
|
||||
// setup the FP on the route-manager, so flightplan() call works
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
rm->setFlightPlan(fp1);
|
||||
rm->activate();
|
||||
|
||||
// modify leg data dfrom Nasal
|
||||
bool ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan(); # retrieve the global flightplan
|
||||
var leg = fp.getWP(3);
|
||||
leg.setAltitude(6000, 'AT');
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
// check the value updated in the leg
|
||||
CPPUNIT_ASSERT_EQUAL(RESTRICT_AT, fp1->legAtIndex(3)->altitudeRestriction());
|
||||
CPPUNIT_ASSERT_EQUAL(6000, fp1->legAtIndex(3)->altitudeFt());
|
||||
|
||||
// insert some waypoints from Nasal
|
||||
|
||||
ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan();
|
||||
var leg = fp.getWP(2);
|
||||
var newWP = createWPFrom(navinfo(leg.lat, leg.lon, 'COA')[0]);
|
||||
fp.insertWPAfter(newWP, 2);
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(string{"COSTA VOR-DME"}, fp1->legAtIndex(3)->waypoint()->source()->name());
|
||||
}
|
||||
|
||||
|
||||
void FPNasalTests::testSegfaultWaypointGhost()
|
||||
{
|
||||
// checking for a segfault here, no segfault indicates success. A runtime error in the log is acceptable here.
|
||||
bool ok = FGTestApi::executeNasal(R"(
|
||||
var fp = createFlightplan();
|
||||
fp.departure = airportinfo("BIKF");
|
||||
fp.destination = airportinfo("EGLL");
|
||||
var wp = fp.getWP(1);
|
||||
fp.deleteWP(1);
|
||||
print(wp.wp_name);
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
}
|
46
test_suite/unit_tests/Navaids/test_fpNasal.hxx
Normal file
46
test_suite/unit_tests/Navaids/test_fpNasal.hxx
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* This file is part of the program FlightGear.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
|
||||
// The flight plan unit tests.
|
||||
class FPNasalTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(FPNasalTests);
|
||||
CPPUNIT_TEST(testBasic);
|
||||
CPPUNIT_TEST(testSegfaultWaypointGhost);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testBasic();
|
||||
void testSegfaultWaypointGhost();
|
||||
};
|
Loading…
Add table
Reference in a new issue