1
0
Fork 0

Partial testing framework.

Compile a useful subset of FG as a shared library, and add two basic
uses of this to exercise some Flightplan / RoutePath / navaid
functions.

The test framework can/will be expanded incrementally from here, this
is just a starting point.
This commit is contained in:
James Turner 2017-03-21 21:43:42 +01:00
parent 7adb2fa851
commit 9e122eaf81
25 changed files with 1499 additions and 113 deletions

View file

@ -512,6 +512,22 @@ add_subdirectory(utils)
add_subdirectory(src)
add_subdirectory(man)
if(ENABLE_TESTS)
# enable CTest / make test target
message(STATUS "Tests: ENABLED")
include (Dart)
enable_testing()
if(WIN32)
# tests disabled until shared library export is fixed on Windows
message(STATUS "Tests disabled on Windows for the moment")
else()
add_subdirectory(tests)
endif()
else()
message(STATUS "Tests: DISABLED")
endif(ENABLE_TESTS)
#-----------------------------------------------------------------------------
### uninstall target
#-----------------------------------------------------------------------------

View file

@ -26,8 +26,13 @@
#include <simgear/constants.h>
#include <simgear/debug/logstream.hxx>
#ifdef FG_TESTLIB
#include <tests/fake_sgSky.hxx>
#else
#include <simgear/scene/sky/sky.hxx>
#include <simgear/scene/model/particles.hxx>
#endif
#include <simgear/structure/event_mgr.hxx>
#include <Main/main.hxx>
@ -47,6 +52,7 @@
#include "gravity.hxx"
#include "magvarmanager.hxx"
#ifndef FG_TESTLIB
class FG3DCloudsListener : public SGPropertyChangeListener {
public:
FG3DCloudsListener( FGClouds * fgClouds );
@ -60,7 +66,7 @@ private:
};
FG3DCloudsListener::FG3DCloudsListener( FGClouds * fgClouds ) :
_fgClouds( fgClouds )
_fgClouds( fgClouds )
{
_enableNode = fgGetNode( "/sim/rendering/clouds3d-enable", true );
_enableNode->addChangeListener( this );
@ -77,21 +83,28 @@ void FG3DCloudsListener::valueChanged( SGPropertyNode * node )
{
_fgClouds->set_3dClouds( _enableNode->getBoolValue() );
}
#endif
FGEnvironmentMgr::FGEnvironmentMgr () :
_environment(new FGEnvironment()),
fgClouds(new FGClouds()),
fgClouds(nullptr),
_cloudLayersDirty(true),
_3dCloudsEnableListener(new FG3DCloudsListener(fgClouds) ),
_3dCloudsEnableListener(nullptr),
_sky(globals->get_renderer()->getSky())
{
#ifndef FG_TESTLIB
fgClouds = new FGClouds;
_3dCloudsEnableListener = new FG3DCloudsListener(fgClouds);
#endif
set_subsystem("controller", Environment::LayerInterpolateController::createInstance( fgGetNode("/environment/config", true ) ));
set_subsystem("realwx", Environment::RealWxController::createInstance( fgGetNode("/environment/realwx", true ) ), 1.0 );
set_subsystem("precipitation", new FGPrecipitationMgr);
#ifndef FG_TESTLIB
set_subsystem("realwx", Environment::RealWxController::createInstance( fgGetNode("/environment/realwx", true ) ), 1.0 );
set_subsystem("terrainsampler", Environment::TerrainSampler::createInstance( fgGetNode("/environment/terrain", true ) ));
#endif
set_subsystem("ridgelift", new FGRidgeLift);
set_subsystem("magvar", new FGMagVarManager);
}
@ -103,23 +116,26 @@ FGEnvironmentMgr::~FGEnvironmentMgr ()
remove_subsystem("realwx");
remove_subsystem("controller");
remove_subsystem("magvar");
delete fgClouds;
delete _environment;
#ifndef FG_TESTLIB
delete fgClouds;
delete _3dCloudsEnableListener;
#endif
delete _environment;
}
SGSubsystem::InitStatus FGEnvironmentMgr::incrementalInit()
{
InitStatus r = SGSubsystemGroup::incrementalInit();
if (r == INIT_DONE) {
#ifndef FG_TESTLIB
fgClouds->Init();
#endif
globals->get_event_mgr()->addTask("updateClosestAirport", this,
&FGEnvironmentMgr::updateClosestAirport, 30 );
}
return r;
}
@ -148,10 +164,11 @@ FGEnvironmentMgr::bind ()
_tiedProperties.Tie( "effective-visibility-m", _sky,
&SGSky::get_visibility );
#ifndef FG_TESTLIB
_tiedProperties.Tie("rebuild-layers", fgClouds,
&FGClouds::get_update_event,
&FGClouds::set_update_event);
#endif
// _tiedProperties.Tie("turbulence/use-cloud-turbulence", &sgEnviro,
// &SGEnviro::get_turbulence_enable_state,
// &SGEnviro::set_turbulence_enable_state);
@ -221,7 +238,6 @@ FGEnvironmentMgr::bind ()
_tiedProperties.Tie("clouds3d-use-impostors", _sky,
&SGSky::get_3dCloudUseImpostors,
&SGSky::set_3dCloudUseImpostors);
}
void
@ -240,15 +256,16 @@ FGEnvironmentMgr::update (double dt)
SGGeod aircraftPos(globals->get_aircraft_position());
_environment->set_elevation_ft( aircraftPos.getElevationFt() );
#ifndef FG_TESTLIB
simgear::Particles::setWindFrom( _environment->get_wind_from_heading_deg(),
_environment->get_wind_speed_kt() );
if( _cloudLayersDirty ) {
_cloudLayersDirty = false;
fgClouds->set_update_event( fgClouds->get_update_event()+1 );
}
#endif
fgSetDouble( "/environment/gravitational-acceleration-mps2",
fgSetDouble( "/environment/gravitational-acceleration-mps2",
Environment::Gravity::instance()->getGravity(aircraftPos));
}
@ -256,7 +273,7 @@ void
FGEnvironmentMgr::updateClosestAirport()
{
SG_LOG(SG_ENVIRONMENT, SG_DEBUG, "FGEnvironmentMgr::update: updating closest airport");
SGGeod pos = globals->get_aircraft_position();
FGAirport * nearestAirport = FGAirport::findClosest(pos, 100.0);
if( nearestAirport == NULL )

View file

@ -36,8 +36,10 @@
class FGPrecipitationMgr : public SGSubsystem
{
private:
#ifndef FG_TESTLIB
osg::ref_ptr<osg::MatrixTransform> transform;
osg::ref_ptr<SGPrecipitation> precipitation;
#endif
float getPrecipitationAtAltitudeMax(void);
simgear::TiedPropertyList _tiedProperties;
@ -50,10 +52,9 @@ public:
virtual void unbind ();
virtual void init ();
virtual void update (double dt);
void setupSceneGraph(void);
void setPrecipitationLevel(double l);
};
#endif

View file

@ -33,6 +33,7 @@
#cmakedefine ENABLE_JSBSIM
#define PKGLIBDIR "@FG_DATA_DIR@"
#define FGSRCDIR "@PROJECT_SOURCE_DIR@"
#define WEB_BROWSER "@WEB_BROWSER@"
// Ensure FG_HAVE_xxx always have a value

View file

@ -242,8 +242,10 @@ setFreeze (bool f)
}
}
#ifndef FG_TESTLIB
// Pause the particle system
simgear::Particles::setFrozen(f);
#endif
}

View file

@ -20,25 +20,25 @@
//
// $Id$
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <config.h>
#include <boost/foreach.hpp>
#include <algorithm>
#ifndef FG_TESTLIB
#include <osgViewer/Viewer>
#include <osgDB/Registry>
#endif
#include <simgear/structure/commands.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/timing/sg_time.hxx>
#include <simgear/ephemeris/ephemeris.hxx>
#include <simgear/scene/material/matlib.hxx>
#include <simgear/structure/subsystem_mgr.hxx>
#include <simgear/structure/event_mgr.hxx>
#include <simgear/sound/soundmgr.hxx>
#include <simgear/misc/ResourceManager.hxx>
#include <simgear/props/propertyObject.hxx>
#include <simgear/props/props_io.hxx>
@ -48,14 +48,21 @@
#include <Aircraft/controls.hxx>
#include <Airports/runways.hxx>
#include <Autopilot/route_mgr.hxx>
#include <Navaids/navlist.hxx>
#include <GUI/gui.h>
#include <Viewer/viewmgr.hxx>
#ifndef FG_TESTLIB
#include <Scenery/scenery.hxx>
#include <Scenery/tilemgr.hxx>
#include <Navaids/navlist.hxx>
#include <Viewer/renderer.hxx>
#include <Viewer/viewmgr.hxx>
#include <GUI/FGFontCache.hxx>
#include <simgear/sound/soundmgr.hxx>
#include <simgear/scene/material/matlib.hxx>
#endif
#include "globals.hxx"
#include "locale.hxx"
@ -147,7 +154,9 @@ FGGlobals *globals = NULL;
// Constructor
FGGlobals::FGGlobals() :
#ifndef FG_TESTLIB
renderer( new FGRenderer ),
#endif
subsystem_mgr( new SGSubsystemMgr ),
event_mgr( new SGEventMgr ),
sim_time_sec( 0.0 ),
@ -195,7 +204,7 @@ FGGlobals::~FGGlobals()
// stop OSG threading first, to avoid thread races while we tear down
// scene-graph pieces
#ifndef FG_TESTLIB
osg::ref_ptr<osgViewer::Viewer> vw(renderer->getViewer());
if (vw) {
// https://code.google.com/p/flightgear-bugs/issues/detail?id=1291
@ -204,10 +213,11 @@ FGGlobals::~FGGlobals()
// GraphicsContext)
vw->stopThreading();
}
#endif
subsystem_mgr->shutdown();
subsystem_mgr->unbind();
#ifndef FG_TESTLIB
// don't cancel the pager until after shutdown, since AIModels (and
// potentially others) can queue delete requests on the pager.
if (vw && vw->getDatabasePager()) {
@ -223,21 +233,24 @@ FGGlobals::~FGGlobals()
FGFontCache::shutdown();
fgCancelSnapShot();
#endif
delete subsystem_mgr;
subsystem_mgr = NULL; // important so ::get_subsystem returns NULL
vw = 0; // don't delete the viewer until now
#ifndef FG_TESTLIB
vw = nullptr; // don't delete the viewer until now
set_matlib(NULL);
#endif
delete time_params;
set_matlib(NULL);
delete channel_options_list;
delete initial_waypoints;
delete channellist;
simgear::PropertyObjectBase::setDefaultRoot(NULL);
#ifndef FG_TESTLIB
simgear::SGModelLib::resetPropertyRoot();
#endif
delete locale;
locale = NULL;
@ -509,12 +522,14 @@ FGGlobals::get_renderer () const
void FGGlobals::set_renderer(FGRenderer *render)
{
#ifndef FG_TESTLIB
if (render == renderer) {
return;
}
delete renderer;
renderer = render;
#endif
}
SGSubsystemMgr *
@ -750,7 +765,11 @@ void FGGlobals::set_warp_delta( long int d )
FGScenery* FGGlobals::get_scenery () const
{
#ifdef FG_TESTLIB
return nullptr;
#else
return get_subsystem<FGScenery>();
#endif
}
FGViewMgr *FGGlobals::get_viewmgr() const
@ -766,7 +785,9 @@ flightgear::View* FGGlobals::get_current_view () const
void FGGlobals::set_matlib( SGMaterialLib *m )
{
#ifndef FG_TESTLIB
matlib = m;
#endif
}
FGControls *FGGlobals::get_controls() const

View file

@ -97,10 +97,10 @@ private:
SGPath fg_root;
/**
* locations to search for (non-scenery) data.
* locations to search for (non-scenery) data.
*/
PathList additional_data_paths;
// Users home directory for data
SGPath fg_home;
// Download directory
@ -118,8 +118,10 @@ private:
// Time structure
SGTime *time_params;
#ifndef FG_TESTLIB
// Material properties library
SGSharedPtr<SGMaterialLib> matlib;
#endif
SGCommandMgr *commands;
@ -142,17 +144,17 @@ private:
SGPropertyNode_ptr positionLon, positionLat, positionAlt;
SGPropertyNode_ptr viewLon, viewLat, viewAlt;
SGPropertyNode_ptr orientHeading, orientPitch, orientRoll;
/**
* helper to initialise standard properties on a new property tree
*/
void initProperties();
void cleanupListeners();
typedef std::vector<SGPropertyChangeListener*> SGPropertyChangeListenerVec;
SGPropertyChangeListenerVec _listeners_to_cleanup;
SGSharedPtr<simgear::pkg::Root> _packageRoot;
public:
@ -161,7 +163,7 @@ public:
virtual FGRenderer *get_renderer () const;
void set_renderer(FGRenderer* render);
SGSubsystemMgr *get_subsystem_mgr () const;
SGSubsystem *get_subsystem (const char * name) const;
@ -203,21 +205,21 @@ public:
* result.
*/
PathList get_data_paths() const;
/**
* Get data locations which contain the file path suffix. Eg pass ing
* 'AI/Traffic' to get all data paths which define <path>/AI/Traffic subdir
*/
PathList get_data_paths(const std::string& suffix) const;
void append_data_path(const SGPath& path);
/**
* Given a path suffix (eg 'Textures' or 'AI/Traffic'), find the
* first data directory which defines it.
*/
SGPath find_data_dir(const std::string& pathSuffix) const;
const SGPath &get_fg_home () const { return fg_home; }
void set_fg_home (const SGPath &home);
@ -245,13 +247,13 @@ public:
* This also makes the path Nasal-readable:
* to avoid can-read-any-file security holes, do NOT call this on paths
* obtained from the property tree or other Nasal-writable places
*/
*/
void append_fg_scenery (const SGPath &scenery);
void append_fg_scenery (const PathList &scenery);
void clear_fg_scenery();
/**
* Allow Nasal to read a path
*
@ -276,10 +278,10 @@ public:
* This also makes the path Nasal-readable:
* to avoid can-read-any-file security holes, do NOT call this on paths
* obtained from the property tree or other Nasal-writable places
*/
*/
void append_aircraft_path(const SGPath& path);
void append_aircraft_paths(const PathList& path);
/**
* Given a path to an aircraft-related resource file, resolve it
* against the appropriate root. This means looking at the location
@ -289,13 +291,13 @@ public:
* if the path could not be resolved, an empty path is returned.
*/
SGPath resolve_aircraft_path(const std::string& branch) const;
/**
* Same as above, but test for non 'Aircraft/' branch paths, and
* always resolve them against fg_root.
*/
SGPath resolve_maybe_aircraft_path(const std::string& branch) const;
/**
* Search in the following directories:
*
@ -317,7 +319,14 @@ public:
inline SGTime *get_time_params() const { return time_params; }
inline void set_time_params( SGTime *t ) { time_params = t; }
inline SGMaterialLib *get_matlib() const { return matlib; }
inline SGMaterialLib *get_matlib() const
{
#ifdef FG_TESTLIB
return nullptr;
#else
return matlib;
#endif
}
void set_matlib( SGMaterialLib *m );
inline SGPropertyNode *get_props () { return props; }
@ -327,7 +336,7 @@ public:
* subsystems are shutdown and unbound before call this.
*/
void resetPropertyRoot();
inline FGLocale* get_locale () { return locale; }
inline SGCommandMgr *get_commands () { return commands; }
@ -337,11 +346,11 @@ public:
SGVec3d get_aircraft_position_cart() const;
void get_aircraft_orientation(double& heading, double& pitch, double& roll);
SGGeod get_view_position() const;
SGVec3d get_view_position_cart() const;
inline string_list *get_channel_options_list () {
return channel_options_list;
}
@ -352,7 +361,7 @@ public:
inline string_list *get_initial_waypoints () {
return initial_waypoints;
}
inline void set_initial_waypoints (string_list *list) {
initial_waypoints = list;
}
@ -363,7 +372,7 @@ public:
FGControls *get_controls() const;
FGScenery * get_scenery () const;
inline FGTACANList *get_channellist() const { return channellist; }
inline void set_channellist( FGTACANList *c ) { channellist = c; }
@ -392,7 +401,7 @@ public:
void saveUserSettings(SGPath userDatapath = SGPath());
void addListenerToCleanup(SGPropertyChangeListener* l);
simgear::pkg::Root* packageRoot();
void setPackageRoot(const SGSharedPtr<simgear::pkg::Root>& p);
};

