first commit

This commit is contained in:
Your Name
2022-10-20 20:29:11 +08:00
commit 4d531f8044
3238 changed files with 1387862 additions and 0 deletions

173
test_suite/CMakeLists.txt Normal file
View File

@@ -0,0 +1,173 @@
# CMake module includes.
include(SetupFGFSEmbeddedResources)
include(SetupFGFSIncludes)
include(SetupFGFSLibraries)
include(SetupMSVCGrouping)
# Add each test suite category.
foreach(test_category
gui_tests
simgear_tests
system_tests
unit_tests
fgdata_tests
)
add_subdirectory(${test_category})
endforeach(test_category)
# Add the testing API.
add_subdirectory(FGTestApi)
# Add all test suite sources and headers.
set(TESTSUITE_SOURCES
${TESTSUITE_SOURCES}
bootstrap.cxx
dataStore.cxx
fgCompilerOutputter.cxx
fgTestListener.cxx
fgTestRunner.cxx
formatting.cxx
logging.cxx
testSuite.cxx
)
set(TESTSUITE_HEADERS
${TESTSUITE_HEADERS}
dataStore.hxx
fgCompilerOutputter.hxx
fgTestListener.hxx
fgTestRunner.hxx
formatting.hxx
logging.hxx
)
# The test suite output directory.
set(TESTSUITE_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}")
# Get the CppUnit sources and headers to be built into the test suite.
if (NOT SYSTEM_CPPUNIT)
message(STATUS "CppUnit: Building the FlightGear supplied CppUnit library")
get_property(CPPUNIT_SOURCES GLOBAL PROPERTY CPPUNIT_SOURCES)
get_property(CPPUNIT_HEADERS GLOBAL PROPERTY CPPUNIT_HEADERS)
add_library(CppUnitLib STATIC ${CPPUNIT_SOURCES} ${CPPUNIT_HEADERS})
target_include_directories(CppUnitLib PUBLIC "${PROJECT_SOURCE_DIR}/3rdparty/cppunit/include")
set(CPPUNIT_LIBRARIES CppUnitLib)
else()
message(STATUS "CppUnit: Linking to the system supplied CppUnit library")
endif()
#-----------------------------------------------------------------------------
# Set up all test suites as CTests.
# Activation.
include(Dart)
# System test suites.
add_test(AeroMeshSystemTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -s AeroMeshTests)
#add_test(GPSSystemTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -s GPSTests)
#add_test(NavaidsSystemTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -s NavaidsTests)
# Unit test suites.
add_test(AddonManagementUnitTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -u AddonManagementTests)
add_test(AeroElementUnitTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -u AeroElementTests)
add_test(AircraftPerformanceUnitTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -u AircraftPerformanceTests)
add_test(AutosaveMigrationUnitTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -u AutosaveMigrationTests)
add_test(FlightplanUnitTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -u FlightplanTests)
add_test(FPNasalUnitTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -u FPNasalTests)
add_test(GPSUnitTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -u GPSTests)
add_test(HoldControllerUnitTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -u HoldControllerTests)
if(ENABLE_HID_INPUT)
add_test(HIDInputUnitTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -u HIDInputTests)
endif()
add_test(LaRCSimMatrixUnitTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -u LaRCSimMatrixTests)
add_test(MktimeUnitTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -u MktimeTests)
add_test(NasalSysUnitTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -u NasalSysTests)
add_test(NavaidsUnitTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -u NavaidsTests)
add_test(NavRadioUnitTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -u NavRadioTests)
add_test(PosInitUnitTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -u PosInitTests)
add_test(RNAVProcedureUnitTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -u RNAVProcedureTests)
add_test(RouteManagerUnitTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -u RouteManagerTests)
add_test(YASimAtmosphereUnitTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -u YASimAtmosphereTests)
# GUI test suites.
# Simgear unit test suites.
add_test(CanvasSimgearUnitTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -m CanvasTests)
add_test(MathGeodesySimgearUnitTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -m MathGeodesyTests)
add_test(SimgearPropsSimgearUnitTests ${TESTSUITE_OUTPUT_DIR}/fgfs_test_suite --ctest -m SimgearPropsTests)
# FGData test suites.
#-----------------------------------------------------------------------------
# Set up the binary.
# Set up the embedded resources.
setup_fgfs_embedded_resources()
# Sort the sources and headers for MSVC.
setup_msvc_grouping()
# most object files come from fgfsObjects target: this property
# contains all the sources which need to be compiled seperately
# for the test-suite, becuase they depend on BUILDING_TESTSUTIE define
# we setup below via target_compile_definitions
get_property(TEST_SOURCES GLOBAL PROPERTY FG_TEST_SOURCES)
# Set up the separate executable for running the test suite.
add_executable(fgfs_test_suite
${TEST_SOURCES}
${TESTSUITE_SOURCES}
${TESTSUITE_HEADERS}
$<TARGET_OBJECTS:fgfsObjects>
)
add_dependencies(fgfs_test_suite buildId)
set_target_properties(fgfs_test_suite
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${TESTSUITE_OUTPUT_DIR}"
)
if(ENABLE_AUTOTESTING)
set(TEST_SUITE_COMMAND "fgfs_test_suite")
set(TEST_SUITE_COMMENT "Running the full FlightGear test suite")
else()
set(TEST_SUITE_COMMENT "Building the FlightGear test suite.")
endif(ENABLE_AUTOTESTING)
# Set up the target links.
setup_fgfs_libraries(fgfs_test_suite)
# Additional search paths for includes.
setup_fgfs_includes(fgfs_test_suite)
# test codce includes its headers as test_suite/foo.h, so we need this
target_include_directories(fgfs_test_suite PUBLIC ${PROJECT_SOURCE_DIR})
target_compile_definitions(fgfs_test_suite PUBLIC BUILDING_TESTSUITE)
# Additional libraries just for the test suite.
target_link_libraries(fgfs_test_suite ${CPPUNIT_LIBRARIES})
# target to run the tests
if (MSVC)
file(TO_NATIVE_PATH "${FG_QT_BIN_DIR}" _qt5_bin_dir_native)
file(TO_NATIVE_PATH "${FINAL_MSVC_3RDPARTY_DIR}/bin" _msvc_3rdparty_bin_dir)
file(TO_NATIVE_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}" _install_bin_dir)
set(CMAKE_MSVCIDE_RUN_PATH "${_install_bin_dir};${_msvc_3rdparty_bin_dir};${_qt5_bin_dir_native}")
endif()
if (FG_QT_ROOT_DIR)
# for QtPlatformHeaders
target_include_directories(fgfs_test_suite PRIVATE ${FG_QT_ROOT_DIR}/include)
endif()
add_custom_target(test_suite
${TEST_SUITE_COMMAND}
DEPENDS fgfs_test_suite
COMMENT ${TEST_SUITE_COMMENT}
)

View File

