mirror of https://github.com/mosra/magnum.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
889 lines
28 KiB
889 lines
28 KiB
/* |
|
This file is part of Magnum. |
|
|
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 |
|
Vladimír Vondruš <mosra@centrum.cz> |
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a |
|
copy of this software and associated documentation files (the "Software"), |
|
to deal in the Software without restriction, including without limitation |
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
|
and/or sell copies of the Software, and to permit persons to whom the |
|
Software is furnished to do so, subject to the following conditions: |
|
|
|
The above copyright notice and this permission notice shall be included |
|
in all copies or substantial portions of the Software. |
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|
DEALINGS IN THE SOFTWARE. |
|
*/ |
|
|
|
#include <sstream> |
|
#include <Corrade/TestSuite/Tester.h> |
|
#include <Corrade/TestSuite/Compare/Numeric.h> |
|
|
|
#include "Magnum/Animation/Player.h" |
|
|
|
namespace Magnum { namespace Animation { namespace Test { |
|
|
|
struct PlayerTest: TestSuite::Tester { |
|
explicit PlayerTest(); |
|
|
|
void constructEmpty(); |
|
void construct(); |
|
void constructChrono(); |
|
void constructCopy(); |
|
void constructMove(); |
|
|
|
void setDurationExtend(); |
|
void setDurationReplace(); |
|
|
|
void trackInvalidIndex(); |
|
|
|
void advanceNotRunning(); |
|
void advancePlaying(); |
|
void advanceRestart(); |
|
void advanceStop(); |
|
void advancePauseResume(); |
|
void advancePauseStop(); |
|
void advancePlayCount(); |
|
void advancePlayCountInfinite(); |
|
void advanceChrono(); |
|
void advanceZeroDuration(); |
|
void advanceZeroDurationChrono(); |
|
|
|
void setState(); |
|
|
|
void add(); |
|
void addWithCallback(); |
|
void addWithCallbackTemplate(); |
|
void addWithCallbackOnChange(); |
|
void addWithCallbackOnChangeTemplate(); |
|
void addRawCallback(); |
|
|
|
void runFor100YearsFloat(); |
|
void runFor100YearsChrono(); |
|
|
|
void debugState(); |
|
}; |
|
|
|
namespace { |
|
const struct { |
|
const char* name; |
|
Float offsetFloat; |
|
std::chrono::minutes offsetChrono; |
|
bool failsFloat, failsFuzzyFloat; |
|
} RunFor100YearsData[]{ |
|
{"0", 0.0f, {}, false, false}, |
|
{"1 minute", 60.0f, std::chrono::minutes(1), false, false}, |
|
{"5 minutes", 5.0f*60.0f, std::chrono::minutes{5}, true, false}, |
|
{"30 minutes", 30.0f*60.0f, std::chrono::minutes{30}, true, false}, |
|
{"1 hour", 60.0f*60.0f, std::chrono::minutes{60}, true, false}, |
|
{"1 day", 24.0f*60.0f*60.0f, std::chrono::minutes{24*60}, true, true}, |
|
{"100 days", 100.0f*24.0f*60.0f*60.0f, std::chrono::minutes{100*24*60}, true, true}, |
|
{"100 years", 100.0f*365.0f*24.0f*60.0f*60.0f, |
|
/* MSVC 2017 (but not 2015) ICEs when assigning hours to minutes |
|
(or just any other two different chrono types) in a struct array |
|
initializer (not when there's just a struct and also not when |
|
the struct is only a chrono member itself). Keeping at least one |
|
instance here so I can monitor when this gets fixed. */ |
|
#if !defined(CORRADE_MSVC2017_COMPATIBILITY) || defined(CORRADE_MSVC2015_COMPATIBLITY) |
|
std::chrono::hours{100*365*24}, |
|
#else |
|
std::chrono::minutes{100*365*24*60}, |
|
#endif |
|
true, true}, |
|
}; |
|
} |
|
|
|
PlayerTest::PlayerTest() { |
|
addTests({&PlayerTest::constructEmpty, |
|
&PlayerTest::construct, |
|
&PlayerTest::constructCopy, |
|
&PlayerTest::constructMove, |
|
|
|
&PlayerTest::setDurationExtend, |
|
&PlayerTest::setDurationReplace, |
|
|
|
&PlayerTest::trackInvalidIndex, |
|
|
|
&PlayerTest::advanceNotRunning, |
|
&PlayerTest::advancePlaying, |
|
&PlayerTest::advanceRestart, |
|
&PlayerTest::advanceStop, |
|
&PlayerTest::advancePauseResume, |
|
&PlayerTest::advancePauseStop, |
|
&PlayerTest::advancePlayCount, |
|
&PlayerTest::advancePlayCountInfinite, |
|
&PlayerTest::advanceChrono, |
|
&PlayerTest::advanceZeroDuration, |
|
&PlayerTest::advanceZeroDurationChrono, |
|
|
|
&PlayerTest::setState, |
|
|
|
&PlayerTest::add, |
|
&PlayerTest::addWithCallback, |
|
&PlayerTest::addWithCallbackTemplate, |
|
&PlayerTest::addWithCallbackOnChange, |
|
&PlayerTest::addWithCallbackOnChangeTemplate, |
|
&PlayerTest::addRawCallback}); |
|
|
|
addInstancedTests({ |
|
&PlayerTest::runFor100YearsFloat, |
|
&PlayerTest::runFor100YearsChrono}, |
|
Containers::arraySize(RunFor100YearsData)); |
|
|
|
addTests({&PlayerTest::debugState}); |
|
} |
|
|
|
void PlayerTest::constructEmpty() { |
|
Player<Float> player; |
|
CORRADE_VERIFY(player.scaler()); |
|
CORRADE_COMPARE(player.duration(), Range1D{}); |
|
CORRADE_COMPARE(player.playCount(), 1); |
|
CORRADE_COMPARE(player.state(), State::Stopped); |
|
CORRADE_VERIFY(player.isEmpty()); |
|
CORRADE_COMPARE(player.size(), 0); |
|
} |
|
|
|
namespace { |
|
const Animation::Track<Float, Float> Track{{ |
|
{1.0f, 1.5f}, |
|
{2.5f, 3.0f}, |
|
{3.0f, 5.0f}, |
|
{4.0f, 2.0f} |
|
}, Math::lerp}; |
|
} |
|
|
|
void PlayerTest::construct() { |
|
Animation::Track<Float, Int> track2{{ |
|
{0.5f, 42}, |
|
{3.0f, 1337}, |
|
{3.5f, -17} |
|
}, Math::select}; |
|
|
|
Float value = -1.0f; |
|
Int value2 = -1; |
|
Player<Float> player; |
|
player.add(Track, value) |
|
.add(track2, value2) |
|
.setPlayCount(37); |
|
CORRADE_VERIFY(player.scaler()); |
|
CORRADE_COMPARE(player.duration(), (Range1D{0.5f, 4.0f})); |
|
CORRADE_COMPARE(player.playCount(), 37); |
|
CORRADE_COMPARE(player.state(), State::Stopped); |
|
CORRADE_VERIFY(!player.isEmpty()); |
|
CORRADE_COMPARE(player.size(), 2); |
|
CORRADE_COMPARE(player.track(0).keys().data(), Track.keys().data()); |
|
CORRADE_COMPARE(player.track(1).keys().data(), track2.keys().data()); |
|
} |
|
|
|
void PlayerTest::constructChrono() { |
|
Animation::Track<Float, Int> track2{{ |
|
{0.5f, 42}, |
|
{3.0f, 1337}, |
|
{3.5f, -17} |
|
}, Math::select}; |
|
|
|
Float value = -1.0f; |
|
Int value2 = -1; |
|
Player<std::chrono::nanoseconds, Float> player; |
|
player.add(Track, value) |
|
.add(track2, value2) |
|
.setPlayCount(37); |
|
CORRADE_VERIFY(player.scaler()); |
|
CORRADE_COMPARE(player.duration(), (Range1D{0.5f, 4.0f})); |
|
CORRADE_COMPARE(player.playCount(), 37); |
|
CORRADE_COMPARE(player.state(), State::Stopped); |
|
CORRADE_VERIFY(!player.isEmpty()); |
|
CORRADE_COMPARE(player.size(), 2); |
|
CORRADE_COMPARE(player.track(0).keys().data(), Track.keys().data()); |
|
CORRADE_COMPARE(player.track(1).keys().data(), track2.keys().data()); |
|
} |
|
|
|
void PlayerTest::constructCopy() { |
|
CORRADE_VERIFY(!(std::is_constructible<Player<Float>, const Player<Float>&>{})); |
|
CORRADE_VERIFY(!(std::is_assignable<Player<Float>, const Player<Float>&>{})); |
|
} |
|
|
|
void PlayerTest::constructMove() { |
|
Animation::Track<Float, Int> track2{{ |
|
{0.5f, 42}, |
|
{3.0f, 1337}, |
|
{3.5f, -17} |
|
}, Math::select}; |
|
|
|
Float value = -1.0f; |
|
Int value2 = -1; |
|
Player<Float> a; |
|
a.add(Track, value) |
|
.add(track2, value2) |
|
.setPlayCount(37) |
|
.play({}); |
|
CORRADE_COMPARE(a.duration(), (Range1D{0.5f, 4.0f})); |
|
CORRADE_COMPARE(a.playCount(), 37); |
|
CORRADE_COMPARE(a.state(), State::Playing); |
|
CORRADE_VERIFY(!a.isEmpty()); |
|
CORRADE_COMPARE(a.size(), 2); |
|
CORRADE_COMPARE(a.track(0).keys().data(), Track.keys().data()); |
|
CORRADE_COMPARE(a.track(1).keys().data(), track2.keys().data()); |
|
|
|
Player<Float> b{std::move(a)}; |
|
CORRADE_COMPARE(b.duration(), (Range1D{0.5f, 4.0f})); |
|
CORRADE_COMPARE(b.playCount(), 37); |
|
CORRADE_COMPARE(b.state(), State::Playing); |
|
CORRADE_VERIFY(!b.isEmpty()); |
|
CORRADE_COMPARE(b.size(), 2); |
|
CORRADE_COMPARE(b.track(0).keys().data(), Track.keys().data()); |
|
CORRADE_COMPARE(b.track(1).keys().data(), track2.keys().data()); |
|
|
|
Player<Float> c; |
|
c.setDuration({1.2f, 1.3f}); |
|
c = std::move(b); |
|
CORRADE_COMPARE(c.duration(), (Range1D{0.5f, 4.0f})); |
|
CORRADE_COMPARE(c.playCount(), 37); |
|
CORRADE_COMPARE(c.state(), State::Playing); |
|
CORRADE_VERIFY(!c.isEmpty()); |
|
CORRADE_COMPARE(c.size(), 2); |
|
CORRADE_COMPARE(c.track(0).keys().data(), Track.keys().data()); |
|
CORRADE_COMPARE(c.track(1).keys().data(), track2.keys().data()); |
|
} |
|
|
|
void PlayerTest::setDurationExtend() { |
|
Float value; |
|
Player<Float> player; |
|
player.setDuration({-1.0f, 2.0f}); |
|
CORRADE_COMPARE(player.duration(), (Range1D{-1.0f, 2.0f})); |
|
|
|
player.add(Track, value); |
|
CORRADE_COMPARE(player.duration(), (Range1D{-1.0f, 4.0f})); |
|
} |
|
|
|
void PlayerTest::setDurationReplace() { |
|
Float value; |
|
Player<Float> player; |
|
player.add(Track, value); |
|
CORRADE_COMPARE(player.duration(), (Range1D{1.0f, 4.0f})); |
|
|
|
player.setDuration({-1.0f, 2.0f}); |
|
CORRADE_COMPARE(player.duration(), (Range1D{-1.0f, 2.0f})); |
|
} |
|
|
|
void PlayerTest::trackInvalidIndex() { |
|
std::ostringstream out; |
|
Error redirectError{&out}; |
|
|
|
Float value; |
|
Player<Float> player; |
|
/* Adding at least one track so the return in the graceful assert can |
|
return the first value and not trigger MSVC debug iterator abort */ |
|
player.add(TrackView<Float, Float>{}, value); |
|
|
|
player.track(1); |
|
|
|
CORRADE_COMPARE(out.str(), "Animation::Player::track(): index out of range\n"); |
|
} |
|
|
|
void PlayerTest::advanceNotRunning() { |
|
Float value = -1.0f; |
|
Player<Float> player; |
|
player.add(Track, value); |
|
|
|
CORRADE_COMPARE(player.state(), State::Stopped); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
player.advance(1.75f); |
|
|
|
CORRADE_COMPARE(player.state(), State::Stopped); |
|
CORRADE_COMPARE(value, -1.0f); |
|
} |
|
|
|
void PlayerTest::advancePlaying() { |
|
Float value = -1.0f; |
|
Player<Float> player; |
|
player.add(Track, value) |
|
.play(2.0f); |
|
|
|
CORRADE_COMPARE(player.duration().size(), 3.0f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* Still before starting time, nothing is done */ |
|
player.advance(1.75f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* 1.75 secs in */ |
|
player.advance(3.75f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, 4.0f); |
|
|
|
/* 2.67 secs in */ |
|
player.advance(4.6666667f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, 3.0f); |
|
|
|
/* When the player gets stopped, the value at the stop time is written */ |
|
player.advance(5.5f); |
|
CORRADE_COMPARE(player.state(), State::Stopped); |
|
CORRADE_COMPARE(value, 2.0f); |
|
|
|
/* But further advancing will not write anything */ |
|
value = -1.0f; |
|
player.advance(100.0f); |
|
CORRADE_COMPARE(player.state(), State::Stopped); |
|
CORRADE_COMPARE(value, -1.0f); |
|
} |
|
|
|
void PlayerTest::advanceRestart() { |
|
Float value = -1.0f; |
|
Player<Float> player; |
|
player.add(Track, value) |
|
.play(2.0f); |
|
|
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* Still before starting time, nothing is done */ |
|
player.advance(1.75f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* 1.75 secs in */ |
|
player.advance(3.75f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, 4.0f); |
|
|
|
/* Call play again, will restart from the beginning... */ |
|
value = -1.0f; |
|
player.play(4.0f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* ... but only after calling advance() again. Now at 1 sec in. */ |
|
player.advance(5.0f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, 2.5f); |
|
} |
|
|
|
void PlayerTest::advanceStop() { |
|
Float value = -1.0f; |
|
Player<Float> player; |
|
player.add(Track, value) |
|
.play(2.0f); |
|
|
|
player.advance(3.75f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, 4.0f); |
|
|
|
/* Stop, should not update anything */ |
|
value = -1.0; |
|
player.stop(); |
|
CORRADE_COMPARE(player.state(), State::Stopped); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* Advancing will update with a value from beginning of the duration */ |
|
player.advance(5.0f); |
|
CORRADE_COMPARE(player.state(), State::Stopped); |
|
CORRADE_COMPARE(value, 1.5f); |
|
|
|
/* But further advancing will not write anything */ |
|
value = -1.0f; |
|
player.advance(100.0f); |
|
CORRADE_COMPARE(player.state(), State::Stopped); |
|
CORRADE_COMPARE(value, -1.0f); |
|
} |
|
|
|
void PlayerTest::advancePauseResume() { |
|
Float value = -1.0f; |
|
Player<Float> player; |
|
player.add(Track, value) |
|
.play(2.0f); |
|
|
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
player.advance(3.75f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, 4.0f); |
|
|
|
/* Pausing should not update anything */ |
|
value = -1.0f; |
|
player.pause(4.0f); |
|
CORRADE_COMPARE(player.state(), State::Paused); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* Pausing again should be a no-op */ |
|
player.pause(4.1f); |
|
CORRADE_COMPARE(player.state(), State::Paused); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* But advance() after should. No matter what time is passed to it, it |
|
should update with time of pause. */ |
|
player.advance(4.5f); |
|
CORRADE_COMPARE(player.state(), State::Paused); |
|
CORRADE_COMPARE(value, 5.0f); /* value at 2.0f, not 2.5f */ |
|
|
|
/* Advancing further should do nothing */ |
|
value = -1.0f; |
|
player.advance(50.0f); |
|
CORRADE_COMPARE(player.state(), State::Paused); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* Resuming the animation, again should not update anything */ |
|
player.play(100.0f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* Advancing the animation should update again. It was paused after two |
|
seconds, so continuing at 2.5 seconds now. */ |
|
player.advance(100.5f); |
|
CORRADE_COMPARE(value, 3.5f); |
|
} |
|
|
|
void PlayerTest::advancePauseStop() { |
|
Float value = -1.0f; |
|
Player<Float> player; |
|
player.add(Track, value) |
|
.play(2.0f); |
|
|
|
player.advance(3.75f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, 4.0f); |
|
|
|
/* Pause, get value from the pause time */ |
|
player.pause(4.0f); |
|
player.advance(4.5f); |
|
CORRADE_COMPARE(player.state(), State::Paused); |
|
CORRADE_COMPARE(value, 5.0f); |
|
|
|
/* Stop, should not update anything */ |
|
value = -1.0; |
|
player.stop(); |
|
CORRADE_COMPARE(player.state(), State::Stopped); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* Advancing will update with a value from beginning of the duration */ |
|
player.advance(5.0f); |
|
CORRADE_COMPARE(player.state(), State::Stopped); |
|
CORRADE_COMPARE(value, 1.5f); |
|
|
|
/* But further advancing will not write anything */ |
|
value = -1.0f; |
|
player.advance(100.0f); |
|
CORRADE_COMPARE(player.state(), State::Stopped); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* Pause while stopped is a no-op */ |
|
player.pause(101.0f); |
|
player.advance(101.0f); |
|
CORRADE_COMPARE(player.state(), State::Stopped); |
|
CORRADE_COMPARE(value, -1.0f); |
|
} |
|
|
|
void PlayerTest::advancePlayCount() { |
|
Float value = -1.0f; |
|
Player<Float> player; |
|
player.add(Track, value) |
|
.setPlayCount(3) |
|
.play(2.0f); |
|
|
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* Still before starting time, nothing is done */ |
|
player.advance(1.75f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* 1.75 secs in */ |
|
player.advance(3.75f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, 4.0f); |
|
|
|
/* 2 secs in, second round */ |
|
player.advance(7.0f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, 5.0f); |
|
|
|
/* 1.75 secs in, third round */ |
|
player.advance(9.75f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, 4.0f); |
|
|
|
/* When the player gets stopped, the value at the stop time is written */ |
|
player.advance(11.5f); |
|
CORRADE_COMPARE(player.state(), State::Stopped); |
|
CORRADE_COMPARE(value, 2.0f); |
|
|
|
/* But further advancing will not write anything */ |
|
value = -1.0f; |
|
player.advance(100.0f); |
|
CORRADE_COMPARE(player.state(), State::Stopped); |
|
CORRADE_COMPARE(value, -1.0f); |
|
} |
|
|
|
void PlayerTest::advancePlayCountInfinite() { |
|
Float value = -1.0f; |
|
Player<Float> player; |
|
player.add(Track, value) |
|
.setPlayCount(0) |
|
.play(2.0f); |
|
|
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* Still before starting time, nothing is done */ |
|
player.advance(1.75f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* 1.75 secs in */ |
|
player.advance(3.75f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, 4.0f); |
|
|
|
/* 2 secs in, second round */ |
|
player.advance(7.0f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, 5.0f); |
|
|
|
/* 1.75 secs in, 10th round */ |
|
player.advance(33.75f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, 4.0f); |
|
} |
|
|
|
void PlayerTest::advanceChrono() { |
|
Float value = -1.0f; |
|
Player<std::chrono::nanoseconds, Float> player; |
|
player.add(Track, value) |
|
.play(std::chrono::seconds{2}); |
|
|
|
CORRADE_COMPARE(player.duration().size(), 3.0f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* Still before starting time, nothing is done */ |
|
player.advance(std::chrono::milliseconds{1750}); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* 1.75 secs in */ |
|
player.advance(std::chrono::milliseconds{3750}); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, 4.0f); |
|
} |
|
|
|
void PlayerTest::advanceZeroDuration() { |
|
Float value = -1.0f; |
|
Player<Float> player; |
|
player.add(Track, value) |
|
/* 1.75 secs since the start of the original duration */ |
|
.setDuration(Range1D::fromSize(1.0f + 1.75f, 0.0f)) |
|
.setPlayCount(0) |
|
.play(2.0f); |
|
|
|
CORRADE_COMPARE(player.duration().size(), 0.0f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* Still before starting time, nothing is done */ |
|
player.advance(1.75f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* After that, the value at 1.75 secs is returned independent of time */ |
|
player.advance(100.0f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, 4.0f); |
|
} |
|
|
|
void PlayerTest::advanceZeroDurationChrono() { |
|
Float value = -1.0f; |
|
Player<std::chrono::nanoseconds, Float> player; |
|
player.add(Track, value) |
|
/* 1.75 secs since the start of the original duration */ |
|
.setDuration(Range1D::fromSize(1.0f + 1.75f, 0.0f)) |
|
.setPlayCount(0) |
|
.play(std::chrono::seconds{2}); |
|
|
|
CORRADE_COMPARE(player.duration().size(), 0.0f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* Still before starting time, nothing is done */ |
|
player.advance(std::chrono::milliseconds{1750}); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* After that, the value at 1.75 seconds is returned independent of the |
|
time */ |
|
player.advance(std::chrono::seconds{100}); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, 4.0f); |
|
} |
|
|
|
void PlayerTest::setState() { |
|
Player<Float> player; |
|
CORRADE_COMPARE(player.state(), State::Stopped); |
|
|
|
player.setState(State::Playing, {}); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
|
|
player.setState(State::Paused, {}); |
|
CORRADE_COMPARE(player.state(), State::Paused); |
|
|
|
player.setState(State::Stopped, {}); |
|
CORRADE_COMPARE(player.state(), State::Stopped); |
|
} |
|
|
|
void PlayerTest::add() { |
|
Float value = -1.0f; |
|
Player<Float> player; |
|
player.add(Track, value) |
|
.play(2.0f); |
|
|
|
CORRADE_COMPARE(player.duration().size(), 3.0f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* 1.75 secs in */ |
|
player.advance(3.75f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, 4.0f); |
|
} |
|
|
|
void PlayerTest::addWithCallback() { |
|
struct Data { |
|
Float value = -1.0f; |
|
Int called = 0; |
|
} data; |
|
Player<Float> player; |
|
player.addWithCallback(Track, [](const Float&, const Float& value, void* userData) { |
|
static_cast<Data*>(userData)->value = value; |
|
++static_cast<Data*>(userData)->called; |
|
}, &data) |
|
.play(2.0f); |
|
|
|
CORRADE_COMPARE(player.duration().size(), 3.0f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(data.value, -1.0f); |
|
CORRADE_COMPARE(data.called, 0); |
|
|
|
/* 1.75 secs in */ |
|
player.advance(3.75f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(data.value, 4.0f); |
|
CORRADE_COMPARE(data.called, 1); |
|
} |
|
|
|
void PlayerTest::addWithCallbackTemplate() { |
|
struct Data { |
|
Float value = -1.0f; |
|
Int called = 0; |
|
} data; |
|
Player<Float> player; |
|
player.addWithCallback(Track, [](const Float&, const Float& value, Data& userData) { |
|
userData.value = value; |
|
++userData.called; |
|
}, data) |
|
.play(2.0f); |
|
|
|
CORRADE_COMPARE(player.duration().size(), 3.0f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(data.value, -1.0f); |
|
CORRADE_COMPARE(data.called, 0); |
|
|
|
/* 1.75 secs in */ |
|
player.advance(3.75f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(data.value, 4.0f); |
|
CORRADE_COMPARE(data.called, 1); |
|
} |
|
|
|
void PlayerTest::addWithCallbackOnChange() { |
|
struct Data { |
|
Float value = -1.0f; |
|
Int called = 0; |
|
} data; |
|
Player<Float> player; |
|
player.addWithCallbackOnChange(Track, [](const Float&, const Float& value, void* userData) { |
|
static_cast<Data*>(userData)->value = value; |
|
++static_cast<Data*>(userData)->called; |
|
}, data.value, &data) |
|
.play(2.0f); |
|
|
|
CORRADE_COMPARE(player.duration().size(), 3.0f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(data.value, -1.0f); |
|
CORRADE_COMPARE(data.called, 0); |
|
|
|
/* 1.75 secs in */ |
|
player.advance(3.75f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(data.value, 4.0f); |
|
CORRADE_COMPARE(data.called, 1); |
|
|
|
/* At the same time, same value, should not be called again */ |
|
player.advance(3.75f); |
|
CORRADE_COMPARE(data.value, 4.0f); |
|
CORRADE_COMPARE(data.called, 1); |
|
|
|
/* Different time, different value, called again */ |
|
player.advance(4.0f); |
|
CORRADE_COMPARE(data.value, 5.0f); |
|
CORRADE_COMPARE(data.called, 2); |
|
} |
|
|
|
void PlayerTest::addWithCallbackOnChangeTemplate() { |
|
struct Data { |
|
Float value = -1.0f; |
|
Int called = 0; |
|
} data; |
|
Player<Float> player; |
|
player.addWithCallbackOnChange(Track, [](const Float&, const Float& value, Data& userData) { |
|
userData.value = value; |
|
++userData.called; |
|
}, data.value, data) |
|
.play(2.0f); |
|
|
|
CORRADE_COMPARE(player.duration().size(), 3.0f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(data.value, -1.0f); |
|
CORRADE_COMPARE(data.called, 0); |
|
|
|
/* 1.75 secs in */ |
|
player.advance(3.75f); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(data.value, 4.0f); |
|
CORRADE_COMPARE(data.called, 1); |
|
|
|
/* At the same time, same value, should not be called again */ |
|
player.advance(3.75f); |
|
CORRADE_COMPARE(data.value, 4.0f); |
|
CORRADE_COMPARE(data.called, 1); |
|
|
|
/* Different time, different value, called again */ |
|
player.advance(4.0f); |
|
CORRADE_COMPARE(data.value, 5.0f); |
|
CORRADE_COMPARE(data.called, 2); |
|
} |
|
|
|
namespace { |
|
/* Can't use raw lambdas because MSVC 2015 */ |
|
void callback(std::vector<Int>& data, Int value) { |
|
data.push_back(value); |
|
} |
|
} |
|
|
|
void PlayerTest::addRawCallback() { |
|
Animation::Track<Float, Int> track; |
|
|
|
Int result = -1; |
|
std::vector<Int> data; |
|
|
|
Animation::Player<Float> player; |
|
player.addRawCallback(track, |
|
[](const Animation::TrackViewStorage<Float>& track, Float key, |
|
std::size_t& hint, void* destination, void(*callback)(), void* userData) { |
|
Int value = static_cast<const Animation::TrackView<Float, Int>&>(track).at(key, hint); |
|
if(value == *static_cast<Int*>(destination)) return; |
|
*static_cast<Int*>(destination) = value; |
|
reinterpret_cast<void(*)(std::vector<Int>&, Int)>(callback)(*static_cast<std::vector<Int>*>(userData), value); |
|
}, &result, reinterpret_cast<void(*)()>(callback), &data) |
|
.play({}); |
|
|
|
/* Should add the default-constructed value into the vector, but only once */ |
|
CORRADE_COMPARE(data, std::vector<Int>{}); |
|
player.advance({}); |
|
CORRADE_COMPARE(data, std::vector<Int>{0}); |
|
player.advance(1.0f); |
|
CORRADE_COMPARE(data, std::vector<Int>{0}); |
|
} |
|
|
|
void PlayerTest::runFor100YearsFloat() { |
|
auto&& data = RunFor100YearsData[testCaseInstanceId()]; |
|
setTestCaseDescription(data.name); |
|
|
|
Float value = -1.0f; |
|
Player<Float> player; |
|
player.add(Track, value) |
|
.setPlayCount(0) |
|
.play({}); |
|
|
|
/* The track must fit an integer number of times into the day for this test |
|
to work (3 seconds do fit) */ |
|
CORRADE_COMPARE(player.duration().size()[0], 3.0f); |
|
|
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* 2.67 secs in given iteration. Comparing with a slightly larger epsilon, |
|
because it fails right after five minutes otherwise. */ |
|
player.advance(data.offsetFloat + 2.6666666666667f); |
|
|
|
if(data.failsFloat || data.failsFuzzyFloat) |
|
Debug{} << "Calculated value:" << value; |
|
|
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
{ |
|
/* Asm.js uses doubles for all floating-point calculations, so we don't |
|
lose any precision and thus even the "run for 100 years" test passes |
|
there. Unfortunately it's not possible to detect if this is asm.js |
|
so the XFAIL is done like this. */ |
|
#ifndef CORRADE_TARGET_EMSCRIPTEN |
|
CORRADE_EXPECT_FAIL_IF(data.failsFuzzyFloat, "Imprecision larger than 2.5e-4f."); |
|
#else |
|
CORRADE_EXPECT_FAIL_IF(data.failsFuzzyFloat && !Math::TypeTraits<Float>::equals(value, 3.0f), "Imprecision larger than 2.5e-4f."); |
|
#endif |
|
CORRADE_COMPARE_WITH(value, 3.0f, TestSuite::Compare::around(0.00025f)); |
|
} |
|
if(!data.failsFuzzyFloat) { |
|
#ifndef CORRADE_TARGET_EMSCRIPTEN |
|
CORRADE_EXPECT_FAIL_IF(data.failsFloat, "Imprecision larger than 1e-6f."); |
|
#else |
|
CORRADE_EXPECT_FAIL_IF(data.failsFloat && !Math::TypeTraits<Float>::equals(value, 3.0f), "Imprecision larger than 2.5e-4f."); |
|
#endif |
|
CORRADE_COMPARE(value, 3.0f); |
|
} |
|
} |
|
|
|
void PlayerTest::runFor100YearsChrono() { |
|
auto&& data = RunFor100YearsData[testCaseInstanceId()]; |
|
setTestCaseDescription(data.name); |
|
|
|
Float value = -1.0f; |
|
Player<std::chrono::nanoseconds, Float> player; |
|
player.add(Track, value) |
|
.setPlayCount(0) |
|
.play({}); |
|
|
|
/* The track must fit an integer number of times into the day for this test |
|
to work (3 seconds do fit) */ |
|
CORRADE_COMPARE(player.duration().size()[0], 3.0f); |
|
|
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, -1.0f); |
|
|
|
/* 2.67 secs in */ |
|
player.advance(data.offsetChrono + std::chrono::nanoseconds{2666666667ull}); |
|
CORRADE_COMPARE(player.state(), State::Playing); |
|
CORRADE_COMPARE(value, 3.0f); |
|
} |
|
|
|
void PlayerTest::debugState() { |
|
std::ostringstream out; |
|
|
|
Debug{&out} << State::Playing << State(0xde); |
|
CORRADE_COMPARE(out.str(), "Animation::State::Playing Animation::State(0xde)\n"); |
|
} |
|
|
|
}}} |
|
|
|
CORRADE_TEST_MAIN(Magnum::Animation::Test::PlayerTest)
|
|
|