View file

@ -58,7 +58,8 @@
#include <GUI/gui.h>
#include <GUI/MessageBox.hxx>
#if defined(HAVE_QT)
#if defined(HAVE_QT) && !defined(FG_TESTLIB)
#include <GUI/QtLauncher.hxx>
#include <GUI/SetupRootDialog.hxx>
#endif
@ -236,9 +237,11 @@ void fgSetDefaults ()
SGPropertyNode* v = globals->get_props()->getNode("/sim/version", true);
v->setValueReadOnly("flightgear", FLIGHTGEAR_VERSION);
v->setValueReadOnly("simgear", SG_STRINGIZE(SIMGEAR_VERSION));
#ifndef FG_TESTLIB
v->setValueReadOnly("openscenegraph", osgGetVersion());
v->setValueReadOnly("openscenegraph-thread-safe-reference-counting",
osg::Referenced::getThreadSafeReferenceCounting());
#endif
v->setValueReadOnly("revision", REVISION);
v->setValueReadOnly("build-number", HUDSON_BUILD_NUMBER);
v->setValueReadOnly("build-id", HUDSON_BUILD_ID);
@ -989,12 +992,14 @@ fgOptJpgHttpd( const char * arg )
static int
fgOptHttpd( const char * arg )
{
#ifndef FG_TESTLIB
// port may be any valid address:port notation
// like 127.0.0.1:8080
// or just the port 8080
string port = simgear::strutils::strip(string(arg));
if( port.empty() ) return FG_OPTIONS_ERROR;
fgSetString( string(flightgear::http::PROPERTY_ROOT).append("/options/listening-port").c_str(), port );
#endif
return FG_OPTIONS_OK;
}
@ -2650,7 +2655,9 @@ void Options::showVersion() const
PathList scn = globals->get_fg_scenery();
cout << SGPath::join(scn, &SGPath::pathListSep) << endl;
cout << "SimGear version: " << SG_STRINGIZE(SIMGEAR_VERSION) << endl;
#ifndef FG_TESTLIB
cout << "OSG version: " << osgGetVersion() << endl;
#endif
cout << "PLIB version: " << PLIB_VERSION << endl;
}
@ -2767,7 +2774,7 @@ void Options::setupRoot(int argc, char **argv)
root = SGPath::fromLocal8Bit(envp);
SG_LOG(SG_GENERAL, SG_INFO, "set from FG_ROOT env var: fg_root = " << root );
} else {
#if defined(HAVE_QT)
#if defined(HAVE_QT) && !defined(FG_TESTLIB)
flightgear::initApp(argc, argv);
root = SetupRootDialog::restoreUserSelectedRoot();
#endif
@ -2785,7 +2792,7 @@ void Options::setupRoot(int argc, char **argv)
string base_version = fgBasePackageVersion(root);
#if defined(HAVE_QT)
#if defined(HAVE_QT) && !defined(FG_TESTLIB)
// only compare major and minor version, not the patch level.
const int versionComp = simgear::strutils::compare_versions(FLIGHTGEAR_VERSION, base_version, 2);

View file

@ -1069,8 +1069,8 @@ WayptRef FlightPlan::waypointFromString(const string& tgt )
SGGeod basePosition;
if (_legs.empty()) {
// route is empty, use current position
basePosition = globals->get_aircraft_position();
// route is empty, use departure position / aircraft position
basePosition = _departure ? _departure->geod() : globals->get_aircraft_position();
} else {
basePosition = _legs.back()->waypoint()->position();
}

View file

@ -990,10 +990,13 @@ static naRef f_geodinfo(naContext c, naRef me, int argc, naRef* args)
SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
if(!globals->get_scenery()->get_elevation_m(geod, elev, &material))
return naNil();
const SGMaterial *mat = dynamic_cast<const SGMaterial *>(material);
naRef vec = naNewVector(c);
naVec_append(vec, naNum(elev));
naRef matdata = naNil();
#ifndef FG_TESTLIB
const SGMaterial *mat = dynamic_cast<const SGMaterial *>(material);
if(mat) {
matdata = naNewHash(c);
naRef names = naNewVector(c);
@ -1009,6 +1012,7 @@ static naRef f_geodinfo(naContext c, naRef me, int argc, naRef* args)
HASHSET("light_coverage", 14, naNum(mat->get_light_coverage()));
}
naVec_append(vec, matdata);
#endif
return vec;
#undef HASHSET
}

View file

