# HG changeset patch # User David Demelier # Date 1459788690 -7200 # Node ID 787c2adb366cd6537b2837ced9d5ee1bf21aad7a # Parent 6400830bb36bd7f5dd5c806e6ce84924984b9409 Client: add JavaScript bindings for ElapsedTimer, #464 diff -r 6400830bb36b -r 787c2adb366c libcommon/CMakeLists.txt --- a/libcommon/CMakeLists.txt Mon Apr 04 13:20:06 2016 +0200 +++ b/libcommon/CMakeLists.txt Mon Apr 04 18:51:30 2016 +0200 @@ -24,6 +24,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/malikania/game.h ${CMAKE_CURRENT_SOURCE_DIR}/malikania/id.h ${CMAKE_CURRENT_SOURCE_DIR}/malikania/js.h + ${CMAKE_CURRENT_SOURCE_DIR}/malikania/js-elapsed-timer.h ${CMAKE_CURRENT_SOURCE_DIR}/malikania/json.h ${CMAKE_CURRENT_SOURCE_DIR}/malikania/resources-loader.h ${CMAKE_CURRENT_SOURCE_DIR}/malikania/resources-locator.h @@ -34,6 +35,7 @@ SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/malikania/application.cpp ${CMAKE_CURRENT_SOURCE_DIR}/malikania/elapsed-timer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/malikania/js-elapsed-timer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/malikania/json.cpp ${CMAKE_CURRENT_SOURCE_DIR}/malikania/resources-loader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/malikania/resources-locator.cpp diff -r 6400830bb36b -r 787c2adb366c libcommon/malikania/js-elapsed-timer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libcommon/malikania/js-elapsed-timer.cpp Mon Apr 04 18:51:30 2016 +0200 @@ -0,0 +1,134 @@ +/* + * js-elapsed-timer.cpp -- + * + * Copyright (c) 2013-2016 David Demelier + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "elapsed-timer.h" +#include "js.h" + +namespace malikania { + +namespace duk { + +template <> +class TypeTraits { +public: + static inline std::string name() + { + return "\xff""\xff""ElapsedTimer"; + } + + static inline std::vector inherits() + { + return {}; + } +}; + +} // !duk + +namespace { + +/* + * Method: ElapsedTimer.pause + * ------------------------------------------------------------------ + * + * Pause the timer, without resetting the current elapsed time stored. + */ +duk::Ret pause(duk::ContextPtr ctx) +{ + duk::self>(ctx)->pause(); + + return 0; +} + +/* + * Method: ElapsedTimer.reset + * ------------------------------------------------------------------ + * + * Reset the elapsed time to 0, the status is not modified. + */ +duk::Ret reset(duk::ContextPtr ctx) +{ + duk::self>(ctx)->reset(); + + return 0; +} + +/* + * Method: ElapsedTimer.restart + * ------------------------------------------------------------------ + * + * Restart the timer without resetting the current elapsed time. + */ +duk::Ret restart(duk::ContextPtr ctx) +{ + duk::self>(ctx)->restart(); + + return 0; +} + +/* + * Method: ElapsedTimer.elapsed + * ------------------------------------------------------------------ + * + * Get the number of elapsed milliseconds. + * + * Returns: + * The time elapsed. + */ +duk::Ret elapsed(duk::ContextPtr ctx) +{ + duk::push(ctx, (int)duk::self>(ctx)->elapsed()); + + return 1; +} + +/* + * Function: Malikania.ElapsedTimer() [constructor] + * ------------------------------------------------------------------ + * + * Construct a new ElapsedTimer object. + */ +duk::Ret constructor(duk::ContextPtr ctx) +{ + duk::construct(ctx, duk::Pointer{new ElapsedTimer}); + + return 0; +} + +const duk::FunctionMap methods{ + { "elapsed", { elapsed, 0 } }, + { "pause", { pause, 0 } }, + { "reset", { reset, 0 } }, + { "restart", { restart, 0 } } +}; + +} // !namespace + +void loadJsElapsedTimer(duk::ContextPtr ctx) noexcept +{ + duk::StackAssert sa(ctx); + + duk::getGlobal(ctx, "Malikania"); + duk::push(ctx, duk::Function{constructor, 0}); + duk::push(ctx, duk::Object{}); + duk::push(ctx, methods); + duk::putProperty(ctx, -2, "prototype"); + duk::putProperty(ctx, -2, "ElapsedTimer"); + duk::pop(ctx); +} + +} // !malikania diff -r 6400830bb36b -r 787c2adb366c libcommon/malikania/js-elapsed-timer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libcommon/malikania/js-elapsed-timer.h Mon Apr 04 18:51:30 2016 +0200 @@ -0,0 +1,31 @@ +/* + * js-elapsed-timer.h -- + * + * Copyright (c) 2013-2016 David Demelier + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MALIKANIA_JS_ELAPSED_TIMER_H +#define MALIKANIA_JS_ELAPSED_TIMER_H + +#include "js.h" + +namespace malikania { + +void loadJsElapsedTimer(duk::ContextPtr ctx) noexcept; + +} // !malikania + +#endif // !MALIKANIA_JS_ELAPSED_TIMER_H + diff -r 6400830bb36b -r 787c2adb366c tests/libcommon/CMakeLists.txt --- a/tests/libcommon/CMakeLists.txt Mon Apr 04 13:20:06 2016 +0200 +++ b/tests/libcommon/CMakeLists.txt Mon Apr 04 18:51:30 2016 +0200 @@ -17,4 +17,5 @@ # add_subdirectory(elapsed-timer) +add_subdirectory(js-elapsed-timer) add_subdirectory(util) diff -r 6400830bb36b -r 787c2adb366c tests/libcommon/js-elapsed-timer/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/libcommon/js-elapsed-timer/CMakeLists.txt Mon Apr 04 18:51:30 2016 +0200 @@ -0,0 +1,24 @@ +# +# CMakeLists.txt -- CMake build system for malikania +# +# Copyright (c) 2013-2016 Malikania Authors +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +malikania_create_test( + NAME js-elapsed-timer + LIBRARIES libcommon + SOURCES main.cpp +) + diff -r 6400830bb36b -r 787c2adb366c tests/libcommon/js-elapsed-timer/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/libcommon/js-elapsed-timer/main.cpp Mon Apr 04 18:51:30 2016 +0200 @@ -0,0 +1,148 @@ +/* + * main.cpp -- test ElapsedTimer (JavaScript binding) + * + * Copyright (c) 2013-2016 Malikania Authors + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include + +using namespace malikania; + +using namespace std::chrono_literals; + +/* + * For all tests, we tolerate 30 ms because some systems have bigger lags. + */ +static constexpr int margin = 30; + +class TestElapsedTimer : public testing::Test { +protected: + duk::Context m_ctx; + + TestElapsedTimer() + { + duk::putGlobal(m_ctx, "Malikania", duk::Object()); + + loadJsElapsedTimer(m_ctx); + } + + inline void assertRange(int value, int expected) const noexcept + { + if (value < (expected - margin) || value > (expected + margin)) + FAIL() << value << " is bigger than [" << (expected - margin) << ", " << (expected + margin) << "]"; + } +}; + +TEST_F(TestElapsedTimer, standard) +{ + try { + if (duk::pevalString(m_ctx, "timer = new Malikania.ElapsedTimer();") != 0) + throw duk::error(m_ctx, -1); + + std::this_thread::sleep_for(300ms); + + if (duk::pevalString(m_ctx, "result = timer.elapsed();") != 0) + throw duk::error(m_ctx, -1); + + assertRange(duk::getGlobal(m_ctx, "result"), 300); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +TEST_F(TestElapsedTimer, reset) +{ + try { + if (duk::pevalString(m_ctx, "timer = new Malikania.ElapsedTimer();") != 0) + throw duk::error(m_ctx, -1); + + std::this_thread::sleep_for(300ms); + + if (duk::pevalString(m_ctx, "timer.reset(); result = timer.elapsed();") != 0) + throw duk::error(m_ctx, -1); + + assertRange(duk::getGlobal(m_ctx, "result"), 0); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +TEST_F(TestElapsedTimer, pause) +{ + try { + if (duk::pevalString(m_ctx, "timer = new Malikania.ElapsedTimer();") != 0) + throw duk::error(m_ctx, -1); + + /* + * Simulate a pause in the game like this: + * + * start pause restart elapsed + * | 10ms |.5ms.| 6ms | + * + * Since the game was paused, the 5ms must not be totalized. + */ + std::this_thread::sleep_for(10ms); + + if (duk::pevalString(m_ctx, "timer.pause();") != 0) + throw duk::error(m_ctx, -1); + + std::this_thread::sleep_for(5ms); + + if (duk::pevalString(m_ctx, "timer.restart();") != 0) + throw duk::error(m_ctx, -1); + + std::this_thread::sleep_for(6ms); + + if (duk::pevalString(m_ctx, "result = timer.elapsed()") != 0) + throw duk::error(m_ctx, -1); + + assertRange(duk::getGlobal(m_ctx, "result"), 16); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +TEST_F(TestElapsedTimer, doublecheck) +{ + try { + if (duk::pevalString(m_ctx, "timer = new Malikania.ElapsedTimer();") != 0) + throw duk::error(m_ctx, -1); + + std::this_thread::sleep_for(50ms); + + if (duk::pevalString(m_ctx, "result = timer.elapsed()") != 0) + throw duk::error(m_ctx, -1); + + std::this_thread::sleep_for(50ms); + + if (duk::pevalString(m_ctx, "result = timer.elapsed()") != 0) + throw duk::error(m_ctx, -1); + + assertRange(duk::getGlobal(m_ctx, "result"), 100); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +}