#include "stdafx.h" #include "V8DataModel/Test.h" #include "V8DataModel/PhysicsSettings.h" #include "Network/../../NetworkSettings.h" #include "V8DataModel/TimerService.h" #include "Script/script.h" #include "Script/ScriptContext.h" #include "util/RunStateOwner.h" #include "v8datamodel/DataModel.h" #include #include #define BOOST_FILESYSTEM_NO_DEPRECATED #include namespace RBX { const char* const sFunctionalTest = "FunctionalTest"; const char* const sTestService = "TestService"; namespace Reflection { template<> EnumDesc::EnumDesc() :EnumDescriptor("FunctionalTestResult") { addPair(FunctionalTest::Passed, "Passed"); addPair(FunctionalTest::Warning, "Warning"); addPair(FunctionalTest::Error, "Error"); } template<> FunctionalTest::Result& Variant::convert(void) { return genericConvert(); } } template<> bool RBX::StringConverter::convertToValue(const std::string& text, FunctionalTest::Result& value) { return Reflection::EnumDesc::singleton().convertToValue(text.c_str(),value); } REFLECTION_BEGIN(); Reflection::BoundProp descDescription("Description", "Data", &FunctionalTest::description); Reflection::BoundProp descTimeout("Timeout", "Settings", &FunctionalTest::timeout, Reflection::PropertyDescriptor::LEGACY); Reflection::BoundProp descPhysicsEnvironmentalThrottle("PhysicsEnvironmentalThrottle", "Physics", &FunctionalTest::isPhysicsThrottled, Reflection::PropertyDescriptor::LEGACY); Reflection::BoundProp descAllowSleep("AllowSleep", "Physics", &FunctionalTest::allowSleep, Reflection::PropertyDescriptor::LEGACY); Reflection::BoundProp descIs30FpsThrottleEnabled("Is30FpsThrottleEnabled", "Physics", &FunctionalTest::is30FpsThrottleEnabled, Reflection::PropertyDescriptor::LEGACY); Reflection::BoundProp prop_HasMigratedSettingsToTestService("HasMigratedSettingsToTestService", "Settings", &FunctionalTest::hasMigratedSettingsToTestService, Reflection::PropertyDescriptor::STREAMING); static Reflection::BoundFuncDesc descWarn(&FunctionalTest::warn, "Warn", "message", "", Security::None); static Reflection::BoundFuncDesc descPassed(&FunctionalTest::pass, "Pass", "message", "", Security::None); static Reflection::BoundFuncDesc descFailed(&FunctionalTest::error, "Error", "message", "", Security::None); static Reflection::BoundFuncDesc descPassedDeprecated(&FunctionalTest::pass, "Passed", "message", "", Security::None); static Reflection::BoundFuncDesc descFailedDeprecated(&FunctionalTest::error, "Failed", "message", "", Security::None); Reflection::BoundProp prop_Description("Description", "Data", &TestService::description); Reflection::BoundProp prop_Timeout("Timeout", "Settings", &TestService::timeout); Reflection::BoundProp prop_IsPhysicsEnvironmentalThrottled("IsPhysicsEnvironmentalThrottled", "Physics", &TestService::isPhysicsThrottled); Reflection::BoundProp prop_IsSleepAllowed("IsSleepAllowed", "Physics", &TestService::allowSleep); Reflection::BoundProp prop_Is30FpsThrottleEnabled("Is30FpsThrottleEnabled", "Physics", &TestService::is30FpsThrottleEnabled); Reflection::BoundProp prop_AutoStart("AutoRuns", "Physics", &TestService::autoRuns); Reflection::BoundProp prop_NumberOfPlayers("NumberOfPlayers", "Settings", &TestService::numberOfPlayers); Reflection::BoundProp prop_SimulateLag("SimulateSecondsLag", "Settings", &TestService::lagSimulation); #ifdef RBX_TEST_BUILD Reflection::BoundProp prop_PhysicsSendRate("PhysicsSendRate", "Settings", &TestService::physicsSendRate); #endif Reflection::BoundProp TestService::TestCount("TestCount", "Results", &TestService::testCount); Reflection::BoundProp TestService::WarnCount("WarnCount", "Results", &TestService::warnCount); Reflection::BoundProp TestService::ErrorCount("ErrorCount", "Results", &TestService::errorCount); static Reflection::BoundYieldFuncDesc func_Run(&TestService::run, "Run", Security::Plugin); static Reflection::BoundFuncDesc, int)> func_Message(&TestService::message, "Message", "text", "source", shared_ptr(), "line", 0, Security::None); static Reflection::BoundFuncDesc, int)> func_Checkpoint(&TestService::checkpoint, "Checkpoint", "text", "source", shared_ptr(), "line", 0, Security::None); static Reflection::BoundFuncDesc, int)> func_Warn(&TestService::warn, "Warn", "condition", "description", "source", shared_ptr(), "line", 0, Security::None); static Reflection::BoundFuncDesc, int)> func_Check(&TestService::check, "Check", "condition", "description", "source", shared_ptr(), "line", 0, Security::None); static Reflection::BoundFuncDesc, int)> func_Require(&TestService::require2, "Require", "condition", "description", "source", shared_ptr(), "line", 0, Security::None); static Reflection::BoundFuncDesc, int)> func_Error(&TestService::error, "Error", "description", "source", shared_ptr(), "line", 0, Security::None); static Reflection::BoundFuncDesc, int)> func_Fails(&TestService::fail, "Fail", "description", "source", shared_ptr(), "line", 0, Security::None); static Reflection::BoundFuncDesc func_Done(&TestService::done, "Done", Security::None); // DoCommand calls getVerb instead of getWhitelistVerb, and thus is an easy exploit. #ifdef RBX_TEST_BUILD static Reflection::BoundFuncDesc()> func_GetCommandNames(&TestService::getCommandNames, "GetCommandNames", Security::LocalUser); static Reflection::BoundFuncDesc func_DoCommand(&TestService::doCommand, "DoCommand", "name", Security::LocalUser); static Reflection::BoundFuncDesc func_IsCommandEnabled(&TestService::isCommandEnabled, "IsCommandEnabled", "name", Security::LocalUser); static Reflection::BoundFuncDesc func_IsCommandChecked(&TestService::isCommandChecked, "IsCommandChecked", "name", Security::LocalUser); #endif static Reflection::RemoteEventDesc, int)> desc_serverCollectResult(&TestService::serverCollectResultSignal, "ServerCollectResult", "text", "script", "line", Security::None, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::CLIENT_SERVER); static Reflection::RemoteEventDesc, int)> desc_serverCollectConditionalResult(&TestService::serverCollectConditionalResultSignal, "ServerCollectConditionalResult", "condition", "text", "script", "line", Security::None, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::CLIENT_SERVER); REFLECTION_END(); TestService::TestService() :description("") ,timeout(10) ,isPhysicsThrottled(true) ,allowSleep(true) ,is30FpsThrottleEnabled(true) ,running(false) ,runCount(0) ,testCount(0) ,warnCount(0) ,errorCount(0) ,autoRuns(true) ,numberOfPlayers(0) ,lagSimulation(0.0) ,clientTestService(false) ,scriptCount(0) { setName(sTestService); RBX::NetworkSettings &ns = RBX::NetworkSettings::singleton(); physicsSendRate = ns.getPhysicsSendRate(); } TestService::~TestService() { } void TestService::message( std::string text, shared_ptr source, int line ) { if(isAClient()) desc_serverCollectResult.fireAndReplicateEvent(this, text, source, line); if (onMessage) onMessage(text, source, line); else output(RBX::MESSAGE_INFO, source, line, text); } void TestService::onRemoteResult( std::string text, shared_ptr source, int line ) { if (!isAClient()) { RBXASSERT(numberOfPlayers > 0); onMessage(text, source, line); } } void TestService::checkpoint( std::string text, shared_ptr source, int line ) { if (onCheckpoint) onCheckpoint(text, source, line); else output(RBX::MESSAGE_INFO, source, line, "checkpoint " + text); } void TestService::warn( bool condition, std::string description, shared_ptr source, int line ) { incrementTest(); if (!condition) incrementWarn(); if (onWarn) onWarn(condition, description, source, line); else if (!condition) output(RBX::MESSAGE_WARNING, source, line, description); } void TestService::check( bool condition, std::string description, shared_ptr source, int line ) { if(isAClient()) desc_serverCollectConditionalResult.fireAndReplicateEvent(this, condition, description, source, line); incrementTest(); if (!condition) incrementError(); if (onCheck) onCheck(condition, description, source, line); else if (!condition) output(RBX::MESSAGE_ERROR, source, line, description); } void TestService::onRemoteConditionalResult( bool condition, std::string description, shared_ptr source, int line ) { incrementTest(); if (!condition) incrementError(); onCheck(condition, description, source, line); } void TestService::error( std::string description, shared_ptr source, int line ) { incrementTest(); incrementError(); if (onCheck) onCheck(false, description, source, line); else output(RBX::MESSAGE_ERROR, source, line, description); } void TestService::require2( bool condition, std::string description, shared_ptr source, int line ) { incrementTest(); if (!condition) incrementError(); if (onCheck) onCheck(condition, description, source, line); else if (!condition) output(RBX::MESSAGE_ERROR, source, line, "fatal " + description); if (!condition) done(); } void TestService::fail( std::string description, shared_ptr source, int line ) { incrementTest(); incrementError(); if (onCheck) onCheck(false, description, source, line); else output(RBX::MESSAGE_ERROR, source, line, "fatal " + description); done(); } void TestService::done() { if (!running) return; running = false; if (onDone) onDone(); else if (testCount == 0) RBX::StandardOut::singleton()->print(RBX::MESSAGE_INFO, "TestService: Doesn't include any assertions"); else RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "TestService: Run completed, tests: %u, warns: %u, errors: %u", testCount, warnCount, errorCount); stop(); } void TestService::stillWaiting(int runCount, double timeout) { if (runCount != this->runCount) return; if (!running) return; if (onStillWaiting) onStillWaiting(timeout); else RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "TestService: Waiting a total of %g seconds for the test to end", timeout); } void TestService::onTimeout(int runCount, double timeout) { if (runCount != this->runCount) return; if (!running) return; running = false; incrementTest(); incrementError(); if (onCheck) onCheck(false, RBX::format("Tests failed to complete in %g seconds", timeout), shared_ptr(), 0); else RBX::StandardOut::singleton()->printf(RBX::MESSAGE_ERROR, "TestService: Tests failed to complete in %g seconds", timeout); if (onDone) onDone(); stop(); } void TestService::run(boost::function resumeFunction, boost::function errorFunction) { if (running) throw std::runtime_error("Run still in progress"); // wire up the callback this->resumeFunction = resumeFunction; running = true; runCount++; testCount = 0; warnCount = 0; errorCount = 0; raisePropertyChanged(TestCount); raisePropertyChanged(WarnCount); raisePropertyChanged(ErrorCount); setConfiguration(); // wire up a timeout { TimerService* timerService = ServiceProvider::find(this); if (!timerService) throw std::runtime_error("Unable to get TimerService"); // Print out a message in case somebody is impatient if (timeout > 10.0) timerService->delay(boost::bind(&TestService::stillWaiting, shared_from(this), runCount, timeout), 5.0); timerService->delay(boost::bind(&TestService::onTimeout, shared_from(this), runCount, timeout), this->timeout); } // run! { RunService* runService = ServiceProvider::find(this); if (!runService) throw std::runtime_error("Unable to get RunService"); wasRunning = runService->isRunState(); if (autoRuns) runService->run(); } try { startScripts(); } catch (...) { fail("Failed to start test scripts"); throw; } } void TestService::incrementTest() { testCount++; raisePropertyChanged(TestCount); } void TestService::incrementWarn() { warnCount++; raisePropertyChanged(WarnCount); } void TestService::incrementError() { errorCount++; raisePropertyChanged(ErrorCount); } void TestService::startScripts() { shared_ptr instances = getChildren().read(); if (!instances) return; scriptCount = 0; std::for_each(instances->begin(), instances->end(), boost::bind(&TestService::countScript, this, _1)); std::for_each(instances->begin(), instances->end(), boost::bind(&TestService::startScript, this, _1)); } void TestService::stopScripts() { shared_ptr instances = getChildren().read(); if (!instances) return; std::for_each(instances->begin(), instances->end(), boost::bind(&TestService::stopScript, this, _1)); } void TestService::countScript( shared_ptr instance ) { if (shared_dynamic_cast