@ -105,16 +105,16 @@ public:
_gcRoot = sys->gcSave(f);
_gcSelf = sys->gcSave(self);
}
virtual ~TimerObj()
{
stop();
_sys->gcRelease(_gcRoot);
_sys->gcRelease(_gcSelf);
}
bool isRunning() const { return _isRunning; }
void stop()
{
if (_isRunning) {
@ -140,7 +140,7 @@ public:
if (_isRunning) {
return;
}
_isRunning = true;
if (_singleShot) {
globals->get_event_mgr()->addEvent(_name, this, &TimerObj::invoke, _interval, _isSimTime);
@ -150,7 +150,7 @@ public:
_isSimTime);
}
}
// stop and then start -
void restart(double newInterval)
{
@ -158,7 +158,7 @@ public:
stop();
start();
}
void invoke()
{
if( _singleShot )
@ -170,12 +170,12 @@ public:
naRef *args = NULL;
_sys->callMethod(_func, _self, 0, args, naNil() /* locals */);
}
void setSingleShot(bool aSingleShot)
{
_singleShot = aSingleShot;
}
bool isSingleShot() const
{ return _singleShot; }
private:
@ -240,7 +240,7 @@ FGNasalSys::FGNasalSys() :
_globals = naNil();
_string = naNil();
_wrappedNodeFunc = naNil();
_log = new simgear::BufferedLogCallback(SG_NASAL, SG_INFO);
_log->truncateAt(255);
sglog().addCallback(_log);
@ -386,10 +386,10 @@ static naRef f_getprop(naContext c, naRef me, int argc, naRef* args)
SG_LOG(SG_NASAL, SG_ALERT, "Nasal getprop: property " << p->getPath() << " is NaN");
return naNil();
}
return naNum(dv);
}
case props::STRING:
case props::UNSPECIFIED:
{
@ -421,11 +421,11 @@ static naRef f_setprop(naContext c, naRef me, int argc, naRef* args)
else {
if(!naIsNum(val))
naRuntimeError(c, "setprop() value is not string or number");
if (SGMisc<double>::isNaN(val.num)) {
naRuntimeError(c, "setprop() passed a NaN");
}
result = p->setDoubleValue(val.num);
}
} catch (const string& err) {
@ -457,7 +457,7 @@ static naRef f_logprint(naContext c, naRef me, int argc, naRef* args)
{
if (argc < 1)
naRuntimeError(c, "no prioirty argument to logprint()");
naRef priority = args[0];
string buf;
int n = argc;
@ -506,7 +506,7 @@ static naRef f_makeTimer(naContext c, naRef me, int argc, naRef* args)
if (!naIsNum(args[0])) {
naRuntimeError(c, "bad interval argument to maketimer");
}
naRef func, self = naNil();
if (naIsFunc(args[1])) {
func = args[1];
@ -514,7 +514,7 @@ static naRef f_makeTimer(naContext c, naRef me, int argc, naRef* args)
self = args[1];
func = args[2];
}
TimerObj* timerObj = new TimerObj(nasalSys, func, self, args[0].num);
return nasal::to_nasal(c, timerObj);
}
@ -618,7 +618,7 @@ static naRef f_directory(naContext c, naRef me, int argc, naRef* args)
std::string p = paths[i].file();
naVec_append(result, naStr_fromdata(naNewString(c), p.c_str(), p.size()));
}
return result;
}
@ -639,7 +639,7 @@ static naRef f_findDataDir(naContext c, naRef me, int argc, naRef* args)
{
if(argc != 1 || !naIsString(args[0]))
naRuntimeError(c, "bad arguments to findDataDir()");
SGPath p = globals->find_data_dir(naStr_data(args[0]));
std::string pdata = p.utf8Str();
return naStr_fromdata(naNewString(c), const_cast<char*>(pdata.c_str()), pdata.length());
@ -656,23 +656,23 @@ public:
globals->get_commands()->addCommandObject(_name, this);
_gcRoot = sys->gcSave(f);
}
virtual ~NasalCommand()
{
_sys->gcRelease(_gcRoot);
}
virtual bool operator()(const SGPropertyNode* aNode)
{
_sys->setCmdArg(const_cast<SGPropertyNode*>(aNode));
naRef args[1];
args[0] = _sys->wrappedPropsNode(const_cast<SGPropertyNode*>(aNode));
_sys->callMethod(_func, naNil(), 1, args, naNil() /* locals */);
return true;
}
private:
FGNasalSys* _sys;
naRef _func;
@ -684,7 +684,7 @@ static naRef f_addCommand(naContext c, naRef me, int argc, naRef* args)
{
if(argc != 2 || !naIsString(args[0]) || !naIsFunc(args[1]))
naRuntimeError(c, "bad arguments to addcommand()");
nasalSys->addCommand(args[1], naStr_data(args[0]));
return naNil();
}
@ -693,7 +693,7 @@ static naRef f_removeCommand(naContext c, naRef me, int argc, naRef* args)
{
if ((argc < 1) || !naIsString(args[0]))
naRuntimeError(c, "bad argument to removecommand()");
globals->get_commands()->removeCommand(naStr_data(args[0]));
return naNil();
}
@ -883,7 +883,7 @@ void FGNasalSys::init()
naNewFunc(_context, naNewCCode(_context, funcs[i].func)));
nasal::Hash io_module = nasal::Hash(_globals, _context).get<nasal::Hash>("io");
io_module.set("open", f_open);
// And our SGPropertyNode wrapper
hashset(_globals, "props", genPropsModule());
@ -895,12 +895,14 @@ void FGNasalSys::init()
initNasalPositioned(_globals, _context);
initNasalPositioned_cppbind(_globals, _context);
initNasalAircraft(_globals, _context);
#ifndef FG_TESTLIB
NasalClipboard::init(this);
initNasalCanvas(_globals, _context);
#endif
initNasalCondition(_globals, _context);
initNasalHTTP(_globals, _context);
initNasalSGPath(_globals, _context);
NasalTimerObj::init("Timer")
.method("start", &TimerObj::start)
.method("stop", &TimerObj::stop)
@ -912,7 +914,7 @@ void FGNasalSys::init()
// Set allowed paths for Nasal I/O
fgInitAllowedPaths();
// Now load the various source files in the Nasal directory
simgear::Dir nasalDir(SGPath(globals->get_fg_root(), "Nasal"));
loadScriptDirectory(nasalDir);
@ -934,11 +936,11 @@ void FGNasalSys::init()
// Pull scripts out of the property tree, too
loadPropertyScripts();
// now Nasal modules are loaded, we can do some delayed work
postinitNasalPositioned(_globals, _context);
postinitNasalGUI(_globals, _context);
_inited = true;
}
@ -947,36 +949,36 @@ void FGNasalSys::shutdown()
if (!_inited) {
return;
}
shutdownNasalPositioned();
map<int, FGNasalListener *>::iterator it, end = _listener.end();
for(it = _listener.begin(); it != end; ++it)
delete it->second;
_listener.clear();
NasalCommandDict::iterator j = _commands.begin();
for (; j != _commands.end(); ++j) {
globals->get_commands()->removeCommand(j->first);
}
_commands.clear();
std::vector<FGNasalModuleListener*>::iterator k = _moduleListeners.begin();
for(; k!= _moduleListeners.end(); ++k)
delete *k;
_moduleListeners.clear();
naClearSaved();
_string = naNil(); // will be freed by _context
naFreeContext(_context);
//setWatchedRef(_globals);
// remove the recursive reference in globals
hashset(_globals, "globals", naNil());
_globals = naNil();
_globals = naNil();
naGC();
_inited = false;
}
@ -987,7 +989,7 @@ naRef FGNasalSys::wrappedPropsNode(SGPropertyNode* aProps)
nasal::Hash props = getGlobals().get<nasal::Hash>("props");
_wrappedNodeFunc = props.get("wrapNode");
}
naRef args[1];
args[0] = propNodeGhost(aProps);
naContext ctx = naNewContext();
@ -998,15 +1000,17 @@ naRef FGNasalSys::wrappedPropsNode(SGPropertyNode* aProps)
void FGNasalSys::update(double)
{
#ifndef FG_TESTLIB
if( NasalClipboard::getInstance() )
NasalClipboard::getInstance()->update();
#endif
if(!_dead_listener.empty()) {
vector<FGNasalListener *>::iterator it, end = _dead_listener.end();
for(it = _dead_listener.begin(); it != end; ++it) delete *it;
_dead_listener.clear();
}
#ifndef FG_TESTLIB
if (!_loadList.empty())
{
if( _delay_load )
@ -1022,7 +1026,7 @@ void FGNasalSys::update(double)
// (only unload one per update loop to avoid excessive lags)
_unloadList.pop()->unload();
}
#endif
// Destroy all queued ghosts
nasal::ghostProcessDestroyList();
@ -1045,7 +1049,7 @@ bool pathSortPredicate(const SGPath& p1, const SGPath& p2)
return p1.file() < p2.file();
}
// Loads all scripts in given directory
// Loads all scripts in given directory
void FGNasalSys::loadScriptDirectory(simgear::Dir nasalDir)
{
simgear::PathList scripts = nasalDir.children(simgear::Dir::TYPE_FILE, ".nas");
@ -1211,7 +1215,7 @@ bool FGNasalSys::createModule(const char* moduleName, const char* fileName,
return false;
}
// See if we already have a module hash to use. This allows the
// user to, for example, add functions to the built-in math
// module. Make a new one if necessary.
@ -1228,7 +1232,7 @@ bool FGNasalSys::createModule(const char* moduleName, const char* fileName,
callWithContext(ctx, code, argc, args, locals);
hashset(_globals, moduleName, locals);
naFreeContext(ctx);
return true;
}
@ -1240,7 +1244,7 @@ void FGNasalSys::deleteModule(const char* moduleName)
// subsystems having Nasal objects.
return;
}
naContext ctx = naNewContext();
naRef modname = naNewString(ctx);
naStr_fromdata(modname, (char*)moduleName, strlen(moduleName));
@ -1443,14 +1447,18 @@ naRef FGNasalSys::removeListener(naContext c, int argc, naRef* args)
void FGNasalSys::registerToLoad(FGNasalModelData *data)
{
#ifndef FG_TESTLIB
if( _loadList.empty() )
_delay_load = true;
_loadList.push(data);
#endif
}
void FGNasalSys::registerToUnload(FGNasalModelData *data)
{
#ifndef FG_TESTLIB
_unloadList.push(data);
#endif
}
void FGNasalSys::addCommand(naRef func, const std::string& name)
@ -1459,7 +1467,7 @@ void FGNasalSys::addCommand(naRef func, const std::string& name)
SG_LOG(SG_NASAL, SG_WARN, "duplicate add of command:" << name);
return;
}
NasalCommand* cmd = new NasalCommand(this, func, name);
_commands[name] = cmd;
}
@ -1626,5 +1634,3 @@ naRef NasalXMLVisitor::make_string(const char* s, int n)
return naStr_fromdata(naNewString(_c), const_cast<char *>(s),
n < 0 ? strlen(s) : n);
}

