From 9e122eaf8174f3e7eb5091f55dbe4ac93255f5e5 Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 21 Mar 2017 21:43:42 +0100 Subject: [PATCH] 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. --- CMakeLists.txt | 16 ++ src/Environment/environment_mgr.cxx | 47 +++- src/Environment/precipitation_mgr.hxx | 5 +- src/Include/config_cmake.h.in | 1 + src/Main/fg_props.cxx | 2 + src/Main/globals.cxx | 47 +++- src/Main/globals.hxx | 61 ++-- src/Main/options.cxx | 13 +- src/Navaids/FlightPlan.cxx | 4 +- src/Scripting/NasalPositioned.cxx | 6 +- src/Scripting/NasalSys.cxx | 104 +++---- src/Scripting/NasalSys.hxx | 4 +- src/Viewer/view.cxx | 4 + src/Viewer/viewmgr.cxx | 2 + tests/CMakeLists.txt | 105 +++++++ tests/fake_sgPrecipitation.cxx | 17 ++ tests/fake_sgSky.cxx | 41 +++ tests/fake_sgSky.hxx | 338 +++++++++++++++++++++++ tests/fake_sound.cxx | 57 ++++ tests/fgTestDriver.cxx | 77 ++++++ tests/testStubs.cxx | 382 ++++++++++++++++++++++++++ tests/test_flightplan.cxx | 147 ++++++++++ tests/test_navaids2.cxx | 29 ++ tests/unitTestHelpers.cxx | 88 ++++++ tests/unitTestHelpers.hxx | 15 + 25 files changed, 1499 insertions(+), 113 deletions(-) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/fake_sgPrecipitation.cxx create mode 100644 tests/fake_sgSky.cxx create mode 100644 tests/fake_sgSky.hxx create mode 100644 tests/fake_sound.cxx create mode 100644 tests/fgTestDriver.cxx create mode 100644 tests/testStubs.cxx create mode 100644 tests/test_flightplan.cxx create mode 100644 tests/test_navaids2.cxx create mode 100644 tests/unitTestHelpers.cxx create mode 100644 tests/unitTestHelpers.hxx diff --git a/CMakeLists.txt b/CMakeLists.txt index a68e53b95..0d7e2ac37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 #----------------------------------------------------------------------------- diff --git a/src/Environment/environment_mgr.cxx b/src/Environment/environment_mgr.cxx index 27066408e..3fdb9db06 100644 --- a/src/Environment/environment_mgr.cxx +++ b/src/Environment/environment_mgr.cxx @@ -26,8 +26,13 @@ #include #include + +#ifdef FG_TESTLIB +#include +#else #include #include +#endif #include #include
@@ -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 ) diff --git a/src/Environment/precipitation_mgr.hxx b/src/Environment/precipitation_mgr.hxx index 3c3b8a614..127ca2923 100644 --- a/src/Environment/precipitation_mgr.hxx +++ b/src/Environment/precipitation_mgr.hxx @@ -36,8 +36,10 @@ class FGPrecipitationMgr : public SGSubsystem { private: +#ifndef FG_TESTLIB osg::ref_ptr transform; osg::ref_ptr 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 - diff --git a/src/Include/config_cmake.h.in b/src/Include/config_cmake.h.in index d54cc51eb..cd5b9fb91 100644 --- a/src/Include/config_cmake.h.in +++ b/src/Include/config_cmake.h.in @@ -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 diff --git a/src/Main/fg_props.cxx b/src/Main/fg_props.cxx index e4d716bcc..dcc8ea05e 100644 --- a/src/Main/fg_props.cxx +++ b/src/Main/fg_props.cxx @@ -242,8 +242,10 @@ setFreeze (bool f) } } +#ifndef FG_TESTLIB // Pause the particle system simgear::Particles::setFrozen(f); +#endif } diff --git a/src/Main/globals.cxx b/src/Main/globals.cxx index b280ece9e..dba68837a 100644 --- a/src/Main/globals.cxx +++ b/src/Main/globals.cxx @@ -20,25 +20,25 @@ // // $Id$ -#ifdef HAVE_CONFIG_H -# include -#endif +#include #include #include +#ifndef FG_TESTLIB #include #include +#endif #include +#include #include #include #include #include -#include #include #include -#include + #include #include #include @@ -48,14 +48,21 @@ #include #include #include +#include + #include +#include + +#ifndef FG_TESTLIB #include #include -#include #include -#include #include +#include +#include +#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 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(); +#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 diff --git a/src/Main/globals.hxx b/src/Main/globals.hxx index 16eabe01b..fad0fee3c 100644 --- a/src/Main/globals.hxx +++ b/src/Main/globals.hxx @@ -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 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 SGPropertyChangeListenerVec; SGPropertyChangeListenerVec _listeners_to_cleanup; - + SGSharedPtr _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 /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& p); }; diff --git a/src/Main/options.cxx b/src/Main/options.cxx index c6e60108e..b18855661 100644 --- a/src/Main/options.cxx +++ b/src/Main/options.cxx @@ -58,7 +58,8 @@ #include #include -#if defined(HAVE_QT) + +#if defined(HAVE_QT) && !defined(FG_TESTLIB) #include #include #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); diff --git a/src/Navaids/FlightPlan.cxx b/src/Navaids/FlightPlan.cxx index 312796990..f9cf95802 100644 --- a/src/Navaids/FlightPlan.cxx +++ b/src/Navaids/FlightPlan.cxx @@ -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(); } diff --git a/src/Scripting/NasalPositioned.cxx b/src/Scripting/NasalPositioned.cxx index 51c0f5d34..7e6f7e491 100644 --- a/src/Scripting/NasalPositioned.cxx +++ b/src/Scripting/NasalPositioned.cxx @@ -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(material); + naRef vec = naNewVector(c); naVec_append(vec, naNum(elev)); naRef matdata = naNil(); + +#ifndef FG_TESTLIB + const SGMaterial *mat = dynamic_cast(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 } diff --git a/src/Scripting/NasalSys.cxx b/src/Scripting/NasalSys.cxx index a2cf02e8a..36657a970 100644 --- a/src/Scripting/NasalSys.cxx +++ b/src/Scripting/NasalSys.cxx @@ -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::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(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(aNode)); naRef args[1]; args[0] = _sys->wrappedPropsNode(const_cast(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("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::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::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("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::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(s), n < 0 ? strlen(s) : n); } - - diff --git a/src/Scripting/NasalSys.hxx b/src/Scripting/NasalSys.hxx index e47c096c9..429ed55cb 100644 --- a/src/Scripting/NasalSys.hxx +++ b/src/Scripting/NasalSys.hxx @@ -155,10 +155,10 @@ private: //friend class FGNasalScript; friend class FGNasalListener; friend class FGNasalModuleListener; - +#ifndef FG_TESTLIB SGLockedQueue > _loadList; SGLockedQueue > _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). diff --git a/src/Viewer/view.cxx b/src/Viewer/view.cxx index 7cb3a1f6b..ae3d3419c 100644 --- a/src/Viewer/view.cxx +++ b/src/Viewer/view.cxx @@ -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 diff --git a/src/Viewer/viewmgr.cxx b/src/Viewer/viewmgr.cxx index 6f463e313..6e3c6bb0f 100644 --- a/src/Viewer/viewmgr.cxx +++ b/src/Viewer/viewmgr.cxx @@ -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 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() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 000000000..a276d58ca --- /dev/null +++ b/tests/CMakeLists.txt @@ -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) diff --git a/tests/fake_sgPrecipitation.cxx b/tests/fake_sgPrecipitation.cxx new file mode 100644 index 000000000..d81a911fb --- /dev/null +++ b/tests/fake_sgPrecipitation.cxx @@ -0,0 +1,17 @@ + +#include + +FGPrecipitationMgr::FGPrecipitationMgr() +{ + +} + +FGPrecipitationMgr::~FGPrecipitationMgr() +{ + +} + +void FGPrecipitationMgr::bind () {} +void FGPrecipitationMgr::unbind () {} +void FGPrecipitationMgr::init () {} +void FGPrecipitationMgr::update (double dt) {} diff --git a/tests/fake_sgSky.cxx b/tests/fake_sgSky.cxx new file mode 100644 index 000000000..492403c7f --- /dev/null +++ b/tests/fake_sgSky.cxx @@ -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) +{ + +} diff --git a/tests/fake_sgSky.hxx b/tests/fake_sgSky.hxx new file mode 100644 index 000000000..29c3c404b --- /dev/null +++ b/tests/fake_sgSky.hxx @@ -0,0 +1,338 @@ +#ifndef FG_TEST_FAKE_SGSKY_HXX +#define FG_TEST_FAKE_SGSKY_HXX + +#include +#include +#include + +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 _cloudLayers; +}; + +#endif diff --git a/tests/fake_sound.cxx b/tests/fake_sound.cxx new file mode 100644 index 000000000..7d6be270b --- /dev/null +++ b/tests/fake_sound.cxx @@ -0,0 +1,57 @@ +#include + +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 SGSoundMgr::get_available_devices() +{ + std::vector result; + return result; +} diff --git a/tests/fgTestDriver.cxx b/tests/fgTestDriver.cxx new file mode 100644 index 000000000..1a72029a6 --- /dev/null +++ b/tests/fgTestDriver.cxx @@ -0,0 +1,77 @@ +#include + +#include +#include +#include + +class TestStep +{ +public: + virtual void run() = 0; + // name for logging purposes +}; + +typedef std::vector 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 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; +} diff --git a/tests/testStubs.cxx b/tests/testStubs.cxx new file mode 100644 index 000000000..fc90d4a09 --- /dev/null +++ b/tests/testStubs.cxx @@ -0,0 +1,382 @@ +#include + +#include +#include + +#include
+#include
+#include +#include +#include +#include +#include + +// 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 + +// 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 \ No newline at end of file diff --git a/tests/test_flightplan.cxx b/tests/test_flightplan.cxx new file mode 100644 index 000000000..48c3fbad7 --- /dev/null +++ b/tests/test_flightplan.cxx @@ -0,0 +1,147 @@ +#include "config.h" + +#include "unitTestHelpers.hxx" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +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(); +} diff --git a/tests/test_navaids2.cxx b/tests/test_navaids2.cxx new file mode 100644 index 000000000..92de78134 --- /dev/null +++ b/tests/test_navaids2.cxx @@ -0,0 +1,29 @@ +#include "unitTestHelpers.hxx" + +#include + +#include +#include +#include + +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(); +} diff --git a/tests/unitTestHelpers.cxx b/tests/unitTestHelpers.cxx new file mode 100644 index 000000000..c23fb6770 --- /dev/null +++ b/tests/unitTestHelpers.cxx @@ -0,0 +1,88 @@ + +#include "config.h" + +#include "unitTestHelpers.hxx" + +#include
+#include +#include