@@ -0,0 +1,24 @@
set(TESTSUITE_SOURCES
${TESTSUITE_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/testGlobals.cxx
${CMAKE_CURRENT_SOURCE_DIR}/NavDataCache.cxx
${CMAKE_CURRENT_SOURCE_DIR}/PrivateAccessorFDM.cxx
${CMAKE_CURRENT_SOURCE_DIR}/scene_graph.cxx
${CMAKE_CURRENT_SOURCE_DIR}/TestPilot.cxx
${CMAKE_CURRENT_SOURCE_DIR}/NasalUnitTesting_TestSuite.cxx
${CMAKE_CURRENT_SOURCE_DIR}/TestDataLogger.cxx
${CMAKE_CURRENT_SOURCE_DIR}/testApis.cxx
PARENT_SCOPE
)
set(TESTSUITE_HEADERS
${TESTSUITE_HEADERS}
${CMAKE_CURRENT_SOURCE_DIR}/testGlobals.hxx
${CMAKE_CURRENT_SOURCE_DIR}/NavDataCache.hxx
${CMAKE_CURRENT_SOURCE_DIR}/PrivateAccessorFDM.hxx
${CMAKE_CURRENT_SOURCE_DIR}/scene_graph.hxx
${CMAKE_CURRENT_SOURCE_DIR}/TestPilot.hxx
${CMAKE_CURRENT_SOURCE_DIR}/TestDataLogger.hxx
PARENT_SCOPE
)

View File

@@ -0,0 +1,131 @@
// Unit-test API for nasal
//
// There are two versions of this module, and we load one or the other
// depending on if we're running the test_suite (using CppUnit) or
// the normal simulator. The logic is that aircraft-developers and
// people hacking Nasal likely don't have a way to run the test-suite,
// whereas core-developers and Jenksin want a way to run all tests
// through the standard CppUnit mechanim. So we have a consistent
// Nasal API, but different implement in fgfs_test_suite vs
// normal fgfs executable.
//
// Copyright (C) 2020 James Turner
//
// 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 "config.h"
#include <Main/globals.hxx>
#include <Main/util.hxx>
#include <Scripting/NasalSys.hxx>
#include <Scripting/NasalSys_private.hxx>
#include <cppunit/TestAssert.h>
#include <simgear/nasal/cppbind/from_nasal.hxx>
#include <simgear/nasal/cppbind/to_nasal.hxx>
#include <simgear/nasal/cppbind/NasalHash.hxx>
#include <simgear/nasal/cppbind/Ghost.hxx>
static CppUnit::SourceLine nasalSourceLine(const nasal::CallContext& ctx)
{
const string fileName = ctx.from_nasal<string>(naGetSourceFile(ctx.c_ctx(), 0));
const int lineNumber = naGetLine(ctx.c_ctx(), 0);
return CppUnit::SourceLine(fileName, lineNumber);
}
static naRef f_assert(const nasal::CallContext& ctx )
{
bool pass = ctx.requireArg<bool>(0);
auto msg = ctx.getArg<string>(1, "assert failed:");
CppUnit::Asserter::failIf(!pass, "assertion failed:" + msg, nasalSourceLine(ctx));
return naNil();
}
static naRef f_fail(const nasal::CallContext& ctx )
{
auto msg = ctx.getArg<string>(0);
CppUnit::Asserter::fail("assertion failed:" + msg,
nasalSourceLine(ctx));
return naNil();
}
static naRef f_assert_equal(const nasal::CallContext& ctx )
{
naRef argA = ctx.requireArg<naRef>(0);
naRef argB = ctx.requireArg<naRef>(1);
auto msg = ctx.getArg<string>(2, "assert_equal failed");
bool same = nasalStructEqual(ctx.c_ctx(), argA, argB);
if (!same) {
string aStr = ctx.from_nasal<string>(argA);
string bStr = ctx.from_nasal<string>(argB);
msg += "; expected:" + aStr + ", actual:" + bStr;
CppUnit::Asserter::fail(msg, nasalSourceLine(ctx));
}
return naNil();
}
static naRef f_equal(const nasal::CallContext& ctx)
{
naRef argA = ctx.requireArg<naRef>(0);
naRef argB = ctx.requireArg<naRef>(1);
bool same = nasalStructEqual(ctx.c_ctx(), argA, argB);
return naNum(same);
}
static naRef f_assert_doubles_equal(const nasal::CallContext& ctx )
{
double argA = ctx.requireArg<double>(0);
double argB = ctx.requireArg<double>(1);
double tolerance = ctx.requireArg<double>(2);
auto msg = ctx.getArg<string>(3, "assert_doubles_equal failed");
const bool same = fabs(argA - argB) < tolerance;
if (!same) {
msg += "; expected:" + std::to_string(argA) + ", actual:" + std::to_string(argB);
CppUnit::Asserter::fail(msg, nasalSourceLine(ctx));
}
return naNil();
}
//------------------------------------------------------------------------------
naRef initNasalUnitTestCppUnit(naRef nasalGlobals, naContext c)
{
nasal::Hash globals_module(nasalGlobals, c),
unitTest = globals_module.createHash("unitTest");
unitTest.set("assert", f_assert);
unitTest.set("fail", f_fail);
unitTest.set("assert_equal", f_assert_equal);
unitTest.set("equal", f_equal);
unitTest.set("assert_doubles_equal", f_assert_doubles_equal);
return naNil();
}
void shutdownNasalUnitTestInSim()
{
// stub
}

View File

@@ -0,0 +1,48 @@
/*
* 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/>.
*/
#include "NavDataCache.hxx"
#include <Navaids/NavDataCache.hxx>
namespace FGTestApi {
namespace setUp {
void initNavDataCache()
{
if (flightgear::NavDataCache::instance())
return;
flightgear::NavDataCache* cache = flightgear::NavDataCache::createInstance();
if (cache->isRebuildRequired()) {
std::cerr << "Navcache rebuild for testing" << std::flush;
while (cache->rebuild() != flightgear::NavDataCache::REBUILD_DONE) {
SGTimeStamp::sleepForMSec(1000);
std::cerr << "." << std::flush;
}
}
}
} // End of namespace setUp.
} // End of namespace FGTestApi.

View File

@@ -0,0 +1,35 @@
/*
* 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/>.
*/
#ifndef FG_NAV_DATA_CACHE_TEST_HELPERS_HXX
#define FG_NAV_DATA_CACHE_TEST_HELPERS_HXX
namespace FGTestApi {
namespace setUp {
void initNavDataCache();
} // End of namespace setUp.
} // End of namespace FGTestApi.
#endif // of FG_NAV_DATA_CACHE_TEST_HELPERS_HXX

View File

@@ -0,0 +1,63 @@
#include "PrivateAccessorFDM.hxx"
#include <FDM/AIWake/AIWakeGroup.hxx>
#include <FDM/AIWake/AircraftMesh.hxx>
#include <FDM/AIWake/WakeMesh.hxx>
#include <FDM/YASim/Atmosphere.hpp>
// Access variables from src/FDM/AIWake/AIWakeGroup.hxx.
WakeMesh*
FGTestApi::PrivateAccessor::FDM::Accessor::read_FDM_AIWake_AIWakeGroup_aiWakeData(AIWakeGroup* instance, int i)
{
return instance->_aiWakeData[i].mesh;
}
// Access variables from src/FDM/AIWake/AircraftMesh.hxx.
const std::vector<SGVec3d>
FGTestApi::PrivateAccessor::FDM::Accessor::read_FDM_AIWake_AircraftMesh_collPt(AircraftMesh* instance) const
{
return instance->collPt;
}
const std::vector<SGVec3d>
FGTestApi::PrivateAccessor::FDM::Accessor::read_FDM_AIWake_AircraftMesh_midPt(AircraftMesh* instance) const
{
return instance->midPt;
}
// Access variables from src/FDM/AIWake/WakeMesh.hxx.
const std::vector<AeroElement_ptr>
FGTestApi::PrivateAccessor::FDM::Accessor::read_FDM_AIWake_WakeMesh_elements(WakeMesh* instance) const
{
return instance->elements;
}
int
FGTestApi::PrivateAccessor::FDM::Accessor::read_FDM_AIWake_WakeMesh_nelm(WakeMesh* instance) const
{
return instance->nelm;
}
double **
FGTestApi::PrivateAccessor::FDM::Accessor::read_FDM_AIWake_WakeMesh_Gamma(WakeMesh* instance) const
{
return instance->Gamma;
}
// Access variables from src/FDM/YASim/Atmosphere.hxx.
float
FGTestApi::PrivateAccessor::FDM::Accessor::read_FDM_YASim_Atmosphere_numColumns(std::unique_ptr<yasim::Atmosphere> &instance) const
{
return instance->numColumns;
}
float
FGTestApi::PrivateAccessor::FDM::Accessor::read_FDM_YASim_Atmosphere_data(std::unique_ptr<yasim::Atmosphere> &instance, int i, int j) const
{
return instance->data[i][j];
}

View File

@@ -0,0 +1,53 @@
#ifndef _FG_PRIVATE_ACCESSOR_FDM_HXX
#define _FG_PRIVATE_ACCESSOR_FDM_HXX
#include <map>
#include <memory>
#include <vector>
#include <simgear/math/SGMath.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
// Forward declarations for src/FDM/AIWake.
class AIWakeData;
class AIWakeGroup;
class AircraftMesh;
class WakeMesh;
class AeroElement;
typedef SGSharedPtr<AeroElement> AeroElement_ptr;
// Forward declaration for: src/FDM/YASim.
namespace yasim {
class Atmosphere;
}
namespace FGTestApi {
namespace PrivateAccessor {
namespace FDM {
class Accessor
{
public:
// Access variables from src/FDM/AIWake/AIWakeGroup.hxx.
WakeMesh* read_FDM_AIWake_AIWakeGroup_aiWakeData(AIWakeGroup* instance, int i);
// Access variables from src/FDM/AIWake/AircraftMesh.hxx.
const std::vector<SGVec3d> read_FDM_AIWake_AircraftMesh_collPt(AircraftMesh* instance) const;
const std::vector<SGVec3d> read_FDM_AIWake_AircraftMesh_midPt(AircraftMesh* instance) const;
// Access variables from src/FDM/AIWake/WakeMesh.hxx.
const std::vector<AeroElement_ptr> read_FDM_AIWake_WakeMesh_elements(WakeMesh* instance) const;
int read_FDM_AIWake_WakeMesh_nelm(WakeMesh* instance) const;
double **read_FDM_AIWake_WakeMesh_Gamma(WakeMesh* instance) const;
// Access variables from src/FDM/YASim/Atmosphere.hxx.
float read_FDM_YASim_Atmosphere_numColumns(std::unique_ptr<yasim::Atmosphere> &instance) const;
float read_FDM_YASim_Atmosphere_data(std::unique_ptr<yasim::Atmosphere> &instance, int i, int j) const;
};
} // End of namespace FDM.
} // End of namespace PrivateAccessor.
} // End of namespace FGTestApi.
#endif // _FG_PRIVATE_ACCESSOR_FDM_HXX

View File

@@ -0,0 +1,170 @@
/*
* Copyright (C) 2020 James Turner
*
* 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/>.
*/
#include "TestDataLogger.hxx"
#include <algorithm>
#include <cmath>
#include <limits>
#include <vector>
#include <simgear/io/iostreams/sgstream.hxx>
#include "Main/globals.hxx"
namespace FGTestApi {
using DoubleVec = std::vector<double>;
static std::unique_ptr<DataLogger> static_instance;
class DataLogger::DataLoggerPrivate
{
public:
sg_ofstream _stream;
struct SampleInfo {
int column;
std::string name;
// range / units info, later
SGPropertyNode_ptr property;
};
double _currentTimeBase;
std::vector<SampleInfo> _samples;
DoubleVec _openRow;
bool _didHeader = false;
void writeCurrentRow()
{
if (!_didHeader) {
writeHeader();
_didHeader = true;
}
// capture property values into the open row data
std::for_each(_samples.begin(), _samples.end(), [this](const SampleInfo& info) {
if (info.property) {
_openRow[info.column] = info.property->getDoubleValue();
}
});
// write time base
_stream << globals->get_sim_time_sec() << ",";
for (const auto v : _openRow) {
if (std::isnan(v)) {
_stream << ","; // skip this data point
} else {
_stream << v << ",";
}
}
_stream << "\n";
std::fill(_openRow.begin(), _openRow.end(), std::numeric_limits<double>::quiet_NaN());
}
void writeHeader()
{
_stream << "sim-time, ";
std::for_each(_samples.begin(), _samples.end(), [this](const SampleInfo& info) {
_stream << info.name << ", ";
});
_stream << "\n";
}
};
DataLogger::DataLogger()
{
d.reset(new DataLoggerPrivate);
}
DataLogger::~DataLogger()
{
d->_stream.close();
}
bool DataLogger::isActive()
{
return static_instance != nullptr;
}
DataLogger* DataLogger::instance()
{
if (!static_instance) {
static_instance.reset(new DataLogger);
}
return static_instance.get();
}
void DataLogger::initTest(const std::string& testName)
{
d->_stream = sg_ofstream(testName + "_trace.csv");
}
void DataLogger::tearDown()
{
if (static_instance) {
static_instance.reset();
}
}
void DataLogger::writeRecord()
{
d->writeCurrentRow();
}
void DataLogger::recordProperty(const std::string& name, SGPropertyNode_ptr prop)
{
int index = static_cast<int>(d->_samples.size());
DataLoggerPrivate::SampleInfo info{index, name, prop};
d->_samples.push_back(info);
if (d->_openRow.size() <= index) {
d->_openRow.resize(index + 1, std::numeric_limits<double>::quiet_NaN());
}
}
void DataLogger::recordSamplePoint(const std::string& name, double value)
{
auto it = std::find_if(d->_samples.begin(), d->_samples.end(), [&name](const DataLoggerPrivate::SampleInfo& sample) {
return name == sample.name;
});
int index = 0;
if (it == d->_samples.end()) {
index = static_cast<int>(d->_samples.size());
DataLoggerPrivate::SampleInfo info{index, name};
d->_samples.push_back(info);
} else {
index = it->column;
}
// grow _openRow as required
if (d->_openRow.size() <= index) {
d->_openRow.resize(index + 1, std::numeric_limits<double>::quiet_NaN());
}
d->_openRow[index] = value;
}
} // namespace FGTestApi

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2020 James Turner
*
* 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 <memory>
#include <string>
#include <simgear/props/props.hxx>
namespace FGTestApi {
class DataLogger
{
public:
~DataLogger();
static DataLogger* instance();
static bool isActive();
void initTest(const std::string& testName);
void recordProperty(const std::string& name, SGPropertyNode_ptr prop);
void setUp();
void tearDown();
void recordSamplePoint(const std::string& name, double value);
void writeRecord();
private:
DataLogger();
class DataLoggerPrivate;
std::unique_ptr<DataLoggerPrivate> d;
};
} // namespace FGTestApi

View File

@@ -0,0 +1,282 @@
/*
* Copyright (C) 2019 James Turner
*
* 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/>.
*/
#include "TestPilot.hxx"
#include <algorithm>
#include <simgear/math/SGGeodesy.hxx>
#include <simgear/props/props.hxx>
#include <simgear/math/SGGeod.hxx>
#include <Aircraft/AircraftPerformance.hxx> // for formulae
#include <Main/globals.hxx>
#include "TestDataLogger.hxx"
namespace FGTestApi {
TestPilot::TestPilot(SGPropertyNode_ptr props) :
_propRoot(props)
{
if (!_propRoot) {
// use default properties
_propRoot = globals->get_props();
}
_latProp = _propRoot->getNode("position/latitude-deg", true);
_lonProp = _propRoot->getNode("position/longitude-deg", true);
_altitudeProp = _propRoot->getNode("position/altitude-ft", true);
_headingProp = _propRoot->getNode("orientation/heading-deg", true);
_speedKnotsProp = _propRoot->getNode("velocities/airspeed-kt", true);
_speedMachProp = _propRoot->getNode("velocities/mach", true);
_groundspeedKnotsProp = _propRoot->getNode("velocities/groundspeed-kt", true);
_verticalFPMProp = _propRoot->getNode("velocities/vertical-fpm", true);
globals->add_subsystem("flight", this, SGSubsystemMgr::FDM);
}
TestPilot::~TestPilot()
{
}
void TestPilot::resetAtPosition(const SGGeod& pos)
{
_turnActive = false;
setPosition(pos);
}
void TestPilot::init()
{
_vspeedFPM = 1200;
}
void TestPilot::update(double dt)
{
updateValues(dt);
}
void TestPilot::setSpeedKts(double knots)
{
_speedKnots = knots;
}
void TestPilot::setVerticalFPM(double fpm)
{
_vspeedFPM = fpm;
}
void TestPilot::setCourseTrue(double deg)
{
_trueCourseDeg = deg;
}
void TestPilot::turnToCourse(double deg)
{
_turnActive = true;
_targetCourseDeg = deg;
}
void TestPilot::flyHeading(double hdg)
{
_lateralMode = LateralMode::Heading;
_turnActive = true;
_targetCourseDeg = hdg;
}
void TestPilot::flyGPSCourse(GPS *gps)
{
_gps = gps;
_gpsNode = globals->get_props()->getNode("instrumentation/gps");
_gpsLegCourse = _gpsNode->getNode("wp/leg-true-course-deg", true);
_courseErrorNm = _gpsNode->getNode("wp/wp[1]/course-error-nm", true);
_lateralMode = LateralMode::GPSCourse;
_turnActive = false;
}
void TestPilot::flyGPSCourseOffset(GPS *gps, double offsetNm)
{
_gps = gps;
_gpsNode = globals->get_props()->getNode("instrumentation/gps");
_gpsLegCourse = _gpsNode->getNode("wp/leg-true-course-deg", true);
_courseErrorNm = _gpsNode->getNode("wp/wp[1]/course-error-nm", true);
_lateralMode = LateralMode::GPSOffset;
_courseOffsetNm = offsetNm;
_turnActive = false;
}
void TestPilot::flyDirectTo(const SGGeod& target)
{
_lateralMode = LateralMode::Direct;
_targetPos = target;
}
void TestPilot::updateValues(double dt)
{
auto dl = DataLogger::instance();
if (_gps && (_lateralMode == LateralMode::GPSCourse)) {
_targetCourseDeg = _gpsLegCourse->getDoubleValue();
// set how aggressively we try to correct our course
double courseCorrectionFactor = 64.0;
double crossTrack = _courseErrorNm->getDoubleValue();
dl->recordSamplePoint("TP-error-nm", crossTrack);
SG_CLAMP_RANGE(crossTrack, -2.0, 2.0); // clamp to 2nm deviation
double correction = courseCorrectionFactor * crossTrack;
const double maxCorrectionAngle = 45;
dl->recordSamplePoint("TP-base-correction-deg", correction);
// within 1nm of the desired course, start to bias
// based on heading error. This is to reduce overshooting
// while still keeping the responsiveness high
if (fabs(crossTrack) < 1.0) {
// compensate for heading
double headingError = _targetCourseDeg - _trueCourseDeg;
SG_NORMALIZE_RANGE(headingError, -180.0, 180.0);
if (fabs(headingError) > 90.0) {
// we're pointing the wrong way, don't compensate
// otherwise we get into knots trying to make the
// turn back the right way
} else {
const double p = 1.0 - fabs(crossTrack);
const double headingErrorFactor = 0.6;
correction += p * headingError * headingErrorFactor;
}
}
dl->recordSamplePoint("TP-correction-deg", correction);
SG_CLAMP_RANGE(correction, -maxCorrectionAngle, maxCorrectionAngle);
_targetCourseDeg += correction;
dl->recordSamplePoint("TP-target-deg", _targetCourseDeg);
SG_NORMALIZE_RANGE(_targetCourseDeg, 0.0, 360.0);
if (!_turnActive &&(fabs(_trueCourseDeg - _targetCourseDeg) > 0.5)) {
_turnActive = true;
}
}
if (_gps && (_lateralMode == LateralMode::GPSOffset)) {
_targetCourseDeg = _gpsLegCourse->getDoubleValue();
double crossTrack = _courseErrorNm->getDoubleValue();
double offsetError = crossTrack - _courseOffsetNm;
const double offsetCorrectionFactor = 25.0;
const double correction = offsetError * offsetCorrectionFactor;
_targetCourseDeg += correction;
SG_NORMALIZE_RANGE(_targetCourseDeg, 0.0, 360.0);
if (!_turnActive &&(fabs(_trueCourseDeg - _targetCourseDeg) > 0.5)) {
_turnActive = true;
}
}
if (_lateralMode == LateralMode::Direct) {
_targetCourseDeg = SGGeodesy::courseDeg(globals->get_aircraft_position(), _targetPos);
SG_NORMALIZE_RANGE(_targetCourseDeg, 0.0, 360.0);
if (!_turnActive && (fabs(_trueCourseDeg - _targetCourseDeg) > 0.5)) {
_turnActive = true;
}
}
if (_turnActive) {
if (fabs(_targetCourseDeg - _trueCourseDeg) < 0.1) {
_trueCourseDeg = _targetCourseDeg;
_turnActive = false;
} else {
// standard 2-minute turn, 180-deg min, thus 3-degrees per second
double turnDeg = 5.0 * dt;
double errorDeg = _targetCourseDeg - _trueCourseDeg;
SG_NORMALIZE_RANGE(errorDeg, -180.0, 180.0);
// clamp turn to error value
turnDeg = std::min(turnDeg, fabs(errorDeg));
// and now ensure we follow the correct sign
turnDeg = copysign(turnDeg, errorDeg);
// simple integral
_trueCourseDeg += turnDeg;
SG_NORMALIZE_RANGE(_trueCourseDeg, 0.0, 360.0);
}
}
SGGeod currentPos = globals->get_aircraft_position();
const double M = flightgear::AircraftPerformance::machForCAS(currentPos.getElevationFt(), _speedKnots);
_speedMachProp->setDoubleValue(M);
const double gs = flightgear::AircraftPerformance::groundSpeedForMach(currentPos.getElevationFt(), M);
_groundspeedKnotsProp->setDoubleValue(gs);
double d = gs * SG_KT_TO_MPS * dt;
SGGeod newPos = SGGeodesy::direct(currentPos, _trueCourseDeg, d);
if (_altActive) {
if (fabs(_targetAltitudeFt - currentPos.getElevationFt()) < 1) {
_altActive = false;
newPos.setElevationFt(_targetAltitudeFt);
} else {
double errorFt = _targetAltitudeFt - currentPos.getElevationFt();
double vspeed = std::min(fabs(errorFt),_vspeedFPM * dt / 60.0);
double dv = copysign(vspeed, errorFt);
newPos.setElevationFt(currentPos.getElevationFt() + dv);
}
}
setPosition(newPos);
}
void TestPilot::setPosition(const SGGeod& pos)
{
_latProp->setDoubleValue(pos.getLatitudeDeg());
_lonProp->setDoubleValue(pos.getLongitudeDeg());
_altitudeProp->setDoubleValue(pos.getElevationFt());
_headingProp->setDoubleValue(_trueCourseDeg);
_speedKnotsProp->setDoubleValue(_speedKnots);
_verticalFPMProp->setDoubleValue(_vspeedFPM);
}
void TestPilot::setTargetAltitudeFtMSL(double altFt)
{
_targetAltitudeFt = altFt;
_altActive = true;
}
bool TestPilot::isOnHeading(double heading) const
{
const double hdgDelta = (_trueCourseDeg - heading);
return fabs(hdgDelta) < 0.5;
}
double TestPilot::trueCourseDeg() const
{
return _trueCourseDeg;
}
} // of namespace

View File

@@ -0,0 +1,112 @@
/*
* Copyright (C) 2019 James Turner
*
* 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/>.
*/
#ifndef _FGTEST_API_TEST_PILOT_HXX
#define _FGTEST_API_TEST_PILOT_HXX
#include <simgear/props/propsfwd.hxx>
#include <simgear/math/SGMathFwd.hxx>
#include <simgear/structure/subsystem_mgr.hxx>
#include <simgear/math/SGGeod.hxx>
class GPS;
namespace FGTestApi {
/**
* @brief simulation of the user (pilot) fying in a particular way
* around the world. Standard property tree values are updated
* (position / orientation / velocity)
*/
class TestPilot : public SGSubsystem
{
public:
TestPilot(SGPropertyNode_ptr props = {});
~TestPilot();
void resetAtPosition(const SGGeod& pos);
void setSpeedKts(double knots);
void setCourseTrue(double deg);
void turnToCourse(double deg);
void setVerticalFPM(double fpm);
void setTargetAltitudeFtMSL(double altFt);
// void setTurnRateDegSec();
void init() override;
void update(double dT) override;
void flyHeading(double hdg);
void flyDirectTo(const SGGeod& target);
void flyGPSCourse(GPS *gps);
void flyGPSCourseOffset(GPS *gps, double offsetNm);
bool isOnHeading(double heading) const;
double trueCourseDeg() const;
private:
enum class LateralMode
{
Heading,
Direct,
GPSCourse,
GPSOffset
};
void updateValues(double dt);
void setPosition(const SGGeod& pos);
SGPropertyNode_ptr _propRoot;
double _trueCourseDeg = 0.0;
double _speedKnots = 0.0; // IAS
double _vspeedFPM = 0.0;
bool _turnActive = false;
bool _altActive = false;
double _targetCourseDeg = 0.0;
double _targetAltitudeFt = 0.0;
LateralMode _lateralMode = LateralMode::Heading;
SGGeod _targetPos;
GPS* _gps = nullptr;
double _courseOffsetNm =0.0;
SGPropertyNode_ptr _latProp;
SGPropertyNode_ptr _lonProp;
SGPropertyNode_ptr _altitudeProp;
SGPropertyNode_ptr _headingProp;
SGPropertyNode_ptr _speedKnotsProp;
SGPropertyNode_ptr _verticalFPMProp;
SGPropertyNode_ptr _groundspeedKnotsProp;
SGPropertyNode_ptr _speedMachProp;
SGPropertyNode_ptr _gpsNode;
SGPropertyNode_ptr _gpsLegCourse;
SGPropertyNode_ptr _courseErrorNm;
};
} // of namespace FGTestApi
#endif // of _FGTEST_API_TEST_PILOT_HXX

View File

@@ -0,0 +1,57 @@
/*
* 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/>.
*/
#include "scene_graph.hxx"
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
#include <Main/locale.hxx>
#include <Scenery/scenery.hxx>
#include <Viewer/renderer.hxx>
#include <Viewer/FGEventHandler.hxx>
namespace FGTestApi {
namespace setUp {
void initScenery()
{
// Read the global defaults from $FG_ROOT/defaults.xml (needed by the renderer).
SGPath defaultsXML = globals->get_fg_root() / "defaults.xml";
if (!defaultsXML.exists())
SG_LOG(SG_GENERAL, SG_ALERT, "Cannot read the global defaults from \"" << defaultsXML.utf8Str() << "\".");
fgLoadProps("defaults.xml", globals->get_props());
// otherwise fgSplashProgress will assert
globals->get_locale()->selectLanguage({});
// Set up the renderer.
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
FGRenderer* render = globals->get_renderer();
render->init();
render->setView(viewer.get());
// Start up the scenery subsystem.
globals->add_new_subsystem<FGScenery>(SGSubsystemMgr::DISPLAY);
globals->get_scenery()->init();
globals->get_scenery()->bind();
}
} // End of namespace setUp.
} // End of namespace FGTestApi.

View File

@@ -0,0 +1,31 @@
/*
* 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/>.
*/
#ifndef FG_TEST_SCENE_GRAPH_HXX
#define FG_TEST_SCENE_GRAPH_HXX
namespace FGTestApi {
namespace setUp {
void initScenery();
} // End of namespace setUp.
} // End of namespace FGTestApi.
#endif // FG_TEST_SCENE_GRAPH_HXX

View File

@@ -0,0 +1,30 @@
#include "config.h"
// implement various test-only methods on classes
#include <Airports/groundnetwork.hxx>
#include <Airports/airport.hxx>
#include <Airports/xmlloader.hxx>
#include <Navaids/procedure.hxx>
void FGAirport::testSuiteInjectGroundnetXML(const SGPath& path)
{
_groundNetwork.reset(new FGGroundNetwork(const_cast<FGAirport*>(this)));
XMLLoader::loadFromPath(_groundNetwork.get(), path);
_groundNetwork->init();
}
void FGAirport::testSuiteInjectProceduresXML(const SGPath& path)
{
if (mProceduresLoaded) {
SG_LOG(SG_GENERAL, SG_ALERT, "Procedures already loaded for" << ident());
mSIDs.clear();
mSTARs.clear();
mApproaches.clear();
}
mProceduresLoaded = true;
flightgear::RouteBase::loadAirportProcedures(path, const_cast<FGAirport*>(this));
}

View File

@@ -0,0 +1,476 @@
#include "config.h"
#include "test_suite/dataStore.hxx"
#include <ctime>
#include "TestDataLogger.hxx"
#include "testGlobals.hxx"
#include <simgear/io/iostreams/sgstream.hxx>
#if defined(HAVE_QT)
#include <GUI/QtLauncher.hxx>
#endif
#include <Main/globals.hxx>
#include <Main/options.hxx>
#include <Main/util.hxx>
#include <Main/FGInterpolator.hxx>
#include <Main/locale.hxx>
#include <Time/TimeManager.hxx>
#include <simgear/structure/event_mgr.hxx>
#include <simgear/timing/timestamp.hxx>
#include <simgear/timing/sg_time.hxx>
#include <simgear/math/sg_geodesy.hxx>
#include <simgear/props/props_io.hxx>
#include <Airports/airport.hxx>
#include <Navaids/FlightPlan.hxx>
#include <Navaids/waypoint.hxx>
#include <Navaids/routePath.hxx>
#include <Scripting/NasalSys.hxx>
using namespace flightgear;
namespace FGTestApi {
bool global_loggingToKML = false;
sg_ofstream global_kmlStream;
bool global_lineStringOpen = false;
namespace setUp {
void initTestGlobals(const std::string& testName)
{
assert(globals == nullptr);
globals = new FGGlobals;
DataStore &data = DataStore::get();
if (!data.getFGRoot().exists()) {
data.findFGRoot("");
}
globals->set_fg_root(data.getFGRoot());
// current dir
SGPath homePath = SGPath::fromUtf8(FGBUILDDIR) / "test_home";
if (!homePath.exists()) {
(homePath / "dummyFile").create_dir(0755);
}
globals->set_fg_home(homePath);
auto props = globals->get_props();
props->setStringValue("sim/fg-home", homePath.utf8Str());
// Activate headless mode.
globals->set_headless(true);
fgSetDefaults();
auto t = globals->add_new_subsystem<TimeManager>(SGSubsystemMgr::INIT);
t->bind();
t->init(); // establish mag-var data
/**
* Both the event manager and subsystem manager are initialised by the
* FGGlobals ctor, but only the subsystem manager is destroyed by the dtor.
* Here the event manager is added to the subsystem manager so it can be
* destroyed via the subsystem manager.
*/
globals->add_subsystem("events", globals->get_event_mgr(), SGSubsystemMgr::DISPLAY);
// necessary to avoid asserts: mark FGLocale as initialized
globals->get_locale()->selectLanguage({});
}
bool logPositionToKML(const std::string& testName)
{
// clear any previous state
if (global_loggingToKML) {
global_kmlStream.close();
global_lineStringOpen = false;
}
SGPath p = SGPath::desktop() / (testName + ".kml");
global_kmlStream.open(p);
if (!global_kmlStream.is_open()) {
SG_LOG(SG_GENERAL, SG_WARN, "unable to open:" << p);
return false;
}
// pre-amble
global_kmlStream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n"
"<Document>\n";
// need more precision for doubles when specifying lat/lon, see
// https://xkcd.com/2170/ :)
global_kmlStream.precision(12);
global_loggingToKML = true;
return true;
}
bool logLinestringsToKML(const std::string& testName)
{
// clear any previous state
if (global_loggingToKML) {
global_kmlStream.close();
global_lineStringOpen = false;
}
SGPath p = SGPath::desktop() / (testName + ".kml");
global_kmlStream.open(p);
if (!global_kmlStream.is_open()) {
SG_LOG(SG_GENERAL, SG_WARN, "unable to open:" << p);
return false;
}
// pre-amble
global_kmlStream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n"
"<Document>\n";
// need more precision for doubles when specifying lat/lon, see
// https://xkcd.com/2170/ :)
global_kmlStream.precision(12);
global_loggingToKML = false;
return true;
}
void initStandardNasal(bool withCanvas)
{
fgInitAllowedPaths();
auto nasalNode = globals->get_props()->getNode("nasal", true);
// load loadpriority.xml, for default modules load order
auto nasalLoadPriority = globals->get_props()->getNode("/sim/nasal-load-priority",true);
readProperties(globals->get_fg_root() / "Nasal/loadpriority.xml", nasalLoadPriority);
// set various props to reduce Nasal errors
auto props = globals->get_props();
props->setStringValue("sim/flight-model", "null");
props->setStringValue("sim/aircraft", "test-suite-aircraft");
props->setDoubleValue("sim/current-view/config/default-field-of-view-deg", 90.0);
// ensure /sim/view/config exists
props->setBoolValue("sim/view/config/foo", false);
props->setBoolValue("sim/rendering/precipitation-gui-enable", false);
props->setBoolValue("sim/rendering/precipitation-aircraft-enable", false);
// disable various larger modules
nasalNode->setBoolValue("canvas/enabled", withCanvas);
nasalNode->setBoolValue("jetways/enabled", false);
nasalNode->setBoolValue("jetways_edit/enabled", false);
nasalNode->setBoolValue("local_weather/enabled", false);
// Nasal needs the interpolator running
globals->add_subsystem("prop-interpolator", new FGInterpolator, SGSubsystemMgr::INIT);
// will be inited, since we already did that
globals->add_new_subsystem<FGNasalSys>(SGSubsystemMgr::INIT);
}
void populateFPWithoutNasal(flightgear::FlightPlanRef f,
const std::string& depICAO, const std::string& depRunway,
const std::string& destICAO, const std::string& destRunway,
const std::string& waypoints)
{
FGAirportRef depApt = FGAirport::getByIdent(depICAO);
f->setDeparture(depApt->getRunwayByIdent(depRunway));
FGAirportRef destApt = FGAirport::getByIdent(destICAO);
f->setDestination(destApt->getRunwayByIdent(destRunway));
// since we don't have the Nasal route-manager delegate, insert the
// runway waypoints manually
auto depRwy = new RunwayWaypt(f->departureRunway(), f);
depRwy->setFlag(WPT_DEPARTURE);
f->insertWayptAtIndex(depRwy, -1);
for (auto ws : simgear::strutils::split(waypoints)) {
WayptRef wpt = f->waypointFromString(ws);
if (!wpt) {
SG_LOG(SG_NAVAID, SG_ALERT, "No waypoint created for:" << ws);
continue;
}
f->insertWayptAtIndex(wpt, -1);
}
auto destRwy = f->destinationRunway();
f->insertWayptAtIndex(new BasicWaypt(destRwy->pointOnCenterline(-8 * SG_NM_TO_METER),
destRwy->ident() + "-8", f), -1);
f->insertWayptAtIndex(new RunwayWaypt(destRwy, f), -1);
}
void populateFPWithNasal(flightgear::FlightPlanRef f,
const std::string& depICAO, const std::string& depRunway,
const std::string& destICAO, const std::string& destRunway,
const std::string& waypoints)
{
FGAirportRef depApt = FGAirport::getByIdent(depICAO);
f->setDeparture(depApt->getRunwayByIdent(depRunway));
FGAirportRef destApt = FGAirport::getByIdent(destICAO);
f->setDestination(destApt->getRunwayByIdent(destRunway));
// insert after the last departure waypoint
int insertIndex = 1;
for (auto ws : simgear::strutils::split(waypoints)) {
WayptRef wpt = f->waypointFromString(ws);
f->insertWayptAtIndex(wpt, insertIndex++);
}
}
} // End of namespace setUp.
void beginLineString(const std::string& ident)
{
global_lineStringOpen = true;
global_kmlStream << "<Placemark>\n";
if (!ident.empty()) {
global_kmlStream << "<name>" << ident << "</name>\n";
}
global_kmlStream << "<LineString>\n";
global_kmlStream << "<tessellate>1</tessellate>\n";
global_kmlStream << "<coordinates>\n";
}
void logCoordinate(const SGGeod& pos)
{
if (!global_lineStringOpen) {
beginLineString({});
}
global_kmlStream << pos.getLongitudeDeg() << "," << pos.getLatitudeDeg() << " " << endl;
}
void rawLogCoordinate(const SGGeod& pos)
{
global_kmlStream << pos.getLongitudeDeg() << "," << pos.getLatitudeDeg() << " " << endl;
}
void endCurrentLineString()
{
global_lineStringOpen = false;
global_kmlStream <<
"</coordinates>\n"
"</LineString>\n"
"</Placemark>\n" << endl;
}
void setPosition(const SGGeod& g)
{
if (global_loggingToKML) {
if (global_lineStringOpen) {
endCurrentLineString();
}
logCoordinate(g);
}
globals->get_props()->setDoubleValue("position/latitude-deg", g.getLatitudeDeg());
globals->get_props()->setDoubleValue("position/longitude-deg", g.getLongitudeDeg());
globals->get_props()->setDoubleValue("position/altitude-ft", g.getElevationFt());
}
const SGGeod getPosition()
{
return SGGeod::fromDegFt(
globals->get_props()->getDoubleValue("position/latitude-deg"),
globals->get_props()->getDoubleValue("position/longitude-deg"),
globals->get_props()->getDoubleValue("position/altitude-ft"));
}
void setPositionAndStabilise(const SGGeod& g)
{
setPosition(g);
for (int i=0; i<60; ++i) {
globals->get_subsystem_mgr()->update(0.015);
}
}
void runForTime(double t)
{
const int tickHz = 30;
const double tickDuration = 1.0 / tickHz;
int ticks = static_cast<int>(t * tickHz);
assert(ticks > 0);
const int logInterval = 0.5 * tickHz;
int nextLog = 0;
long startTime = globals->get_time_params()->get_cur_time();
for (int t = 0; t < ticks; ++t) {
globals->inc_sim_time_sec(tickDuration);
globals->get_time_params()->update(globals->get_view_position(), startTime, t * tickDuration);
globals->get_subsystem_mgr()->update(tickDuration);
if (nextLog == 0) {
if (global_loggingToKML) {
logCoordinate(globals->get_aircraft_position());
}
if (DataLogger::isActive()) {
DataLogger::instance()->writeRecord();
}
nextLog = logInterval;
} else {
nextLog--;
}
}
}
bool runForTimeWithCheck(double t, RunCheck check)
{
const int tickHz = 30;
const double tickDuration = 1.0 / tickHz;
int ticks = static_cast<int>(t * tickHz);
assert(ticks > 0);
const int logInterval = 0.5 * tickHz;
int nextLog = 0;
for (int t = 0; t < ticks; ++t) {
globals->inc_sim_time_sec(tickDuration);
globals->get_subsystem_mgr()->update(tickDuration);
if (nextLog == 0) {
if (global_loggingToKML) {
logCoordinate(globals->get_aircraft_position());
}
if (DataLogger::isActive()) {
DataLogger::instance()->writeRecord();
}
nextLog = logInterval;
} else {
nextLog--;
}
bool done = check();
if (done) {
if (global_loggingToKML) {
endCurrentLineString();
}
return true;
}
}
return false;
}
void adjustSimulationWorldTime(time_t desiredUnixTime)
{
int timeOffset = desiredUnixTime - time(nullptr);
globals->get_props()->setIntValue("/sim/time/cur-time-override", 0);
globals->get_props()->setIntValue("/sim/time/warp", timeOffset);
globals->get_subsystem<TimeManager>()->update(0.0);
}
void writeFlightPlanToKML(flightgear::FlightPlanRef fp)
{
if (!global_loggingToKML)
return;
RoutePath rpath(fp);
for (int i=0; i<fp->numLegs(); ++i) {
SGGeodVec legPath = rpath.pathForIndex(i);
auto wp = fp->legAtIndex(i)->waypoint();
writeGeodsToKML("FP-leg-" + wp->ident(), legPath);
SGGeod legWPPosition = wp->position();
writePointToKML("WP " + wp->ident(), legWPPosition);
}
}
void writeGeodsToKML(const std::string &label, const flightgear::SGGeodVec& geods)
{
if (global_lineStringOpen) {
endCurrentLineString();
}
beginLineString(label);
for (const auto& g : geods) {
logCoordinate(g);
}
endCurrentLineString();
}
void writePointToKML(const std::string& ident, const SGGeod& pos)
{
global_kmlStream << "<Placemark>\n";
global_kmlStream << "<name>" << ident << "</name>\n";
global_kmlStream << "<Point>\n";
global_kmlStream << "<coordinates>\n";
rawLogCoordinate(pos);
global_kmlStream << "</coordinates>\n";
global_kmlStream << "</Point>\n";
global_kmlStream << "</Placemark>\n";
}
bool executeNasal(const std::string& code)
{
auto nasal = globals->get_subsystem<FGNasalSys>();
if (!nasal) {
throw sg_exception("Nasal not available");
}
nasal->getAndClearErrorList();
std::string output, parseErrors;
bool ok = nasal->parseAndRunWithOutput(code, output, parseErrors);
if (!parseErrors.empty()) {
SG_LOG(SG_NASAL, SG_ALERT, "Errors running Nasal:" << parseErrors);
return false;
}
return ok;
}
namespace tearDown {
void shutdownTestGlobals()
{
// The QApplication instance must be destroyed before exit() begins, see
// <https://bugreports.qt.io/browse/QTBUG-48709> (otherwise, segfault).
#if defined(HAVE_QT)
flightgear::shutdownQtApp();
#endif
delete globals;
globals = nullptr;
if (global_kmlStream) {
if (global_lineStringOpen) {
endCurrentLineString();
}
// post-amble
global_kmlStream << "</Document>\n"
"</kml>" << endl;
global_kmlStream.close();
global_loggingToKML = false;
}
}
} // End of namespace tearDown.
} // End of namespace FGTestApi.

View File

@@ -0,0 +1,75 @@
#ifndef FG_TEST_GLOBALS_HELPERS_HXX
#define FG_TEST_GLOBALS_HELPERS_HXX
#include <string>
#include <functional>
#include <vector>
#include <simgear/math/SGGeod.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
namespace flightgear
{
class FlightPlan;
typedef SGSharedPtr<FlightPlan> FlightPlanRef;
typedef std::vector<SGGeod> SGGeodVec;
}
namespace FGTestApi {
namespace setUp {
void initTestGlobals(const std::string& testName);
bool logPositionToKML(const std::string& testName);
/**Don't log aircraft positions*/
bool logLinestringsToKML(const std::string& testName);
void initStandardNasal(bool withCanvas = false);
void populateFPWithoutNasal(flightgear::FlightPlanRef f,
const std::string& depICAO, const std::string& depRunway,
const std::string& destICAO, const std::string& destRunway,
const std::string& waypoints);
void populateFPWithNasal(flightgear::FlightPlanRef f,
const std::string& depICAO, const std::string& depRunway,
const std::string& destICAO, const std::string& destRunway,
const std::string& waypoints);
} // End of namespace setUp.
// helpers during tests
const SGGeod getPosition();
void setPosition(const SGGeod& g);
void setPositionAndStabilise(const SGGeod& g);
void runForTime(double t);
/**
@brief set the simulation date/time clock to 'time'
*/
void adjustSimulationWorldTime(time_t time);
using RunCheck = std::function<bool(void)>;
bool runForTimeWithCheck(double t, RunCheck check);
void writeFlightPlanToKML(flightgear::FlightPlanRef fp);
void writeGeodsToKML(const std::string &label, const flightgear::SGGeodVec& geods);
void writePointToKML(const std::string& ident, const SGGeod& pos);
bool executeNasal(const std::string& code);
namespace tearDown {
void shutdownTestGlobals();
} // End of namespace tearDown.
} // End of namespace FGTestApi.
#endif // of FG_TEST_GLOBALS_HELPERS_HXX

1
test_suite/attic/README Normal file
View File

@@ -0,0 +1 @@
This is a collection of ancient testing code.

View File

@@ -0,0 +1,318 @@
/* -*- mode: C; tab-width:8; c-basic-offset:8 -*-
* vi:set ts=8:
*
* alcinfo.x
*
* alcinfo display info about a ALC extension and OpenAL renderer
*
* This file is in the Public Domain and comes with no warranty.
* Erik Hofman <erik@ehofman.com>
*
*/
#if HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef __APPLE__
# include <OpenAL/al.h>
# include <OpenAL/alc.h>
#else
# include <AL/al.h>
# include <AL/alc.h>
# include <AL/alext.h>
#endif
#ifndef AL_VERSION_1_1
# ifdef __APPLE__
# include <OpenAL/altypes.h>
# include <OpenAL/alctypes.h>
#else
# include <AL/altypes.h>
# include <AL/alctypes.h>
# endif
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <string>
using std::string;
#ifndef ALC_ALL_DEVICES_SPECIFIER
# define ALC_ALL_DEVICES_SPECIFIER 0x1013
#endif
#define MAX_DATA 16
static const int indentation = 4;
static const int maxmimumWidth = 79;
void printExtensions (const char *, char, const char *);
void displayDevices(const char *, const char *);
char *getDeviceName(int, char **);
void testForError(void *, const string&);
void testForALCError(ALCdevice *);
int main(int argc, char **argv)
{
ALCint data[MAX_DATA];
ALCdevice *device = NULL;
ALCcontext *context = NULL;
ALenum error;
char *s;
if (alcIsExtensionPresent(NULL, "ALC_enumeration_EXT") == AL_TRUE)
{
if (alcIsExtensionPresent(NULL, "ALC_enumerate_all_EXT") == AL_FALSE)
s = (char *)alcGetString(NULL, ALC_DEVICE_SPECIFIER);
else
s = (char *)alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
displayDevices("output", s);
s = (char *)alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
displayDevices("input", s);
}
s = getDeviceName(argc, argv);
device = alcOpenDevice(s);
testForError(device, "Audio device not available.");
context = alcCreateContext(device, NULL);
testForError(context, "Unable to create a valid context.");
alcMakeContextCurrent(context);
testForALCError(device);
s = (char *)alcGetString(device, ALC_DEFAULT_DEVICE_SPECIFIER);
printf("default output device: %s\n", s);
testForALCError(device);
error = alcIsExtensionPresent(device, "ALC_EXT_capture");
if (error)
{
s = (char *)alcGetString(device, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
printf("default input device: %s\n", s);
testForALCError(device);
}
printf("capture support: %s\n", (error) ? "yes" : "no");
alcGetIntegerv(device, ALC_FREQUENCY, 1, data);
printf("mixer frequency: %u hz\n", data[0]);
testForALCError(device);
alcGetIntegerv(device, ALC_REFRESH, 1, data+1);
printf("refresh rate : %u hz\n", data[0]/data[1]);
testForALCError(device);
data[0] = 0;
alcGetIntegerv(device, ALC_MONO_SOURCES, 1, data);
error = alcGetError(device);
if (error == AL_NONE) {
printf("supported sources; mono: %u, ", data[0]);
data[0] = 0;
alcGetIntegerv(device, ALC_STEREO_SOURCES, 1, data);
printf("stereo: %u\n", data[0]);
testForALCError(device);
}
printf("ALC version: ");
alcGetIntegerv(device, ALC_MAJOR_VERSION, 1, data);
printf("%i.", *data);
alcGetIntegerv(device, ALC_MINOR_VERSION, 1, data);
printf("%i\n", *data);
testForALCError(device);
s = (char *)alcGetString(device, ALC_EXTENSIONS);
printExtensions ("ALC extensions", ' ', s);
testForALCError(device);
s = (char *)alGetString(AL_VENDOR);
error = alGetError();
if ((error = alGetError()) != AL_NO_ERROR)
printf("Error #%x: %s\n", error, alGetString(error));
else
printf("OpenAL vendor string: %s\n", s);
s = (char *)alGetString(AL_RENDERER);
if ((error = alGetError()) != AL_NO_ERROR)
printf("Error #%x: %s\n", error, alGetString(error));
else
printf("OpenAL renderer string: %s\n", s);
s = (char *)alGetString(AL_VERSION);
if ((error = alGetError()) != AL_NO_ERROR)
printf("Error #%x: %s\n", error, alGetString(error));
else if (!s)
printf("Querying AL_VERSION returned NULL pointer!\n");
else
printf("OpenAL version string: %s\n", s);
s = (char *)alGetString(AL_EXTENSIONS);
printExtensions ("OpenAL extensions", ' ', s);
testForALCError(device);
if (alcMakeContextCurrent(NULL) == 0)
printf("alcMakeContextCurrent failed.\n");
device = alcGetContextsDevice(context);
alcDestroyContext(context);
testForALCError(device);
if (alcCloseDevice(device) == 0)
printf("alcCloseDevice failed.\n");
return 0;
}
/* -------------------------------------------------------------------------- */
void
printChar (int c, int *width)
{
putchar (c);
*width = (c == '\n') ? 0 : (*width + 1);
}
void
indent (int *width)
{
int i;
for (i = 0; i < indentation; i++)
{
printChar (' ', width);
}
}
void
printExtensions (const char *header, char separator, const char *extensions)
{
int width = 0, start = 0, end = 0;
printf ("%s:\n", header);
if (extensions == NULL || extensions[0] == '\0')
{
return;
}
indent (&width);
while (1)
{
if (extensions[end] == separator || extensions[end] == '\0')
{
if (width + end - start + 2 > maxmimumWidth)
{
printChar ('\n', &width);
indent (&width);
}
while (start < end)
{
printChar (extensions[start], &width);
start++;
}
if (extensions[end] == '\0')
{
break;
}
start++;
end++;
if (extensions[end] == '\0')
{
break;
}
printChar (',', &width);
printChar (' ', &width);
}
end++;
}
printChar ('\n', &width);
}
char *
getCommandLineOption(int argc, char **argv, const string& option)
{
int slen = option.size();
char *rv = 0;
int i;
for (i=0; i<argc; i++)
{
if (strncmp(argv[i], option.c_str(), slen) == 0)
{
i++;
if (i<argc) rv = argv[i];
}
}
return rv;
}
char *
getDeviceName(int argc, char **argv)
{
static char devname[255];
int len = 255;
char *s;
s = getCommandLineOption(argc, argv, "-d");
if (s)
{
strncpy((char *)&devname, s, len);
len -= strlen(s);
s = getCommandLineOption(argc, argv, "-r");
if (s)
{
strncat((char *)&devname, " on ", len);
len -= 4;
strncat((char *)&devname, s, len);
}
s = (char *)&devname;
}
return s;
}
void
displayDevices(const char *type, const char *list)
{
ALCchar *ptr, *nptr;
ptr = (ALCchar *)list;
printf("list of all available %s devices:\n", type);
if (!list)
{
printf("none\n");
}
else
{
nptr = ptr;
while (*(nptr += strlen(ptr)+1) != 0)
{
printf(" %s\n", ptr);
ptr = nptr;
}
printf(" %s\n", ptr);
}
}
void
testForError(void *p, const string& s)
{
if (p == NULL)
{
printf("\nError: %s\n\n", s.c_str());
exit(-1);
}
}
void
testForALCError(ALCdevice *device)
{
ALenum error;
error = alcGetError(device);
if (error != ALC_NO_ERROR)
printf("\nALC Error %x occurred: %s\n", error, alcGetString(device, error));
}

View File

@@ -0,0 +1,34 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef HAVE_WINDOWS_H
# include <windows.h>
#endif
#include <stdio.h>
#include <simgear/compiler.h>
#if defined( __APPLE__)
# include <OpenGL/OpenGL.h>
#else
# include <GL/gl.h>
#endif
int main() {
GLfloat a, t;
a = 1.0;
do {
printf("a = %.10f\n", a);
a = a / 2.0;
t = 1.0 + a;
} while ( t > 1.0 );
a = a + a;
printf("Estimated GLfloat epsilon = %.10f\n", a);
return(0);
}

View File

@@ -0,0 +1,123 @@
/*
From: Steve Baker <sbaker@link.com>
Sender: root@fatcity.com
To: OPENGL-GAMEDEV-L <OPENGL-GAMEDEV-L@fatcity.com>
Subject: Re: Win32 OpenGL Resource Page
Date: Fri, 24 Apr 1998 07:33:51 -0800
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef HAVE_WINDOWS_H
# include <windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <simgear/compiler.h>
#if defined( __APPLE__)
# include <OpenGL/OpenGL.h>
# include <GLUT/glut.h>
#else
# include <GL/gl.h>
# ifdef HAVE_GLUT_H
# include <GL/glut.h>
# endif
#endif
void getPrints ( GLenum token, char *string )
{
printf ( "%s = \"%s\"\n", string, glGetString ( token ) ) ;
}
void getPrint2f ( GLenum token, char *string )
{
GLfloat f[2] ;
glGetFloatv ( token, f ) ;
printf ( "%s = %g,%g\n", string, f[0],f[1] ) ;
}
void getPrintf ( GLenum token, char *string )
{
GLfloat f ;
glGetFloatv ( token, &f ) ;
printf ( "%s = %g\n", string, f ) ;
}
void getPrint2i ( GLenum token, char *string )
{
GLint i[2] ;
glGetIntegerv ( token, i ) ;
printf ( "%s = %d,%d\n", string, i[0],i[1] ) ;
}
void getPrinti ( GLenum token, char *string )
{
GLint i ;
glGetIntegerv ( token, &i ) ;
printf ( "%s = %d\n", string, i ) ;
}
int main ( int argc, char **argv )
{
#ifdef HAVE_GLUT_H
glutInit ( &argc, argv ) ;
glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ;
glutCreateWindow ( "You should never see this window!" ) ;
getPrints ( GL_VENDOR , "GL_VENDOR" ) ;
getPrints ( GL_RENDERER , "GL_RENDERER" ) ;
getPrints ( GL_VERSION , "GL_VERSION" ) ;
getPrints ( GL_EXTENSIONS , "GL_EXTENSIONS" ) ;
getPrinti ( GL_RED_BITS , "GL_RED_BITS" ) ;
getPrinti ( GL_GREEN_BITS , "GL_GREEN_BITS" ) ;
getPrinti ( GL_BLUE_BITS , "GL_BLUE_BITS" ) ;
getPrinti ( GL_ALPHA_BITS , "GL_ALPHA_BITS" ) ;
getPrinti ( GL_DEPTH_BITS , "GL_DEPTH_BITS" ) ;
getPrinti ( GL_INDEX_BITS , "GL_INDEX_BITS" ) ;
getPrinti ( GL_STENCIL_BITS, "GL_STENCIL_BITS" ) ;
getPrinti ( GL_ACCUM_RED_BITS , "GL_ACCUM_RED_BITS" ) ;
getPrinti ( GL_ACCUM_GREEN_BITS, "GL_ACCUM_GREEN_BITS" ) ;
getPrinti ( GL_ACCUM_BLUE_BITS , "GL_ACCUM_BLUE_BITS" ) ;
getPrinti ( GL_ACCUM_ALPHA_BITS, "GL_ACCUM_ALPHA_BITS" ) ;
getPrinti ( GL_AUX_BUFFERS, "GL_AUX_BUFFERS" ) ;
getPrinti ( GL_MAX_ATTRIB_STACK_DEPTH , "GL_MAX_ATTRIB_STACK_DEPTH" ) ;
getPrinti ( GL_MAX_NAME_STACK_DEPTH , "GL_MAX_NAME_STACK_DEPTH" ) ;
getPrinti ( GL_MAX_TEXTURE_STACK_DEPTH , "GL_MAX_TEXTURE_STACK_DEPTH" ) ;
getPrinti ( GL_MAX_PROJECTION_STACK_DEPTH, "GL_MAX_PROJECTION_STACK_DEPTH" ) ;
getPrinti ( GL_MAX_MODELVIEW_STACK_DEPTH , "GL_MAX_MODELVIEW_STACK_DEPTH" ) ;
getPrinti ( GL_MAX_CLIP_PLANES , "GL_MAX_CLIP_PLANES" ) ;
getPrinti ( GL_MAX_EVAL_ORDER , "GL_MAX_EVAL_ORDER" ) ;
getPrinti ( GL_MAX_LIGHTS , "GL_MAX_LIGHTS" ) ;
getPrinti ( GL_MAX_LIST_NESTING , "GL_MAX_LIST_NESTING" ) ;
getPrinti ( GL_MAX_TEXTURE_SIZE , "GL_MAX_TEXTURE_SIZE" ) ;
getPrint2i( GL_MAX_VIEWPORT_DIMS , "GL_MAX_VIEWPORT_DIMS" ) ;
getPrintf ( GL_POINT_SIZE_GRANULARITY, "GL_POINT_SIZE_GRANULARITY" ) ;
getPrint2f( GL_POINT_SIZE_RANGE , "GL_POINT_SIZE_RANGE" ) ;
printf("Default values:\n\n");
getPrinti( GL_UNPACK_ALIGNMENT , "GL_UNPACK_ALIGNMENT" ) ;
getPrinti( GL_UNPACK_ROW_LENGTH , "GL_UNPACK_ROW_LENGTH" ) ;
getPrinti( GL_UNPACK_SKIP_PIXELS , "GL_UNPACK_SKIP_PIXELS" ) ;
getPrinti( GL_UNPACK_SKIP_ROWS , "GL_UNPACK_SKIP_ROWS" ) ;
getPrinti( GL_BLEND_SRC , "GL_BLEND_SRC" ) ;
getPrinti( GL_BLEND_DST , "GL_BLEND_DST" ) ;
#else
printf("GL Utility Toolkit (glut) was not found on this system.\n");
#endif
return 0 ;
}

View File

@@ -0,0 +1,61 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <iostream>
#include <simgear/compiler.h>
#include <osg/GL>
#include <plib/pw.h>
#include <plib/pu.h>
#include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx>
#include "layout.hxx"
// Takes a property file on the command line, lays it out, and writes
// the resulting tree back to stdout. Requires that the
// "Helvetica.txf" font file from the base package be in the current
// directory.
// g++ -Wall -g -o layout layout.cxx layout-props.cxx layout-test.cxx
// -I/fg/include -L/fg/lib -I.. -lsgprops -lsgdebug -lsgstructure
// -lsgmisc -lsgxml -lplibpw -lplibpu -lplibfnt -lplibul -lGL
// We can't load a plib fntTexFont without a GL context, so we use the
// PW library to initialize things. The callbacks are required, but
// just stubs.
void exitCB(){ pwCleanup(); exit(0); }
void resizeCB(int w, int h){ }
void mouseMotionCB(int x, int y){ puMouse(x, y); }
void mouseButtonCB(int button, int updn, int x, int y){ puMouse(button, updn, x, y); }
void keyboardCB(int key, int updn, int x, int y){ puKeyboard(key, updn, x, y); }
const char* FONT_FILE = "Helvetica.txf";
int main(int argc, char** argv)
{
FILE* tmp;
if(!(tmp = fopen(FONT_FILE, "r"))) {
fprintf(stderr, "Could not open %s for reading.\n", FONT_FILE);
exit(1);
}
fclose(tmp);
pwInit(0, 0, 600, 400, 0, const_cast<char*>("Layout Test"), true, 0);
pwSetCallbacks(keyboardCB, mouseButtonCB, mouseMotionCB,
resizeCB, exitCB);
fntTexFont helv;
helv.load(FONT_FILE);
puFont puhelv(&helv);
LayoutWidget::setDefaultFont(&puhelv, 15);
SGPropertyNode props;
readProperties(argv[1], &props);
LayoutWidget w(&props);
int pw=0, ph=0;
w.calcPrefSize(&pw, &ph);
w.layout(0, 0, pw, ph);
writeProperties(std::cout, &props, true);
}

View File

@@ -0,0 +1,274 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef HAVE_WINDOWS_H
# include <windows.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <simgear/compiler.h>
#if defined( __APPLE__)
# include <OpenGL/OpenGL.h>
# include <GLUT/glut.h>
#else
# include <GL/gl.h>
# ifdef HAVE_GLUT_H
# include <GL/glut.h>
# endif
#endif
#define TEXRES_X 256
#define TEXRES_Y 256
#ifdef HAVE_GLUT_H
unsigned char env_map[TEXRES_X][TEXRES_Y][4];
GLuint texName;
int window_x = 640, window_y = 480;
float alpha = 0.0, beta = 0.0;
/*****************************************************************/
/*****************************************************************/
/*****************************************************************/
void setColor(float x, float y, float z, float angular_size, float r, float g, float b, float a)
{
//normalize
float inv_length = 1.0 / sqrt(x*x + y*y + z*z);
x *= inv_length; y *= inv_length; z *= inv_length;
printf("x = %f, y = %f, z = %f\n", x, y, z);
float cos_angular_size = cos(angular_size*3.1415/180.0);
printf("cos_angular_size = %f\n", cos_angular_size);
for(float tz_sign = -1.0; tz_sign < 3.0; tz_sign += 2.0)
{
for(float tx = -1.0; tx <= 1.0; tx += 0.01)
{
for(float ty = -1.0; ty <= 1.0; ty += 0.01)
{
if ((1.0 - tx*tx - ty*ty)<0.0)
continue;
float tz = tz_sign * sqrt(1.0 - tx*tx - ty*ty);
float cos_phi = x*tx + y*ty + z*tz;
if (cos_angular_size < cos_phi)
{
float rx = tx; //mirroring on the z=0 plane
float ry = ty;
float rz = -tz;
float inv_m = 1.0 / (2.0 * sqrt(rx*rx + ry*ry + (rz + 1)*(rz + 1)));
int s = (int)(TEXRES_X * (rx * inv_m + 0.5));
int t = (int)(TEXRES_Y * (ry * inv_m + 0.5));
//seg_fault protection:
if (s<0) s=0;
if (s>=TEXRES_X) s=TEXRES_X-1;
if (t<0) t=0;
if (t>=TEXRES_Y) t=TEXRES_Y-1;
env_map[s][t][0] = (unsigned char)(r * 255);
env_map[s][t][1] = (unsigned char)(g * 255);
env_map[s][t][2] = (unsigned char)(b * 255);
env_map[s][t][3] = (unsigned char)(a * 255);
}
}
}
}
}
/*****************************************************************/
/*****************************************************************/
/*****************************************************************/
void init(void)
{
glClearColor(0.0, 0.0, 0.0, 0.0);
glShadeModel(GL_FLAT);
glEnable(GL_DEPTH_TEST);
for(int x=0; x<TEXRES_X; x++)
{
for(int y=0; y<TEXRES_Y; y++)
{
env_map[x][y][0] = x; //a few colors
env_map[x][y][1] = y;
env_map[x][y][2] = 128;
env_map[x][y][3] = 128;
}
}
/*****************************************************************/
/*****************************************************************/
/*****************************************************************/
// x y z ang r g b a
setColor(0.0, 0.0, -1.0 , 23.0, 1.0, 1.0, 1.0, 1.0);
setColor(0.7071067, 0.0, -0.7071067, 23.0, 1.0, 0.0, 0.0, 1.0);
/*****************************************************************/
/*****************************************************************/
/*****************************************************************/
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(1, &texName);
glBindTexture(GL_TEXTURE_2D, texName);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEXRES_X, TEXRES_Y, 0, GL_RGBA, GL_UNSIGNED_BYTE, env_map);
}
void reshape(int w, int h)
{
window_x = w; window_y = h;
glViewport(0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective((GLdouble) 60.0, ((GLdouble)w)/((GLdouble)h), (GLdouble) 10.0, (GLdouble) 1000.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0, 0.0, -20.0);
}
void display()
{
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
//show light
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective((GLdouble) 60.0, (float)window_x/window_y, (GLdouble) 1.0, (GLdouble) 100.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0, 0.0, -10.0);
glRotatef(alpha, 1.0, 0.0, 0.0);
glRotatef(beta, 0.0, 1.0, 0.0);
glBegin(GL_LINES);
glColor3f(1.0, 1.0, 1.0);
glVertex3f(0.0, 0.0, 0.0);
glVertex3f(0.0, 0.0, 1.0);
glEnd();
/*****************************************************************/
/*****************************************************************/
/*****************************************************************/
glEnable(GL_TEXTURE_2D);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glBindTexture(GL_TEXTURE_2D, texName);
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glBegin(GL_QUADS);
glNormal3f(0.0, 0.0, 1.0);
glVertex3f(1.0, 1.0, 0.0);
glVertex3f(-1.0, 1.0, 0.0);
glVertex3f(-1.0, -1.0, 0.0);
glVertex3f(1.0, -1.0, 0.0);
glEnd();
/*
glPointSize(48.0);
glBegin(GL_POINTS);
glNormal3f(0.0, 0.0, 1.0);
glVertex3f(0.0, 0.0, 0.0);
glEnd();
*/
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glDisable(GL_TEXTURE_2D);
/*****************************************************************/
/*****************************************************************/
/*****************************************************************/
//show how the map looks like
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//gluOrtho2D(0.0 -10.0, 1.0 +10.0, -(float)window_x/window_y -10.0,0.0 +10.0);
//glOrtho(0.0, 5.0, -5.0, 0.0, -10.0, 10.0);
glOrtho(0.0, 5.0*((float)window_x/window_y), -5.0, 0.0, -10.0, 10.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_TEXTURE_2D);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glBindTexture(GL_TEXTURE_2D, texName);
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0); glVertex3f(0.0, 0.0, 0.0);
glTexCoord2f(0.0, 1.0); glVertex3f(0.0, -1.0, 0.0);
glTexCoord2f(1.0, 1.0); glVertex3f(1.0, -1.0, 0.0);
glTexCoord2f(1.0, 0.0); glVertex3f(1.0, 0.0, 0.0);
glEnd();
glFlush();
glDisable(GL_TEXTURE_2D);
glutPostRedisplay () ;
glutSwapBuffers () ;
}
void keyboard (unsigned char key, int x, int y)
{
switch(key)
{
case 27:
exit(0);
case '8':
alpha += 1.0;
if (alpha >= 360.0) alpha -= 360.0;
break;
case '2':
alpha -= 1.0;
if (alpha < 0.0) alpha += 360.0;
break;
case '4':
beta -= 1.0;
if (beta <= -90.0) beta = -90.0;
break;
case '6':
beta += 1.0;
if (beta >= 90.0) beta = 90.0;
break;
case '5':
alpha = 0.0; beta = 0.0;
break;
}
}
#endif /* HAVE_GLUT_H */
int main(int argc, char** argv)
{
#ifdef HAVE_GLUT_H
glutInitWindowSize(window_x, window_y);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutCreateWindow(argv[0]);
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutMainLoop();
#else
printf("GL Utility Toolkit (glut) was not found on this system.\n");
#endif
return 0;
}

View File

@@ -0,0 +1,18 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#if defined( unix ) || defined( __CYGWIN__ )
# include <unistd.h> // for gethostname()
#endif
#include <stdio.h>
int main() {
#if defined( unix ) || defined( __CYGWIN__ )
char name[256];
gethostname( name, 256 );
printf("hostname = %s\n", name);
#endif
return 0;
}

View File

@@ -0,0 +1,116 @@
// do some non-destructive read tests relating text files
#include <stdio.h>
#include <string.h>
static void readchars( FILE *fd ) {
int c;
while ( (c = fgetc(fd)) != EOF ) {
printf("c = %d", c);
if ( c == 10 ) {
printf(" LF \\n");
}
if ( c == 13 ) {
printf(" CR \\r");
}
printf("\n");
}
}
static void readlines( FILE *fd ) {
char line[256];
while ( fgets(line, 255, fd) != NULL ) {
int len = strlen(line);
printf("line = %s", line);
if ( len >= 2 ) {
printf(" char[n - 1] = %d char[n] = %d\n",
line[len-2], line[len-1]);
} else if ( len >= 1 ) {
printf(" char[n] = %d\n",line[len-1]);
} else {
printf("empty string\n");
}
}
}
int main( int argc, char **argv ) {
// check usage
if ( argc != 2 ) {
printf("usage: %s file\n", argv[0]);
return -1;
}
char file[256];
strcpy( file, argv[1] );
FILE *fd;
// open a file in (default) text mode
printf("TEXT MODE (DEFAULT) by character\n\n");
fd = fopen( file, "r" );
if ( fd != NULL ) {
readchars( fd );
fclose(fd);
} else {
printf("Cannot open %s\n", file);
}
printf("\n");
// open a file in (explicit) text mode
printf("TEXT MODE (EXPLICIT) by character\n\n");
fd = fopen( file, "rt" );
if ( fd != NULL ) {
readchars( fd );
fclose(fd);
} else {
printf("Cannot open %s\n", file);
}
printf("\n");
// open a file in (explicit) binary mode
printf("BINARY MODE (EXPLICIT) by character\n\n");
fd = fopen( file, "rb" );
if ( fd != NULL ) {
readchars( fd );
fclose(fd);
} else {
printf("Cannot open %s\n", file);
}
printf("\n");
// open a file in (default) text mode
printf("TEXT MODE (DEFAULT) by line\n\n");
fd = fopen( file, "r" );
if ( fd != NULL ) {
readlines( fd );
fclose(fd);
} else {
printf("Cannot open %s\n", file);
}
printf("\n");
// open a file in (explicit) text mode
printf("TEXT MODE (EXPLICIT) by line\n\n");
fd = fopen( file, "rt" );
if ( fd != NULL ) {
readlines( fd );
fclose(fd);
} else {
printf("Cannot open %s\n", file);
}
printf("\n");
// open a file in (explicit) binary mode
printf("BINARY MODE (EXPLICIT) by line\n\n");
fd = fopen( file, "rb" );
if ( fd != NULL ) {
readlines( fd );
fclose(fd);
} else {
printf("Cannot open %s\n", file);
}
printf("\n");
return 0;
}

26
test_suite/bootstrap.cxx Normal file
View File

@@ -0,0 +1,26 @@
// bootstrap.cxx -- replacement bootstrap routines for the test suite.
//
// Written by Edward d'Auvergne, started May 2017.
//
// Copyright (C) 2017 Edward d'Auvergne
//
// 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.
//
// $Id$
#include <iostream>
int _bootstrap_OSInit;
std::string hostname;

135
test_suite/dataStore.cxx Normal file
View File

@@ -0,0 +1,135 @@
/*
* Copyright (C) 2018 James Turner
* 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/>.
*/
#include "config.h"
#include <iostream>
#include <simgear/misc/strutils.hxx>
#include <Main/fg_init.hxx>
#include "dataStore.hxx"
// Sanity check.
bool looksLikeFGData(const SGPath& path)
{
return (path / "defaults.xml").exists();
}
// Set up the path to FGData.
int DataStore::findFGRoot(const std::string& fgRootCmdLineOpt, bool debug)
{
SGPath fgRoot;
// Command line supplied path.
if (!fgRootCmdLineOpt.empty()) {
fgRoot = SGPath::fromUtf8(fgRootCmdLineOpt);
if (looksLikeFGData(fgRoot)) {
if (debug)
std::cerr << "FGdata from the command line: " << fgRoot << std::endl;
_fgRootPath = fgRoot;
return 0;
// Fail if an invalid path is supplied.
} else {
std::cerr << "The supplied path \"" << fgRootCmdLineOpt << "\" is not a valid FGData path." << std::endl;
return 1;
}
}
// The FG_DATA_DIR CMake option.
fgRoot = SGPath::fromUtf8(PKGLIBDIR);
if (looksLikeFGData(fgRoot)) {
if (debug)
std::cerr << "FGdata found via $PKGLIBDIR: " << fgRoot << std::endl;
_fgRootPath = fgRoot;
return 0;
}
// The FG_ROOT environmental variable.
if (std::getenv("FG_ROOT")) {
fgRoot = SGPath::fromEnv("FG_ROOT");
if (looksLikeFGData(fgRoot)) {
if (debug)
std::cerr << "FGdata found via $FG_ROOT: " << fgRoot << std::endl;
_fgRootPath = fgRoot;
return 0;
}
}
// Try in ../fgdata.
fgRoot = SGPath::fromUtf8(FGSRCDIR) / ".." / "fgdata";
if (looksLikeFGData(fgRoot)) {
if (debug)
std::cerr << "FGdata found at \"$FGSRCDIR/../fgdata\": " << fgRoot << std::endl;
_fgRootPath = fgRoot;
return 0;
}
// Try in ../data.
fgRoot = SGPath::fromUtf8(FGSRCDIR) / ".." / "data";
if (looksLikeFGData(fgRoot)) {
if (debug)
std::cerr << "FGdata found at \"$FGSRCDIR/../data\": " << std::endl;
_fgRootPath = fgRoot;
return 0;
}
// No FGData.
std::cerr << "FGData not found." << std::endl;
return 1;
}
// Get the path to FGData.
SGPath DataStore::getFGRoot()
{
return _fgRootPath;
}
// Perform a version validation of FGData, returning zero if ok.
int DataStore::validateFGRoot()
{
// The FGData version.
SGPath root = getFGRoot();
std::string base_version = fgBasePackageVersion(root);
if (base_version.empty()) {
std::cout << "Base package not found." << std::endl;
std::cout << "Required FGData files not found, please check your configuration." << std::endl;
std::cout << "Looking for FGData files in '" << root.str() << "'." << std::endl;
std::cout.flush();
return 1;
}
// Comparison of only the major and minor version numbers.
const int versionComp = simgear::strutils::compare_versions(FLIGHTGEAR_VERSION, base_version, 2);
if (versionComp != 0) {
std::cout << "Base package version mismatch." << std::endl;
std::cout << "Version check failed, please check your configuration." << std::endl;
std::cout << "The FGData version '" << base_version << "' in '" << root.str();
std::cout << "does not match the FlightGear version '" << std::string(FLIGHTGEAR_VERSION);
std::cout << "'." << std::endl;
std::cout.flush();
}
return versionComp;
}

54
test_suite/dataStore.hxx Normal file
View File

@@ -0,0 +1,54 @@
/*
* 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/>.
*/
#ifndef _FG_TEST_SUITE_DATA_STORE_HXX
#define _FG_TEST_SUITE_DATA_STORE_HXX
#include <simgear/misc/sg_path.hxx>
// The data store singleton.
class DataStore
{
public:
// Return the singleton, instantiating it if required.
static DataStore& get()
{
static DataStore instance;
return instance;
}
// Function deletion to allow the class to be a singleton.
DataStore(DataStore const&) = delete;
void operator=(DataStore const&) = delete;
// FGData path functions.
int findFGRoot(const std::string& fgRootCmdLineOpt, bool debug = false);
SGPath getFGRoot();
int validateFGRoot();
private:
DataStore() = default;
// The path to FGData.
SGPath _fgRootPath;
};
#endif // _FG_TEST_SUITE_DATA_STORE_HXX

View File

@@ -0,0 +1,167 @@
/*
* Copyright (C) 2016 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/>.
*/
#include <algorithm>
#include <iomanip>
#include <cppunit/SourceLine.h>
#include <cppunit/TestResultCollector.h>
#include "fgCompilerOutputter.hxx"
#include "formatting.hxx"
#include "logging.hxx"
using namespace std;
// Create a new class instance.
fgCompilerOutputter * fgCompilerOutputter::defaultOutputter(CppUnit::TestResultCollector *result, vector<TestIOCapt> *capt, const clock_t *clock, CppUnit::OStream &stream)
{
return new fgCompilerOutputter(result, capt, clock, stream);
}
// Printout for each failed test.
void fgCompilerOutputter::printFailureDetail(CppUnit::TestFailure *failure)
{
// Declarations.
TestIOCapt test_io;
vector<TestIOCapt>::iterator test_iter;
// Initial separator.
#ifdef _WIN32
fg_stream << endl;
#endif
fg_stream << string(WIDTH_DIVIDER, '=') << endl;
// Test info.
fg_stream << (failure->isError() ? "ERROR: " : "FAIL: ") << failure->failedTestName() << endl;
fg_stream << string(WIDTH_DIVIDER, '-') << endl;
fg_stream << (failure->isError() ? "Error" : "Assertion") << ": ";
printFailureLocation(failure->sourceLine());
printFailureMessage(failure);
fg_stream.flush();
if (debug)
return;
// The captured IO for this test.
test_iter = find_if(io_capt->begin(), io_capt->end(), matchTestName(failure->failedTestName()));
if (test_iter != io_capt->end())
test_io = *test_iter;
// SG_LOG IO streams.
if (!test_io.sg_interleaved.empty())
fgCompilerOutputter::printIOStreamMessages("SG_LOG, "+test_io.log_class+" class, "+test_io.log_priority+" priority", test_io.sg_interleaved, true);
if (!test_io.sg_bulk_only.empty())
fgCompilerOutputter::printIOStreamMessages("SG_LOG, "+test_io.log_class+" class, SG_BULK only priority", test_io.sg_bulk_only);
if (!test_io.sg_debug_only.empty())
fgCompilerOutputter::printIOStreamMessages("SG_LOG, "+test_io.log_class+" class, SG_DEBUG only priority", test_io.sg_debug_only);
if (!test_io.sg_info_only.empty())
fgCompilerOutputter::printIOStreamMessages("SG_LOG, "+test_io.log_class+" class, SG_INFO only priority", test_io.sg_info_only);
if (!test_io.sg_warn_only.empty())
fgCompilerOutputter::printIOStreamMessages("SG_LOG, "+test_io.log_class+" class, SG_WARN only priority", test_io.sg_warn_only);
if (!test_io.sg_alert_only.empty())
fgCompilerOutputter::printIOStreamMessages("SG_LOG, "+test_io.log_class+" class, SG_ALERT only priority", test_io.sg_alert_only);
// Default IO streams.
fgCompilerOutputter::printIOStreamMessages("STDOUT and STDERR", test_io.stdio);
}
// Detailed printout after a failed run of the test suite.
void fgCompilerOutputter::printFailureReport()
{
// Custom printouts for each failed test.
printFailuresList();
// CTest output (nothing).
if (ctest_output)
return;
// A summary with timing info.
printSuiteStats();
// Final summary.
fg_stream << endl << "[ FAILED ]" << endl << endl;
fg_stream.flush();
}
void fgCompilerOutputter::printIOStreamMessages(string heading, string messages, bool empty)
{
// Silence.
if (!empty && messages.size() == 0)
return;
// Divider.
fg_stream << string(WIDTH_DIVIDER, '-') << endl;
// Heading.
fg_stream << "# " << heading << endl << endl;
// Nothing to do
if (messages.size() == 0)
fg_stream << "(empty)" << endl << endl;
// The IO stream contents.
else
fg_stream << messages << endl;
}
// Printout of the test suite stats.
void fgCompilerOutputter::printSuiteStats()
{
// A divider.
#ifdef _WIN32
fg_stream << endl;
#endif
fg_stream << string(WIDTH_DIVIDER, '-') << endl;
// Timing and test count line.
fg_stream << "Ran " << fg_result->runTests() << " tests";
streamsize prec = fg_stream.precision();
fg_stream << setprecision(3);
fg_stream << " in " << ((double)*suite_timer)/CLOCKS_PER_SEC << " seconds." << endl;
fg_stream << setprecision(prec);
// Failure line.
if (!fg_result->wasSuccessful()) {
fg_stream << endl << "Failures = " << fg_result->testFailures() << endl;
fg_stream << "Errors = " << fg_result->testErrors() << endl;
}
}
// Print a summary after a successful run of the test suite.
void fgCompilerOutputter::printSuccess()
{
// CTest output (nothing).
if (ctest_output)
return;
// A summary with timing info.
printSuiteStats();
// Final summary.
fg_stream << endl << "[ OK ]" << endl << endl;
fg_stream.flush();
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2016 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/>.
*/
#ifndef _FG_COMPILER_OUTPUTTER_HXX
#define _FG_COMPILER_OUTPUTTER_HXX
#include <cppunit/CompilerOutputter.h>
#include <cppunit/TestFailure.h>
#include "fgTestListener.hxx"
// The custom outputter for the FlightGear test suite.
class fgCompilerOutputter : public CppUnit::CompilerOutputter
{
public:
// Constructor.
fgCompilerOutputter(CppUnit::TestResultCollector *result,
std::vector<TestIOCapt> *capt,
const clock_t *clock,
CppUnit::OStream &stream,
bool ctest = false,
bool debug = false,
const std::string &locationFormat = CPPUNIT_COMPILER_LOCATION_FORMAT)
: CppUnit::CompilerOutputter(result, stream, locationFormat)
, io_capt(capt)
, fg_result(result)
, fg_stream(stream)
, suite_timer(clock)
, ctest_output(ctest)
, debug(debug)
{
}
// Create a new class instance.
static fgCompilerOutputter *defaultOutputter(CppUnit::TestResultCollector *result, std::vector<TestIOCapt> *capt, const clock_t *clock, CppUnit::OStream &stream);
// Print a summary after a successful run of the test suite.
void printSuccess();
// Detailed printout after a failed run of the test suite.
void printFailureReport();
// Printout for each failed test.
void printFailureDetail(CppUnit::TestFailure *failure);
// Printout of the test suite stats.
void printSuiteStats();
// The captured IO for each failed test.
std::vector<TestIOCapt> *io_capt;
private:
// Store copies of the base class objects.
CppUnit::TestResultCollector *fg_result;
CppUnit::OStream &fg_stream;
// The test suite time, in clock ticks.
const clock_t *suite_timer;
// Output control.
bool ctest_output;
bool debug;
// Simgear logstream IO printout.
void printIOStreamMessages(std::string heading, std::string messages, bool empty);
void printIOStreamMessages(std::string heading, std::string messages) {printIOStreamMessages(heading, messages, false);}
};
#endif // _FG_COMPILER_OUTPUTTER_HXX

View File

@@ -0,0 +1,138 @@
/*
* Copyright (C) 2016 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/>.
*/
#include <cppunit/Test.h>
#include <cppunit/TestFailure.h>
#include <cppunit/TestListener.h>
#include "fgTestListener.hxx"
#include "logging.hxx"
using namespace std;
// Handle failures.
void fgTestListener::addFailure(const CppUnit::TestFailure &failure)
{
m_failure = true;
if (failure.isError())
m_error = true;
}
// Override the base class function to restore IO streams.
void fgTestListener::endTest(CppUnit::Test *test)
{
// Test timing.
sum_time += clock() - m_time;
// Restore the IO streams.
if (!debug) {
cout.rdbuf(orig_cout);
cerr.rdbuf(orig_cerr);
}
// Debugging output.
if (debug)
cerr << string(WIDTH_DIVIDER, '-') << endl;
// Per-test single character status feedback.
if (m_failure)
cerr << (m_error ? "E" : "F");
else
cerr << '.';
// Timing output.
if (timings || ctest_output || debug) {
// Test timing.
float time = ((float)(clock()-m_time))/CLOCKS_PER_SEC;
char buffer[100];
if (time > 60.0)
snprintf(buffer, sizeof(buffer), "%10.3f %-3s", time/60, "min");
else if (time > 1.0)
snprintf(buffer, sizeof(buffer), "%10.3f %-3s", time, "s");
else
snprintf(buffer, sizeof(buffer), "%10.3f %-3s", time*1000, "ms");
cerr << buffer;
// Test name.
cerr << " for " << test->getName() << endl;
}
cerr.flush();
// Store the captured IO for any failed tests.
if (m_failure && !debug) {
// Set up the data structure.
TestIOCapt test_io;
test_io.name = test->getName();
// Standard IO.
test_io.stdio = capt.str();
// The simgear logstreams.
capturedIO &obj = getIOstreams();
test_io.log_class = obj.log_class;
test_io.log_priority = obj.log_priority;
test_io.sg_interleaved = obj.sg_interleaved.str();
test_io.sg_bulk_only = obj.sg_bulk_only.str();
test_io.sg_debug_only = obj.sg_debug_only.str();
test_io.sg_info_only = obj.sg_info_only.str();
test_io.sg_warn_only = obj.sg_warn_only.str();
test_io.sg_alert_only = obj.sg_alert_only.str();
// Add the test's IO to the list.
io_capt.push_back(test_io);
}
}
// Override the base class function to capture IO streams.
void fgTestListener::startTest(CppUnit::Test *test)
{
// IO capture.
if (!debug) {
// Clear the simgear logstream buffers.
capturedIO &obj = getIOstreams();
obj.sg_interleaved.str("");
obj.sg_bulk_only.str("");
obj.sg_debug_only.str("");
obj.sg_info_only.str("");
obj.sg_warn_only.str("");
obj.sg_alert_only.str("");
// Store the original STDOUT and STDERR for restoring later on.
orig_cout = cout.rdbuf();
orig_cerr = cerr.rdbuf();
// Clear the captured stream and then catch stdout and stderr.
capt.str(string());
cout.rdbuf(capt.rdbuf());
cerr.rdbuf(capt.rdbuf());
// Debugging output.
} else {
cerr << string(WIDTH_DIVIDER, '=') << endl;
cerr << "Starting test: " << test->getName() << endl;
cerr << string(WIDTH_DIVIDER, '-') << endl;
}
// Reset the test status.
m_failure = false;
m_error = false;
m_time = clock();
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2016 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/>.
*/
#ifndef _FG_TEST_LISTENER_HXX
#define _FG_TEST_LISTENER_HXX
#include <time.h>
#include <vector>
#include "formatting.hxx"
// Data structure for holding the captured IO for a failed test.
struct TestIOCapt {
std::string name;
std::string log_class;
std::string log_priority;
std::string stdio;
std::string sg_interleaved;
std::string sg_bulk_only;
std::string sg_debug_only;
std::string sg_info_only;
std::string sg_warn_only;
std::string sg_alert_only;
};
// Match the test by name for std:find using a vector<TestIOCapt>.
class matchTestName
{
std::string _name;
public:
matchTestName(const std::string &name) : _name(name) {}
bool operator()(const TestIOCapt &item) const {
return item.name == _name;
}
};
// The custom test runner for the FlightGear test suite.
class fgTestListener : public CppUnit::TestListener
{
protected:
// Failure state.
bool m_failure, m_error;
public:
// Constructor.
fgTestListener(): m_failure(false), m_error(false), sum_time(0) { };
// Override the base class function to capture IO. streams
void startTest(CppUnit::Test *test);
// Override the base class function to restore IO streams.
void endTest(CppUnit::Test *test);
// Handle failures.
void addFailure(const CppUnit::TestFailure &failure);
// Test suite timing.
clock_t sum_time;
// IO capture for all failed tests.
std::vector<TestIOCapt> io_capt;
// Output settings.
bool timings;
bool ctest_output;
bool debug;
protected:
// The original IO streams.
std::streambuf *orig_cerr, *orig_cout;
// Captured IO streams.
std::stringstream capt;
// Test timings.
clock_t m_time;
};
#endif // _FG_TEST_LISTENER_HXX

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2016-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/>.
*/
#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/TextTestRunner.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include "fgCompilerOutputter.hxx"
#include "fgTestListener.hxx"
#include "formatting.hxx"
// Execute all test suites for the given test category.
int testRunner(const std::string& type, const std::string& title, char *subset, bool timings, bool ctest_output, bool debug)
{
// Declarations.
CppUnit::TextTestRunner runner;
// Print out a title.
if (!ctest_output)
printTitle(std::cerr, title);
// Get all tests.
runner.addTest(CppUnit::TestFactoryRegistry::getRegistry(type).makeTest());
// Set up the test listener.
fgTestListener *testListener;
testListener = new fgTestListener;
runner.eventManager().addListener(testListener);
testListener->timings = timings;
testListener->ctest_output = ctest_output;
testListener->debug = debug;
// Set the test suite output IO stream.
runner.setOutputter(new fgCompilerOutputter(&runner.result(), &testListener->io_capt, &testListener->sum_time, std::cerr, ctest_output, debug));
// Execute the tests.
if (subset == NULL)
runner.run("", false, true, false);
else {
std::stringstream testStream(subset);
std::string testName;
while( testStream.good() ) {
getline( testStream, testName, ',' );
runner.run(testName, false, true, false);
}
}
// Clean up.
delete testListener;
// Return the status of the tests.
CppUnit::TestResultCollector &status = runner.result();
return status.testFailuresTotal();
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2016-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/>.
*/
#ifndef _FG_TEST_RUNNER_HXX
#define _FG_TEST_RUNNER_HXX
// Execute all test suites for the given test category.
int testRunner(const std::string&, const std::string&, char*, bool, bool, bool);
#endif // _FG_TEST_RUNNER_HXX

View File

@@ -0,0 +1,19 @@
# Add each simgear test category.
foreach( simgear_test_category
)
add_subdirectory(${simgear_test_category})
endforeach( simgear_test_category )
set(TESTSUITE_SOURCES
${TESTSUITE_SOURCES}
PARENT_SCOPE
)
set(TESTSUITE_HEADERS
${TESTSUITE_HEADERS}
PARENT_SCOPE
)

82
test_suite/formatting.cxx Normal file
View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2016 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/>.
*/
#include <string>
#include "formatting.hxx"
using namespace std;
// Printout of a section label.
void printSection(CppUnit::OStream &stream, const string &text)
{
// Declarations.
string::size_type width = text.size();
// Format the text.
stream << endl;
stream << text << endl;
stream << string(width, '=') << endl << endl;
}
// Print a summary line.
void printSummaryLine(CppUnit::OStream &stream, const string &name, int passed)
{
// Declarations.
int n;
string dots, state;
// Passed.
if (!passed)
state = "OK";
// Skipped.
else if (passed == -1)
state = "Skipped";
// Failed.
else
state = "Failed";
// Dots.
n = WIDTH_DIVIDER - name.size() - state.size() - 6;
dots = string(n, '.');
// Write out the line.
stream << name;
stream << " " << dots << " ";
stream << "[ " << state << " ]" << endl;
}
// Printout of a title label.
void printTitle(CppUnit::OStream &stream, const string &text)
{
// Declarations.
string::size_type width = text.size() + 4;
// Format the text.
stream << endl << endl;
stream << string(width, '=') << endl;
stream << "= " << text << " =" << endl;
stream << string(width, '=') << endl << endl;
}

40
test_suite/formatting.hxx Normal file
View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2016-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/>.
*/
#ifndef _FG_TEST_SUITE_FORMATTING_HXX
#define _FG_TEST_SUITE_FORMATTING_HXX
#include <cppunit/portability/Stream.h>
// Divider width.
const int WIDTH_DIVIDER = 70;
// Printout of a section label.
void printSection(CppUnit::OStream &stream, const std::string &text);
// Print a summary line.
void printSummaryLine(CppUnit::OStream &stream, const std::string &name, int passed);
// Printout of a title label.
void printTitle(CppUnit::OStream &stream, const std::string &text);
#endif // _FG_TEST_SUITE_FORMATTING_HXX

View File

@@ -0,0 +1,19 @@
# Add each GUI test category.
foreach( gui_test_category
)
add_subdirectory(${gui_test_category})
endforeach( gui_test_category )
set(TESTSUITE_SOURCES
${TESTSUITE_SOURCES}
PARENT_SCOPE
)
set(TESTSUITE_HEADERS
${TESTSUITE_HEADERS}
PARENT_SCOPE
)

148
test_suite/logging.cxx Normal file
View File

@@ -0,0 +1,148 @@
/*
* Copyright (C) 2016 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/>.
*/
#include <iomanip>
#include "logging.hxx"
#include <simgear/debug/logstream.hxx>
#include <simgear/debug/OsgIoCapture.hxx>
// The global stream capture data structure.
static capturedIO *_iostreams = NULL;
// capturedIO constructor.
capturedIO::capturedIO(sgDebugClass c, sgDebugPriority p)
{
callback_interleaved = new StreamLogCallback(sg_interleaved, c, p, false);
callback_bulk_only = new StreamLogCallback(sg_bulk_only, c, SG_BULK, true);
callback_debug_only = new StreamLogCallback(sg_debug_only, c, SG_DEBUG, true);
callback_info_only = new StreamLogCallback(sg_info_only, c, SG_INFO, true);
callback_warn_only = new StreamLogCallback(sg_warn_only, c, SG_WARN, true);
callback_alert_only = new StreamLogCallback(sg_alert_only, c, SG_ALERT, true);
// Store the class as a string.
if (c == SG_ALL)
log_class = "SG_ALL";
else {
std::stringstream stream;
stream << "0x" << std::right << std::setfill('0') << std::setw(8) << std::hex << c;
log_class = stream.str();
}
// Store the priority as a string.
if (p == SG_BULK)
log_priority = "SG_BULK";
else if (p == SG_DEBUG)
log_priority = "SG_DEBUG";
else if (p == SG_INFO)
log_priority = "SG_INFO";
else if (p == SG_WARN)
log_priority = "SG_WARN";
else if (p == SG_ALERT)
log_priority = "SG_ALERT";
else if (p == SG_POPUP)
log_priority = "SG_POPUP";
else if (p == SG_DEV_WARN)
log_priority = "SG_DEV_WARN";
else if (p == SG_DEV_ALERT)
log_priority = "SG_DEV_ALERT";
}
// capturedIO destructor.
capturedIO::~capturedIO()
{
// Destroy the callback objects.
delete callback_interleaved;
delete callback_bulk_only;
delete callback_debug_only;
delete callback_info_only;
delete callback_warn_only;
delete callback_alert_only;
}
// Return the global stream capture data structure, creating it if needed.
capturedIO & getIOstreams(sgDebugClass c, sgDebugPriority p)
{
// Initialise the global stream capture data structure, if needed.
if (!_iostreams)
_iostreams = new capturedIO(c, p);
// Return a pointer to the global object.
return *_iostreams;
}
// Set up to capture all the simgear logging priorities as separate streams.
void setupLogging(sgDebugClass c, sgDebugPriority p, bool split)
{
// Get the single logstream instance.
logstream &log = sglog();
// Set up the logstream testing mode.
log.setTestingMode(true);
// OSG IO capture.
osg::setNotifyHandler(new NotifyLogger);
// IO capture.
capturedIO &obj = getIOstreams(c, p);
if (!split)
log.addCallback(obj.callback_interleaved);
else {
if (p <= SG_BULK)
log.addCallback(obj.callback_bulk_only);
if (p <= SG_DEBUG)
log.addCallback(obj.callback_debug_only);
if (p <= SG_INFO)
log.addCallback(obj.callback_info_only);
if (p <= SG_WARN)
log.addCallback(obj.callback_warn_only);
if (p <= SG_ALERT)
log.addCallback(obj.callback_alert_only);
}
}
// Deactivate all the simgear logging priority IO captures.
void stopLogging()
{
// Get the single logstream instance.
logstream &log = sglog();
// IO decapture.
capturedIO &obj = getIOstreams();
log.removeCallback(obj.callback_interleaved);
log.removeCallback(obj.callback_bulk_only);
log.removeCallback(obj.callback_debug_only);
log.removeCallback(obj.callback_info_only);
log.removeCallback(obj.callback_warn_only);
log.removeCallback(obj.callback_alert_only);
// Clean up the IO stream object.
delete _iostreams;
_iostreams = NULL;
// Stop the simgear logstream.
simgear::shutdownLogging();
}

100
test_suite/logging.hxx Normal file
View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2016 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/>.
*/
#ifndef _FG_TEST_SUITE_LOGGING_HXX
#define _FG_TEST_SUITE_LOGGING_HXX
#include <sstream>
#include <simgear/debug/LogCallback.hxx>
// A logstream callback based on the simgear FileLogCallback class.
class StreamLogCallback : public simgear::LogCallback
{
public:
StreamLogCallback(std::ostringstream& stream, sgDebugClass c, sgDebugPriority p, bool priorityMatch):
simgear::LogCallback(c, p),
m_priority(p),
m_stream(stream),
m_priorityMatch(priorityMatch)
{
}
virtual void operator()(sgDebugClass c, sgDebugPriority p, const char* file, int line, const std::string& message)
{
if (m_priorityMatch && p != m_priority) return;
if (!shouldLog(c, p)) return;
if (m_priorityMatch)
m_stream << simgear::LogCallback::debugClassToString(c) << ":" << file << ":" << line << ": " << message << std::endl;
else
m_stream << simgear::LogCallback::debugClassToString(c) << ":" << (int) p << ":" << file << ":" << line << ": " << message << std::endl;
}
private:
sgDebugPriority m_priority;
std::ostringstream &m_stream;
bool m_priorityMatch;
};
// All of the captured IO streams.
class capturedIO
{
public:
// Constructor and destructor.
capturedIO(sgDebugClass, sgDebugPriority);
~capturedIO();
// The IO streams.
std::ostringstream sg_interleaved;
std::ostringstream sg_bulk_only;
std::ostringstream sg_debug_only;
std::ostringstream sg_info_only;
std::ostringstream sg_warn_only;
std::ostringstream sg_alert_only;
// The callback objects.
StreamLogCallback *callback_interleaved;
StreamLogCallback *callback_bulk_only;
StreamLogCallback *callback_debug_only;
StreamLogCallback *callback_info_only;
StreamLogCallback *callback_warn_only;
StreamLogCallback *callback_alert_only;
// The logging class and priority text.
std::string log_class;
std::string log_priority;
};
// Return the global stream capture data structure, creating it if needed.
capturedIO & getIOstreams(sgDebugClass c=SG_ALL, sgDebugPriority p=SG_BULK);
// Set up to capture all the simgear logging priorities as separate streams.
void setupLogging(sgDebugClass, sgDebugPriority, bool);
// Deactivate all the simgear logging priority IO captures.
void stopLogging();
#endif // _FG_TEST_SUITE_LOGGING_HXX

View File

@@ -0,0 +1,23 @@
# Add each simgear test category.
foreach( simgear_test_category
math
props
canvas
structure
)
add_subdirectory(${simgear_test_category})
endforeach( simgear_test_category )
set(TESTSUITE_SOURCES
${TESTSUITE_SOURCES}
PARENT_SCOPE
)
set(TESTSUITE_HEADERS
${TESTSUITE_HEADERS}
PARENT_SCOPE
)

View File

@@ -0,0 +1,12 @@
set(TESTSUITE_SOURCES
${TESTSUITE_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
${CMAKE_CURRENT_SOURCE_DIR}/testCanvas.cxx
PARENT_SCOPE
)
set(TESTSUITE_HEADERS
${TESTSUITE_HEADERS}
${CMAKE_CURRENT_SOURCE_DIR}/testCanvas.hxx
PARENT_SCOPE
)

View File

@@ -0,0 +1,24 @@
/*
* Copyright (C) 2016 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/>.
*/
#include "testCanvas.hxx"
// Set up the tests.
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CanvasTests, "Simgear unit tests");

View File

@@ -0,0 +1,123 @@
#include "testCanvas.hxx"
#include <simgear/canvas/Canvas.hxx>
#include <simgear/canvas/elements/CanvasImage.hxx>
#include "test_suite/FGTestApi/testGlobals.hxx"
#include <Main/globals.hxx>
#include <Main/fg_commands.hxx>
#include <Canvas/canvas_mgr.hxx>
#include <Canvas/FGCanvasSystemAdapter.hxx>
extern bool global_nasalMinimalInit;
void verifyPixel(osg::Image* img, int x, int y, const uint8_t* pixel)
{
auto rawPixel = img->data(x, y);
CPPUNIT_ASSERT(memcmp(rawPixel, pixel, 4) == 0);
}
void CanvasTests::setUp()
{
global_nasalMinimalInit = false;
FGTestApi::setUp::initTestGlobals("canvas");
// Canvas needs loadxml command
fgInitCommands();
simgear::canvas::Canvas::setSystemAdapter(
simgear::canvas::SystemAdapterPtr(new canvas::FGCanvasSystemAdapter)
);
globals->add_subsystem("Canvas", new CanvasMgr, SGSubsystemMgr::DISPLAY);
globals->get_subsystem_mgr()->bind();
globals->get_subsystem_mgr()->init();
FGTestApi::setUp::initStandardNasal(true /* withCanvas */);
globals->get_subsystem_mgr()->postinit();
}
// Clean up after each test.
void CanvasTests::tearDown()
{
FGTestApi::tearDown::shutdownTestGlobals();
}
void CanvasTests::testCanvasBasic()
{
bool ok = FGTestApi::executeNasal(R"(
var my_canvas = canvas.new({
"name": "PFD-Test",
"size": [512, 512],
"view": [768, 1024],
"mipmapping": 1
});
globals.pfd = my_canvas;
)");
CPPUNIT_ASSERT(ok);
auto canvasPtr = globals->get_subsystem<CanvasMgr>()->getCanvas("PFD-Test");
CPPUNIT_ASSERT(canvasPtr.valid());
ok = FGTestApi::executeNasal(R"(
globals.pfd.createGroup("wibble");
)");
CPPUNIT_ASSERT(ok);
auto wibbleGroupPtr = canvasPtr->getGroup("wibble");
CPPUNIT_ASSERT(wibbleGroupPtr.valid());
}
void CanvasTests::testImagePixelOps()
{
bool ok = FGTestApi::executeNasal(R"(
var my_canvas = canvas.new({
"name": "ImagePixelOpsCanvas",
"size": [512, 512],
"view": [768, 1024],
"mipmapping": 1
});
var g = my_canvas.createGroup("root");
var img = g.createChild('image', 'ops-image');
# deliberate non-power-of-two sizes here
img.setSize(150, 170);
globals.pixelOps = my_canvas;
)");
CPPUNIT_ASSERT(ok);
auto canvasPtr = globals->get_subsystem<CanvasMgr>()->getCanvas("ImagePixelOpsCanvas");
CPPUNIT_ASSERT(canvasPtr.valid());
auto imagePtr = canvasPtr->getRootGroup()->getElementById("ops-image");
CPPUNIT_ASSERT(imagePtr.valid());
auto image = dynamic_cast<simgear::canvas::Image*>(imagePtr.get());
image->setFill(osg::Vec4(1.0, 0.0, 0.5, 1.0));
image->fillRect(SGRecti(20, 40, 70, 55), "#7f3f1f");
ok = FGTestApi::executeNasal(R"(
var canvas = globals.pixelOps;
var img = canvas.getGroup('root').getElementById('ops-image');
print('Image is:', img);
img.fillRect([5, 8, 180, 37], '#aabbcc');
)");
CPPUNIT_ASSERT(ok);
auto osgImage = image->getImage();
const uint8_t testColor1[] = {0xff, 0xcc, 0xbb, 0xaa}; // little endian
verifyPixel(osgImage.get(), 5, 8, testColor1);
verifyPixel(osgImage.get(), 149, 44, testColor1);
// test pixles should not have been touched by the second fillRect
const uint8_t testColor2[] = {0xff, 0x1f, 0x3f, 0x7f}; // little endian
verifyPixel(osgImage.get(), 20, 45, testColor2);
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2016 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 unit tests.
class CanvasTests : public CppUnit::TestFixture
{
// Set up the test suite.
CPPUNIT_TEST_SUITE(CanvasTests);
CPPUNIT_TEST(testCanvasBasic);
CPPUNIT_TEST(testImagePixelOps);
CPPUNIT_TEST_SUITE_END();
public:
// Set up function for each test.
void setUp();
// Clean up after each test.
void tearDown();
// The tests.
void testCanvasBasic();
void testImagePixelOps();
};

View File

@@ -0,0 +1,12 @@
set(TESTSUITE_SOURCES
${TESTSUITE_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
${CMAKE_CURRENT_SOURCE_DIR}/test-up.cxx
PARENT_SCOPE
)
set(TESTSUITE_HEADERS
${TESTSUITE_HEADERS}
${CMAKE_CURRENT_SOURCE_DIR}/testSGGeodesy.hxx
PARENT_SCOPE
)

View File

@@ -0,0 +1,24 @@
/*
* Copyright (C) 2016 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/>.
*/
#include "testSGGeodesy.hxx"
// Set up the tests.
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(MathGeodesyTests, "Simgear unit tests");

View File

@@ -0,0 +1,172 @@
// do some test relating to the concept of "up"
#include <iostream>
#include <simgear/math/sg_geodesy.hxx>
#include "testSGGeodesy.hxx"
using std::cout;
using std::endl;
// The test-up unit test.
void MathGeodesyTests::testUp()
{
// for each lat/lon given in goedetic coordinates, calculate
// geocentric coordinates, cartesian coordinates, the local "up"
// vector (based on original geodetic lat/lon), as well as the "Z"
// intercept (for which 0 = center of earth)
double lon = 0;
double alt = 0;
int i;
const int n = 19;
double lat[n] = {0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90};
// Hard coded expected results.
double lat_geoc_expected[n] = {
0,
4.96669608700923,
9.93439421027913,
14.9040671396528,
19.8766298623747,
24.8529125604827,
29.8336358098291,
34.8193887023496,
39.8106105519284,
44.8075767840180,
49.8103895262907,
54.8189733092144,
59.8330761504926,
64.8522761370246,
69.8759934364236,
74.9035074740039,
79.9339788099657,
84.9664750566862,
90
};
double cart_expected[n][3] = {
{6378137, 0, 0},
{6353866.26310279, 555891.26758132, 0},
{6281238.76737403, 1107551.86696002, 0},
{6160807.25190988, 1650783.32787306, 0},
{5993488.27326157, 2181451.33089075, 0},
{5780555.22988658, 2695517.17208404, 0},
{5523628.67081747, 3189068.5, 0},
{5224663.96230526, 3658349.09101875, 0},
{4885936.40630155, 4099787.43648327, 0},
{4510023.92403682, 4510023.92403682, 0},
{4099787.43648328, 4885936.40630155, 0},
{3658349.09101875, 5224663.96230526, 0},
{3189068.5, 5523628.67081747, 0},
{2695517.17208404, 5780555.22988658, 0},
{2181451.33089075, 5993488.27326157, 0},
{1650783.32787306, 6160807.25190988, 0},
{1107551.86696002, 6281238.76737403, 0},
{555891.26758132, 6353866.26310279, 0},
{3.90548253078665e-10, 6378137, 0}
};
double up_expected[n][3] = {
{6378137, 0, -0},
{6354027.82056206, 0, -552183.96002777},
{6281872.82960345, 0, -1100248.54773536},
{6162189.08803834, 0, -1640100.14019589},
{5995836.38389635, 0, -2167696.78782876},
{5784014.11472572, 0, -2679074.46295779},
{5528256.63929284, 0, -3170373.73538364},
{5230426.84020036, 0, -3637866.9093781},
{4892707.60007269, 0, -4077985.57220038},
{4517590.87884893, 0, -4487348.40886592},
{4107864.09120678, 0, -4862789.03770643},
{3666593.52237417, 0, -5201383.52320227},
{3197104.58692395, 0, -5500477.13393864},
{2702958.82600294, 0, -5757709.84149597},
{2187927.64927902, 0, -5971040.00711856},
{1655962.9523645, 0, -6138765.68235824},
{1111164.87081001, 0, -6259542.96102869},
{557747.059253584, 0, -6332400.86398617},
{3.91862092481447e-10, 0, -6356752.31424518}
};
double geod_intercept[n] = {
0,
552169.92020293,
1100137.4939038,
1639732.35699488,
2166847.86675177,
2677472.35586416,
3167719.66364641,
3633858.71206688,
4072341.90150465,
4479832.11012469,
4853228.0913901,
5189688.07642079,
5486651.40157058,
5741857.99662372,
5953365.5852939,
6119564.46711958,
6239189.76825642,
6311331.06793104,
6335439.32729282
};
double geoc_intercept[n] = {
0,
552169.92020293,
1100137.4939038,
1639732.35699488,
2166847.86675177,
2677472.35586416,
3167719.66364641,
3633858.71206688,
4072341.90150465,
4479832.11012469,
4853228.0913901,
5189688.07642079,
5486651.40157058,
5741857.99662372,
5953365.5852939,
6119564.46711958,
6239189.76825642,
6311331.06793104,
6378137 // Sole difference with geod, due to truncation artifacts.
};
for (i=0; i < n; i++) {
cout << "lon = " << lon << " geod lat = " << lat[i];
double sl_radius, lat_geoc;
sgGeodToGeoc( lat[i] * SGD_DEGREES_TO_RADIANS, alt, &sl_radius, &lat_geoc );
cout << " geoc lat = " << lat_geoc * SGD_RADIANS_TO_DEGREES << endl;
CPPUNIT_ASSERT_DOUBLES_EQUAL(lat_geoc_expected[i], lat_geoc*SGD_RADIANS_TO_DEGREES, 1e-10);
double pc[3];
sgGeodToCart( lon * SGD_DEGREES_TO_RADIANS, lat[i] * SGD_DEGREES_TO_RADIANS, 0.0, pc );
cout << " cartesian = " << SGVec3d(pc[0], pc[1], pc[2]) << endl;
for (int j=0; j < 3; j++)
CPPUNIT_ASSERT_DOUBLES_EQUAL(cart_expected[i][j], pc[j], 1e-7);
SGGeod geod_up = SGGeod::fromDeg(lon, -lat[i]);
SGVec3d geod_up_vect = SGVec3d::fromGeod(geod_up);
cout << " geod up = " << geod_up_vect << endl;
for (int j=0; j < 3; j++)
CPPUNIT_ASSERT_DOUBLES_EQUAL(up_expected[i][j], geod_up_vect[j], 1e-7);
SGGeoc geoc_up = SGGeoc::fromGeod(geod_up);
SGVec3d geoc_up_vect = SGVec3d::fromGeoc(geoc_up);
cout << " geoc up = " << geoc_up_vect << endl;
for (int j=0; j < 3; j++)
CPPUNIT_ASSERT_DOUBLES_EQUAL(up_expected[i][j], geoc_up_vect[j], 1e-7);
double slope = geod_up_vect[2] / geod_up_vect[0];
double intercept = pc[2] - slope * pc[0];
cout << " Z intercept (based on geodetic up) = " << intercept << endl;
CPPUNIT_ASSERT_DOUBLES_EQUAL(geod_intercept[i], intercept, 1e-7);
slope = geoc_up_vect[2] / geoc_up_vect[0];
intercept = pc[2] - slope * pc[0];
cout << " Z intercept (based on geocentric up) = " << intercept << endl;
CPPUNIT_ASSERT_DOUBLES_EQUAL(geoc_intercept[i], intercept, 1e-7);
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2016 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/>.
*/
#ifndef _FG_MATH_GEODESY_SIMGEAR_UNIT_TESTS_HXX
#define _FG_MATH_GEODESY_SIMGEAR_UNIT_TESTS_HXX
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestFixture.h>
// The unit tests.
class MathGeodesyTests : public CppUnit::TestFixture
{
// Set up the test suite.
CPPUNIT_TEST_SUITE(MathGeodesyTests);
CPPUNIT_TEST(testUp);
CPPUNIT_TEST_SUITE_END();
public:
// Set up function for each test.
void setUp() {}
// Clean up after each test.
void tearDown() {}
// The tests.
void testUp();
};
#endif // _FG_MATH_GEODESY_SIMGEAR_UNIT_TESTS_HXX

View File

@@ -0,0 +1,12 @@
set(TESTSUITE_SOURCES
${TESTSUITE_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
${CMAKE_CURRENT_SOURCE_DIR}/test_props.cxx
PARENT_SCOPE
)
set(TESTSUITE_HEADERS
${TESTSUITE_HEADERS}
${CMAKE_CURRENT_SOURCE_DIR}/test_props.hxx
PARENT_SCOPE
)

View File

@@ -0,0 +1,24 @@
/*
* Copyright (C) 2016 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/>.
*/
#include "test_props.hxx"
// Set up the tests.
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(SimgearPropsTests, "Simgear unit tests");

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2016 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/>.
*/
#include "test_props.hxx"
#include <simgear/props/props_io.hxx>
// Set up function for each test.
void SimgearPropsTests::setUp()
{
// Create a property tree.
tree = new SGPropertyNode;
}
// Clean up after each test.
void SimgearPropsTests::tearDown()
{
// Delete the tree (avoiding the memory leak).
delete tree;
}
// Test property aliasing, to catch possible memory leaks.
void SimgearPropsTests::testAliasLeak()
{
// Declarations.
SGPropertyNode *alias;
// Create a new node.
tree->getNode("test-node", true);
// Aliased node.
alias = tree->getNode("test-alias", true);
alias->alias("test-node");
}
void SimgearPropsTests::testPropsCopyIf()
{
// dummy property tree structure
tree->setIntValue("a/a/a", 42);
tree->setStringValue("a/a/b", "foo");
tree->setIntValue("a/a/c[2]", 99);
tree->setIntValue("a/a/d", 1);
tree->setIntValue("a/b/a[0]", 50);
tree->setIntValue("a/b/a[1]", 100);
tree->setStringValue("a/b/b", "foo");
SGPropertyNode_ptr destA(new SGPropertyNode);
copyPropertiesIf(tree, destA, [](const SGPropertyNode* src) {
// always copy non-leaf nodes
if (src->nChildren() > 0)
return true;
return (src->getType() == simgear::props::INT) &&
src->getIntValue() > 50;
});
CPPUNIT_ASSERT_EQUAL(1, destA->getNode("a/a")->nChildren()); // only 99
CPPUNIT_ASSERT_EQUAL(99, destA->getIntValue("a/a/c[2]"));
CPPUNIT_ASSERT_EQUAL(1, destA->getNode("a/b")->nChildren()); // only 100
CPPUNIT_ASSERT_EQUAL(100, destA->getIntValue("a/b/a[1]"));
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2016 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/>.
*/
#ifndef _FG_SIMGEAR_PROPS_SIMGEAR_UNIT_TESTS_HXX
#define _FG_SIMGEAR_PROPS_SIMGEAR_UNIT_TESTS_HXX
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestFixture.h>
#include <simgear/props/props.hxx>
// The unit tests of the simgear property tree.
class SimgearPropsTests : public CppUnit::TestFixture
{
// Set up the test suite.
CPPUNIT_TEST_SUITE(SimgearPropsTests);
CPPUNIT_TEST(testAliasLeak);
CPPUNIT_TEST(testPropsCopyIf);
CPPUNIT_TEST_SUITE_END();
public:
// Set up function for each test.
void setUp();
// Clean up after each test.
void tearDown();
// The tests.
void testAliasLeak();
void testPropsCopyIf();
private:
// A property tree.
SGPropertyNode *tree;
};
#endif // _FG_SIMGEAR_PROPS_SIMGEAR_UNIT_TESTS_HXX

View File

@@ -0,0 +1,12 @@
set(TESTSUITE_SOURCES
${TESTSUITE_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
${CMAKE_CURRENT_SOURCE_DIR}/test_event.cxx
PARENT_SCOPE
)
set(TESTSUITE_HEADERS
${TESTSUITE_HEADERS}
${CMAKE_CURRENT_SOURCE_DIR}/test_event.hxx
PARENT_SCOPE
)

View File

@@ -0,0 +1,24 @@
/*
* Copyright (C) 2016 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/>.
*/
#include "test_event.hxx"
// Set up the tests.
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(SimgearEventTests, "Simgear unit tests");

View File

@@ -0,0 +1,142 @@
/*
* Copyright (C) 2016 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/>.
*/
#include <algorithm>
#include <memory>
#include "test_event.hxx"
using namespace std;
namespace {
SGEventMgr* global_eventManager = nullptr;
double global_realTime = 0.0;
// class which behaves similarly to TimerObj in NasalSys
class FakeNasalTimer
{
public:
FakeNasalTimer(string n) : _name(n)
{
}
void invoke()
{
++_invokeCount;
if (_expectedTime > 0.0) {
CPPUNIT_ASSERT_DOUBLES_EQUAL(_expectedTime, global_realTime, 0.1);
}
// rescheudle base don the next time
if (!_rescheduleTimes.empty()) {
const auto t = _rescheduleTimes.front();
_rescheduleTimes.pop_front();
// TimerObj does this, eugh
if (global_eventManager) {
global_eventManager->removeTask(_name);
start(t);
}
}
}
void start(double interval)
{
if (!global_eventManager)
return;
global_eventManager->addTask(_name, [this](){ this->invoke(); },
interval, interval /* delay */, false);
_expectedTime = global_realTime + interval;
}
string _name;
deque<double> _rescheduleTimes;
int _invokeCount = 0;
double _expectedTime = 0.0;
};
} // namespace
// Set up function for each test.
void SimgearEventTests::setUp()
{
_eventManager = new SGEventMgr;
global_eventManager = _eventManager;
_eventManager->init();
_realTimeProp = new SGPropertyNode{};
_eventManager->setRealtimeProperty(_realTimeProp);
}
// Clean up after each test.
void SimgearEventTests::tearDown()
{
global_eventManager = nullptr;
}
void SimgearEventTests::runForTestTime(double totalTime, double updateHz, double simTimeScaling)
{
const double dt = 1.0 / updateHz;
const int updateCount = static_cast<int>(totalTime * updateHz);
const double simDt = dt * simTimeScaling;
_realTimeProp->setDoubleValue(dt);
global_realTime = 0.0;
for (int c = 0; c < updateCount; ++c) {
const double newRealTime = global_realTime + dt;
global_realTime = newRealTime;
_eventManager->update(simDt);
}
}
void SimgearEventTests::testTaskRescheduleDuringRun()
{
auto t1 = std::make_unique<FakeNasalTimer>("timer_aaa");
t1->_rescheduleTimes.resize(10);
fill(t1->_rescheduleTimes.begin(), t1->_rescheduleTimes.end(), 0.5);
t1->_rescheduleTimes.push_back(30.0); // large final value
global_realTime = 0.0;
t1->start(1.0);
runForTestTime(8.0, 20);
CPPUNIT_ASSERT_EQUAL(11, t1->_invokeCount);
}
void SimgearEventTests::testTaskRescheduleDuringRun2()
{
auto t1 = std::make_unique<FakeNasalTimer>("timer_bbb");
const std::initializer_list<double> times = {
0.02, 0.05, 0.02, 0.1, 0.02, 0.125, 0.05, 0.02};
t1->_rescheduleTimes = times;
t1->_rescheduleTimes.push_back(30.0); // large final value
global_realTime = 0.0;
t1->start(1.0);
// running at 60Hz, check we don't miss any
runForTestTime(8.0, 60);
CPPUNIT_ASSERT_EQUAL(9, t1->_invokeCount);
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2016 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/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <simgear/props/props.hxx>
#include <simgear/structure/event_mgr.hxx>
// The unit tests of the simgear property tree.
class SimgearEventTests : public CppUnit::TestFixture
{
// Set up the test suite.
CPPUNIT_TEST_SUITE(SimgearEventTests);
CPPUNIT_TEST(testTaskRescheduleDuringRun);
CPPUNIT_TEST(testTaskRescheduleDuringRun2);
CPPUNIT_TEST_SUITE_END();
public:
// Set up function for each test.
void setUp();
// Clean up after each test.
void tearDown();
void runForTestTime(double totalTime, double updateHz, double simTimeScaling = 1.0);
// The tests.
void testTaskRescheduleDuringRun();
void testTaskRescheduleDuringRun2();
private:
SGSharedPtr<SGEventMgr> _eventManager;
SGPropertyNode_ptr _realTimeProp;
SGPropertyNode_ptr _freezeProp;
};

View File

@@ -0,0 +1,23 @@
# Add each system test category.
foreach( system_test_category
FDM
Instrumentation
Navaids
Main
)
add_subdirectory(${system_test_category})
endforeach( system_test_category )
set(TESTSUITE_SOURCES
${TESTSUITE_SOURCES}
PARENT_SCOPE
)
set(TESTSUITE_HEADERS
${TESTSUITE_HEADERS}
PARENT_SCOPE
)

View File

@@ -0,0 +1,12 @@
set(TESTSUITE_SOURCES
${TESTSUITE_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
${CMAKE_CURRENT_SOURCE_DIR}/testAeroMesh.cxx
PARENT_SCOPE
)
set(TESTSUITE_HEADERS
${TESTSUITE_HEADERS}
${CMAKE_CURRENT_SOURCE_DIR}/testAeroMesh.hxx
PARENT_SCOPE
)

View File

@@ -0,0 +1,24 @@
/*
* 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/>.
*/
#include "testAeroMesh.hxx"
// Set up the unit tests.
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(AeroMeshTests, "System tests");

View File

@@ -0,0 +1,222 @@
#include "testAeroMesh.hxx"
#include "test_suite/FGTestApi/testGlobals.hxx"
#include "test_suite/FGTestApi/PrivateAccessorFDM.hxx"
#include "test_suite/FGTestApi/scene_graph.hxx"
#include <vector>
#include <map>
#include <iostream>
#include <simgear/constants.h>
#include <simgear/misc/test_macros.hxx>
#include <simgear/math/SGVec3.hxx>
#include <simgear/math/SGGeod.hxx>
#include <simgear/math/SGQuat.hxx>
#include <simgear/math/SGGeoc.hxx>
#include <simgear/props/props.hxx>
#include <AIModel/AIAircraft.hxx>
#include <AIModel/AIManager.hxx>
#include <AIModel/performancedata.hxx>
#include <AIModel/performancedb.hxx>
#include "FDM/AIWake/AircraftMesh.hxx"
#include "FDM/AIWake/AIWakeGroup.hxx"
extern "C" {
#include "src/FDM/LaRCsim/ls_matrix.h"
}
#include "FDM/JSBSim/math/FGLocation.h"
#include "FDM/JSBSim/math/FGQuaternion.h"
#include <Main/globals.hxx>
using namespace std;
using namespace JSBSim;
double rho = 2.0E-3;
// Set up function for each test.
void AeroMeshTests::setUp()
{
FGTestApi::setUp::initTestGlobals("aeromesh");
FGTestApi::setUp::initScenery();
globals->get_props()->getNode("environment/density-slugft3", true)
->setDoubleValue(rho);
}
// Clean up after each test.
void AeroMeshTests::tearDown()
{
FGTestApi::tearDown::shutdownTestGlobals();
}
void AeroMeshTests::testLiftComputation()
{
double b = 10.0;
double c = 2.0;
AircraftMesh_ptr mesh = new AircraftMesh(b, c, "test");
SGGeod geodPos = SGGeod::fromDeg(0.0, 0.0);
SGVec3d pos;
double vel = 100.;
double weight = 50.;
SGGeodesy::SGGeodToCart(geodPos, pos);
mesh->setPosition(pos, SGQuatd::unit());
SGPropertyNode* props = globals->get_props()->getNode("ai/models", true);
props->setDoubleValue("acceleration-kts-hour", 0.0);
props->setDoubleValue("deceleration-kts-hour", 0.0);
props->setDoubleValue("climbrate-fpm", 0.0);
props->setDoubleValue("decentrate-fpm", 0.0);
props->setDoubleValue("rotate-speed-kts", 0.0);
props->setDoubleValue("takeoff-speed-kts", 0.0);
props->setDoubleValue("climb-speed-kts", 0.0);
props->setDoubleValue("cruise-speed-kts", 0.0);
props->setDoubleValue("decent-speed-kts", 0.0);
props->setDoubleValue("approach-speed-kts", 0.0);
props->setDoubleValue("touchdown-speed-kts", 0.0);
props->setDoubleValue("taxi-speed-kts", 0.0);
props->setDoubleValue("geometry/wing/span-ft", b);
props->setDoubleValue("geometry/wing/chord-ft", c);
props->setDoubleValue("geometry/weight-lbs", weight);
globals->add_new_subsystem<PerformanceDB>(SGSubsystemMgr::POST_FDM);
globals->get_subsystem<PerformanceDB>()->bind();
globals->get_subsystem<PerformanceDB>()->init();
FGAIManager *aiManager = new FGAIManager;
FGAIAircraft *ai = new FGAIAircraft;
ai->setGeodPos(geodPos);
ai->setSpeed(vel * SG_FPS_TO_KT);
ai->setPerformance("", "jet_transport");
ai->getPerformance()->initFromProps(props);
aiManager->attach(ai);
AIWakeGroup wg;
wg.AddAI(ai);
SGVec3d force = mesh->GetForce(wg, SGVec3d(vel, 0., 0.), rho);
CPPUNIT_ASSERT_DOUBLES_EQUAL(force[1], 0.0, 1e-9);
CPPUNIT_ASSERT_DOUBLES_EQUAL(force[2], -weight, 1e-9);
SGVec3d moment = mesh->GetMoment();
CPPUNIT_ASSERT_DOUBLES_EQUAL(moment[0], 0.0, 1e-9);
CPPUNIT_ASSERT_DOUBLES_EQUAL(moment[1], -0.5*weight, 1e-9);
CPPUNIT_ASSERT_DOUBLES_EQUAL(moment[2], 0.0, 1e-9);
auto accessor = FGTestApi::PrivateAccessor::FDM::Accessor();
for (int i=1; i<= accessor.read_FDM_AIWake_WakeMesh_nelm(mesh); ++i)
CPPUNIT_ASSERT_DOUBLES_EQUAL(accessor.read_FDM_AIWake_WakeMesh_Gamma(accessor.read_FDM_AIWake_AIWakeGroup_aiWakeData(&wg, 1))[i][1],
accessor.read_FDM_AIWake_WakeMesh_Gamma(mesh)[i][1], 1e-9);
}
void AeroMeshTests::testFourierLiftingLine()
{
double b = 10.0;
double c = 2.0;
double vel = 100.;
double weight = 50.;
auto accessor = FGTestApi::PrivateAccessor::FDM::Accessor();
WakeMesh_ptr mesh = new WakeMesh(b, c, "testMesh");
int N = accessor.read_FDM_AIWake_WakeMesh_nelm(mesh);
double **mtx = nr_matrix(1, N, 1, N);
double **coef = nr_matrix(1, N, 1, 1);
mesh->computeAoA(vel, rho, weight);
for (int m=1; m<=N; ++m) {
double vm = M_PI*m/(N+1);
double sm = sin(vm);
coef[m][1] = c*M_PI/(2*b);
for (int n=1; n<=N; ++n)
mtx[m][n] = sin(n*vm)*(1.0+coef[m][1]*n/sm);
}
nr_gaussj(mtx, N, coef, 1);
double S = b*c;
double AR = b*b/S;
double lift = 0.5*rho*S*vel*vel*coef[1][1]*M_PI*AR;
double sinAlpha = weight / lift;
lift *= sinAlpha;
cout << "y, Lift (Fourier), Lift (VLM), Corrected lift (VLM)" << endl;
for (int i=1; i<=N; ++i) {
double y = accessor.read_FDM_AIWake_WakeMesh_elements(mesh)[i-1]->getBoundVortexMidPoint()[1];
double theta = acos(2.0*y/b);
double gamma = 0.0;
for (int n=1; n<=N; ++n)
gamma += coef[n][1]*sin(n*theta);
gamma *= 2.0*b*vel*sinAlpha;
cout << y << ", " << gamma << ", " << accessor.read_FDM_AIWake_WakeMesh_Gamma(mesh)[i][1] << ", "
<< accessor.read_FDM_AIWake_WakeMesh_Gamma(mesh)[i][1] / gamma - 1.0 << endl;
}
nr_free_matrix(mtx, 1, N, 1, N);
nr_free_matrix(coef, 1, N, 1, 1);
}
void AeroMeshTests::testFrameTransformations()
{
double b = 10.0;
double c = 2.0;
double yaw = 80. * SGD_DEGREES_TO_RADIANS;
double pitch = 5. * SGD_DEGREES_TO_RADIANS;
double roll = -10. * SGD_DEGREES_TO_RADIANS;
SGQuatd orient = SGQuatd::fromYawPitchRoll(yaw, pitch, roll);
SGGeod geodPos = SGGeod::fromDeg(45.0, 10.0);
SGVec3d pos;
SGGeodesy::SGGeodToCart(geodPos, pos);
SGGeoc geoc = SGGeoc::fromCart(pos);
FGLocation loc(geoc.getLongitudeRad(),
geoc.getLatitudeRad(),
geoc.getRadiusFt());
CPPUNIT_ASSERT_DOUBLES_EQUAL(pos[0] * SG_METER_TO_FEET, loc(1), 1e-7);
CPPUNIT_ASSERT_DOUBLES_EQUAL(pos[1] * SG_METER_TO_FEET, loc(2), 1e-7);
CPPUNIT_ASSERT_DOUBLES_EQUAL(pos[2] * SG_METER_TO_FEET, loc(3), 1e-7);
AircraftMesh_ptr mesh = new AircraftMesh(b, c, "test");
mesh->setPosition(pos, orient);
FGQuaternion qJ(roll, pitch, yaw);
FGMatrix33 Tb2l = qJ.GetTInv();
FGColumnVector3 refPos = loc.GetTec2l() * loc;
for (int i=0; i < 4; ++i)
CPPUNIT_ASSERT_DOUBLES_EQUAL(orient(i), qJ((i+1) % 4 + 1), 1e-9);
auto accessor = FGTestApi::PrivateAccessor::FDM::Accessor();
for (int i=0; i < accessor.read_FDM_AIWake_WakeMesh_nelm(mesh); ++i) {
SGVec3d pt = accessor.read_FDM_AIWake_WakeMesh_elements(mesh)[i]->getBoundVortexMidPoint();
FGColumnVector3 ptJ(pt[0], pt[1], pt[2]);
FGColumnVector3 p = loc.GetTl2ec() * (refPos + Tb2l * ptJ);
CPPUNIT_ASSERT_DOUBLES_EQUAL(accessor.read_FDM_AIWake_AircraftMesh_midPt(mesh)[i][0], p(1), 1e-7);
CPPUNIT_ASSERT_DOUBLES_EQUAL(accessor.read_FDM_AIWake_AircraftMesh_midPt(mesh)[i][1], p(2), 1e-7);
CPPUNIT_ASSERT_DOUBLES_EQUAL(accessor.read_FDM_AIWake_AircraftMesh_midPt(mesh)[i][2], p(3), 1e-7);
pt = accessor.read_FDM_AIWake_WakeMesh_elements(mesh)[i]->getCollocationPoint();
ptJ.InitMatrix(pt[0], pt[1], pt[2]);
p = loc.GetTl2ec() * (refPos + Tb2l * ptJ);
CPPUNIT_ASSERT_DOUBLES_EQUAL(accessor.read_FDM_AIWake_AircraftMesh_collPt(mesh)[i][0], p(1), 1e-7);
CPPUNIT_ASSERT_DOUBLES_EQUAL(accessor.read_FDM_AIWake_AircraftMesh_collPt(mesh)[i][1], p(2), 1e-7);
CPPUNIT_ASSERT_DOUBLES_EQUAL(accessor.read_FDM_AIWake_AircraftMesh_collPt(mesh)[i][2], p(3), 1e-7);
}
}

View File

@@ -0,0 +1,52 @@
/*
* 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/>.
*/
#ifndef _FG_AERO_MESH_SYSTEM_TESTS_HXX
#define _FG_AERO_MESH_SYSTEM_TESTS_HXX
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestFixture.h>
// The system tests.
class AeroMeshTests : public CppUnit::TestFixture
{
// Set up the test suite.
CPPUNIT_TEST_SUITE(AeroMeshTests);
CPPUNIT_TEST(testFourierLiftingLine);
CPPUNIT_TEST(testFrameTransformations);
CPPUNIT_TEST(testLiftComputation);
CPPUNIT_TEST_SUITE_END();
public:
// Set up function for each test.
void setUp();
// Clean up after each test.
void tearDown();
// The tests.
void testFourierLiftingLine();
void testFrameTransformations();
void testLiftComputation();
};
#endif // _FG_AERO_MESH_SYSTEM_TESTS_HXX

View File

@@ -0,0 +1,12 @@
set(TESTSUITE_SOURCES
${TESTSUITE_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
#${CMAKE_CURRENT_SOURCE_DIR}/testgps.cxx
PARENT_SCOPE
)
set(TESTSUITE_HEADERS
${TESTSUITE_HEADERS}
#${CMAKE_CURRENT_SOURCE_DIR}/testgps.hxx
PARENT_SCOPE
)

View File

@@ -0,0 +1,24 @@
/*
* 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/>.
*/
#include "testgps.hxx"
// Set up the unit tests.
//CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(GPSTests, "System tests");

View File

@@ -0,0 +1,351 @@
#include <fstream>
#include <simgear/misc/sg_path.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/structure/exception.hxx>
#include <Main/fg_init.hxx>
#include <Main/fg_props.hxx>
#include <Instrumentation/gps.hxx>
#include <Autopilot/route_mgr.hxx>
#include <Environment/environment_mgr.hxx>
#include <Navaids/airways.hxx>
#include <Navaids/waypoint.hxx>
#include <Navaids/procedure.hxx>
#include "test_suite/helpers/globals.hxx"
#include "test_suite/helpers/NavDataCache.hxx"
#include "testgps.hxx"
using std::string;
using namespace flightgear;
char *homedir = ::getenv( "HOME" );
char *hostname = ::getenv( "HOSTNAME" );
bool free_hostname = false;
void testSetPosition(const SGGeod& aPos)
{
fgSetDouble("/position/longitude-deg", aPos.getLongitudeDeg());
fgSetDouble("/position/latitude-deg", aPos.getLatitudeDeg());
fgSetDouble("/position/altitude-ft", aPos.getElevationFt());
}
void printScratch(SGPropertyNode* scratch)
{
if (!scratch->getBoolValue("valid", false)) {
SG_LOG(SG_GENERAL, SG_ALERT, "Scratch is invalid.");
return;
}
SG_LOG(SG_GENERAL, SG_ALERT, "Scratch:" <<
scratch->getStringValue("ident") << "/" << scratch->getStringValue("name"));
SG_LOG(SG_GENERAL, SG_ALERT, "\t" << scratch->getDoubleValue("longitude-deg")
<< " " << scratch->getDoubleValue("latitude-deg") << " @ " << scratch->getDoubleValue("altitude-ft"));
SG_LOG(SG_GENERAL, SG_ALERT, "\t" << scratch->getDoubleValue("true-bearing-deg") <<
" (" << scratch->getDoubleValue("mag-bearing-deg") << " magnetic) " << scratch->getDoubleValue("distance-nm"));
if (scratch->hasChild("result-index")) {
SG_LOG(SG_GENERAL, SG_ALERT, "\tresult-index:" << scratch->getIntValue("result-index"));
}
if (scratch->hasChild("route-index")) {
SG_LOG(SG_GENERAL, SG_ALERT, "\troute-index:" << scratch->getIntValue("route-index"));
}
}
void printRoute(const WayptVec& aRoute)
{
SG_LOG(SG_GENERAL, SG_INFO, "route size=" << aRoute.size());
for (unsigned int r=0; r<aRoute.size();++r) {
Waypt* w = aRoute[r];
SG_LOG(SG_GENERAL, SG_ALERT, "\t" << r << ": " << w->ident() << " "
<< w->owner()->ident());
}
}
void createDummyRoute(FGRouteMgr* rm)
{
SGPropertyNode* rmInput = fgGetNode("/autopilot/route-manager/input", true);
rmInput->setStringValue("UW");
rmInput->setStringValue("TLA/347/13");
rmInput->setStringValue("TLA");
rmInput->setStringValue("HAVEN");
rmInput->setStringValue("NEW/305/29");
rmInput->setStringValue("NEW");
rmInput->setStringValue("OTR");
}
void GPSTests::setUp()
{
fgtest::initTestGlobals("GPS");
fgtest::initNavDataCache();
}
void GPSTests::tearDown()
{
fgtest::shutdownTestGlobals();
}
void GPSTests::testGPS()
{
fgSetDouble("/environment/magnetic-variation-deg", 0.0);
Airway::load();
SG_LOG(SG_GENERAL, SG_ALERT, "hello world!");
const FGAirport* egph = fgFindAirportID("EGPH");
SG_LOG(SG_GENERAL, SG_ALERT, "egph: cart location:" << egph->cart());
FGAirport::AirportFilter af;
FGPositionedList l = FGPositioned::findClosestN(egph->geod(), 20, 2000.0, &af);
for (unsigned int i=0; i<l.size(); ++i) {
SG_LOG(SG_GENERAL, SG_ALERT, "\t" << l[i]->ident() << "/" << l[i]->name());
}
//l = FGPositioned::findWithinRange(egph->geod(), 500.0, &af);
//for (unsigned int i=0; i<l.size(); ++i) {
// SG_LOG(SG_GENERAL, SG_ALERT, "\t" << l[i]->ident() << "/" << l[i]->name());
//}
FGRouteMgr* rm = new FGRouteMgr;
globals->add_subsystem( "route-manager", rm );
// FGEnvironmentMgr* envMgr = new FGEnvironmentMgr;
// globals->add_subsystem("environment", envMgr);
// envMgr->init();
fgSetBool("/sim/realism/simple-gps", true);
// _realismSimpleGps
SGPropertyNode* nd = fgGetNode("/instrumentation/gps", true);
GPS* gps = new GPS(nd);
globals->add_subsystem("gps", gps);
const FGAirport* egph = fgFindAirportID("EGPH");
testSetPosition(egph->geod());
// startup the route manager
rm->init();
nd->setBoolValue("serviceable", true);
fgSetBool("/systems/electrical/outputs/gps", true);
gps->init();
SGPropertyNode* scratch = nd->getChild("scratch", 0, true);
SGPropertyNode* wp = nd->getChild("wp", 0, true);
SGPropertyNode* wp1 = wp->getChild("wp", 1, true);
// update a few times
gps->update(0.05);
gps->update(0.05);
gps->update(0.05);
scratch->setStringValue("query", "TL");
scratch->setStringValue("type", "Vor");
scratch->setBoolValue("exact", false);
nd->setStringValue("command", "search");
printScratch(scratch);
nd->setStringValue("command", "next");
printScratch(scratch);
nd->setStringValue("command", "next");
printScratch(scratch);
// alphanumeric sort, partial matching
nd->setDoubleValue("config/min-runway-length-ft", 5000.0);
scratch->setBoolValue("exact", false);
scratch->setBoolValue("order-by-distance", false);
scratch->setStringValue("query", "KS");
scratch->setStringValue("type", "apt");
nd->setStringValue("command", "search");
printScratch(scratch);
nd->setStringValue("command", "next");
printScratch(scratch);
nd->setStringValue("command", "next");
printScratch(scratch);
// alphanumeric sort, explicit matching
scratch->setBoolValue("exact", true);
scratch->setBoolValue("order-by-distance", true);
scratch->setStringValue("type", "vor");
scratch->setStringValue("query", "DCS");
nd->setStringValue("command", "search");
printScratch(scratch);
nd->setStringValue("command", "next");
printScratch(scratch);
// search on totally missing
scratch->setBoolValue("exact", true);
scratch->setBoolValue("order-by-distance", true);
scratch->setStringValue("query", "FOFOFOFOF");
nd->setStringValue("command", "search");
printScratch(scratch);
// nearest
scratch->setStringValue("type", "apt");
scratch->setIntValue("max-results", 10);
nd->setStringValue("command", "nearest");
printScratch(scratch);
nd->setStringValue("command", "next");
printScratch(scratch);
nd->setStringValue("command", "next");
printScratch(scratch);
// direct to
nd->setStringValue("command", "direct");
SG_LOG(SG_GENERAL, SG_ALERT, "mode:" << nd->getStringValue("mode") << "\n\t"
<< wp1->getStringValue("ID") << " " << wp1->getDoubleValue("longitude-deg")
<< " " << wp1->getDoubleValue("latitude-deg"));
// OBS mode
scratch->setStringValue("query", "UW");
scratch->setBoolValue("order-by-distance", true);
nd->setStringValue("command", "search");
printScratch(scratch);
nd->setStringValue("command", "obs");
SG_LOG(SG_GENERAL, SG_ALERT, "mode:" << nd->getStringValue("mode") << "\n\t"
<< wp1->getStringValue("ID") << " " << wp1->getDoubleValue("longitude-deg")
<< " " << wp1->getDoubleValue("latitude-deg"));
// load route waypoints
createDummyRoute(rm);
scratch->setIntValue("route-index", 5);
nd->setStringValue("command", "load-route-wpt");
printScratch(scratch);
nd->setStringValue("command", "next");
printScratch(scratch);
nd->setStringValue("command", "next");
printScratch(scratch);
scratch->setIntValue("route-index", 2);
nd->setStringValue("command", "load-route-wpt");
nd->setStringValue("command", "direct");
SG_LOG(SG_GENERAL, SG_ALERT, "mode:" << nd->getStringValue("mode") << "\n\t"
<< wp1->getStringValue("ID") << " " << wp1->getDoubleValue("longitude-deg")
<< " " << wp1->getDoubleValue("latitude-deg"));
// route editing
SGGeod pos = egph->geod();
scratch->setStringValue("ident", "FOOBAR");
scratch->setDoubleValue("longitude-deg", pos.getLongitudeDeg());
scratch->setDoubleValue("latitude-deg", pos.getLatitudeDeg());
nd->setStringValue("command", "define-user-wpt");
printScratch(scratch);
// airways
FGPositioned::TypeFilter vorFilt(FGPositioned::VOR);
FGPositionedRef tla = FGPositioned::findClosestWithIdent("TLA", pos, &vorFilt);
FGPositionedRef big = FGPositioned::findClosestWithIdent("BIG", pos, &vorFilt);
FGPositionedRef pol = FGPositioned::findClosestWithIdent("POL", pos, &vorFilt);
const FGAirport* eddm = fgFindAirportID("EDDM");
FGPositionedRef mun = FGPositioned::findClosestWithIdent("MUN",
eddm->geod(), &vorFilt);
const FGAirport* ksfo = fgFindAirportID("KSFO");
FGPositionedRef sfo = FGPositioned::findClosestWithIdent("SFO",
ksfo->geod(), &vorFilt);
WayptRef awy1 = new NavaidWaypoint(tla, NULL);
WayptRef awy2 = new NavaidWaypoint(big, NULL);
WayptRef awy3 = new NavaidWaypoint(pol, NULL);
WayptRef awy4 = new NavaidWaypoint(mun, NULL);
WayptRef awy5 = new NavaidWaypoint(sfo, NULL);
WayptRef awy6 = new NavaidWaypoint(
(FGPositioned*) fgFindAirportID("KJFK"), NULL);
SGPath p("/Users/jmt/Desktop/airways.kml");
sg_ofstream f;
f.open(p, fstream::out | fstream::trunc);
// pre-amble
f << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n"
"<Document>\n";
WayptVec route;
Airway::highLevel()->route(awy1, awy3, route);
Route::dumpRouteToLineString("egph-egcc", route, f);
Airway::lowLevel()->route(awy1, awy2, route);
Route::dumpRouteToLineString("egph-big", route, f);
Airway::lowLevel()->route(awy2, awy4, route);
Route::dumpRouteToLineString("big-mun", route, f);
Airway::highLevel()->route(awy4, awy5, route);
Route::dumpRouteToLineString("mun-sfo", route, f);
Airway::lowLevel()->route(awy5, awy6, route);
Route::dumpRouteToLineString("sfo-jfk", route, f);
// post-amble
f << "</Document>\n"
"</kml>" << endl;
f.close();
// procedures
SGPath op("/Users/jmt/Desktop/procedures.kml");
f.open(op, fstream::out | fstream::trunc);
FGAirport* eham = (FGAirport*) fgFindAirportID("EHAM");
FGPositioned::TypeFilter fixFilt(FGPositioned::FIX);
WayptVec approach;
FGPositionedRef redfa = FGPositioned::findClosestWithIdent("REDFA",
eham->geod(), &fixFilt);
bool ok = eham->buildApproach(new NavaidWaypoint(redfa, NULL),
eham->getRunwayByIdent("18R"), approach);
if (!ok ) {
SG_LOG(SG_GENERAL, SG_INFO, "failed to build approach");
}
FGAirport* egll = (FGAirport*) fgFindAirportID("EGLL");
WayptVec approach2;
ok = egll->buildApproach(new NavaidWaypoint(big, NULL),
egll->getRunwayByIdent("27R"), approach2);
if (!ok ) {
SG_LOG(SG_GENERAL, SG_INFO, "failed to build approach");
}
// pre-amble
f << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n"
"<Document>\n";
Route::dumpRouteToLineString("REDFA 18R", approach, f);
Route::dumpRouteToLineString("EGLL 27R", approach2, f);
// post-amble
f << "</Document>\n"
"</kml>" << endl;
f.close();
}

View File

@@ -0,0 +1,48 @@
/*
* 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/>.
*/
#ifndef _FG_GPS_SYSTEM_TESTS_HXX
#define _FG_GPS_SYSTEM_TESTS_HXX
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestFixture.h>
// The system tests.
class GPSTests : public CppUnit::TestFixture
{
// Set up the test suite.
CPPUNIT_TEST_SUITE(GPSTests);
CPPUNIT_TEST(testGPS);
CPPUNIT_TEST_SUITE_END();
public:
// Set up function for each test.
void setUp();
// Clean up after each test.
void tearDown();
// The tests.
void testGPS();
};
#endif // _FG_GPS_SYSTEM_TESTS_HXX

View File

@@ -0,0 +1,12 @@
set(TESTSUITE_SOURCES
${TESTSUITE_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
${CMAKE_CURRENT_SOURCE_DIR}/testOptions.cxx
PARENT_SCOPE
)
set(TESTSUITE_HEADERS
${TESTSUITE_HEADERS}
${CMAKE_CURRENT_SOURCE_DIR}/testOptions.hxx
PARENT_SCOPE
)

View File

@@ -0,0 +1,24 @@
/*
* 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/>.
*/
#include "testOptions.hxx"
// Set up the unit tests.
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(OptionsTests, "System tests");

View File

@@ -0,0 +1,171 @@
// Written by James Turner, started 2017.
//
// Copyright (C) 2017 James Turner
//
// 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 "testOptions.hxx"
#include "config.h"
#include <chrono>
#include <thread>
#include "test_suite/FGTestApi/NavDataCache.hxx"
#include "test_suite/FGTestApi/scene_graph.hxx"
#include "test_suite/FGTestApi/testGlobals.hxx"
#include <simgear/package/Root.hxx>
#include <simgear/props/props_io.hxx>
#include "Main/fg_props.hxx"
#include "Main/globals.hxx"
#include "Main/options.hxx"
#include <Main/fg_init.hxx>
using namespace std::string_literals;
using namespace flightgear;
using namespace simgear::pkg;
void OptionsTests::setUp()
{
FGTestApi::setUp::initTestGlobals("options");
FGTestApi::setUp::initNavDataCache();
Options::reset();
fgLoadProps("defaults.xml", globals->get_props());
}
void OptionsTests::tearDown()
{
FGTestApi::tearDown::shutdownTestGlobals();
}
void OptionsTests::testLoadDefaultAircraft()
{
const auto customFGAircraftPath = SGPath::fromUtf8(FG_TEST_SUITE_DATA) / "customAircraftDir";
{
Options* opts = Options::sharedInstance();
opts->setShouldLoadDefaultConfig(false);
const string fgAircraftArg = "--fg-aircraft=" + customFGAircraftPath.utf8Str();
const char* args[] = {"dummypath", fgAircraftArg.c_str()};
opts->init(2, (char**)args, SGPath());
opts->processOptions();
}
fgInitAircraftPaths(false);
fgInitAircraft(false, false);
CPPUNIT_ASSERT_EQUAL("c172p"s, string{fgGetString("/sim/aircraft")});
CPPUNIT_ASSERT_EQUAL("c172p"s, string{fgGetString("/sim/aircraft-id")});
//CPPUNIT_ASSERT_EQUAL(adPath.utf8Str(), string{fgGetString("/sim/aircraft-dir")});
}
void OptionsTests::testOptionAircraftWithAircraftDir()
{
const auto adPath = SGPath::fromUtf8(FG_TEST_SUITE_DATA) / "customAircraftDir" / "overrideUfo";
{
Options* opts = Options::sharedInstance();
opts->setShouldLoadDefaultConfig(false);
const string aircraftDirArg = "--aircraft-dir=" + adPath.utf8Str();
const char* args[] = {"dummypath", "--aircraft=ufo", aircraftDirArg.c_str()};
opts->init(3, (char**)args, SGPath());
opts->processOptions();
}
fgInitAircraftPaths(false);
fgInitAircraft(false, false);
CPPUNIT_ASSERT_EQUAL("ufo"s, string{fgGetString("/sim/aircraft")});
CPPUNIT_ASSERT_EQUAL("ufo"s, string{fgGetString("/sim/aircraft-id")});
CPPUNIT_ASSERT_EQUAL(adPath.utf8Str(), string{fgGetString("/sim/aircraft-dir")});
}
void OptionsTests::testOptionAircraftWithFGAircraft()
{
const auto customFGAircraftPath = SGPath::fromUtf8(FG_TEST_SUITE_DATA) / "customAircraftDir";
{
Options* opts = Options::sharedInstance();
opts->setShouldLoadDefaultConfig(false);
const string fgAircraftArg = "--fg-aircraft=" + customFGAircraftPath.utf8Str();
const char* args[] = {"dummypath", "--aircraft=ufo", fgAircraftArg.c_str()};
opts->init(3, (char**)args, SGPath());
opts->processOptions();
}
fgInitAircraftPaths(false);
fgInitAircraft(false, false);
CPPUNIT_ASSERT_EQUAL("ufo"s, string{fgGetString("/sim/aircraft")});
CPPUNIT_ASSERT_EQUAL("ufo"s, string{fgGetString("/sim/aircraft-id")});
const auto correctDir = (customFGAircraftPath / "overrideUfo").utf8Str();
CPPUNIT_ASSERT_EQUAL(correctDir, string{fgGetString("/sim/aircraft-dir")});
}
void OptionsTests::testOptionAircraftUnqualified()
{
const auto packageAircraftDir = SGPath::fromUtf8(FG_TEST_SUITE_DATA) / "dummy_package_root";
SGSharedPtr<Root> pkgRoot(new Root(packageAircraftDir, FLIGHTGEAR_VERSION));
globals->setPackageRoot(pkgRoot);
{
Options* opts = Options::sharedInstance();
opts->setShouldLoadDefaultConfig(false);
const char* args[] = {"dummypath", "--aircraft=bob"};
opts->init(2, (char**)args, SGPath());
opts->processOptions();
}
fgInitAircraftPaths(false);
fgInitAircraft(false, false);
CPPUNIT_ASSERT_EQUAL("bob"s, string{fgGetString("/sim/aircraft")});
CPPUNIT_ASSERT_EQUAL("org.fg.test.catalog1.bob"s, string{fgGetString("/sim/aircraft-id")});
const auto correctDir = (packageAircraftDir / "org.fg.test.catalog1" / "Aircraft" / "bobCraft").utf8Str();
CPPUNIT_ASSERT_EQUAL(correctDir, string{fgGetString("/sim/aircraft-dir")});
}
void OptionsTests::testOptionAircraftFullyQualified()
{
const auto packageAircraftDir = SGPath::fromUtf8(FG_TEST_SUITE_DATA) / "dummy_package_root";
SGSharedPtr<Root> pkgRoot(new Root(packageAircraftDir, FLIGHTGEAR_VERSION));
globals->setPackageRoot(pkgRoot);
{
Options* opts = Options::sharedInstance();
opts->setShouldLoadDefaultConfig(false);
const char* args[] = {"dummypath", "--aircraft=org.fg.test.catalog1.bob"};
opts->init(2, (char**)args, SGPath());
opts->processOptions();
}
fgInitAircraftPaths(false);
fgInitAircraft(false, false);
CPPUNIT_ASSERT_EQUAL("bob"s, string{fgGetString("/sim/aircraft")});
CPPUNIT_ASSERT_EQUAL("org.fg.test.catalog1.bob"s, string{fgGetString("/sim/aircraft-id")});
const auto correctDir = (packageAircraftDir / "org.fg.test.catalog1" / "Aircraft" / "bobCraft").utf8Str();
CPPUNIT_ASSERT_EQUAL(correctDir, string{fgGetString("/sim/aircraft-dir")});
}

View File

@@ -0,0 +1,52 @@
/*
* 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/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
// The system tests.
class OptionsTests : public CppUnit::TestFixture
{
// Set up the test suite.
CPPUNIT_TEST_SUITE(OptionsTests);
CPPUNIT_TEST(testLoadDefaultAircraft);
CPPUNIT_TEST(testOptionAircraftWithAircraftDir);
CPPUNIT_TEST(testOptionAircraftUnqualified);
CPPUNIT_TEST(testOptionAircraftFullyQualified);
CPPUNIT_TEST(testOptionAircraftWithFGAircraft);
CPPUNIT_TEST_SUITE_END();
public:
// Set up function for each test.
void setUp();
// Clean up after each test.
void tearDown();
// The tests.
void testOptionAircraftWithAircraftDir();
void testOptionAircraftUnqualified();
void testOptionAircraftFullyQualified();
void testOptionAircraftWithFGAircraft();
void testLoadDefaultAircraft();
};

View File

@@ -0,0 +1,12 @@
set(TESTSUITE_SOURCES
${TESTSUITE_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
#${CMAKE_CURRENT_SOURCE_DIR}/testnavs.cxx
PARENT_SCOPE
)
set(TESTSUITE_HEADERS
${TESTSUITE_HEADERS}
#${CMAKE_CURRENT_SOURCE_DIR}/testnavs.hxx
PARENT_SCOPE
)

View File

@@ -0,0 +1,24 @@
/*
* 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/>.
*/
#include "testnavs.hxx"
// Set up the unit tests.
//CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(NavaidsTests, "System tests");

View File

@@ -0,0 +1,107 @@
// Written by James Turner, started 2009.
//
// Copyright (C) 2009 Curtis L. Olson
//
// 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 <iostream>
#include <simgear/misc/sg_path.hxx>
#include <Navaids/fixlist.hxx>
//#include "ilslist.hxx"
#include <Navaids/navlist.hxx>
//#include "mkrbeacons.hxx"
#include "test_suite/dataStore.hxx"
#include "testnavs.hxx"
void NavaidsTests::testNavs()
{
double heading, dist;
DataStore &data = DataStore::get();
FGNavList *current_navlist = new FGNavList;
SGPath p_nav = data.getFGRoot() / "/Navaids/default.nav";
current_navlist->init( p_nav );
FGNavRecord *n;
if ( (n = current_navlist->findByFreq( -93.2 * SG_DEGREES_TO_RADIANS,
45.14 * SG_DEGREES_TO_RADIANS,
3000, 117.30)) != NULL )
{
std::cout << "Found a vor station in range" << std::endl;
std::cout << " id = " << n->get_ident() << std::endl;
} else {
std::cout << "not picking up vor. :-(" << std::endl;
}
FGNavRecord *dcs;
if ( (dcs = current_navlist->findByIdent( "DCS",
-3.3 * SG_DEGREES_TO_RADIANS,
55.9 * SG_DEGREES_TO_RADIANS))
!= NULL ) {
std::cout << "Found DCS by ident" << std::endl;
if (dcs->get_freq() != 11520)
std::cout << "Frequency for DCS VOR is wrong (should be 115.20), it's "
<< dcs->get_freq() << std::endl;
} else {
std::cout << "couldn't locate DCS (Dean-Cross) VOR" << std::endl;
}
// we have to init the marker beacon storage before we parse the ILS file
FGMarkerBeacons *current_beacons = new FGMarkerBeacons;
current_beacons->init();
FGILSList *current_ilslist = new FGILSList;
SGPath p_ils = data.getFGRoot() / "/Navaids/default.ils";
current_ilslist->init( p_ils );
FGILS *i = current_ilslist->findByFreq( -93.1 * SG_DEGREES_TO_RADIANS,
45.24 * SG_DEGREES_TO_RADIANS,
3000, 110.30);
if ( i != NULL ) {
std::cout << "Found an ils station in range" << std::endl;
std::cout << " apt = " << i->get_aptcode() << std::endl;
std::cout << " rwy = " << i->get_rwyno() << std::endl;
} else {
std::cout << "not picking up ils. :-(" << std::endl;
}
FGFixList *current_fixlist = new FGFixList;
SGPath p_fix = data.getFGRoot() / "/Navaids/default.fix";
current_fixlist->init( p_fix );
FGFix fix;
// attempting to get the position relative to the OTR VOR; heading
// should be 108 degrees, distance 74nm (according to my SimCharts
// v1.5)
if ( current_fixlist->query_and_offset( "DOGGA",
-0.103 * SG_DEGREES_TO_RADIANS,
53.698 * SG_DEGREES_TO_RADIANS,
3000, &fix, &heading, &dist) )
{
std::cout << "Found a matching fix" << std::endl;
std::cout << " id = " << fix.get_ident() << std::endl;
std::cout << " heading = " << heading << " dist = " << dist * SG_METER_TO_NM
<< std::endl;
} else {
CPPUNIT_FAIL("did not find fix. :-(");
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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/>.
*/
#ifndef _FG_NAVAIDS_SYSTEM_TESTS_HXX
#define _FG_NAVAIDS_SYSTEM_TESTS_HXX
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestFixture.h>
// The system tests.
class NavaidsTests : public CppUnit::TestFixture
{
// Set up the test suite.
CPPUNIT_TEST_SUITE(NavaidsTests);
CPPUNIT_TEST(testNavs);
CPPUNIT_TEST_SUITE_END();
public:
// Set up function for each test.
void setUp() {}
// Clean up after each test.
void tearDown() {}
// The tests.
void testNavs();
};
#endif // _FG_NAVAIDS_SYSTEM_TESTS_HXX

457
test_suite/testSuite.cxx Normal file
View File

@@ -0,0 +1,457 @@
/*
* Copyright (C) 2016-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/>.
*/
#include <algorithm>
#include <cstring>
#include <iostream>
#include <simgear/debug/logstream.hxx>
#include "dataStore.hxx"
#include "fgTestRunner.hxx"
#include "formatting.hxx"
#include "logging.hxx"
using namespace std;
// The help message.
void helpPrintout(std::ostream &stream) {
stream << "Usage: run_test_suite [options]" << std::endl << std::endl;
stream << "Options:" << std::endl;
stream << " -h, --help show this help message and exit." << std::endl;
stream << std::endl;
stream << " Test selection options:" << std::endl;
stream << " -s, --system-tests execute the system/functional tests." << std::endl;
stream << " -u, --unit-tests execute the unit tests." << std::endl;
stream << " -g, --gui-tests execute the GUI tests." << std::endl;
stream << " -m, --simgear-tests execute the simgear tests." << std::endl;
stream << " -f, --fgdata-tests execute the FGData tests." << std::endl;
stream << std::endl;
stream << " The -s, -u, -g, and -m options accept an optional argument to perform a" << std::endl;
stream << " subset of all tests. This argument should either be the name of a test" << std::endl;
stream << " suite, the full name of an individual test, or a comma separated list." << std::endl;
stream << " E.g. -u DigitalFilterTests\n";
stream << std::endl;
stream << " Full test names consist of the test suite name, the separator '::' and then" << std::endl;
stream << " the individual test name. The test names can revealed with the verbose" << std::endl;
stream << " option." << std::endl;
stream << std::endl;
stream << " Logging options:" << std::endl;
stream << " --log-level={bulk,debug,info,warn,alert,popup,dev_warn,dev_alert}" << std::endl;
stream << " specify the minimum logging level to output" << std::endl;
stream << " --log-class=[none, terrain, astro, flight, input, gl, view, cockpit," << std::endl;
stream << " general, math, event, aircraft, autopilot, io, clipper," << std::endl;
stream << " network, atc, nasal, instrumentation, systems, ai, environment," << std::endl;
stream << " sound, navaid, gui, terrasync, particles, headless, osg," << std::endl;
stream << " undefined, all]" << std::endl;
stream << " select the logging class(es) to output." << std::endl;
stream << " --log-split output the different non-interleaved log streams" << std::endl;
stream << " sequentially." << std::endl;
stream << std::endl;
stream << " Verbosity options:" << std::endl;
stream << " -t, --timings verbose output including names and timings for all" << std::endl;
stream << " tests." << std::endl;
stream << " -c, --ctest simplified output suitable for running via CTest." << std::endl;
stream << " -d, --debug disable IO capture for debugging (super verbose output)." << std::endl;
stream << " --no-summary disable the final summary printout." << std::endl;
stream << std::endl;
stream << " FG options:" << std::endl;
stream << " --fg-root the path to FGData." << std::endl;
stream << std::endl;
stream << "Environmental variables:" << std::endl;
stream << " FG_TEST_LOG_LEVEL equivalent to the --log-level option." << std::endl;
stream << " FG_TEST_LOG_CLASS equivalent to the --log-class option." << std::endl;
stream << " FG_TEST_LOG_SPLIT equivalent to the --log-split option." << std::endl;
stream << " FG_TEST_TIMINGS equivalent to the -t or --timings option." << std::endl;
stream << " FG_TEST_DEBUG equivalent to the -d or --debug option." << std::endl;
stream << " FG_ROOT the path to FGData. The order of precedence is" << std::endl;
stream << " --fg-root, the FG_DATA_DIR CMake option, FG_ROOT," << std::endl;
stream << " '../fgdata/', and '../data/'." << std::endl;
stream.flush();
}
// Convert the log class comma separated string list into a simgear debug class value.
sgDebugClass processLogClass(std::string classList, bool &failure) {
// Declarations.
std::string logClassItem, val;
unsigned int logClass=0;
// Convert the command line value into a string array.
std::replace(classList.begin(), classList.end(), ',', ' ');
std::vector<std::string> logClasses;
stringstream temp(classList);
while (temp >> val)
logClasses.push_back(val);
// Build up the value.
for (auto const& logClassItem: logClasses)
if (logClassItem == "none")
logClass |= SG_NONE;
else if (logClassItem == "terrain")
logClass |= SG_TERRAIN;
else if (logClassItem == "astro")
logClass |= SG_ASTRO;
else if (logClassItem == "flight")
logClass |= SG_FLIGHT;
else if (logClassItem == "input")
logClass |= SG_INPUT;
else if (logClassItem == "gl")
logClass |= SG_GL;
else if (logClassItem == "view")
logClass |= SG_VIEW;
else if (logClassItem == "cockpit")
logClass |= SG_COCKPIT;
else if (logClassItem == "general")
logClass |= SG_GENERAL;
else if (logClassItem == "math")
logClass |= SG_MATH;
else if (logClassItem == "event")
logClass |= SG_EVENT;
else if (logClassItem == "aircraft")
logClass |= SG_AIRCRAFT;
else if (logClassItem == "autopilot")
logClass |= SG_AUTOPILOT;
else if (logClassItem == "io")
logClass |= SG_IO;
else if (logClassItem == "clipper")
logClass |= SG_CLIPPER;
else if (logClassItem == "network")
logClass |= SG_NETWORK;
else if (logClassItem == "atc")
logClass |= SG_ATC;
else if (logClassItem == "nasal")
logClass |= SG_NASAL;
else if (logClassItem == "instrumentation")
logClass |= SG_INSTR;
else if (logClassItem == "systems")
logClass |= SG_SYSTEMS;
else if (logClassItem == "ai")
logClass |= SG_AI;
else if (logClassItem == "environment")
logClass |= SG_ENVIRONMENT;
else if (logClassItem == "sound")
logClass |= SG_SOUND;
else if (logClassItem == "navaid")
logClass |= SG_NAVAID;
else if (logClassItem == "gui")
logClass |= SG_GUI;
else if (logClassItem == "terrasync")
logClass |= SG_TERRASYNC;
else if (logClassItem == "particles")
logClass |= SG_PARTICLES;
else if (logClassItem == "headless")
logClass |= SG_HEADLESS;
else if (logClassItem == "osg")
logClass |= SG_OSG;
else if (logClassItem == "undefined")
logClass |= SG_UNDEFD;
else if (logClassItem == "all")
logClass |= SG_ALL;
else {
std::cout << "The log class \"" << logClassItem << "\" must be one of:" << std::endl;
std::cout << " {none, terrain, astro, flight, input, gl, view, cockpit, general, math," << std::endl;
std::cout << " event, aircraft, autopilot, io, clipper, network, atc, nasal," << std::endl;
std::cout << " instrumentation, systems, ai, environment, sound, navaid, gui, terrasync," << std::endl;
std::cout << " particles, headless, osg, undefined, all}" << std::endl << std::endl;
std::cout.flush();
failure = true;
}
// Return a simgear debug class.
return sgDebugClass(logClass);
}
// Convert the log priority string into a simgear debug priority value.
sgDebugPriority processLogPriority(std::string logLevel, bool &failure) {
// Declarations.
sgDebugPriority logPriority=SG_INFO;
// Conversion.
if (logLevel == "bulk")
logPriority = SG_BULK;
else if (logLevel == "debug")
logPriority = SG_DEBUG;
else if (logLevel == "info")
logPriority = SG_INFO;
else if (logLevel == "warn")
logPriority = SG_WARN;
else if (logLevel == "alert")
logPriority = SG_ALERT;
else if (logLevel == "popup")
logPriority = SG_POPUP;
else if (logLevel == "dev_warn")
logPriority = SG_DEV_WARN;
else if (logLevel == "dev_alert")
logPriority = SG_DEV_ALERT;
else {
std::cout << "The log level setting of \"" << logLevel << "\" must be one of {bulk,debug,info,warn,alert,popup,dev_warn,dev_alert}.\n\n";
std::cout.flush();
failure = true;
}
// Return the simgear debug priority.
return logPriority;
}
// Print out a summary of the relax test suite.
void summary(CppUnit::OStream &stream, int system_result, int unit_result, int gui_result, int simgear_result, int fgdata_result)
{
int synopsis = 0;
// Title.
string text = "Summary of the FlightGear test suite";
printTitle(stream, text);
// Subtitle.
text = "Synopsis";
printSection(stream, text);
// System/functional test summary.
if (system_result != -1) {
text = "System/functional tests";
printSummaryLine(stream, text, system_result);
synopsis += system_result;
}
// Unit test summary.
if (unit_result != -1) {
text = "Unit tests";
printSummaryLine(stream, text, unit_result);
synopsis += unit_result;
}
// GUI test summary.
if (gui_result != -1) {
text = "GUI tests";
printSummaryLine(stream, text, gui_result);
synopsis += gui_result;
}
// Simgear unit test summary.
if (simgear_result != -1) {
text = "Simgear unit tests";
printSummaryLine(stream, text, simgear_result);
synopsis += simgear_result;
}
// FGData test summary.
if (fgdata_result != -1) {
text = "FGData tests";
printSummaryLine(stream, text, fgdata_result);
synopsis += fgdata_result;
}
// Synopsis.
text ="Synopsis";
printSummaryLine(stream, text, synopsis);
// End.
stream << endl << endl;
}
int main(int argc, char **argv)
{
// Declarations.
int status_gui=-1, status_simgear=-1, status_system=-1, status_unit=-1, status_fgdata=-1;
bool run_system=false, run_unit=false, run_gui=false, run_simgear=false, run_fgdata=false;
bool logSplit=false;
bool timings=false, ctest_output=false, debug=false, printSummary=true, help=false;
char *subset_system=NULL, *subset_unit=NULL, *subset_gui=NULL, *subset_simgear=NULL, *subset_fgdata=NULL;
bool failure=false;
char firstchar;
std::string arg, delim, fgRoot, logClassVal, logLevel;
size_t delimPos;
// The default logging class and priority to show.
sgDebugClass logClass=SG_ALL;
sgDebugPriority logPriority=SG_INFO;
// Process environmental variables before the command line options.
if (getenv("FG_TEST_LOG_LEVEL"))
logPriority = processLogPriority(getenv("FG_TEST_LOG_LEVEL"), failure);
if (getenv("FG_TEST_LOG_CLASS"))
logClass = processLogClass(getenv("FG_TEST_LOG_CLASS"), failure);
if (getenv("FG_TEST_LOG_SPLIT"))
logSplit = true;
if (getenv("FG_TEST_TIMINGS"))
timings = true;
if (getenv("FG_TEST_DEBUG"))
debug = true;
if (failure)
return 1;
// Argument parsing.
for (int i = 1; i < argc; i++) {
firstchar = '\0';
arg = argv[i];
if (i < argc-1)
firstchar = argv[i+1][0];
// System test.
if (arg == "-s" || arg == "--system-tests") {
run_system = true;
if (firstchar != '-')
subset_system = argv[i+1];
// Unit test.
} else if (arg == "-u" || arg == "--unit-tests") {
run_unit = true;
if (firstchar != '-')
subset_unit = argv[i+1];
// GUI test.
} else if (arg == "-g" || arg == "--gui-tests") {
run_gui = true;
if (firstchar != '-')
subset_gui = argv[i+1];
// Simgear test.
} else if (arg == "-m" || arg == "--simgear-tests") {
run_simgear = true;
if (firstchar != '-')
subset_simgear = argv[i+1];
// FGData test.
} else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--fgdata-tests") == 0) {
run_fgdata = true;
if (firstchar != '-')
subset_fgdata = argv[i+1];
// Log class.
} else if (arg.find( "--log-class" ) == 0) {
// Process the command line.
logClass = SG_NONE;
delimPos = arg.find('=');
logClassVal = arg.substr(delimPos + 1);
logClass = processLogClass(logClassVal, failure);
if (failure)
return 1;
// Log level.
} else if (arg.find( "--log-level" ) == 0) {
// Process the command line level.
delimPos = arg.find('=');
logLevel = arg.substr(delimPos + 1);
logPriority = processLogPriority(logLevel, failure);
if (failure)
return 1;
// Log splitting.
} else if (arg == "--log-split") {
logSplit = true;
// Timing output.
} else if (arg == "-t" || arg == "--timings") {
timings = true;
// CTest suitable output.
} else if (arg == "-c" || arg == "--ctest") {
ctest_output = true;
// Debug output.
} else if (arg == "-d" || arg == "--debug") {
debug = true;
// No summary output.
} else if (arg == "--no-summary") {
printSummary = false;
// Help.
} else if (arg == "-h" || arg == "--help") {
help = true;
// FGData path.
} else if (arg == "--fg-root") {
if (firstchar != '-')
fgRoot = argv[i+1];
}
}
// Help.
if (help) {
helpPrintout(std::cout);
return 0;
}
// Turn on all tests if no subset was specified.
if (!run_system && !run_unit && !run_gui && !run_simgear && !run_fgdata) {
run_system = true;
run_unit = true;
run_gui = true;
run_simgear = true;
run_fgdata = true;
}
// Set up the data store singleton and FGData path.
DataStore& data = DataStore::get();
if (data.findFGRoot(fgRoot, debug) != 0) {
return 1;
}
if (data.validateFGRoot() != 0) {
return 1;
}
// Set up logging.
sglog().setDeveloperMode(true);
if (debug)
sglog().setLogLevels(sgDebugClass(logClass), logPriority);
else
setupLogging(sgDebugClass(logClass), logPriority, logSplit);
// Execute each of the test suite categories.
if (run_system)
status_system = testRunner("System tests", "System / functional tests", subset_system, timings, ctest_output, debug);
if (run_unit)
status_unit = testRunner("Unit tests", "Unit tests", subset_unit, timings, ctest_output, debug);
if (run_gui && 0) // Disabled as there are no GUI tests yet.
status_gui = testRunner("GUI tests", "GUI tests", subset_gui, timings, ctest_output, debug);
if (run_simgear)
status_simgear = testRunner("Simgear unit tests", "Simgear unit tests", subset_simgear, timings, ctest_output, debug);
if (run_fgdata)
status_fgdata = testRunner("FGData tests", "FGData tests", subset_fgdata, timings, ctest_output, debug);
// Summary printout.
if (printSummary && !ctest_output)
summary(cerr, status_system, status_unit, status_gui, status_simgear, status_fgdata);
// Deactivate the logging.
if (!debug)
stopLogging();
// Failure.
if (status_system > 0)
return 1;
if (status_unit > 0)
return 1;
if (status_gui > 0)
return 1;
if (status_simgear > 0)
return 1;
if (status_fgdata > 0)
return 1;
// Success.
return 0;
}

View File

@@ -0,0 +1,6 @@
<?xml version="1.0"?>
<PropertyList include="BN-2-main.xml">
<texture-path>Textures/Directflight</texture-path>
</PropertyList>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0"?>
<PropertyList include="BN-2-main.xml">
<texture-path>Textures/Hebridean</texture-path>
</PropertyList>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0"?>
<PropertyList include="BN-2-main.xml">
<texture-path>Textures/Loganair</texture-path>
</PropertyList>

View File

@@ -0,0 +1,87 @@
<?xml version="1.0"?>
<PropertyList>
<description>Britten-Norman BN-2 Islander</description>
<author>Paul RICHTER, Syd Adams, Keith Paterson</author>
<path>Models/bn2.ac</path>
<animation>
<type>select</type>
<object-name>prop-blur-left</object-name>
<condition>
<greater-than>
<property>velocities/true-airspeed-kt</property>
<value>10</value>
</greater-than>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>landinglight.L</object-name>
<condition>
<less-than>
<property>velocities/true-airspeed-kt</property>
<value>10</value>
</less-than>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>landinglight.R</object-name>
<condition>
<less-than>
<property>velocities/true-airspeed-kt</property>
<value>10</value>
</less-than>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>prop-left</object-name>
<condition>
<less-than>
<property>velocities/true-airspeed-kt</property>
<value>10</value>
</less-than>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>prop-disc-left</object-name>
<condition>
<greater-than>
<property>velocities/true-airspeed-kt</property>
<value>20</value>
</greater-than>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>prop-blur-right</object-name>
<condition>
<greater-than>
<property>velocities/true-airspeed-kt</property>
<value>10</value>
</greater-than>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>prop-disc-right</object-name>
<condition>
<greater-than>
<property>velocities/true-airspeed-kt</property>
<value>20</value>
</greater-than>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>prop-right</object-name>
<condition>
<less-than>
<property>velocities/true-airspeed-kt</property>
<value>10</value>
</less-than>
</condition>
</animation>
</PropertyList>

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
[LocalizedFileNames]
bn2.ac=@bn2.ac,0

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<PropertyList>
<flightplan>
<wp>
<name>on1</name>
<lat>59.534235</lat>
<lon>-1.629072</lon>
<ktas>10</ktas>
<on-ground>1</on-ground>
</wp>
<wp>
<name>on2</name>
<lat>59.534315</lat>
<lon>-1.629182</lon>
<ktas>10</ktas>
<on-ground>1</on-ground>
</wp>
<wp>
<name>on5</name>
<lat>59.534364</lat>
<lon>-1.629345</lon>
<ktas>10</ktas>
<on-ground>1</on-ground>
</wp>
<wp>
<name>on4</name>
<lat>59.534372</lat>
<lon>-1.629544</lon>
<ktas>10</ktas>
<on-ground>1</on-ground>
</wp>
<wp>
<name>on10</name>
<lat>59.534363</lat>
<lon>-1.629681</lon>
<ktas>10</ktas>
<on-ground>1</on-ground>
</wp>
<wp>
<name>on3</name>
<lat>59.5343438</lat>
<lon>-1.6298094</lon>
<ktas>10</ktas>
<on-ground>1</on-ground>
</wp>
<wp>
<name>on11</name>
<lat>59.5341711</lat>
<lon>-1.63037</lon>
<ktas>10</ktas>
<on-ground>1</on-ground>
</wp>
<wp>
<name>END</name>
</wp>
</flightplan>
</PropertyList>

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<PropertyList>
<flightplan>
<wp>
<name>on1</name>
<lat>59.534235</lat>
<lon>-1.629072</lon>
<ktas>10</ktas>
<on-ground>1</on-ground>
</wp>
<wp>
<name>on2</name>
<lat>59.534315</lat>
<lon>-1.629182</lon>
<ktas>10</ktas>
<on-ground>1</on-ground>
</wp>
<wp>
<name>on6</name>
<lat>59.5343928</lat>
<lon>-1.6292113</lon>
<ktas>10</ktas>
<on-ground>1</on-ground>
</wp>
<wp>
<name>on7</name>
<lat>59.5344594</lat>
<lon>-1.6292113</lon>
<ktas>10</ktas>
<on-ground>1</on-ground>
</wp>
<wp>
<name>on8</name>
<lat>59.5345519</lat>
<lon>-1.6291362</lon>
<ktas>10</ktas>
<on-ground>1</on-ground>
</wp>
<wp>
<name>on9</name>
<lat>59.5346335</lat>
<lon>-1.6288868</lon>
<ktas>10</ktas>
<on-ground>1</on-ground>
</wp>
<wp>
<name>on12</name>
<lat>59.5347749</lat>
<lon>-1.6284227</lon>
<ktas>10</ktas>
<on-ground>1</on-ground>
</wp>
<wp>
<name>END</name>
</wp>
</flightplan>
</PropertyList>

View File

@@ -0,0 +1,62 @@
<?xml version="1.0"?>
<!-- Updated 29NOV20 to remove EGEL flights as no runway defined in FG -->
<trafficlist>
<aircraft>
<model>Aircraft/BN-2/BN-2-Hebridean.xml</model>
<livery>HBR</livery>
<airline>HBR</airline>
<home-port>EGEO</home-port>
<required-aircraft>HBR_BN_2</required-aircraft>
<actype>BN2</actype>
<offset>0</offset>
<radius>8</radius>
<flighttype>gate</flighttype>
<performance-class>turboprop_transport</performance-class>
<registration>G-HEBO</registration>
<heavy>false</heavy>
</aircraft>
<aircraft>
<model>Aircraft/BN-2/BN-2-Hebridean.xml</model>
<livery>HBR</livery>
<airline>HBR</airline>
<home-port>EGEO</home-port>
<required-aircraft>HBR_BN_2</required-aircraft>
<actype>BN2</actype>
<offset>0</offset>
<radius>8</radius>
<flighttype>gate</flighttype>
<performance-class>turboprop_transport</performance-class>
<registration>G-HEBS</registration>
<heavy>false</heavy>
</aircraft>
<flight>
<callsign>Hebridean_1000</callsign>
<required-aircraft>HBR_BN_2</required-aircraft>
<fltrules>VFR</fltrules>
<departure>
<port>EGEO</port>
<time>2/08:25:00</time>
</departure>
<cruise-alt>50</cruise-alt>
<arrival>
<port>EGEY</port>
<time>2/08:50:00</time>
</arrival>
<repeat>WEEK</repeat>
</flight>
<flight>
<callsign>Hebridean_1001</callsign>
<required-aircraft>HBR_BN_2</required-aircraft>
<fltrules>VFR</fltrules>
<departure>
<port>EGEO</port>
<time>2/14:10:00</time>
</departure>
<cruise-alt>50</cruise-alt>
<arrival>
<port>EGEY</port>
<time>2/15:20:00</time>
</arrival>
<repeat>WEEK</repeat>
</flight>
</trafficlist>

View File

@@ -0,0 +1,75 @@
<?xml version="1.0"?>
<!-- Updated 29NOV20 to remove EGEL flights as no runway defined in FG -->
<trafficlist>
<aircraft>
<model>Aircraft/BN-2/BN-2-Hebridean.xml</model>
<livery>TST</livery>
<airline>TST</airline>
<home-port>YSSY</home-port>
<required-aircraft>TST_BN_1</required-aircraft>
<actype>BN2</actype>
<offset>0</offset>
<radius>8</radius>
<performance-class>turboprop_transport</performance-class>
<registration>G-HEBO</registration>
<heavy>false</heavy>
</aircraft>
<aircraft>
<model>Aircraft/BN-2/BN-2-Hebridean.xml</model>
<livery>TST</livery>
<airline>TST</airline>
<home-port>YBBN</home-port>
<required-aircraft>TST_BN_2</required-aircraft>
<actype>BN2</actype>
<offset>0</offset>
<radius>8</radius>
<performance-class>turboprop_transport</performance-class>
<registration>G-HEBY</registration>
<heavy>false</heavy>
</aircraft>
<flight>
<callsign>gaParkYSSY1</callsign>
<required-aircraft>TST_BN_1</required-aircraft>
<fltrules>VFR</fltrules>
<departure>
<port>YSSY</port>
<time>00:40:00</time>
</departure>
<cruise-alt>50</cruise-alt>
<arrival>
<port>YBBN</port>
<time>00:52:00</time>
</arrival>
<repeat>03Hr</repeat>
</flight>
<flight>
<callsign>gaParkYSSY2</callsign>
<required-aircraft>TST_BN_1</required-aircraft>
<fltrules>VFR</fltrules>
<departure>
<port>YBBN</port>
<time>01:20:00</time>
</departure>
<cruise-alt>50</cruise-alt>
<arrival>
<port>YSSY</port>
<time>01:50:00</time>
</arrival>
<repeat>03Hr</repeat>
</flight>
<flight>
<callsign>Test_YSSY_1002</callsign>
<required-aircraft>TST_BN_2</required-aircraft>
<fltrules>VFR</fltrules>
<departure>
<port>YSSY</port>
<time>00:48:00</time>
</departure>
<cruise-alt>50</cruise-alt>
<arrival>
<port>YBBN</port>
<time>00:49:00</time>
</arrival>
<repeat>03Hr</repeat>
</flight>
</trafficlist>

View File

@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<PropertyList>
<submodel n="0">
<name>testLoadXML</name>
<model>Models/Geometry/null.ac</model>
<trigger>ai/submodels/submodel[0]/trigger</trigger>
<speed>100</speed>
<speed-prop>ai/submodels/submodel[0]/speed</speed-prop>
<count>42</count>
<repeat>true</repeat>
<delay>0.5</delay>
<offsets>
<x-m>2</x-m>
<y-m>0</y-m>
<z-m>0</z-m>
<heading-deg>90</heading-deg>
<pitch-deg>45</pitch-deg>
</offsets>
<life>1</life>
<buoyancy>10</buoyancy>
<wind>true</wind>
<weight>3</weight>
<contents>ai/submodels/submodel[0]/content</contents>
<cd>0.2</cd>
<eda>0.1</eda>
<aero-stabilized>false</aero-stabilized>
<external-force>true</external-force>
<force-path>ai/submodels/submodel[0]/force</force-path>
<force-stabilized>true</force-stabilized>
<no-roll>true</no-roll>
<random>true</random>
<randomness>
<azimuth>5</azimuth>
<elevation>5</elevation>
<cd>0.1</cd>
<life>0.2</life>
</randomness>
<collision>true</collision>
<fuze-range>1</fuze-range>
<impact>true</impact>
<expiry>true</expiry>
<impact-reports>ai/submodels/submodel[0]/report</impact-reports>
<slaved>true</slaved>
</submodel>
<submodel n="1">
<name>testRelease</name>
<model>Models/Geometry/null.ac</model>
<trigger>ai/submodels/submodel[1]/trigger</trigger>
<count>-1</count>
<repeat>false</repeat>
<delay>1</delay>
<life>10</life>
</submodel>
<submodel n="2">
<name>testInitialState</name>
<model>Models/Geometry/null.ac</model>
<trigger>ai/submodels/submodel[2]/trigger</trigger>
<speed>100</speed>
<repeat>false</repeat>
<delay>-1</delay>
<count>-1</count>
<aero-stabilised>false</aero-stabilised>
<wind>true</wind>
<offsets>
<x-m>10</x-m>
<y-m>6</y-m>
<z-m>1</z-m>
<heading-deg>30</heading-deg>
<pitch-deg>50</pitch-deg>
</offsets>
<life>10</life>
</submodel>
</PropertyList>

View File

@@ -0,0 +1,813 @@
<?xml version="1.0"?>
<groundnet>
<version>1</version>
<parkingList>
<Parking index="0" type="gate" name="V109" lat="N50 2.999935" lon="E8 35.484849" heading="160.6" radius="44" pushBackRoute="92" airlineCodes="" />
<Parking index="1" type="gate" name="V115" lat="N50 2.956904" lon="E8 35.314977" heading="160.6" radius="44" pushBackRoute="96" airlineCodes="" />
<Parking index="2" type="gate" name="V118" lat="N50 2.888276" lon="E8 35.303240" heading="341.45" radius="44" pushBackRoute="95" airlineCodes="" />
<Parking index="3" type="gate" name="V110" lat="N50 2.932207" lon="E8 35.467774" heading="341.45" radius="44" pushBackRoute="91" airlineCodes="" />
<Parking index="4" type="gate" name="V100" lat="N50 2.934293" lon="E8 35.797018" heading="341.45" radius="44" pushBackRoute="167" airlineCodes="" />
<Parking index="5" type="gate" name="V95" lat="N50 2.967058" lon="E8 35.940585" heading="341.45" radius="44" pushBackRoute="166" airlineCodes="" />
<Parking index="6" type="gate" name="V92" lat="N50 2.987622" lon="E8 36.025770" heading="341.45" radius="44" pushBackRoute="165" airlineCodes="" />
<Parking index="7" type="gate" name="C16" lat="N50 2.970491" lon="E8 34.976214" heading="-20.5" radius="44" pushBackRoute="97" airlineCodes="" />
<Parking index="8" type="gate" name="D4" lat="N50 3.020838" lon="E8 35.186507" heading="342.71" radius="44" pushBackRoute="177" airlineCodes="" />
<Parking index="9" type="gate" name="E5" lat="N50 3.064577" lon="E8 35.388765" heading="342.71" radius="44" pushBackRoute="94" airlineCodes="" />
<Parking index="10" type="gate" name="E9" lat="N50 3.093001" lon="E8 35.514331" heading="342.71" radius="44" pushBackRoute="93" airlineCodes="" />
<Parking index="11" type="gate" name="V268" lat="N50 2.217246" lon="E8 31.436516" heading="339.3" radius="44" pushBackRoute="81" airlineCodes="" />
<Parking index="12" type="gate" name="C4" lat="N50 3.002208" lon="E8 34.510773" heading="21.75" radius="44" pushBackRoute="102" airlineCodes="" />
<Parking index="13" type="gate" name="B28" lat="N50 2.784223" lon="E8 34.361486" heading="298.11" radius="44" pushBackRoute="105" airlineCodes="" />
<Parking index="14" type="gate" name="B48" lat="N50 2.829055" lon="E8 34.522248" heading="28.08" radius="44" pushBackRoute="104" airlineCodes="" />
<Parking index="15" type="gate" name="B44" lat="N50 2.901543" lon="E8 34.574913" heading="205.71" radius="44" pushBackRoute="101" airlineCodes="" />
<Parking index="16" type="gate" name="C8" lat="N50 2.959101" lon="E8 34.669919" heading="21.75" radius="44" pushBackRoute="100" airlineCodes="" />
<Parking index="17" type="gate" name="C14" lat="N50 2.934648" lon="E8 34.840979" heading="340.18" radius="44" pushBackRoute="98" airlineCodes="" />
<Parking index="18" type="gate" name="C2" lat="N50 3.000238" lon="E8 34.441947" heading="79.63" radius="44" pushBackRoute="102" airlineCodes="" />
<Parking index="19" type="gate" name="B22" lat="N50 2.854101" lon="E8 34.270133" heading="119.89" radius="44" pushBackRoute="109" airlineCodes="" />
<Parking index="20" type="gate" name="B23" lat="N50 2.825169" lon="E8 34.377412" heading="-84.06" radius="44" pushBackRoute="105" airlineCodes="" />
<Parking index="21" type="gate" name="B10" lat="N50 2.940697" lon="E8 34.286900" heading="159.59" radius="44" pushBackRoute="110" airlineCodes="" />
<Parking index="22" type="gate" name="A66" lat="N50 2.792857" lon="E8 33.631193" heading="340.34" radius="44" pushBackRoute="116" airlineCodes="" />
<Parking index="23" type="gate" name="A54" lat="N50 2.841211" lon="E8 33.838648" heading="340.34" radius="44" pushBackRoute="117" airlineCodes="" />
<Parking index="24" type="gate" name="A16" lat="N50 2.846693" lon="E8 34.029215" heading="113.9" radius="44" pushBackRoute="123" airlineCodes="" />
<Parking index="25" type="gate" name="A28" lat="N50 2.695002" lon="E8 33.884392" heading="157.98" radius="44" pushBackRoute="122" airlineCodes="" />
<Parking index="26" type="gate" name="F224" lat="N50 2.365898" lon="E8 32.469863" heading="250.54" radius="44" pushBackRoute="83" airlineCodes="" />
<Parking index="27" type="gate" name="F211" lat="N50 2.375008" lon="E8 32.623026" heading="70.16" radius="44" pushBackRoute="83" airlineCodes="" />
<Parking index="28" type="gate" name="F219" lat="N50 2.498238" lon="E8 32.981234" heading="70.16" radius="44" pushBackRoute="190" airlineCodes="" />
<Parking index="29" type="gate" name="V266" lat="N50 2.237159" lon="E8 31.501546" heading="339.44" radius="44" pushBackRoute="82" airlineCodes="" />
<Parking index="30" type="gate" name="F235" lat="N50 2.204985" lon="E8 31.785992" heading="339.44" radius="44" pushBackRoute="70" airlineCodes="" />
<Parking index="31" type="gate" name="F231" lat="N50 2.280024" lon="E8 32.101077" heading="339.44" radius="44" pushBackRoute="69" airlineCodes="" />
<Parking index="32" type="gate" name="V178" lat="N50 2.159443" lon="E8 32.191372" heading="159.54" radius="44" pushBackRoute="194" airlineCodes="" />
<Parking index="33" type="gate" name="V174" lat="N50 2.199657" lon="E8 32.361192" heading="159.54" radius="44" pushBackRoute="85" airlineCodes="" />
<Parking index="34" type="gate" name="V169" lat="N50 2.300216" lon="E8 32.746177" heading="159.54" radius="44" pushBackRoute="86" airlineCodes="" />
<Parking index="35" type="gate" name="V164" lat="N50 2.373806" lon="E8 33.066649" heading="159.54" radius="44" pushBackRoute="190" airlineCodes="" />
<Parking index="36" type="gate" name="V159" lat="N50 2.462450" lon="E8 33.442180" heading="159.09" radius="44" pushBackRoute="88" airlineCodes="" />
<Parking index="37" type="gate" name="A40" lat="N50 2.646960" lon="E8 33.694767" heading="155.92" radius="44" pushBackRoute="121" airlineCodes="" />
<Parking index="38" type="gate" name="A17" lat="N50 2.846756" lon="E8 34.142889" heading="292.36" radius="44" pushBackRoute="108" airlineCodes="" />
<Parking index="39" type="gate" name="A1" lat="N50 2.952545" lon="E8 34.235065" heading="99.04" radius="44" pushBackRoute="110" airlineCodes="" />
</parkingList>
<TaxiNodes>
<node index="40" lat="N50 2.768508" lon="E8 35.248439" isOnRunway="0" holdPointType="none" />
<node index="41" lat="N50 2.723872" lon="E8 35.194098" isOnRunway="0" holdPointType="none" />
<node index="42" lat="N50 2.002157" lon="E8 32.028565" isOnRunway="0" holdPointType="none" />
<node index="43" lat="N50 1.598113" lon="E8 32.515177" isOnRunway="0" holdPointType="none" />
<node index="44" lat="N50 1.688136" lon="E8 32.918501" isOnRunway="0" holdPointType="none" />
<node index="45" lat="N50 1.738117" lon="E8 32.892184" isOnRunway="0" holdPointType="none" />
<node index="46" lat="N50 2.366208" lon="E8 35.519847" isOnRunway="0" holdPointType="none" />
<node index="47" lat="N50 2.387651" lon="E8 35.577599" isOnRunway="0" holdPointType="none" />
<node index="48" lat="N50 2.418646" lon="E8 35.623960" isOnRunway="0" holdPointType="none" />
<node index="49" lat="N50 2.456653" lon="E8 35.656871" isOnRunway="0" holdPointType="none" />
<node index="50" lat="N50 2.490318" lon="E8 35.675517" isOnRunway="0" holdPointType="none" />
<node index="51" lat="N50 2.533802" lon="E8 35.692053" isOnRunway="0" holdPointType="none" />
<node index="52" lat="N50 2.626921" lon="E8 35.687006" isOnRunway="0" holdPointType="none" />
<node index="53" lat="N50 2.772243" lon="E8 35.679001" isOnRunway="0" holdPointType="none" />
<node index="54" lat="N50 2.813156" lon="E8 35.668730" isOnRunway="0" holdPointType="none" />
<node index="55" lat="N50 2.872962" lon="E8 35.634612" isOnRunway="0" holdPointType="none" />
<node index="56" lat="N50 2.019011" lon="E8 34.065677" isOnRunway="0" holdPointType="none" />
<node index="57" lat="N50 1.968782" lon="E8 34.092582" isOnRunway="0" holdPointType="none" />
<node index="58" lat="N50 2.284498" lon="E8 35.411532" isOnRunway="0" holdPointType="none" />
<node index="59" lat="N50 2.318854" lon="E8 35.558524" isOnRunway="0" holdPointType="none" />
<node index="60" lat="N50 2.347394" lon="E8 35.629704" isOnRunway="0" holdPointType="none" />
<node index="61" lat="N50 2.382562" lon="E8 35.685301" isOnRunway="0" holdPointType="none" />
<node index="62" lat="N50 2.431933" lon="E8 35.729527" isOnRunway="0" holdPointType="none" />
<node index="63" lat="N50 2.493341" lon="E8 35.765540" isOnRunway="0" holdPointType="none" />
<node index="64" lat="N50 2.531756" lon="E8 35.780072" isOnRunway="0" holdPointType="none" />
<node index="65" lat="N50 2.576257" lon="E8 35.783231" isOnRunway="0" holdPointType="none" />
<node index="66" lat="N50 2.785100" lon="E8 35.770384" isOnRunway="0" holdPointType="none" />
<node index="67" lat="N50 2.816616" lon="E8 35.768067" isOnRunway="0" holdPointType="none" />
<node index="68" lat="N50 2.855165" lon="E8 35.749532" isOnRunway="0" holdPointType="none" />
<node index="69" lat="N50 2.230702" lon="E8 32.119703" isOnRunway="0" holdPointType="PushBack" />
<node index="70" lat="N50 2.156528" lon="E8 31.809707" isOnRunway="0" holdPointType="PushBack" />
<node index="71" lat="N50 2.111141" lon="E8 31.022815" isOnRunway="0" holdPointType="none" />
<node index="72" lat="N50 2.104445" lon="E8 31.053390" isOnRunway="0" holdPointType="none" />
<node index="73" lat="N50 2.118431" lon="E8 31.248425" isOnRunway="0" holdPointType="none" />
<node index="74" lat="N50 2.134649" lon="E8 31.407790" isOnRunway="0" holdPointType="none" />
<node index="75" lat="N50 2.153669" lon="E8 31.493328" isOnRunway="0" holdPointType="none" />
<node index="76" lat="N50 2.158680" lon="E8 31.590320" isOnRunway="0" holdPointType="none" />
<node index="77" lat="N50 2.184821" lon="E8 31.711053" isOnRunway="0" holdPointType="none" />
<node index="78" lat="N50 2.138639" lon="E8 31.736488" isOnRunway="0" holdPointType="none" />
<node index="79" lat="N50 2.146045" lon="E8 31.459077" isOnRunway="0" holdPointType="none" />
<node index="80" lat="N50 2.187217" lon="E8 31.435337" isOnRunway="0" holdPointType="none" />
<node index="81" lat="N50 2.201594" lon="E8 31.451616" isOnRunway="0" holdPointType="PushBack" />
<node index="82" lat="N50 2.218150" lon="E8 31.521140" isOnRunway="0" holdPointType="PushBack" />
<node index="83" lat="N50 2.378574" lon="E8 32.544269" isOnRunway="0" holdPointType="PushBack" />
<node index="84" lat="N50 2.119540" lon="E8 32.211832" isOnRunway="0" holdPointType="none" />
<node index="85" lat="N50 2.161474" lon="E8 32.381569" isOnRunway="0" holdPointType="PushBack" />
<node index="86" lat="N50 2.261876" lon="E8 32.767313" isOnRunway="0" holdPointType="PushBack" />
<node index="87" lat="N50 2.334242" lon="E8 33.085543" isOnRunway="0" holdPointType="none" />
<node index="88" lat="N50 2.422424" lon="E8 33.464592" isOnRunway="0" holdPointType="PushBack" />
<node index="89" lat="N50 2.962635" lon="E8 35.943877" isOnRunway="0" holdPointType="none" />
<node index="90" lat="N50 2.930355" lon="E8 35.801090" isOnRunway="0" holdPointType="none" />
<node index="91" lat="N50 2.918671" lon="E8 35.477894" isOnRunway="0" holdPointType="PushBack" />
<node index="92" lat="N50 3.013926" lon="E8 35.475427" isOnRunway="0" holdPointType="PushBack" />
<node index="93" lat="N50 3.084823" lon="E8 35.519220" isOnRunway="0" holdPointType="PushBack" />
<node index="94" lat="N50 3.043830" lon="E8 35.401102" isOnRunway="0" holdPointType="PushBack" />
<node index="95" lat="N50 2.873717" lon="E8 35.315062" isOnRunway="0" holdPointType="PushBack" />
<node index="96" lat="N50 2.973329" lon="E8 35.304268" isOnRunway="0" holdPointType="PushBack" />
<node index="97" lat="N50 2.951846" lon="E8 34.988089" isOnRunway="0" holdPointType="PushBack" />
<node index="98" lat="N50 2.915488" lon="E8 34.855981" isOnRunway="0" holdPointType="PushBack" />
<node index="99" lat="N50 2.897627" lon="E8 34.729840" isOnRunway="0" holdPointType="none" />
<node index="100" lat="N50 2.920271" lon="E8 34.656833" isOnRunway="0" holdPointType="PushBack" />
<node index="101" lat="N50 2.939089" lon="E8 34.593759" isOnRunway="0" holdPointType="PushBack" />
<node index="102" lat="N50 2.976405" lon="E8 34.465127" isOnRunway="0" holdPointType="PushBack" />
<node index="103" lat="N50 2.747990" lon="E8 34.502624" isOnRunway="0" holdPointType="none" />
<node index="104" lat="N50 2.792754" lon="E8 34.460763" isOnRunway="0" holdPointType="PushBack" />
<node index="105" lat="N50 2.819903" lon="E8 34.431622" isOnRunway="0" holdPointType="PushBack" />
<node index="106" lat="N50 2.645840" lon="E8 34.108153" isOnRunway="0" holdPointType="none" />
<node index="107" lat="N50 2.714132" lon="E8 34.107406" isOnRunway="0" holdPointType="none" />
<node index="108" lat="N50 2.831943" lon="E8 34.188566" isOnRunway="0" holdPointType="PushBack" />
<node index="109" lat="N50 2.863974" lon="E8 34.212603" isOnRunway="0" holdPointType="PushBack" />
<node index="110" lat="N50 2.923791" lon="E8 34.254066" isOnRunway="0" holdPointType="PushBack" />
<node index="111" lat="N50 2.546371" lon="E8 33.617272" isOnRunway="0" holdPointType="none" />
<node index="112" lat="N50 2.562927" lon="E8 33.583692" isOnRunway="0" holdPointType="none" />
<node index="113" lat="N50 2.610634" lon="E8 33.552826" isOnRunway="0" holdPointType="none" />
<node index="114" lat="N50 2.646578" lon="E8 33.542650" isOnRunway="0" holdPointType="none" />
<node index="115" lat="N50 2.729139" lon="E8 33.578944" isOnRunway="0" holdPointType="none" />
<node index="116" lat="N50 2.748527" lon="E8 33.646785" isOnRunway="0" holdPointType="PushBack" />
<node index="117" lat="N50 2.801023" lon="E8 33.865967" isOnRunway="0" holdPointType="PushBack" />
<node index="118" lat="N50 2.568248" lon="E8 33.730581" isOnRunway="0" holdPointType="none" />
<node index="119" lat="N50 2.580447" lon="E8 33.648159" isOnRunway="0" holdPointType="none" />
<node index="120" lat="N50 2.654077" lon="E8 33.611525" isOnRunway="0" holdPointType="none" />
<node index="121" lat="N50 2.672812" lon="E8 33.681738" isOnRunway="0" holdPointType="PushBack" />
<node index="122" lat="N50 2.714636" lon="E8 33.853373" isOnRunway="0" holdPointType="PushBack" />
<node index="123" lat="N50 2.814625" lon="E8 33.924607" isOnRunway="0" holdPointType="PushBack" />
<node index="124" lat="N50 2.580757" lon="E8 32.866928" isOnRunway="0" holdPointType="none" />
<node index="125" lat="N50 2.607402" lon="E8 32.844502" isOnRunway="0" holdPointType="none" />
<node index="126" lat="N50 2.642508" lon="E8 32.791240" isOnRunway="0" holdPointType="none" />
<node index="127" lat="N50 2.666092" lon="E8 32.719475" isOnRunway="0" holdPointType="none" />
<node index="128" lat="N50 2.671133" lon="E8 32.633694" isOnRunway="0" holdPointType="none" />
<node index="129" lat="N50 2.638547" lon="E8 32.136389" isOnRunway="0" holdPointType="none" />
<node index="130" lat="N50 2.633326" lon="E8 32.074997" isOnRunway="0" holdPointType="none" />
<node index="131" lat="N50 2.573375" lon="E8 31.831956" isOnRunway="0" holdPointType="none" />
<node index="132" lat="N50 2.134276" lon="E8 29.995441" isOnRunway="0" holdPointType="none" />
<node index="133" lat="N50 2.263648" lon="E8 30.537065" isOnRunway="0" holdPointType="none" />
<node index="134" lat="N50 2.129774" lon="E8 29.971337" isOnRunway="0" holdPointType="none" />
<node index="135" lat="N50 2.133015" lon="E8 29.925371" isOnRunway="0" holdPointType="none" />
<node index="136" lat="N50 2.146517" lon="E8 29.888655" isOnRunway="0" holdPointType="none" />
<node index="137" lat="N50 2.696267" lon="E8 32.037025" isOnRunway="0" holdPointType="none" />
<node index="138" lat="N50 2.171461" lon="E8 29.867013" isOnRunway="0" holdPointType="none" />
<node index="139" lat="N50 2.081473" lon="E8 32.118503" isOnRunway="0" holdPointType="none" />
<node index="140" lat="N50 2.047358" lon="E8 31.976352" isOnRunway="0" holdPointType="none" />
<node index="141" lat="N50 2.323862" lon="E8 33.132875" isOnRunway="0" holdPointType="none" />
<node index="142" lat="N50 2.315552" lon="E8 33.097108" isOnRunway="0" holdPointType="none" />
<node index="143" lat="N50 2.298799" lon="E8 33.028902" isOnRunway="0" holdPointType="none" />
<node index="144" lat="N50 2.240071" lon="E8 32.781490" isOnRunway="0" holdPointType="none" />
<node index="145" lat="N50 2.146617" lon="E8 32.390895" isOnRunway="0" holdPointType="none" />
<node index="146" lat="N50 2.105162" lon="E8 32.218547" isOnRunway="0" holdPointType="none" />
<node index="147" lat="N50 2.507168" lon="E8 33.900910" isOnRunway="0" holdPointType="none" />
<node index="148" lat="N50 2.478498" lon="E8 33.780101" isOnRunway="0" holdPointType="none" />
<node index="149" lat="N50 2.452265" lon="E8 33.670862" isOnRunway="0" holdPointType="none" />
<node index="150" lat="N50 2.404931" lon="E8 33.472427" isOnRunway="0" holdPointType="none" />
<node index="151" lat="N50 2.716334" lon="E8 34.775263" isOnRunway="0" holdPointType="none" />
<node index="152" lat="N50 2.664357" lon="E8 34.558436" isOnRunway="0" holdPointType="none" />
<node index="153" lat="N50 2.549294" lon="E8 34.077749" isOnRunway="0" holdPointType="none" />
<node index="154" lat="N50 2.813551" lon="E8 35.181499" isOnRunway="0" holdPointType="none" />
<node index="155" lat="N50 2.730184" lon="E8 34.832643" isOnRunway="0" holdPointType="none" />
<node index="156" lat="N50 2.840393" lon="E8 35.279110" isOnRunway="0" holdPointType="none" />
<node index="157" lat="N50 2.835443" lon="E8 35.260718" isOnRunway="0" holdPointType="none" />
<node index="158" lat="N50 2.832217" lon="E8 35.250756" isOnRunway="0" holdPointType="none" />
<node index="159" lat="N50 2.903896" lon="E8 35.546206" isOnRunway="0" holdPointType="none" />
<node index="160" lat="N50 2.890550" lon="E8 35.490847" isOnRunway="0" holdPointType="none" />
<node index="161" lat="N50 2.851735" lon="E8 35.326780" isOnRunway="0" holdPointType="none" />
<node index="162" lat="N50 2.924845" lon="E8 35.644838" isOnRunway="0" holdPointType="none" />
<node index="163" lat="N50 2.915776" lon="E8 35.602059" isOnRunway="0" holdPointType="none" />
<node index="164" lat="N50 2.895221" lon="E8 35.694028" isOnRunway="0" holdPointType="none" />
<node index="165" lat="N50 2.976072" lon="E8 36.037339" isOnRunway="0" holdPointType="PushBack" />
<node index="166" lat="N50 2.955110" lon="E8 35.948503" isOnRunway="0" holdPointType="PushBack" />
<node index="167" lat="N50 2.921839" lon="E8 35.806025" isOnRunway="0" holdPointType="PushBack" />
<node index="168" lat="N50 2.896288" lon="E8 35.698426" isOnRunway="0" holdPointType="none" />
<node index="169" lat="N50 2.924681" lon="E8 35.645347" isOnRunway="0" holdPointType="none" />
<node index="170" lat="N50 3.027955" lon="E8 35.585198" isOnRunway="0" holdPointType="none" />
<node index="171" lat="N50 3.045139" lon="E8 35.565827" isOnRunway="0" holdPointType="none" />
<node index="172" lat="N50 3.044321" lon="E8 35.519185" isOnRunway="0" holdPointType="none" />
<node index="173" lat="N50 3.044424" lon="E8 35.535257" isOnRunway="0" holdPointType="none" />
<node index="174" lat="N50 2.875253" lon="E8 34.810636" isOnRunway="0" holdPointType="none" />
<node index="175" lat="N50 2.888696" lon="E8 34.869391" isOnRunway="0" holdPointType="none" />
<node index="176" lat="N50 2.922823" lon="E8 35.008451" isOnRunway="0" holdPointType="none" />
<node index="177" lat="N50 2.966518" lon="E8 35.190721" isOnRunway="0" holdPointType="PushBack" />
<node index="178" lat="N50 2.990360" lon="E8 35.291932" isOnRunway="0" holdPointType="none" />
<node index="179" lat="N50 3.019669" lon="E8 35.414054" isOnRunway="0" holdPointType="none" />
<node index="180" lat="N50 3.030958" lon="E8 35.463399" isOnRunway="0" holdPointType="none" />
<node index="181" lat="N50 2.819771" lon="E8 34.722454" isOnRunway="0" holdPointType="none" />
<node index="182" lat="N50 2.844818" lon="E8 34.762262" isOnRunway="0" holdPointType="none" />
<node index="183" lat="N50 2.691620" lon="E8 34.186239" isOnRunway="0" holdPointType="none" />
<node index="184" lat="N50 2.763718" lon="E8 34.487916" isOnRunway="0" holdPointType="none" />
<node index="185" lat="N50 2.538592" lon="E8 33.545312" isOnRunway="0" holdPointType="none" />
<node index="186" lat="N50 2.552705" lon="E8 33.604424" isOnRunway="0" holdPointType="none" />
<node index="187" lat="N50 2.573827" lon="E8 33.692887" isOnRunway="0" holdPointType="none" />
<node index="188" lat="N50 2.672906" lon="E8 34.107857" isOnRunway="0" holdPointType="none" />
<node index="189" lat="N50 2.321243" lon="E8 32.636606" isOnRunway="0" holdPointType="none" />
<node index="190" lat="N50 2.401028" lon="E8 32.970175" isOnRunway="0" holdPointType="PushBack" />
<node index="191" lat="N50 2.280818" lon="E8 32.538248" isOnRunway="0" holdPointType="none" />
<node index="192" lat="N50 2.300655" lon="E8 32.588034" isOnRunway="0" holdPointType="none" />
<node index="193" lat="N50 2.133100" lon="E8 31.931101" isOnRunway="0" holdPointType="none" />
<node index="194" lat="N50 2.188574" lon="E8 32.159503" isOnRunway="0" holdPointType="PushBack" />
<node index="195" lat="N50 2.090723" lon="E8 31.753430" isOnRunway="0" holdPointType="none" />
<node index="196" lat="N50 2.058914" lon="E8 31.636988" isOnRunway="0" holdPointType="none" />
<node index="197" lat="N50 2.072246" lon="E8 31.689653" isOnRunway="0" holdPointType="none" />
<node index="198" lat="N50 1.959143" lon="E8 31.691518" isOnRunway="0" holdPointType="none" />
<node index="199" lat="N50 1.979281" lon="E8 31.691186" isOnRunway="0" holdPointType="none" />
<node index="200" lat="N50 1.641881" lon="E8 31.694873" isOnRunway="0" holdPointType="none" />
<node index="201" lat="N49 59.998617" lon="E8 31.711146" isOnRunway="0" holdPointType="none" />
<node index="202" lat="N50 1.402856" lon="E8 31.697371" isOnRunway="0" holdPointType="none" />
<node index="203" lat="N49 59.974172" lon="E8 31.709017" isOnRunway="0" holdPointType="none" />
<node index="204" lat="N49 59.943965" lon="E8 31.668430" isOnRunway="0" holdPointType="none" />
<node index="205" lat="N49 59.928482" lon="E8 31.646584" isOnRunway="0" holdPointType="none" />
<node index="206" lat="N49 59.922001" lon="E8 31.625299" isOnRunway="0" holdPointType="none" />
<node index="207" lat="N50 1.966928" lon="E8 31.639534" isOnRunway="0" holdPointType="none" />
<node index="208" lat="N50 2.056045" lon="E8 31.623504" isOnRunway="0" holdPointType="none" />
<node index="209" lat="N50 2.037712" lon="E8 31.555410" isOnRunway="1" holdPointType="none" />
<node index="210" lat="N49 59.925487" lon="E8 31.577437" isOnRunway="1" holdPointType="none" />
<node index="211" lat="N50 1.947323" lon="E8 31.556298" isOnRunway="1" holdPointType="none" />
<node index="212" lat="N50 2.393382" lon="E8 35.154155" isOnRunway="1" holdPointType="none" />
<node index="213" lat="N50 1.657742" lon="E8 32.074192" isOnRunway="1" holdPointType="none" />
<node index="214" lat="N50 2.698705" lon="E8 35.184926" isOnRunway="1" holdPointType="none" />
<node index="215" lat="N50 1.965053" lon="E8 32.114816" isOnRunway="1" holdPointType="none" />
<node index="216" lat="N50 2.745050" lon="E8 31.999783" isOnRunway="1" holdPointType="none" />
<node index="217" lat="N50 2.228933" lon="E8 29.842340" isOnRunway="1" holdPointType="none" />
<node index="218" lat="N50 2.297290" lon="E8 35.231582" isOnRunway="0" holdPointType="none" />
<node index="219" lat="N50 2.383706" lon="E8 35.179090" isOnRunway="0" holdPointType="none" />
<node index="220" lat="N50 2.393526" lon="E8 35.154118" isOnRunway="0" holdPointType="none" />
<node index="221" lat="N50 1.511581" lon="E8 32.153321" isOnRunway="0" holdPointType="none" />
<node index="222" lat="N50 1.657691" lon="E8 32.073653" isOnRunway="0" holdPointType="none" />
<node index="223" lat="N50 3.025520" lon="E8 35.475840" isOnRunway="0" holdPointType="none" />
<node index="224" lat="N50 2.982489" lon="E8 35.305968" isOnRunway="0" holdPointType="none" />
<node index="225" lat="N50 2.862561" lon="E8 35.311869" isOnRunway="0" holdPointType="none" />
<node index="226" lat="N50 2.906491" lon="E8 35.476404" isOnRunway="0" holdPointType="none" />
<node index="227" lat="N50 2.908578" lon="E8 35.805647" isOnRunway="0" holdPointType="none" />
<node index="228" lat="N50 2.941343" lon="E8 35.949214" isOnRunway="0" holdPointType="none" />
<node index="229" lat="N50 2.961906" lon="E8 36.034399" isOnRunway="0" holdPointType="none" />
<node index="230" lat="N50 2.945084" lon="E8 34.985713" isOnRunway="0" holdPointType="none" />
<node index="231" lat="N50 2.994939" lon="E8 35.194569" isOnRunway="0" holdPointType="none" />
<node index="232" lat="N50 3.038678" lon="E8 35.396827" isOnRunway="0" holdPointType="none" />
<node index="233" lat="N50 3.067102" lon="E8 35.522393" isOnRunway="0" holdPointType="none" />
<node index="234" lat="N50 2.191873" lon="E8 31.446103" isOnRunway="0" holdPointType="none" />
<node index="235" lat="N50 2.977014" lon="E8 34.500721" isOnRunway="0" holdPointType="none" />
<node index="236" lat="N50 2.771443" lon="E8 34.385411" isOnRunway="0" holdPointType="none" />
<node index="237" lat="N50 2.805123" lon="E8 34.509480" isOnRunway="0" holdPointType="none" />
<node index="238" lat="N50 2.925982" lon="E8 34.586681" isOnRunway="0" holdPointType="none" />
<node index="239" lat="N50 2.933907" lon="E8 34.659868" isOnRunway="0" holdPointType="none" />
<node index="240" lat="N50 2.909130" lon="E8 34.850176" isOnRunway="0" holdPointType="none" />
<node index="241" lat="N50 2.995356" lon="E8 34.415266" isOnRunway="0" holdPointType="none" />
<node index="242" lat="N50 2.867618" lon="E8 34.246616" isOnRunway="0" holdPointType="none" />
<node index="243" lat="N50 2.822362" lon="E8 34.404391" isOnRunway="0" holdPointType="none" />
<node index="244" lat="N50 2.966119" lon="E8 34.277440" isOnRunway="0" holdPointType="none" />
<node index="245" lat="N50 2.767314" lon="E8 33.640319" isOnRunway="0" holdPointType="none" />
<node index="246" lat="N50 2.815668" lon="E8 33.847773" isOnRunway="0" holdPointType="none" />
<node index="247" lat="N50 2.857682" lon="E8 34.004416" isOnRunway="0" holdPointType="none" />
<node index="248" lat="N50 2.720148" lon="E8 33.874222" isOnRunway="0" holdPointType="none" />
<node index="249" lat="N50 2.374934" lon="E8 32.495439" isOnRunway="0" holdPointType="none" />
<node index="250" lat="N50 2.365802" lon="E8 32.597511" isOnRunway="0" holdPointType="none" />
<node index="251" lat="N50 2.489032" lon="E8 32.955720" isOnRunway="0" holdPointType="none" />
<node index="252" lat="N50 2.211762" lon="E8 31.511072" isOnRunway="0" holdPointType="none" />
<node index="253" lat="N50 2.179588" lon="E8 31.795518" isOnRunway="0" holdPointType="none" />
<node index="254" lat="N50 2.254627" lon="E8 32.110603" isOnRunway="0" holdPointType="none" />
<node index="255" lat="N50 2.184857" lon="E8 32.181891" isOnRunway="0" holdPointType="none" />
<node index="256" lat="N50 2.225071" lon="E8 32.351710" isOnRunway="0" holdPointType="none" />
<node index="257" lat="N50 2.325630" lon="E8 32.736696" isOnRunway="0" holdPointType="none" />
<node index="258" lat="N50 2.399219" lon="E8 33.057168" isOnRunway="0" holdPointType="none" />
<node index="259" lat="N50 2.487788" lon="E8 33.432499" isOnRunway="0" holdPointType="none" />
<node index="260" lat="N50 2.671725" lon="E8 33.683700" isOnRunway="0" holdPointType="none" />
<node index="261" lat="N50 2.836437" lon="E8 34.167974" isOnRunway="0" holdPointType="none" />
<node index="262" lat="N50 2.956807" lon="E8 34.208277" isOnRunway="0" holdPointType="none" />
</TaxiNodes>
<TaxiWaySegments>
<arc begin="40" end="158" isPushBackRoute="0" name="L1" />
<arc begin="158" end="40" isPushBackRoute="0" name="L1" />
<arc begin="41" end="40" isPushBackRoute="0" name="L1" />
<arc begin="40" end="41" isPushBackRoute="0" name="L1" />
<arc begin="214" end="41" isPushBackRoute="0" name="L1" />
<arc begin="41" end="214" isPushBackRoute="0" name="L1" />
<arc begin="42" end="140" isPushBackRoute="0" name="L21" />
<arc begin="140" end="42" isPushBackRoute="0" name="L21" />
<arc begin="215" end="42" isPushBackRoute="0" name="L21" />
<arc begin="42" end="215" isPushBackRoute="0" name="L21" />
<arc begin="43" end="202" isPushBackRoute="0" name="S" />
<arc begin="202" end="43" isPushBackRoute="0" name="S" />
<arc begin="44" end="43" isPushBackRoute="0" name="S" />
<arc begin="43" end="44" isPushBackRoute="0" name="S" />
<arc begin="45" end="44" isPushBackRoute="0" name="S" />
<arc begin="44" end="45" isPushBackRoute="0" name="S" />
<arc begin="56" end="45" isPushBackRoute="0" name="R" />
<arc begin="45" end="56" isPushBackRoute="0" name="R" />
<arc begin="46" end="56" isPushBackRoute="0" name="R" />
<arc begin="56" end="46" isPushBackRoute="0" name="R" />
<arc begin="47" end="46" isPushBackRoute="0" name="R" />
<arc begin="46" end="47" isPushBackRoute="0" name="R" />
<arc begin="48" end="47" isPushBackRoute="0" name="R" />
<arc begin="47" end="48" isPushBackRoute="0" name="R" />
<arc begin="49" end="48" isPushBackRoute="0" name="R" />
<arc begin="48" end="49" isPushBackRoute="0" name="R" />
<arc begin="50" end="49" isPushBackRoute="0" name="R" />
<arc begin="49" end="50" isPushBackRoute="0" name="R" />
<arc begin="51" end="50" isPushBackRoute="0" name="R" />
<arc begin="50" end="51" isPushBackRoute="0" name="R" />
<arc begin="52" end="51" isPushBackRoute="0" name="R" />
<arc begin="51" end="52" isPushBackRoute="0" name="R" />
<arc begin="53" end="52" isPushBackRoute="0" name="R" />
<arc begin="52" end="53" isPushBackRoute="0" name="R" />
<arc begin="54" end="53" isPushBackRoute="0" name="R" />
<arc begin="53" end="54" isPushBackRoute="0" name="R" />
<arc begin="55" end="54" isPushBackRoute="0" name="R" />
<arc begin="54" end="55" isPushBackRoute="0" name="R" />
<arc begin="163" end="55" isPushBackRoute="0" name="R" />
<arc begin="55" end="163" isPushBackRoute="0" name="R" />
<arc begin="57" end="56" isPushBackRoute="0" name="S" />
<arc begin="56" end="57" isPushBackRoute="0" name="S" />
<arc begin="58" end="57" isPushBackRoute="0" name="S" />
<arc begin="57" end="58" isPushBackRoute="0" name="S" />
<arc begin="59" end="58" isPushBackRoute="0" name="S" />
<arc begin="58" end="59" isPushBackRoute="0" name="S" />
<arc begin="60" end="59" isPushBackRoute="0" name="S" />
<arc begin="59" end="60" isPushBackRoute="0" name="S" />
<arc begin="61" end="60" isPushBackRoute="0" name="S" />
<arc begin="60" end="61" isPushBackRoute="0" name="S" />
<arc begin="62" end="61" isPushBackRoute="0" name="S" />
<arc begin="61" end="62" isPushBackRoute="0" name="S" />
<arc begin="63" end="62" isPushBackRoute="0" name="S" />
<arc begin="62" end="63" isPushBackRoute="0" name="S" />
<arc begin="64" end="63" isPushBackRoute="0" name="S" />
<arc begin="63" end="64" isPushBackRoute="0" name="S" />
<arc begin="65" end="64" isPushBackRoute="0" name="S" />
<arc begin="64" end="65" isPushBackRoute="0" name="S" />
<arc begin="66" end="65" isPushBackRoute="0" name="S" />
<arc begin="65" end="66" isPushBackRoute="0" name="S" />
<arc begin="67" end="66" isPushBackRoute="0" name="S" />
<arc begin="66" end="67" isPushBackRoute="0" name="S" />
<arc begin="68" end="67" isPushBackRoute="0" name="S" />
<arc begin="67" end="68" isPushBackRoute="0" name="S" />
<arc begin="168" end="68" isPushBackRoute="0" name="S" />
<arc begin="68" end="168" isPushBackRoute="0" name="S" />
<arc begin="69" end="194" isPushBackRoute="0" name="NN" />
<arc begin="194" end="69" isPushBackRoute="0" name="NN" />
<arc begin="70" end="69" isPushBackRoute="0" name="NN" />
<arc begin="69" end="70" isPushBackRoute="0" name="NN" />
<arc begin="78" end="70" isPushBackRoute="0" name="NN" />
<arc begin="70" end="78" isPushBackRoute="0" name="NN" />
<arc begin="71" end="133" isPushBackRoute="0" name="P1" />
<arc begin="133" end="71" isPushBackRoute="0" name="P1" />
<arc begin="72" end="71" isPushBackRoute="0" name="P1" />
<arc begin="71" end="72" isPushBackRoute="0" name="P1" />
<arc begin="73" end="72" isPushBackRoute="0" name="P1" />
<arc begin="72" end="73" isPushBackRoute="0" name="P1" />
<arc begin="74" end="73" isPushBackRoute="0" name="P1" />
<arc begin="73" end="74" isPushBackRoute="0" name="P1" />
<arc begin="79" end="74" isPushBackRoute="0" name="P1" />
<arc begin="74" end="79" isPushBackRoute="0" name="P1" />
<arc begin="75" end="79" isPushBackRoute="0" name="P1" />
<arc begin="79" end="75" isPushBackRoute="0" name="P1" />
<arc begin="76" end="75" isPushBackRoute="0" name="P1" />
<arc begin="75" end="76" isPushBackRoute="0" name="P1" />
<arc begin="77" end="76" isPushBackRoute="0" name="P1" />
<arc begin="76" end="77" isPushBackRoute="0" name="P1" />
<arc begin="78" end="77" isPushBackRoute="0" name="P1" />
<arc begin="77" end="78" isPushBackRoute="0" name="P1" />
<arc begin="80" end="79" isPushBackRoute="0" name="N19" />
<arc begin="79" end="80" isPushBackRoute="0" name="N19" />
<arc begin="81" end="80" isPushBackRoute="0" name="N19" />
<arc begin="80" end="81" isPushBackRoute="0" name="N19" />
<arc begin="82" end="81" isPushBackRoute="0" name="N19" />
<arc begin="81" end="82" isPushBackRoute="0" name="N19" />
<arc begin="83" end="192" isPushBackRoute="0" name="N14" />
<arc begin="192" end="83" isPushBackRoute="0" name="N14" />
<arc begin="84" end="146" isPushBackRoute="0" name="" />
<arc begin="146" end="84" isPushBackRoute="0" name="" />
<arc begin="85" end="145" isPushBackRoute="0" name="" />
<arc begin="145" end="85" isPushBackRoute="0" name="" />
<arc begin="86" end="144" isPushBackRoute="0" name="" />
<arc begin="144" end="86" isPushBackRoute="0" name="" />
<arc begin="87" end="142" isPushBackRoute="0" name="" />
<arc begin="142" end="87" isPushBackRoute="0" name="" />
<arc begin="88" end="150" isPushBackRoute="0" name="" />
<arc begin="150" end="88" isPushBackRoute="0" name="" />
<arc begin="89" end="166" isPushBackRoute="0" name="" />
<arc begin="166" end="89" isPushBackRoute="0" name="" />
<arc begin="90" end="167" isPushBackRoute="0" name="" />
<arc begin="167" end="90" isPushBackRoute="0" name="" />
<arc begin="91" end="160" isPushBackRoute="0" name="" />
<arc begin="160" end="91" isPushBackRoute="0" name="" />
<arc begin="92" end="180" isPushBackRoute="0" name="" />
<arc begin="180" end="92" isPushBackRoute="0" name="" />
<arc begin="93" end="173" isPushBackRoute="0" name="" />
<arc begin="173" end="93" isPushBackRoute="0" name="" />
<arc begin="94" end="179" isPushBackRoute="0" name="" />
<arc begin="179" end="94" isPushBackRoute="0" name="" />
<arc begin="95" end="161" isPushBackRoute="0" name="" />
<arc begin="161" end="95" isPushBackRoute="0" name="" />
<arc begin="96" end="178" isPushBackRoute="0" name="" />
<arc begin="178" end="96" isPushBackRoute="0" name="" />
<arc begin="177" end="157" isPushBackRoute="0" name="N1" />
<arc begin="157" end="177" isPushBackRoute="0" name="N1" />
<arc begin="97" end="176" isPushBackRoute="0" name="" />
<arc begin="176" end="97" isPushBackRoute="0" name="" />
<arc begin="98" end="175" isPushBackRoute="0" name="" />
<arc begin="175" end="98" isPushBackRoute="0" name="" />
<arc begin="182" end="155" isPushBackRoute="0" name="N3" />
<arc begin="155" end="182" isPushBackRoute="0" name="N3" />
<arc begin="99" end="182" isPushBackRoute="0" name="N3" />
<arc begin="182" end="99" isPushBackRoute="0" name="N3" />
<arc begin="100" end="99" isPushBackRoute="0" name="N3" />
<arc begin="99" end="100" isPushBackRoute="0" name="N3" />
<arc begin="101" end="100" isPushBackRoute="0" name="N3" />
<arc begin="100" end="101" isPushBackRoute="0" name="N3" />
<arc begin="102" end="101" isPushBackRoute="0" name="N3" />
<arc begin="101" end="102" isPushBackRoute="0" name="N3" />
<arc begin="103" end="152" isPushBackRoute="0" name="N4" />
<arc begin="152" end="103" isPushBackRoute="0" name="N4" />
<arc begin="184" end="103" isPushBackRoute="0" name="N4" />
<arc begin="103" end="184" isPushBackRoute="0" name="N4" />
<arc begin="104" end="184" isPushBackRoute="0" name="N4" />
<arc begin="184" end="104" isPushBackRoute="0" name="N4" />
<arc begin="105" end="104" isPushBackRoute="0" name="N4" />
<arc begin="104" end="105" isPushBackRoute="0" name="N4" />
<arc begin="106" end="153" isPushBackRoute="0" name="N5" />
<arc begin="153" end="106" isPushBackRoute="0" name="N5" />
<arc begin="188" end="106" isPushBackRoute="0" name="N5" />
<arc begin="106" end="188" isPushBackRoute="0" name="N5" />
<arc begin="107" end="188" isPushBackRoute="0" name="N5" />
<arc begin="188" end="107" isPushBackRoute="0" name="N5" />
<arc begin="108" end="107" isPushBackRoute="0" name="N5" />
<arc begin="107" end="108" isPushBackRoute="0" name="N5" />
<arc begin="109" end="108" isPushBackRoute="0" name="N5" />
<arc begin="108" end="109" isPushBackRoute="0" name="N5" />
<arc begin="110" end="109" isPushBackRoute="0" name="N5" />
<arc begin="109" end="110" isPushBackRoute="0" name="N5" />
<arc begin="111" end="149" isPushBackRoute="0" name="N8" />
<arc begin="149" end="111" isPushBackRoute="0" name="N8" />
<arc begin="186" end="111" isPushBackRoute="0" name="N8" />
<arc begin="111" end="186" isPushBackRoute="0" name="N8" />
<arc begin="112" end="186" isPushBackRoute="0" name="N8" />
<arc begin="186" end="112" isPushBackRoute="0" name="N8" />
<arc begin="113" end="112" isPushBackRoute="0" name="N8" />
<arc begin="112" end="113" isPushBackRoute="0" name="N8" />
<arc begin="114" end="113" isPushBackRoute="0" name="N8" />
<arc begin="113" end="114" isPushBackRoute="0" name="N8" />
<arc begin="115" end="114" isPushBackRoute="0" name="N8" />
<arc begin="114" end="115" isPushBackRoute="0" name="N8" />
<arc begin="116" end="115" isPushBackRoute="0" name="N8" />
<arc begin="115" end="116" isPushBackRoute="0" name="N8" />
<arc begin="117" end="116" isPushBackRoute="0" name="N8" />
<arc begin="116" end="117" isPushBackRoute="0" name="N8" />
<arc begin="123" end="117" isPushBackRoute="0" name="N8" />
<arc begin="117" end="123" isPushBackRoute="0" name="N8" />
<arc begin="118" end="148" isPushBackRoute="0" name="N7" />
<arc begin="148" end="118" isPushBackRoute="0" name="N7" />
<arc begin="187" end="118" isPushBackRoute="0" name="N7" />
<arc begin="118" end="187" isPushBackRoute="0" name="N7" />
<arc begin="119" end="187" isPushBackRoute="0" name="N7" />
<arc begin="187" end="119" isPushBackRoute="0" name="N7" />
<arc begin="120" end="119" isPushBackRoute="0" name="N7" />
<arc begin="119" end="120" isPushBackRoute="0" name="N7" />
<arc begin="121" end="120" isPushBackRoute="0" name="N7" />
<arc begin="120" end="121" isPushBackRoute="0" name="N7" />
<arc begin="122" end="121" isPushBackRoute="0" name="N7" />
<arc begin="121" end="122" isPushBackRoute="0" name="N7" />
<arc begin="123" end="122" isPushBackRoute="0" name="N7" />
<arc begin="122" end="123" isPushBackRoute="0" name="N7" />
<arc begin="137" end="130" isPushBackRoute="0" name="P4" />
<arc begin="130" end="137" isPushBackRoute="0" name="P4" />
<arc begin="190" end="143" isPushBackRoute="0" name="N11" />
<arc begin="143" end="190" isPushBackRoute="0" name="N11" />
<arc begin="124" end="190" isPushBackRoute="0" name="N11" />
<arc begin="190" end="124" isPushBackRoute="0" name="N11" />
<arc begin="125" end="124" isPushBackRoute="0" name="P" />
<arc begin="124" end="125" isPushBackRoute="0" name="P" />
<arc begin="126" end="125" isPushBackRoute="0" name="P" />
<arc begin="125" end="126" isPushBackRoute="0" name="P" />
<arc begin="127" end="126" isPushBackRoute="0" name="P" />
<arc begin="126" end="127" isPushBackRoute="0" name="P" />
<arc begin="128" end="127" isPushBackRoute="0" name="P" />
<arc begin="127" end="128" isPushBackRoute="0" name="P" />
<arc begin="129" end="128" isPushBackRoute="0" name="P" />
<arc begin="128" end="129" isPushBackRoute="0" name="P" />
<arc begin="130" end="129" isPushBackRoute="0" name="P" />
<arc begin="129" end="130" isPushBackRoute="0" name="P" />
<arc begin="131" end="130" isPushBackRoute="0" name="P" />
<arc begin="130" end="131" isPushBackRoute="0" name="P" />
<arc begin="132" end="133" isPushBackRoute="0" name="P" />
<arc begin="133" end="132" isPushBackRoute="0" name="P" />
<arc begin="133" end="131" isPushBackRoute="0" name="P" />
<arc begin="131" end="133" isPushBackRoute="0" name="P" />
<arc begin="134" end="132" isPushBackRoute="0" name="P" />
<arc begin="132" end="134" isPushBackRoute="0" name="P" />
<arc begin="135" end="134" isPushBackRoute="0" name="P" />
<arc begin="134" end="135" isPushBackRoute="0" name="P" />
<arc begin="136" end="135" isPushBackRoute="0" name="P" />
<arc begin="135" end="136" isPushBackRoute="0" name="P" />
<arc begin="138" end="136" isPushBackRoute="0" name="P" />
<arc begin="136" end="138" isPushBackRoute="0" name="P" />
<arc begin="216" end="137" isPushBackRoute="0" name="P" />
<arc begin="137" end="216" isPushBackRoute="0" name="P" />
<arc begin="217" end="138" isPushBackRoute="0" name="P" />
<arc begin="138" end="217" isPushBackRoute="0" name="P" />
<arc begin="199" end="207" isPushBackRoute="0" name="L" />
<arc begin="207" end="199" isPushBackRoute="0" name="L" />
<arc begin="139" end="140" isPushBackRoute="0" name="L" />
<arc begin="140" end="139" isPushBackRoute="0" name="L" />
<arc begin="140" end="199" isPushBackRoute="0" name="L" />
<arc begin="199" end="140" isPushBackRoute="0" name="L" />
<arc begin="141" end="142" isPushBackRoute="0" name="L" />
<arc begin="142" end="141" isPushBackRoute="0" name="L" />
<arc begin="142" end="143" isPushBackRoute="0" name="L" />
<arc begin="143" end="142" isPushBackRoute="0" name="L" />
<arc begin="143" end="144" isPushBackRoute="0" name="L" />
<arc begin="144" end="143" isPushBackRoute="0" name="L" />
<arc begin="144" end="145" isPushBackRoute="0" name="L" />
<arc begin="145" end="144" isPushBackRoute="0" name="L" />
<arc begin="145" end="146" isPushBackRoute="0" name="L" />
<arc begin="146" end="145" isPushBackRoute="0" name="L" />
<arc begin="146" end="139" isPushBackRoute="0" name="L" />
<arc begin="139" end="146" isPushBackRoute="0" name="L" />
<arc begin="147" end="148" isPushBackRoute="0" name="L" />
<arc begin="148" end="147" isPushBackRoute="0" name="L" />
<arc begin="148" end="149" isPushBackRoute="0" name="L" />
<arc begin="149" end="148" isPushBackRoute="0" name="L" />
<arc begin="149" end="150" isPushBackRoute="0" name="L" />
<arc begin="150" end="149" isPushBackRoute="0" name="L" />
<arc begin="150" end="141" isPushBackRoute="0" name="L" />
<arc begin="141" end="150" isPushBackRoute="0" name="L" />
<arc begin="151" end="152" isPushBackRoute="0" name="L" />
<arc begin="152" end="151" isPushBackRoute="0" name="L" />
<arc begin="152" end="153" isPushBackRoute="0" name="L" />
<arc begin="153" end="152" isPushBackRoute="0" name="L" />
<arc begin="153" end="147" isPushBackRoute="0" name="L" />
<arc begin="147" end="153" isPushBackRoute="0" name="L" />
<arc begin="154" end="155" isPushBackRoute="0" name="L" />
<arc begin="155" end="154" isPushBackRoute="0" name="L" />
<arc begin="155" end="151" isPushBackRoute="0" name="L" />
<arc begin="151" end="155" isPushBackRoute="0" name="L" />
<arc begin="156" end="157" isPushBackRoute="0" name="L" />
<arc begin="157" end="156" isPushBackRoute="0" name="L" />
<arc begin="157" end="158" isPushBackRoute="0" name="L" />
<arc begin="158" end="157" isPushBackRoute="0" name="L" />
<arc begin="158" end="154" isPushBackRoute="0" name="L" />
<arc begin="154" end="158" isPushBackRoute="0" name="L" />
<arc begin="159" end="160" isPushBackRoute="0" name="L" />
<arc begin="160" end="159" isPushBackRoute="0" name="L" />
<arc begin="160" end="161" isPushBackRoute="0" name="L" />
<arc begin="161" end="160" isPushBackRoute="0" name="L" />
<arc begin="161" end="156" isPushBackRoute="0" name="L" />
<arc begin="156" end="161" isPushBackRoute="0" name="L" />
<arc begin="162" end="163" isPushBackRoute="0" name="L" />
<arc begin="163" end="162" isPushBackRoute="0" name="L" />
<arc begin="163" end="159" isPushBackRoute="0" name="L" />
<arc begin="159" end="163" isPushBackRoute="0" name="L" />
<arc begin="169" end="162" isPushBackRoute="0" name="L" />
<arc begin="162" end="169" isPushBackRoute="0" name="L" />
<arc begin="164" end="169" isPushBackRoute="0" name="L" />
<arc begin="169" end="164" isPushBackRoute="0" name="L" />
<arc begin="165" end="166" isPushBackRoute="0" name="L" />
<arc begin="166" end="165" isPushBackRoute="0" name="L" />
<arc begin="166" end="167" isPushBackRoute="0" name="L" />
<arc begin="167" end="166" isPushBackRoute="0" name="L" />
<arc begin="167" end="168" isPushBackRoute="0" name="L" />
<arc begin="168" end="167" isPushBackRoute="0" name="L" />
<arc begin="168" end="164" isPushBackRoute="0" name="L" />
<arc begin="164" end="168" isPushBackRoute="0" name="L" />
<arc begin="170" end="169" isPushBackRoute="0" name="N" />
<arc begin="169" end="170" isPushBackRoute="0" name="N" />
<arc begin="171" end="170" isPushBackRoute="0" name="N" />
<arc begin="170" end="171" isPushBackRoute="0" name="N" />
<arc begin="172" end="173" isPushBackRoute="0" name="N" />
<arc begin="173" end="172" isPushBackRoute="0" name="N" />
<arc begin="173" end="171" isPushBackRoute="0" name="N" />
<arc begin="171" end="173" isPushBackRoute="0" name="N" />
<arc begin="174" end="175" isPushBackRoute="0" name="N" />
<arc begin="175" end="174" isPushBackRoute="0" name="N" />
<arc begin="175" end="176" isPushBackRoute="0" name="N" />
<arc begin="176" end="175" isPushBackRoute="0" name="N" />
<arc begin="176" end="177" isPushBackRoute="0" name="N" />
<arc begin="177" end="176" isPushBackRoute="0" name="N" />
<arc begin="177" end="178" isPushBackRoute="0" name="N" />
<arc begin="178" end="177" isPushBackRoute="0" name="N" />
<arc begin="178" end="179" isPushBackRoute="0" name="N" />
<arc begin="179" end="178" isPushBackRoute="0" name="N" />
<arc begin="179" end="180" isPushBackRoute="0" name="N" />
<arc begin="180" end="179" isPushBackRoute="0" name="N" />
<arc begin="180" end="172" isPushBackRoute="0" name="N" />
<arc begin="172" end="180" isPushBackRoute="0" name="N" />
<arc begin="181" end="182" isPushBackRoute="0" name="N" />
<arc begin="182" end="181" isPushBackRoute="0" name="N" />
<arc begin="182" end="174" isPushBackRoute="0" name="N" />
<arc begin="174" end="182" isPushBackRoute="0" name="N" />
<arc begin="183" end="184" isPushBackRoute="0" name="N" />
<arc begin="184" end="183" isPushBackRoute="0" name="N" />
<arc begin="184" end="181" isPushBackRoute="0" name="N" />
<arc begin="181" end="184" isPushBackRoute="0" name="N" />
<arc begin="185" end="186" isPushBackRoute="0" name="N" />
<arc begin="186" end="185" isPushBackRoute="0" name="N" />
<arc begin="186" end="187" isPushBackRoute="0" name="N" />
<arc begin="187" end="186" isPushBackRoute="0" name="N" />
<arc begin="187" end="188" isPushBackRoute="0" name="N" />
<arc begin="188" end="187" isPushBackRoute="0" name="N" />
<arc begin="188" end="183" isPushBackRoute="0" name="N" />
<arc begin="183" end="188" isPushBackRoute="0" name="N" />
<arc begin="189" end="190" isPushBackRoute="0" name="N" />
<arc begin="190" end="189" isPushBackRoute="0" name="N" />
<arc begin="190" end="185" isPushBackRoute="0" name="N" />
<arc begin="185" end="190" isPushBackRoute="0" name="N" />
<arc begin="191" end="192" isPushBackRoute="0" name="N" />
<arc begin="192" end="191" isPushBackRoute="0" name="N" />
<arc begin="192" end="189" isPushBackRoute="0" name="N" />
<arc begin="189" end="192" isPushBackRoute="0" name="N" />
<arc begin="193" end="194" isPushBackRoute="0" name="N" />
<arc begin="194" end="193" isPushBackRoute="0" name="N" />
<arc begin="194" end="191" isPushBackRoute="0" name="N" />
<arc begin="191" end="194" isPushBackRoute="0" name="N" />
<arc begin="195" end="193" isPushBackRoute="0" name="N" />
<arc begin="193" end="195" isPushBackRoute="0" name="N" />
<arc begin="197" end="195" isPushBackRoute="0" name="N" />
<arc begin="195" end="197" isPushBackRoute="0" name="N" />
<arc begin="196" end="197" isPushBackRoute="0" name="N" />
<arc begin="197" end="196" isPushBackRoute="0" name="N" />
<arc begin="198" end="199" isPushBackRoute="0" name="Y" />
<arc begin="199" end="198" isPushBackRoute="0" name="Y" />
<arc begin="199" end="197" isPushBackRoute="0" name="Y" />
<arc begin="197" end="199" isPushBackRoute="0" name="Y" />
<arc begin="200" end="198" isPushBackRoute="0" name="Y" />
<arc begin="198" end="200" isPushBackRoute="0" name="Y" />
<arc begin="201" end="202" isPushBackRoute="0" name="Y" />
<arc begin="202" end="201" isPushBackRoute="0" name="Y" />
<arc begin="202" end="200" isPushBackRoute="0" name="Y" />
<arc begin="200" end="202" isPushBackRoute="0" name="Y" />
<arc begin="203" end="201" isPushBackRoute="0" name="Y" />
<arc begin="201" end="203" isPushBackRoute="0" name="Y" />
<arc begin="204" end="203" isPushBackRoute="0" name="Y" />
<arc begin="203" end="204" isPushBackRoute="0" name="Y" />
<arc begin="205" end="204" isPushBackRoute="0" name="" />
<arc begin="204" end="205" isPushBackRoute="0" name="" />
<arc begin="206" end="205" isPushBackRoute="0" name="" />
<arc begin="205" end="206" isPushBackRoute="0" name="" />
<arc begin="210" end="206" isPushBackRoute="0" name="" />
<arc begin="206" end="210" isPushBackRoute="0" name="" />
<arc begin="211" end="207" isPushBackRoute="0" name="L" />
<arc begin="207" end="211" isPushBackRoute="0" name="L" />
<arc begin="209" end="208" isPushBackRoute="0" name="N" />
<arc begin="208" end="209" isPushBackRoute="0" name="N" />
<arc begin="219" end="218" isPushBackRoute="0" name="R3" />
<arc begin="218" end="219" isPushBackRoute="0" name="R3" />
<arc begin="220" end="219" isPushBackRoute="0" name="R3" />
<arc begin="219" end="220" isPushBackRoute="0" name="R3" />
<arc begin="222" end="221" isPushBackRoute="0" name="R17" />
<arc begin="221" end="222" isPushBackRoute="0" name="R17" />
<arc begin="0" end="223" isPushBackRoute="1" name="V109" />
<arc begin="223" end="0" isPushBackRoute="1" name="V109" />
<arc begin="223" end="92" isPushBackRoute="1" name="V109" />
<arc begin="92" end="223" isPushBackRoute="1" name="V109" />
<arc begin="1" end="224" isPushBackRoute="1" name="V115" />
<arc begin="224" end="1" isPushBackRoute="1" name="V115" />
<arc begin="224" end="96" isPushBackRoute="1" name="V115" />
<arc begin="96" end="224" isPushBackRoute="1" name="V115" />
<arc begin="2" end="225" isPushBackRoute="1" name="V118" />
<arc begin="225" end="2" isPushBackRoute="1" name="V118" />
<arc begin="225" end="95" isPushBackRoute="1" name="V118" />
<arc begin="95" end="225" isPushBackRoute="1" name="V118" />
<arc begin="3" end="226" isPushBackRoute="1" name="V110" />
<arc begin="226" end="3" isPushBackRoute="1" name="V110" />
<arc begin="226" end="91" isPushBackRoute="1" name="V110" />
<arc begin="91" end="226" isPushBackRoute="1" name="V110" />
<arc begin="4" end="227" isPushBackRoute="1" name="V100" />
<arc begin="227" end="4" isPushBackRoute="1" name="V100" />
<arc begin="227" end="167" isPushBackRoute="1" name="V100" />
<arc begin="167" end="227" isPushBackRoute="1" name="V100" />
<arc begin="5" end="228" isPushBackRoute="1" name="V95" />
<arc begin="228" end="5" isPushBackRoute="1" name="V95" />
<arc begin="228" end="166" isPushBackRoute="1" name="V95" />
<arc begin="166" end="228" isPushBackRoute="1" name="V95" />
<arc begin="6" end="229" isPushBackRoute="1" name="V92" />
<arc begin="229" end="6" isPushBackRoute="1" name="V92" />
<arc begin="229" end="165" isPushBackRoute="1" name="V92" />
<arc begin="165" end="229" isPushBackRoute="1" name="V92" />
<arc begin="7" end="230" isPushBackRoute="1" name="C16" />
<arc begin="230" end="7" isPushBackRoute="1" name="C16" />
<arc begin="230" end="97" isPushBackRoute="1" name="C16" />
<arc begin="97" end="230" isPushBackRoute="1" name="C16" />
<arc begin="8" end="231" isPushBackRoute="1" name="D4" />
<arc begin="231" end="8" isPushBackRoute="1" name="D4" />
<arc begin="231" end="177" isPushBackRoute="1" name="D4" />
<arc begin="177" end="231" isPushBackRoute="1" name="D4" />
<arc begin="9" end="232" isPushBackRoute="1" name="E5" />
<arc begin="232" end="9" isPushBackRoute="1" name="E5" />
<arc begin="232" end="94" isPushBackRoute="1" name="E5" />
<arc begin="94" end="232" isPushBackRoute="1" name="E5" />
<arc begin="10" end="233" isPushBackRoute="1" name="E9" />
<arc begin="233" end="10" isPushBackRoute="1" name="E9" />
<arc begin="233" end="93" isPushBackRoute="1" name="E9" />
<arc begin="93" end="233" isPushBackRoute="1" name="E9" />
<arc begin="11" end="234" isPushBackRoute="1" name="V268" />
<arc begin="234" end="11" isPushBackRoute="1" name="V268" />
<arc begin="234" end="81" isPushBackRoute="1" name="V268" />
<arc begin="81" end="234" isPushBackRoute="1" name="V268" />
<arc begin="12" end="235" isPushBackRoute="1" name="C4" />
<arc begin="235" end="12" isPushBackRoute="1" name="C4" />
<arc begin="235" end="102" isPushBackRoute="1" name="C4" />
<arc begin="102" end="235" isPushBackRoute="1" name="C4" />
<arc begin="13" end="236" isPushBackRoute="1" name="B28" />
<arc begin="236" end="13" isPushBackRoute="1" name="B28" />
<arc begin="236" end="105" isPushBackRoute="1" name="B28" />
<arc begin="105" end="236" isPushBackRoute="1" name="B28" />
<arc begin="14" end="237" isPushBackRoute="1" name="B48" />
<arc begin="237" end="14" isPushBackRoute="1" name="B48" />
<arc begin="237" end="104" isPushBackRoute="1" name="B48" />
<arc begin="104" end="237" isPushBackRoute="1" name="B48" />
<arc begin="15" end="238" isPushBackRoute="1" name="B44" />
<arc begin="238" end="15" isPushBackRoute="1" name="B44" />
<arc begin="238" end="101" isPushBackRoute="1" name="B44" />
<arc begin="101" end="238" isPushBackRoute="1" name="B44" />
<arc begin="16" end="239" isPushBackRoute="1" name="C8" />
<arc begin="239" end="16" isPushBackRoute="1" name="C8" />
<arc begin="239" end="100" isPushBackRoute="1" name="C8" />
<arc begin="100" end="239" isPushBackRoute="1" name="C8" />
<arc begin="17" end="240" isPushBackRoute="1" name="C14" />
<arc begin="240" end="17" isPushBackRoute="1" name="C14" />
<arc begin="240" end="98" isPushBackRoute="1" name="C14" />
<arc begin="98" end="240" isPushBackRoute="1" name="C14" />
<arc begin="18" end="241" isPushBackRoute="1" name="C2" />
<arc begin="241" end="18" isPushBackRoute="1" name="C2" />
<arc begin="241" end="102" isPushBackRoute="1" name="C2" />
<arc begin="102" end="241" isPushBackRoute="1" name="C2" />
<arc begin="19" end="242" isPushBackRoute="1" name="B22" />
<arc begin="242" end="19" isPushBackRoute="1" name="B22" />
<arc begin="242" end="109" isPushBackRoute="1" name="B22" />
<arc begin="109" end="242" isPushBackRoute="1" name="B22" />
<arc begin="20" end="243" isPushBackRoute="1" name="B23" />
<arc begin="243" end="20" isPushBackRoute="1" name="B23" />
<arc begin="243" end="105" isPushBackRoute="1" name="B23" />
<arc begin="105" end="243" isPushBackRoute="1" name="B23" />
<arc begin="21" end="244" isPushBackRoute="1" name="B10" />
<arc begin="244" end="21" isPushBackRoute="1" name="B10" />
<arc begin="244" end="110" isPushBackRoute="1" name="B10" />
<arc begin="110" end="244" isPushBackRoute="1" name="B10" />
<arc begin="22" end="245" isPushBackRoute="1" name="A66" />
<arc begin="245" end="22" isPushBackRoute="1" name="A66" />
<arc begin="245" end="116" isPushBackRoute="1" name="A66" />
<arc begin="116" end="245" isPushBackRoute="1" name="A66" />
<arc begin="23" end="246" isPushBackRoute="1" name="A54" />
<arc begin="246" end="23" isPushBackRoute="1" name="A54" />
<arc begin="246" end="117" isPushBackRoute="1" name="A54" />
<arc begin="117" end="246" isPushBackRoute="1" name="A54" />
<arc begin="24" end="247" isPushBackRoute="1" name="A16" />
<arc begin="247" end="24" isPushBackRoute="1" name="A16" />
<arc begin="247" end="123" isPushBackRoute="1" name="A16" />
<arc begin="123" end="247" isPushBackRoute="1" name="A16" />
<arc begin="25" end="248" isPushBackRoute="1" name="A28" />
<arc begin="248" end="25" isPushBackRoute="1" name="A28" />
<arc begin="248" end="122" isPushBackRoute="1" name="A28" />
<arc begin="122" end="248" isPushBackRoute="1" name="A28" />
<arc begin="26" end="249" isPushBackRoute="1" name="F224" />
<arc begin="249" end="26" isPushBackRoute="1" name="F224" />
<arc begin="249" end="83" isPushBackRoute="1" name="F224" />
<arc begin="83" end="249" isPushBackRoute="1" name="F224" />
<arc begin="27" end="250" isPushBackRoute="1" name="F211" />
<arc begin="250" end="27" isPushBackRoute="1" name="F211" />
<arc begin="250" end="83" isPushBackRoute="1" name="F211" />
<arc begin="83" end="250" isPushBackRoute="1" name="F211" />
<arc begin="28" end="251" isPushBackRoute="1" name="F219" />
<arc begin="251" end="28" isPushBackRoute="1" name="F219" />
<arc begin="251" end="190" isPushBackRoute="1" name="F219" />
<arc begin="190" end="251" isPushBackRoute="1" name="F219" />
<arc begin="29" end="252" isPushBackRoute="1" name="V266" />
<arc begin="252" end="29" isPushBackRoute="1" name="V266" />
<arc begin="252" end="82" isPushBackRoute="1" name="V266" />
<arc begin="82" end="252" isPushBackRoute="1" name="V266" />
<arc begin="30" end="253" isPushBackRoute="1" name="F235" />
<arc begin="253" end="30" isPushBackRoute="1" name="F235" />
<arc begin="253" end="70" isPushBackRoute="1" name="F235" />
<arc begin="70" end="253" isPushBackRoute="1" name="F235" />
<arc begin="31" end="254" isPushBackRoute="1" name="F231" />
<arc begin="254" end="31" isPushBackRoute="1" name="F231" />
<arc begin="254" end="69" isPushBackRoute="1" name="F231" />
<arc begin="69" end="254" isPushBackRoute="1" name="F231" />
<arc begin="32" end="255" isPushBackRoute="1" name="V178" />
<arc begin="255" end="32" isPushBackRoute="1" name="V178" />
<arc begin="255" end="194" isPushBackRoute="1" name="V178" />
<arc begin="194" end="255" isPushBackRoute="1" name="V178" />
<arc begin="33" end="256" isPushBackRoute="1" name="V174" />
<arc begin="256" end="33" isPushBackRoute="1" name="V174" />
<arc begin="256" end="85" isPushBackRoute="1" name="V174" />
<arc begin="85" end="256" isPushBackRoute="1" name="V174" />
<arc begin="34" end="257" isPushBackRoute="1" name="V169" />
<arc begin="257" end="34" isPushBackRoute="1" name="V169" />
<arc begin="257" end="86" isPushBackRoute="1" name="V169" />
<arc begin="86" end="257" isPushBackRoute="1" name="V169" />
<arc begin="35" end="258" isPushBackRoute="1" name="V164" />
<arc begin="258" end="35" isPushBackRoute="1" name="V164" />
<arc begin="258" end="190" isPushBackRoute="1" name="V164" />
<arc begin="190" end="258" isPushBackRoute="1" name="V164" />
<arc begin="36" end="259" isPushBackRoute="1" name="V159" />
<arc begin="259" end="36" isPushBackRoute="1" name="V159" />
<arc begin="259" end="88" isPushBackRoute="1" name="V159" />
<arc begin="88" end="259" isPushBackRoute="1" name="V159" />
<arc begin="37" end="260" isPushBackRoute="1" name="A40" />
<arc begin="260" end="37" isPushBackRoute="1" name="A40" />
<arc begin="260" end="121" isPushBackRoute="1" name="A40" />
<arc begin="121" end="260" isPushBackRoute="1" name="A40" />
<arc begin="38" end="261" isPushBackRoute="1" name="A17" />
<arc begin="261" end="38" isPushBackRoute="1" name="A17" />
<arc begin="261" end="108" isPushBackRoute="1" name="A17" />
<arc begin="108" end="261" isPushBackRoute="1" name="A17" />
<arc begin="39" end="262" isPushBackRoute="1" name="A1" />
<arc begin="262" end="39" isPushBackRoute="1" name="A1" />
<arc begin="262" end="110" isPushBackRoute="1" name="A1" />
<arc begin="110" end="262" isPushBackRoute="1" name="A1" />
</TaxiWaySegments>
</groundnet>

View File

@@ -0,0 +1,98 @@
 <Approach Name="ILS28">
<App_Waypoint ID="1">
<Name>CF28</Name>
<Type>Normal</Type>
<Latitude>49.085211</Latitude>
<Longitude>10.104772</Longitude>
<Speed>0</Speed>
<Altitude>5000</Altitude>
<AltitudeCons>0</AltitudeCons>
<AltitudeRestriction>above</AltitudeRestriction>
<Flytype>Fly-by</Flytype>
<BankLimit>25</BankLimit>
<Sp_Turn>Auto</Sp_Turn>
</App_Waypoint>
<App_Waypoint ID="2">
<Name>ESUVA</Name>
<Type>Normal</Type>
<Latitude>49.087931</Latitude>
<Longitude>10.079558</Longitude>
<Speed>0</Speed>
<Altitude>5000</Altitude>
<AltitudeCons>0</AltitudeCons>
<AltitudeRestriction>at</AltitudeRestriction>
<Flytype>Fly-by</Flytype>
<BankLimit>25</BankLimit>
<Sp_Turn>Auto</Sp_Turn>
</App_Waypoint>
<App_Waypoint ID="3">
<Name>RW28</Name>
<Type>Runway</Type>
<Latitude>49.117467</Latitude>
<Longitude>9.792336</Longitude>
<Speed>0</Speed>
<Altitude>1363</Altitude>
<AltitudeCons>0</AltitudeCons>
<AltitudeRestriction>at</AltitudeRestriction>
<Flytype>Fly-by</Flytype>
<BankLimit>25</BankLimit>
<Sp_Turn>Auto</Sp_Turn>
</App_Waypoint>
<App_Waypoint ID="4">
<Name>TY021</Name>
<Type>Normal</Type>
<Latitude>49.131786</Latitude>
<Longitude>9.650253</Longitude>
<Speed>0</Speed>
<Altitude>4000</Altitude>
<AltitudeCons>0</AltitudeCons>
<AltitudeRestriction>above</AltitudeRestriction>
<Flytype>Fly-over</Flytype>
<BankLimit>25</BankLimit>
<Sp_Turn>Auto</Sp_Turn>
</App_Waypoint>
<App_Waypoint ID="5">
<Name>DKB</Name>
<Type>Normal</Type>
<Latitude>49.142753</Latitude>
<Longitude>10.238306</Longitude>
<Speed>0</Speed>
<Altitude>5000</Altitude>
<AltitudeCons>0</AltitudeCons>
<AltitudeRestriction>at</AltitudeRestriction>
<Flytype>Fly-by</Flytype>
<BankLimit>25</BankLimit>
<Sp_Turn>Right</Sp_Turn>
</App_Waypoint>
<App_Transition Name="DKB">
<AppTr_Waypoint ID="1">
<Name>DKB</Name>
<Type>Normal</Type>
<Latitude>49.142753</Latitude>
<Longitude>10.238306</Longitude>
<Speed>0</Speed>
<Altitude>0</Altitude>
<AltitudeCons>0</AltitudeCons>
<AltitudeRestriction>at</AltitudeRestriction>
<Flytype>Fly-by</Flytype>
<BankLimit>25</BankLimit>
<Sp_Turn>Auto</Sp_Turn>
</AppTr_Waypoint>
<AppTr_Waypoint ID="2">
<Name>DKB</Name>
<Type>DmeIntc</Type>
<Latitude>49.142753</Latitude>
<Longitude>10.238306</Longitude>
<Speed>0</Speed>
<Altitude>5000</Altitude>
<AltitudeCons>0</AltitudeCons>
<AltitudeRestriction>above</AltitudeRestriction>
<Hdg_Crs>0</Hdg_Crs>
<Hdg_Crs_value>214</Hdg_Crs_value>
<Flytype>Fly-by</Flytype>
<BankLimit>25</BankLimit>
<DMEtoIntercept>4.0</DMEtoIntercept>
<Sp_Turn>Auto</Sp_Turn>
</AppTr_Waypoint>
</App_Transition>
</Approach>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
This is a simulation of a user-provided custom aircraft dir.
It contains a fake UFO which intentinoally is different to the one in FGData,
for checking precedence of paths and options.

View File

@@ -0,0 +1,9 @@
<?xml version="1.0"?>
<PropertyList>
<sim>
<description>Better UFO than FG data</description>
</sim>
<zot>42</zot>
</PropertyList>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0"?>
<PropertyList>
<id>org.fg.test.catalog1</id>
<description>First test catalog</description>
<url>http://localhost:2000/catalogTest1/catalog.xml</url>
<catalog-version>4</catalog-version>
<version>*</version>
<package>
<id>bob</id>
<name>The Bob Craft</name>
<revision type="int">8</revision>
<file-size-bytes type="int">593</file-size-bytes>
<md5>a469c4b837f0521db48616cfe65ac1ea</md5>
<url>http://localhost:2000/catalogTest1/alpha.zip</url>
<dir>bobCraft</dir>
</package>
</PropertyList>

View File

@@ -0,0 +1,25 @@
set(TESTSUITE_SOURCES
${TESTSUITE_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
${CMAKE_CURRENT_SOURCE_DIR}/test_AIFlightPlan.cxx
${CMAKE_CURRENT_SOURCE_DIR}/test_AIManager.cxx
${CMAKE_CURRENT_SOURCE_DIR}/test_traffic.cxx
${CMAKE_CURRENT_SOURCE_DIR}/test_TrafficMgr.cxx
${CMAKE_CURRENT_SOURCE_DIR}/test_groundnet.cxx
${CMAKE_CURRENT_SOURCE_DIR}/test_submodels.cxx
${CMAKE_CURRENT_SOURCE_DIR}/test_VectorMath.cxx
PARENT_SCOPE
)
set(TESTSUITE_HEADERS
${TESTSUITE_HEADERS}
${CMAKE_CURRENT_SOURCE_DIR}/test_AIFlightPlan.hxx
${CMAKE_CURRENT_SOURCE_DIR}/test_AIManager.hxx
${CMAKE_CURRENT_SOURCE_DIR}/test_traffic.hxx
${CMAKE_CURRENT_SOURCE_DIR}/test_TrafficMgr.hxx
${CMAKE_CURRENT_SOURCE_DIR}/test_groundnet.hxx
${CMAKE_CURRENT_SOURCE_DIR}/test_submodels.hxx
${CMAKE_CURRENT_SOURCE_DIR}/test_VectorMath.hxx
PARENT_SCOPE
)

Some files were not shown because too many files have changed in this diff Show More