View file

@ -155,10 +155,10 @@ private:
//friend class FGNasalScript;
friend class FGNasalListener;
friend class FGNasalModuleListener;
#ifndef FG_TESTLIB
SGLockedQueue<SGSharedPtr<FGNasalModelData> > _loadList;
SGLockedQueue<SGSharedPtr<FGNasalModelData> > _unloadList;
#endif
// Delay removing items of the _loadList to ensure the are already attached
// to the scene graph (eg. enables to retrieve world position in load
// callback).

View file

@ -1078,7 +1078,11 @@ double View::getOrientation_z() const{
double View::get_aspect_ratio() const
{
#ifdef FG_TESTLIB
return 4.0 / 3.0;
#else
return flightgear::CameraGroup::getDefault()->getMasterAspectRatio();
#endif
}
double View::getLon_deg() const

View file

@ -137,6 +137,7 @@ FGViewMgr::update (double dt)
// Update the current view
currentView->update(dt);
#ifndef FG_TESTLIB
// update the camera now
osg::ref_ptr<flightgear::CameraGroup> cameraGroup = flightgear::CameraGroup::getDefault();
@ -144,6 +145,7 @@ FGViewMgr::update (double dt)
toOsg(currentView->getViewOrientation()));
cameraGroup->setCameraParameters(currentView->get_v_fov(),
cameraGroup->getMasterAspectRatio());
#endif
}
void FGViewMgr::clear()

105
tests/CMakeLists.txt Normal file
View file

@ -0,0 +1,105 @@
set(sources
Main/options.cxx
Main/fg_commands.cxx
Main/fg_props.cxx
Main/globals.cxx
Main/locale.cxx
Main/util.cxx
Aircraft/controls.cxx
Aircraft/FlightHistory.cxx
Aircraft/flightrecorder.cxx
Aircraft/replay.cxx
Autopilot/route_mgr.cxx
Airports/airport.cxx
Airports/airport.hxx
Airports/apt_loader.cxx
Airports/airportdynamicsmanager.cxx
Airports/airportdynamicsmanager.hxx
Airports/dynamicloader.cxx
Airports/dynamics.cxx
Airports/xmlloader.cxx
Airports/runwaybase.cxx
Airports/pavement.cxx
Airports/parking.cxx
Airports/groundnetwork.cxx
Airports/gnnode.cxx
Airports/runways.cxx
Airports/runwayprefs.cxx
Airports/runwayprefloader.cxx
ATC/CommStation.cxx
# ATC/GroundController.cxx
# ATC/atc_mgr.cxx
Environment/atmosphere.cxx
Environment/environment.cxx
Environment/environment_mgr.cxx
Environment/environment_ctrl.cxx
Environment/presets.cxx
Environment/gravity.cxx
Environment/ridge_lift.cxx
Environment/magvarmanager.cxx
Navaids/airways.cxx
Navaids/fixlist.cxx
Navaids/markerbeacon.cxx
Navaids/NavDataCache.cxx
Navaids/navdb.cxx
Navaids/navlist.cxx
Navaids/navrecord.cxx
Navaids/poidb.cxx
Navaids/procedure.cxx
Navaids/positioned.cxx
Navaids/PositionedOctree.cxx
Navaids/routePath.cxx
Navaids/route.cxx
Navaids/waypoint.cxx
Navaids/FlightPlan.cxx
Navaids/LevelDXML.cxx
Network/HTTPClient.cxx
Time/TimeManager.cxx
Time/bodysolver.cxx
Scripting/NasalSys.cxx
Scripting/NasalCondition.cxx
Scripting/NasalAircraft.cxx
Scripting/NasalString.cxx
Scripting/NasalPositioned.cxx
Scripting/NasalPositioned_cppbind.cxx
Scripting/nasal-props.cxx
Scripting/NasalSGPath.cxx
Scripting/NasalHTTP.cxx
Viewer/view.cxx
Viewer/viewmgr.cxx
)
foreach(s ${sources})
set_property(DIRECTORY APPEND PROPERTY fgtestlib_sources "${CMAKE_SOURCE_DIR}/src/${s}")
endforeach()
set_property(DIRECTORY APPEND PROPERTY fgtestlib_sources "${CMAKE_SOURCE_DIR}/3rdparty/cjson/cJSON.c")
get_property(fgtestlib_sources DIRECTORY PROPERTY fgtestlib_sources)
add_library(fgtestlib SHARED ${fgtestlib_sources}
unitTestHelpers.cxx
testStubs.cxx
fake_sgSky.cxx
fake_sgPrecipitation.cxx
fake_sound.cxx)
set_target_properties (fgtestlib
PROPERTIES
COMPILE_DEFINITIONS "FG_TESTLIB"
)
target_link_libraries(fgtestlib SimGearCore fgsqlite3 ${PLATFORM_LIBS})
add_executable(fgtest fgTestDriver.cxx)
target_link_libraries(fgtest fgtestlib)
# repeat this section for each unit-test executable
add_executable(testnavs test_navaids2.cxx)
target_link_libraries(testnavs fgtestlib)
add_test(testnavs ${EXECUTABLE_OUTPUT_PATH}/testnavs)
add_executable(testflightplan test_flightplan.cxx)
target_link_libraries(testflightplan fgtestlib)
add_test(testflightplan ${EXECUTABLE_OUTPUT_PATH}/testflightplan)

View file

@ -0,0 +1,17 @@
#include <Environment/precipitation_mgr.hxx>
FGPrecipitationMgr::FGPrecipitationMgr()
{
}
FGPrecipitationMgr::~FGPrecipitationMgr()
{
}
void FGPrecipitationMgr::bind () {}
void FGPrecipitationMgr::unbind () {}
void FGPrecipitationMgr::init () {}
void FGPrecipitationMgr::update (double dt) {}

41
tests/fake_sgSky.cxx Normal file
View file

@ -0,0 +1,41 @@
#include "fake_sgSky.hxx"
void SGSky::add_cloud_layer(SGCloudLayer *layer)
{
_cloudLayers.push_back(layer);
}
SGCloudLayer *SGSky::get_cloud_layer(int i)
{
return _cloudLayers.at(i);
}
int SGSky::get_cloud_layer_count() const
{
return _cloudLayers.size();
}
void SGSky::set_clouds_enabled(bool enabled)
{
_3dcloudsEnabled = enabled;
}
const std::string &SGCloudLayer::getCoverageString() const
{
return std::string();
}
const std::string &SGCloudLayer::getCoverageString(SGCloudLayer::Coverage coverage)
{
return std::string();
}
void SGCloudLayer::setCoverageString(const std::string &coverage)
{
}
void SGCloudLayer::set_enable3dClouds(bool enable)
{
}

338
tests/fake_sgSky.hxx Normal file
View file

@ -0,0 +1,338 @@
#ifndef FG_TEST_FAKE_SGSKY_HXX
#define FG_TEST_FAKE_SGSKY_HXX
#include <simgear/structure/SGReferenced.hxx>
#include <string>
#include <vector>
class SGCloudLayer : public SGReferenced {
public:
/**
* This is the list of available cloud coverages/textures
*/
enum Coverage {
SG_CLOUD_OVERCAST = 0,
SG_CLOUD_BROKEN,
SG_CLOUD_SCATTERED,
SG_CLOUD_FEW,
SG_CLOUD_CIRRUS,
SG_CLOUD_CLEAR,
SG_MAX_CLOUD_COVERAGES
};
static const std::string SG_CLOUD_OVERCAST_STRING; // "overcast"
static const std::string SG_CLOUD_BROKEN_STRING; // "broken"
static const std::string SG_CLOUD_SCATTERED_STRING; // "scattered"
static const std::string SG_CLOUD_FEW_STRING; // "few"
static const std::string SG_CLOUD_CIRRUS_STRING; // "cirrus"
static const std::string SG_CLOUD_CLEAR_STRING; // "clear"
/**
* Constructor
* @param tex_path the path to the set of cloud textures
*/
SGCloudLayer( const std::string &tex_path ) { }
/**
* Destructor
*/
~SGCloudLayer( void ) { }
/** get the cloud span (in meters) */
float getSpan_m () const
{ return _spanM; }
/**
* set the cloud span
* @param span_m the cloud span in meters
*/
void setSpan_m (float span_m)
{ _spanM = span_m; }
/** get the layer elevation (in meters) */
float getElevation_m () const
{
return _elevationM;
}
/**
* set the layer elevation. Note that this specifies the bottom
* of the cloud layer. The elevation of the top of the layer is
* elevation_m + thickness_m.
* @param elevation_m the layer elevation in meters
* @param set_span defines whether it is allowed to adjust the span
*/
void setElevation_m (float elevation_m, bool set_span = true)
{
_elevationM = elevation_m;
}
/** get the layer thickness */
float getThickness_m () const
{
return _thicknessM;
}
/**
* set the layer thickness.
* @param thickness_m the layer thickness in meters.
*/
void setThickness_m (float thickness_m)
{
_thicknessM = thickness_m;
}
/** get the layer visibility */
float getVisibility_m() const
{
return _visibilityM;
}
/**
* set the layer visibility
* @param visibility_m the layer minimum visibility in meters.
*/
void setVisibility_m(float visibility_m)
{
_visibilityM = visibility_m;
}
/**
* get the transition/boundary layer depth in meters. This
* allows gradual entry/exit from the cloud layer via adjusting
* visibility.
*/
float getTransition_m () const
{
return _transitionM;
}
/**
* set the transition layer size in meters
* @param transition_m the transition layer size in meters
*/
void setTransition_m (float transition_m)
{
_transitionM = transition_m;
}
/** get coverage type */
Coverage getCoverage () const
{
return _coverage;
}
/**
* set coverage type
* @param coverage the coverage type
*/
void setCoverage (Coverage coverage)
{
_coverage = coverage;
}
/** get coverage as string */
const std::string & getCoverageString() const;
/** get coverage as string */
static const std::string & getCoverageString( Coverage coverage );
/** get coverage type from string */
static Coverage getCoverageType( const std::string & coverage );
/** set coverage as string */
void setCoverageString( const std::string & coverage );
/**
* set the cloud movement direction
* @param dir the cloud movement direction
*/
inline void setDirection(float dir) {
// cout << "cloud dir = " << dir << endl;
direction = dir;
}
/** get the cloud movement direction */
inline float getDirection() { return direction; }
/**
* set the cloud movement speed
* @param sp the cloud movement speed
*/
inline void setSpeed(float sp) {
// cout << "cloud speed = " << sp << endl;
speed = sp;
}
/** get the cloud movement speed */
float getSpeed() { return speed; }
void setAlpha( float alpha );
void setMaxAlpha( float alpha )
{
_maxAlpha = alpha;
}
float getMaxAlpha() const
{
return _maxAlpha;
}
/** Enable/disable 3D clouds in this layer */
void set_enable3dClouds(bool enable);
private:
float _spanM = 0.0f;
float _elevationM = 0.0f;
float _thicknessM = 0.0f;
float _transitionM = 0.0f;
float _visibilityM = 0.0f;
Coverage _coverage;
float scale = 0.0f;
float speed = 0.0f;
float direction = 0.0f;
float _maxAlpha = 0.0f;
};
class SGSky
{
public:
void add_cloud_layer (SGCloudLayer * layer);
const SGCloudLayer * get_cloud_layer (int i) const
{
return _cloudLayers.at(i);
}
SGCloudLayer * get_cloud_layer (int i);
int get_cloud_layer_count () const;
/** @return current effective visibility */
float get_visibility() const
{ return _visibility; }
/** Set desired clear air visibility.
* @param v visibility in meters
*/
void set_visibility( float v )
{ _visibility = v; }
/** Get 3D cloud density */
double get_3dCloudDensity() const
{
return _3dcloudDensity;
}
/** Set 3D cloud density
* @param density 3D cloud density
*/
void set_3dCloudDensity(double density)
{
_3dcloudDensity = density;
}
/** Get 3D cloud visibility range*/
float get_3dCloudVisRange() const
{
return _3dcloudVisRange;
}
/** Set 3D cloud visibility range
*
* @param vis 3D cloud visibility range
*/
void set_3dCloudVisRange(float vis)
{
_3dcloudVisRange = vis;
}
/** Get 3D cloud impostor distance*/
float get_3dCloudImpostorDistance() const
{
return _3dcloudImpostorDistance;
}
/** Set 3D cloud impostor distance
*
* @param vis 3D cloud impostor distance
*/
void set_3dCloudImpostorDistance(float vis)
{
_3dcloudImpostorDistance = vis;
}
/** Get 3D cloud LoD1 Range*/
float get_3dCloudLoD1Range() const
{
return _3dcloudLoDRange1;
}
/** Set 3D cloud LoD1 Range
* @param vis LoD1 Range
*/
void set_3dCloudLoD1Range(float vis)
{
_3dcloudLoDRange1 = vis;
}
/** Get 3D cloud LoD2 Range*/
float get_3dCloudLoD2Range() const
{
return _3dcloudLoDRange2;
}
/** Set 3D cloud LoD2 Range
* @param vis LoD2 Range
*/
void set_3dCloudLoD2Range(float vis)
{
_3dcloudLoDRange2 = vis;
}
/** Get 3D cloud impostor usage */
bool get_3dCloudUseImpostors() const
{
return _3dcloudUSeImpostors;
}
/** Set 3D cloud impostor usage
*
* @param imp whether use impostors for 3D clouds
*/
void set_3dCloudUseImpostors(bool imp)
{
_3dcloudUSeImpostors = imp;
}
/** Get 3D cloud wrapping */
bool get_3dCloudWrap() const
{ return _3dcloudWrap; }
/** Set 3D cloud wrapping
* @param wrap whether to wrap 3D clouds
*/
void set_3dCloudWrap(bool wrap)
{ _3dcloudWrap = wrap; }
void set_clouds_enabled(bool enabled);
private:
float _visibility = 0.0;
bool _3dcloudsEnabled = false;
bool _3dcloudWrap = false;
bool _3dcloudUSeImpostors = false;
float _3dcloudImpostorDistance = 0.0;
float _3dcloudLoDRange1 = 0.0;
float _3dcloudLoDRange2 = 0.0;
float _3dcloudVisRange = 0.0;
float _3dcloudDensity = 0.0;
std::vector<SGCloudLayer*> _cloudLayers;
};
#endif

57
tests/fake_sound.cxx Normal file
View file

@ -0,0 +1,57 @@
#include <simgear/sound/soundmgr.hxx>
class SGSoundMgr::SoundManagerPrivate
{
public:
};
SGSoundMgr::SGSoundMgr()
{
}
SGSoundMgr::~SGSoundMgr()
{
}
void SGSoundMgr::init()
{
}
void SGSoundMgr::stop()
{
}
void SGSoundMgr::suspend()
{
}
void SGSoundMgr::resume()
{
}
void SGSoundMgr::update(double dt)
{
}
void SGSoundMgr::reinit()
{
}
bool SGSoundMgr::load(const std::string &samplepath, void **data, int *format, size_t *size, int *freq, int *block)
{
return false;
}
std::vector<const char*> SGSoundMgr::get_available_devices()
{
std::vector<const char*> result;
return result;
}

77
tests/fgTestDriver.cxx Normal file
View file

@ -0,0 +1,77 @@
#include <vector>
#include <simgear/props/condition.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/math/SGMath.hxx>
class TestStep
{
public:
virtual void run() = 0;
// name for logging purposes
};
typedef std::vector<TestStep*> TestStepSequence;
class SimulateStep : public TestStep
{
public:
void run() override
{
}
private:
double m_count;
double m_dt;
SGGeod m_endPosition;
double m_endHeadingTrueDeg;
// other fake FDM data
};
class CommandStep : public TestStep
{
public:
void run() override
{
// build the command and run it
}
private:
SGPropertyNode_ptr m_cmd;
};
class CheckStep : public TestStep
{
public:
void run() override
{
for (auto cond : m_conditions) {
// eval
// if failed, boom
}
}
private:
std::vector<SGConditionRef> m_conditions;
};
int main(int argc, char* argv[])
{
// parse XML
// find test name
// wipe working dir
// create working dir
// build test stages
// read and create subsystems
// execute in turn
return EXIT_SUCCESS;
}

382
tests/testStubs.cxx Normal file
View file

@ -0,0 +1,382 @@
#include <string>
#include <simgear/misc/sg_path.hxx>
#include <simgear/structure/exception.hxx>
#include <Main/locale.hxx>
#include <Main/options.hxx>
#include <Scripting/NasalSys.hxx>
#include <GUI/MessageBox.hxx>
#include <ATC/trafficcontrol.hxx>
#include <ATC/GroundController.hxx>
#include <Scenery/scenery.hxx>
// declared extern in main.hxx
std::string hostname;
string fgBasePackageVersion(const SGPath& base_path)
{
return "";
}
void fgHiResDump()
{
}
void fgDumpSnapShot()
{
}
void fgCancelSnapShot()
{
}
void fgDumpSceneGraph()
{
}
void fgOSFullScreen()
{
}
void guiErrorMessage(const char *txt)
{
}
void guiErrorMessage(const char *txt, const sg_throwable &throwable)
{
}
void fgPrintVisibleSceneInfoCommand()
{
}
void openBrowser(const std::string& s)
{
}
void fgDumpTerrainBranch()
{
}
void fgOSExit(int code)
{
}
void postinitNasalGUI(naRef globals, naContext c)
{
}
void syncPausePopupState()
{
}
void
SGSetTextureFilter( int max)
{
}
int
SGGetTextureFilter()
{
return 0;
}
bool FGScenery::get_elevation_m(const SGGeod& geod, double& alt,
const simgear::BVHMaterial** material,
const osg::Node* butNotFrom)
{
return false;
}
namespace flightgear
{
MessageBoxResult modalMessageBox(const std::string& caption,
const std::string& msg,
const std::string& moreText)
{
return MSG_BOX_OK;
}
MessageBoxResult fatalMessageBox(const std::string& caption,
const std::string& msg,
const std::string& moreText)
{
return MSG_BOX_OK;
}
}
#ifdef __APPLE__
string_list
FGLocale::getUserLanguage()
{
string_list result;
const char* langEnv = ::getenv("LANG");
if (langEnv) {
result.push_back(langEnv);
}
return result;
}
namespace flightgear
{
SGPath Options::platformDefaultRoot() const
{
SGPath dataDir;
dataDir.append("data");
return dataDir;
}
} // of namespace flightgear
#endif
FGATCController::FGATCController()
{
}
FGATCController::~FGATCController()
{
}
FGATCInstruction::FGATCInstruction()
{
}
////////////////////////////////////////////////
FGTowerController::FGTowerController(FGAirportDynamics*)
{
}
FGTowerController::~FGTowerController()
{
}
void FGTowerController::render(bool)
{
}
void FGTowerController::signOff(int id)
{
}
std::string FGTowerController::getName()
{
return "tower";
}
void FGTowerController::update(double dt)
{
}
void FGTowerController::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentRoute, double lat, double lon, double hdg, double spd, double alt, double radius, int leg, FGAIAircraft *aircraft)
{
}
void FGTowerController::updateAircraftInformation(int id, double lat, double lon, double heading, double speed, double alt, double dt)
{
}
bool FGTowerController::hasInstruction(int id)
{
return false;
}
FGATCInstruction FGTowerController::getInstruction(int id)
{
return FGATCInstruction();
}
//////////////////////////////////////////////////////////////
/// \brief FGApproachController::FGApproachController
///
FGApproachController::FGApproachController(FGAirportDynamics*)
{
}
FGApproachController::~FGApproachController()
{
}
void FGApproachController::render(bool)
{
}
void FGApproachController::signOff(int id)
{
}
std::string FGApproachController::getName()
{
return "approach";
}
void FGApproachController::update(double dt)
{
}
void FGApproachController::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentRoute, double lat, double lon, double hdg, double spd, double alt, double radius, int leg, FGAIAircraft *aircraft)
{
}
void FGApproachController::updateAircraftInformation(int id, double lat, double lon, double heading, double speed, double alt, double dt)
{
}
bool FGApproachController::hasInstruction(int id)
{
return false;
}
FGATCInstruction FGApproachController::getInstruction(int id)
{
return FGATCInstruction();
}
//////////////////////////////////////////////////////
FGStartupController::FGStartupController(FGAirportDynamics*)
{
}
FGStartupController::~FGStartupController()
{
}
void FGStartupController::render(bool)
{
}
void FGStartupController::signOff(int id)
{
}
std::string FGStartupController::getName()
{
return "startup";
}
void FGStartupController::update(double dt)
{
}
void FGStartupController::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentRoute, double lat, double lon, double hdg, double spd, double alt, double radius, int leg, FGAIAircraft *aircraft)
{
}
void FGStartupController::updateAircraftInformation(int id, double lat, double lon, double heading, double speed, double alt, double dt)
{
}
bool FGStartupController::hasInstruction(int id)
{
return false;
}
FGATCInstruction FGStartupController::getInstruction(int id)
{
return FGATCInstruction();
}
//////////////////////////////////////////////////////
FGGroundController::FGGroundController()
{
}
FGGroundController::~FGGroundController()
{
}
void FGGroundController::init(FGAirportDynamics* pr)
{
}
void FGGroundController::render(bool)
{
}
void FGGroundController::signOff(int id)
{
}
std::string FGGroundController::getName()
{
return "ground";
}
void FGGroundController::update(double dt)
{
}
void FGGroundController::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentRoute, double lat, double lon, double hdg, double spd, double alt, double radius, int leg, FGAIAircraft *aircraft)
{
}
void FGGroundController::updateAircraftInformation(int id, double lat, double lon, double heading, double speed, double alt, double dt)
{
}
bool FGGroundController::hasInstruction(int id)
{
return false;
}
FGATCInstruction FGGroundController::getInstruction(int id)
{
return FGATCInstruction();
}
#if defined(SG_WINDOWS)
#include <plib/sg.h>
// this stub is needed on Windows because sg.h decleares this function as extern
void sgdMakeCoordMat4(sgdMat4 m, const SGDfloat x, const SGDfloat y, const SGDfloat z, const SGDfloat h, const SGDfloat p, const SGDfloat r)
{
}
#endif

147
tests/test_flightplan.cxx Normal file
View file

@ -0,0 +1,147 @@
#include "config.h"
#include "unitTestHelpers.hxx"
#include <simgear/misc/test_macros.hxx>
#include <simgear/misc/strutils.hxx>
#include <Navaids/FlightPlan.hxx>
#include <Navaids/routePath.hxx>
#include <Navaids/NavDataCache.hxx>
#include <Navaids/waypoint.hxx>
#include <Navaids/navlist.hxx>
#include <Navaids/navrecord.hxx>
#include <Airports/airport.hxx>
using namespace flightgear;
FlightPlanRef makeTestFP(const std::string& depICAO, const std::string& depRunway,
const std::string& destICAO, const std::string& destRunway,
const std::string& waypoints)
{
FlightPlanRef f = new FlightPlan;
FGAirportRef depApt = FGAirport::getByIdent(depICAO);
f->setDeparture(depApt->getRunwayByIdent(depRunway));
FGAirportRef destApt = FGAirport::getByIdent(destICAO);
f->setDestination(destApt->getRunwayByIdent(destRunway));
for (auto ws : simgear::strutils::split(waypoints)) {
WayptRef wpt = f->waypointFromString(ws);
f->insertWayptAtIndex(wpt, -1);
}
return f;
}
void testBasic()
{
FlightPlanRef fp1 = makeTestFP("EGCC", "23L", "EHAM", "24",
"TNT CLN");
fp1->setIdent("testplan");
SG_CHECK_EQUAL(fp1->ident(), "testplan");
SG_CHECK_EQUAL(fp1->departureAirport()->ident(), "EGCC");
SG_CHECK_EQUAL(fp1->departureRunway()->ident(), "23L");
SG_CHECK_EQUAL(fp1->destinationAirport()->ident(), "EHAM");
SG_CHECK_EQUAL(fp1->destinationRunway()->ident(), "24");
SG_CHECK_EQUAL(fp1->numLegs(), 2);
SG_CHECK_EQUAL(fp1->legAtIndex(0)->waypoint()->source()->ident(), "TNT");
SG_CHECK_EQUAL(fp1->legAtIndex(0)->waypoint()->source()->name(), "TRENT VOR-DME");
SG_CHECK_EQUAL(fp1->legAtIndex(1)->waypoint()->source()->ident(), "CLN");
SG_CHECK_EQUAL(fp1->legAtIndex(1)->waypoint()->source()->name(), "CLACTON VOR-DME");
}
void testRoutePathBasic()
{
FlightPlanRef fp1 = makeTestFP("EGHI", "20", "EDDM", "08L",
"SFD LYD BNE CIV ELLX LUX SAA KRH WLD");
RoutePath rtepath(fp1);
const unsigned int legCount = fp1->numLegs();
for (int leg = 0; leg < legCount; ++leg) {
rtepath.trackForIndex(leg);
rtepath.pathForIndex(leg);
rtepath.distanceForIndex(leg);
}
rtepath.distanceBetweenIndices(2, 5);
// check some leg parameters
// BOLOUGNE SUR MER, near LFAY (AMIENS)
FGNavRecordRef bne = FGNavList::findByFreq(113.8, FGAirport::getByIdent("LFAY")->geod());
// CHIEVRES
FGNavRecordRef civ = FGNavList::findByFreq(113.2, FGAirport::getByIdent("EBCI")->geod());
double distM = SGGeodesy::distanceM(bne->geod(), civ->geod());
double trackDeg = SGGeodesy::courseDeg(bne->geod(), civ->geod());
SG_CHECK_EQUAL_EP2(trackDeg, rtepath.trackForIndex(3), 0.5);
SG_CHECK_EQUAL_EP2(distM, rtepath.distanceForIndex(3), 2000); // 2km precision, allow for turns
}
// https://sourceforge.net/p/flightgear/codetickets/1703/
// https://sourceforge.net/p/flightgear/codetickets/1939/
void testRoutePathSkipped()
{
FlightPlanRef fp1 = makeTestFP("EHAM", "24", "EDDM", "08L",
"EHEH KBO TAU FFM FFM/100/0.01 FFM/120/0.02 WUR WLD");
RoutePath rtepath(fp1);
// skipped point uses inbound track
SG_CHECK_EQUAL_EP(rtepath.trackForIndex(3), rtepath.trackForIndex(4));
SG_CHECK_EQUAL_EP(0.0, rtepath.distanceForIndex(4));
SG_CHECK_EQUAL_EP(0.0, rtepath.distanceForIndex(5));
SG_CHECK_EQUAL_EP2(101000, rtepath.distanceForIndex(6), 1000);
// this tests skipping two preceeding points works as it should
SGGeodVec vec = rtepath.pathForIndex(6);
SG_CHECK_EQUAL(vec.size(), 9);
}
void testRoutePathTrivialFlightPlan()
{
FlightPlanRef fp1 = makeTestFP("EGPH", "24", "EGPH", "06",
"");
RoutePath rtepath(fp1);
const unsigned int legCount = fp1->numLegs();
for (int leg = 0; leg < legCount; ++leg) {
rtepath.trackForIndex(leg);
rtepath.pathForIndex(leg);
rtepath.distanceForIndex(leg);
}
SG_CHECK_EQUAL_EP(0.0, fp1->totalDistanceNm());
}
int main(int argc, char* argv[])
{
fgtest::initTestGlobals("flightplan");
testBasic();
testRoutePathBasic();
testRoutePathSkipped();
testRoutePathTrivialFlightPlan();
fgtest::shutdownTestGlobals();
}

29
tests/test_navaids2.cxx Normal file
View file

@ -0,0 +1,29 @@
#include "unitTestHelpers.hxx"
#include <simgear/misc/test_macros.hxx>
#include <Navaids/NavDataCache.hxx>
#include <Navaids/navrecord.hxx>
#include <Navaids/navlist.hxx>
void testBasic()
{
SGGeod egccPos = SGGeod::fromDeg(-2.27, 53.35);
FGNavRecordRef tla = FGNavList::findByFreq(115.7, egccPos);
SG_CHECK_EQUAL(strcmp(tla->get_ident(), "TNT"), 0);
SG_CHECK_EQUAL(tla->ident(), "TNT");
SG_CHECK_EQUAL(tla->name(), "TRENT VOR-DME");
SG_CHECK_EQUAL(tla->get_freq(), 11570);
SG_CHECK_EQUAL(tla->get_range(), 130);
}
int main(int argc, char* argv[])
{
fgtest::initTestGlobals("navaids2");
testBasic();
fgtest::shutdownTestGlobals();
}

88
tests/unitTestHelpers.cxx Normal file
View file

@ -0,0 +1,88 @@
#include "config.h"
#include "unitTestHelpers.hxx"
#include <Main/globals.hxx>
#include <Navaids/NavDataCache.hxx>
#include <Time/TimeManager.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/timing/timestamp.hxx>
#include <iostream>
namespace fgtest
{
bool looksLikeFGData(const SGPath& path)
{
return (path / "defaults.xml").exists();
}
void initTestGlobals(const std::string& testName)
{
sglog().setLogLevels( SG_ALL, SG_WARN );
sglog().setDeveloperMode(true);
globals = new FGGlobals;
bool foundRoot = false;
if (std::getenv("FG_ROOT")) {
SGPath fg_root = SGPath::fromEnv("FG_ROOT");
if (looksLikeFGData(fg_root)) {
globals->set_fg_root(fg_root);
foundRoot = true;
}
}
if (!foundRoot) {
SGPath pkgLibDir = SGPath::fromUtf8(PKGLIBDIR);
if (looksLikeFGData(pkgLibDir)) {
globals->set_fg_root(pkgLibDir);
foundRoot = true;
}
}
if (!foundRoot) {
SGPath dataDir = SGPath::fromUtf8(FGSRCDIR) / ".." / "fgdata";
if (looksLikeFGData(dataDir)) {
globals->set_fg_root(dataDir);
foundRoot = true;
}
}
if (!foundRoot) {
std::cerr << "FGData not found" << std::endl;
exit(EXIT_FAILURE);
}
// current dir
SGPath homePath = simgear::Dir::current().path() / "test_home";
if (!homePath.exists()) {
(homePath / "dummyFile").create_dir(0755);
}
globals->set_fg_home(homePath);
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;
}
}
TimeManager* t = new TimeManager;
t->init(); // establish mag-var data
}
void shutdownTestGlobals()
{
delete globals;
}
} // of namespace fgtest

15
tests/unitTestHelpers.hxx Normal file
View file

@ -0,0 +1,15 @@
#ifndef FG_TEST_HELPERS_HXX
#define FG_TEST_HELPERS_HXX
#include <string>
#include <simgear/misc/sg_path.hxx>
namespace fgtest
{
void initTestGlobals(const std::string& testName);
void shutdownTestGlobals();
}
#endif // of FG_TEST_HELPERS_HXX