Move ComputeTest,GraphicsTest,RenderTest and Std140Debug to the tests folder
Also renamed NazaraUnitTests to UnitTests
This commit is contained in:
20
tests/UnitTests/Engine/Audio/AlgorithmAudioTest.cpp
Normal file
20
tests/UnitTests/Engine/Audio/AlgorithmAudioTest.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include <Nazara/Audio/Algorithm.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <array>
|
||||
|
||||
TEST_CASE("MixToMono", "[AUDIO][ALGORITHM]")
|
||||
{
|
||||
SECTION("Mix two channels together")
|
||||
{
|
||||
std::array<int, 4> input = { { 1, 3, 5, 3 } };
|
||||
std::array<int, 2> output = { { 0, 0 } };
|
||||
|
||||
// Two channels and two frames !
|
||||
Nz::MixToMono(input.data(), output.data(), 2, 2);
|
||||
|
||||
std::array<int, 2> theoric = { { 2, 4 } }; // It's the mean of the two channels
|
||||
CHECK(output == theoric);
|
||||
}
|
||||
}
|
||||
109
tests/UnitTests/Engine/Audio/MusicTest.cpp
Normal file
109
tests/UnitTests/Engine/Audio/MusicTest.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
#include <Nazara/Audio/Audio.hpp>
|
||||
#include <Nazara/Audio/Music.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
std::filesystem::path GetAssetDir();
|
||||
|
||||
SCENARIO("Music", "[AUDIO][MUSIC]")
|
||||
{
|
||||
GIVEN("A music")
|
||||
{
|
||||
Nz::Music music;
|
||||
|
||||
WHEN("We load our music")
|
||||
{
|
||||
REQUIRE(music.OpenFromFile(GetAssetDir() / "Audio/The_Brabanconne.ogg"));
|
||||
|
||||
THEN("We can ask the informations of the file")
|
||||
{
|
||||
CHECK(music.GetDuration() == 63059); // 1 min 03 = 63s = 63000ms
|
||||
CHECK(music.GetFormat() == Nz::AudioFormat::I16_Stereo);
|
||||
CHECK(music.GetPlayingOffset() == 0);
|
||||
CHECK(music.GetSampleCount() <= 64 * 44100 * 2); // * 2 (stereo)
|
||||
CHECK(music.GetSampleCount() >= 63 * 44100 * 2); // * 2 (stereo)
|
||||
CHECK(music.GetSampleRate() == 44100 /* Hz */);
|
||||
CHECK(music.GetStatus() == Nz::SoundStatus::Stopped);
|
||||
CHECK_FALSE(music.IsLooping());
|
||||
CHECK(music.IsSpatializationEnabled());
|
||||
CHECK(music.GetMinDistance() == 1.f);
|
||||
CHECK(music.GetPitch() == 1.f);
|
||||
CHECK(music.GetPlayingOffset() == 0);
|
||||
CHECK(music.GetPosition() == Nz::Vector3f::Zero());
|
||||
CHECK(music.GetVelocity() == Nz::Vector3f::Zero());
|
||||
CHECK(music.GetVolume() == 1.f);
|
||||
}
|
||||
|
||||
THEN("We can play it and get the time offset")
|
||||
{
|
||||
Nz::Audio::Instance()->GetDefaultDevice()->SetGlobalVolume(0.f);
|
||||
|
||||
music.Play();
|
||||
CHECK(music.GetStatus() == Nz::SoundStatus::Playing);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
CHECK(music.GetPlayingOffset() >= 950);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
CHECK(music.GetPlayingOffset() <= 1500);
|
||||
|
||||
music.SetPlayingOffset(4200);
|
||||
CHECK(music.GetStatus() == Nz::SoundStatus::Playing);
|
||||
CHECK(music.GetPlayingOffset() >= 4150);
|
||||
CHECK(music.GetPlayingOffset() < 4500);
|
||||
CHECK(music.GetStatus() == Nz::SoundStatus::Playing);
|
||||
|
||||
music.Pause();
|
||||
Nz::UInt32 playingOffset = music.GetPlayingOffset();
|
||||
CHECK(music.GetStatus() == Nz::SoundStatus::Paused);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
CHECK(music.GetStatus() == Nz::SoundStatus::Paused);
|
||||
CHECK(music.GetPlayingOffset() == playingOffset);
|
||||
|
||||
music.SetPlayingOffset(3500);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
CHECK(music.GetPlayingOffset() == 3500);
|
||||
|
||||
music.Play();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
CHECK(music.GetStatus() == Nz::SoundStatus::Playing);
|
||||
CHECK(music.GetPlayingOffset() >= 3650);
|
||||
|
||||
AND_WHEN("We let the sound stop by itself")
|
||||
{
|
||||
REQUIRE(music.GetDuration() == 63059);
|
||||
|
||||
music.SetPlayingOffset(62900);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
CHECK(music.GetStatus() == Nz::SoundStatus::Stopped);
|
||||
CHECK(music.GetPlayingOffset() == 0);
|
||||
|
||||
music.SetPlayingOffset(64000);
|
||||
music.Play();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
CHECK(music.GetStatus() == Nz::SoundStatus::Playing);
|
||||
CHECK(music.GetPlayingOffset() < 100);
|
||||
|
||||
music.Stop();
|
||||
music.SetPlayingOffset(62900);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
CHECK(music.GetStatus() == Nz::SoundStatus::Stopped);
|
||||
CHECK(music.GetPlayingOffset() == 0); //< playing offset has no effect until Play()
|
||||
|
||||
AND_WHEN("We enable looping")
|
||||
{
|
||||
music.EnableLooping(true);
|
||||
CHECK(music.IsLooping());
|
||||
music.Play();
|
||||
CHECK(music.GetStatus() == Nz::SoundStatus::Playing);
|
||||
CHECK(music.GetPlayingOffset() >= 62900);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
CHECK(music.GetStatus() == Nz::SoundStatus::Playing);
|
||||
CHECK(music.GetPlayingOffset() < 300);
|
||||
}
|
||||
}
|
||||
Nz::Audio::Instance()->GetDefaultDevice()->SetGlobalVolume(100.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
63
tests/UnitTests/Engine/Audio/SoundBufferTest.cpp
Normal file
63
tests/UnitTests/Engine/Audio/SoundBufferTest.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#include <Nazara/Audio/SoundBuffer.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
std::filesystem::path GetAssetDir();
|
||||
|
||||
SCENARIO("SoundBuffer", "[AUDIO][SOUNDBUFFER]")
|
||||
{
|
||||
GIVEN("A sound buffer")
|
||||
{
|
||||
WHEN("We load a .flac file")
|
||||
{
|
||||
std::shared_ptr<Nz::SoundBuffer> soundBuffer = Nz::SoundBuffer::LoadFromFile(GetAssetDir() / "Audio/Cat.flac");
|
||||
REQUIRE(soundBuffer);
|
||||
|
||||
THEN("We can ask the informations of the file")
|
||||
{
|
||||
CHECK(soundBuffer->GetDuration() == 8192);
|
||||
CHECK(soundBuffer->GetFormat() == Nz::AudioFormat::I16_Stereo);
|
||||
CHECK(soundBuffer->GetSampleRate() == 96000);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We load a .mp3 file")
|
||||
{
|
||||
std::shared_ptr<Nz::SoundBuffer> soundBuffer = Nz::SoundBuffer::LoadFromFile(GetAssetDir() / "Audio/file_example_MP3_700KB.mp3");
|
||||
REQUIRE(soundBuffer);
|
||||
|
||||
THEN("We can ask the informations of the file")
|
||||
{
|
||||
CHECK(soundBuffer->GetDuration() == 27193);
|
||||
CHECK(soundBuffer->GetFormat() == Nz::AudioFormat::I16_Stereo);
|
||||
CHECK(soundBuffer->GetSampleRate() == 32000);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We load a .ogg file")
|
||||
{
|
||||
std::shared_ptr<Nz::SoundBuffer> soundBuffer = Nz::SoundBuffer::LoadFromFile(GetAssetDir() / "Audio/The_Brabanconne.ogg");
|
||||
REQUIRE(soundBuffer);
|
||||
|
||||
THEN("We can ask the informations of the file")
|
||||
{
|
||||
CHECK(soundBuffer->GetDuration() == 63059);
|
||||
CHECK(soundBuffer->GetFormat() == Nz::AudioFormat::I16_Stereo);
|
||||
CHECK(soundBuffer->GetSampleRate() == 44100);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We load a .wav file")
|
||||
{
|
||||
std::shared_ptr<Nz::SoundBuffer> soundBuffer = Nz::SoundBuffer::LoadFromFile(GetAssetDir() / "Audio/explosion1.wav");
|
||||
REQUIRE(soundBuffer);
|
||||
|
||||
THEN("We can ask the informations of the file")
|
||||
{
|
||||
CHECK(soundBuffer->GetDuration() == 2490);
|
||||
CHECK(soundBuffer->GetFormat() == Nz::AudioFormat::I16_Mono);
|
||||
CHECK(soundBuffer->GetSampleRate() == 44100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
42
tests/UnitTests/Engine/Audio/SoundEmitterTest.cpp
Normal file
42
tests/UnitTests/Engine/Audio/SoundEmitterTest.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include <Nazara/Audio/Sound.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
std::filesystem::path GetAssetDir();
|
||||
|
||||
SCENARIO("SoundEmitter", "[AUDIO][SOUNDEMITTER]")
|
||||
{
|
||||
GIVEN("A sound emitter")
|
||||
{
|
||||
Nz::Sound sound;
|
||||
|
||||
WHEN("We load our sound")
|
||||
{
|
||||
REQUIRE(sound.LoadFromFile(GetAssetDir() / "Audio/Cat.flac"));
|
||||
|
||||
THEN("We can ask information about position and velocity")
|
||||
{
|
||||
sound.EnableSpatialization(true);
|
||||
sound.SetPosition(Nz::Vector3f::Zero());
|
||||
sound.SetVelocity(Nz::Vector3f::UnitX());
|
||||
|
||||
REQUIRE(sound.IsSpatializationEnabled());
|
||||
REQUIRE(sound.GetPosition() == Nz::Vector3f::Zero());
|
||||
REQUIRE(sound.GetVelocity() == Nz::Vector3f::UnitX());
|
||||
}
|
||||
|
||||
THEN("We can ask information about attenuation, pitch, ...")
|
||||
{
|
||||
sound.SetAttenuation(0.4f);
|
||||
sound.SetMinDistance(40.f);
|
||||
sound.SetPitch(0.8f);
|
||||
sound.SetVolume(50.f);
|
||||
|
||||
REQUIRE(Catch::Approx(sound.GetAttenuation()) == 0.4f);
|
||||
REQUIRE(Catch::Approx(sound.GetMinDistance()) == 40.f);
|
||||
REQUIRE(Catch::Approx(sound.GetPitch()) == 0.8f);
|
||||
REQUIRE(Catch::Approx(sound.GetVolume()) == 50.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
63
tests/UnitTests/Engine/Audio/SoundStreamTest.cpp
Normal file
63
tests/UnitTests/Engine/Audio/SoundStreamTest.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#include <Nazara/Audio/SoundStream.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
std::filesystem::path GetAssetDir();
|
||||
|
||||
SCENARIO("SoundStream", "[AUDIO][SoundStream]")
|
||||
{
|
||||
GIVEN("A sound buffer")
|
||||
{
|
||||
WHEN("We load a .flac file")
|
||||
{
|
||||
std::shared_ptr<Nz::SoundStream> soundStream = Nz::SoundStream::OpenFromFile(GetAssetDir() / "Audio/Cat.flac");
|
||||
REQUIRE(soundStream);
|
||||
|
||||
THEN("We can ask the informations of the file")
|
||||
{
|
||||
CHECK(soundStream->GetDuration() == 8192);
|
||||
CHECK(soundStream->GetFormat() == Nz::AudioFormat::I16_Stereo);
|
||||
CHECK(soundStream->GetSampleRate() == 96000);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We load a .mp3 file")
|
||||
{
|
||||
std::shared_ptr<Nz::SoundStream> soundStream = Nz::SoundStream::OpenFromFile(GetAssetDir() / "Audio/file_example_MP3_700KB.mp3");
|
||||
REQUIRE(soundStream);
|
||||
|
||||
THEN("We can ask the informations of the file")
|
||||
{
|
||||
CHECK(soundStream->GetDuration() == 27193);
|
||||
CHECK(soundStream->GetFormat() == Nz::AudioFormat::I16_Stereo);
|
||||
CHECK(soundStream->GetSampleRate() == 32000);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We load a .ogg file")
|
||||
{
|
||||
std::shared_ptr<Nz::SoundStream> soundStream = Nz::SoundStream::OpenFromFile(GetAssetDir() / "Audio/The_Brabanconne.ogg");
|
||||
REQUIRE(soundStream);
|
||||
|
||||
THEN("We can ask the informations of the file")
|
||||
{
|
||||
CHECK(soundStream->GetDuration() == 63059);
|
||||
CHECK(soundStream->GetFormat() == Nz::AudioFormat::I16_Stereo);
|
||||
CHECK(soundStream->GetSampleRate() == 44100);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We load a .wav file")
|
||||
{
|
||||
std::shared_ptr<Nz::SoundStream> soundStream = Nz::SoundStream::OpenFromFile(GetAssetDir() / "Audio/explosion1.wav");
|
||||
REQUIRE(soundStream);
|
||||
|
||||
THEN("We can ask the informations of the file")
|
||||
{
|
||||
CHECK(soundStream->GetDuration() == 2490);
|
||||
CHECK(soundStream->GetFormat() == Nz::AudioFormat::I16_Mono);
|
||||
CHECK(soundStream->GetSampleRate() == 44100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
95
tests/UnitTests/Engine/Audio/SoundTest.cpp
Normal file
95
tests/UnitTests/Engine/Audio/SoundTest.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include <Nazara/Audio/Audio.hpp>
|
||||
#include <Nazara/Audio/Sound.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
std::filesystem::path GetAssetDir();
|
||||
|
||||
SCENARIO("Sound", "[AUDIO][SOUND]")
|
||||
{
|
||||
GIVEN("A sound")
|
||||
{
|
||||
Nz::Sound sound;
|
||||
|
||||
WHEN("We load our sound")
|
||||
{
|
||||
REQUIRE(sound.LoadFromFile(GetAssetDir() / "Audio/Cat.flac"));
|
||||
|
||||
THEN("We can ask the informations of the file")
|
||||
{
|
||||
CHECK(sound.GetDuration() == 8192);
|
||||
CHECK(sound.GetStatus() == Nz::SoundStatus::Stopped);
|
||||
CHECK_FALSE(sound.IsLooping());
|
||||
CHECK(sound.IsPlayable());
|
||||
CHECK(sound.IsSpatializationEnabled());
|
||||
CHECK(sound.GetMinDistance() == 1.f);
|
||||
CHECK(sound.GetPitch() == 1.f);
|
||||
CHECK(sound.GetPlayingOffset() == 0);
|
||||
CHECK(sound.GetPosition() == Nz::Vector3f::Zero());
|
||||
CHECK(sound.GetVelocity() == Nz::Vector3f::Zero());
|
||||
CHECK(sound.GetVolume() == 1.f);
|
||||
}
|
||||
|
||||
THEN("We can play it and get the time offset")
|
||||
{
|
||||
Nz::Audio::Instance()->GetDefaultDevice()->SetGlobalVolume(0.f);
|
||||
|
||||
sound.Play();
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
CHECK(sound.GetPlayingOffset() >= 950);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
CHECK(sound.GetPlayingOffset() <= 1500);
|
||||
sound.Pause();
|
||||
Nz::UInt32 playingOffset = sound.GetPlayingOffset();
|
||||
CHECK(sound.GetStatus() == Nz::SoundStatus::Paused);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
CHECK(sound.GetStatus() == Nz::SoundStatus::Paused);
|
||||
CHECK(sound.GetPlayingOffset() == playingOffset);
|
||||
|
||||
sound.SetPlayingOffset(3500);
|
||||
CHECK(sound.GetPlayingOffset() == 3500);
|
||||
|
||||
sound.Play();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
CHECK(sound.GetPlayingOffset() >= 1650);
|
||||
|
||||
AND_WHEN("We let the sound stop by itself")
|
||||
{
|
||||
REQUIRE(sound.GetDuration() == 8192);
|
||||
|
||||
sound.SetPlayingOffset(8000);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
CHECK(sound.GetStatus() == Nz::SoundStatus::Stopped);
|
||||
CHECK(sound.GetPlayingOffset() == 0);
|
||||
|
||||
sound.SetPlayingOffset(9000);
|
||||
sound.Play();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
CHECK(sound.GetStatus() == Nz::SoundStatus::Playing);
|
||||
|
||||
sound.Stop();
|
||||
sound.SetPlayingOffset(8000);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
CHECK(sound.GetStatus() == Nz::SoundStatus::Stopped);
|
||||
CHECK(sound.GetPlayingOffset() == 0); //< playing offset has no effect until Play()
|
||||
|
||||
AND_WHEN("We enable looping")
|
||||
{
|
||||
sound.EnableLooping(true);
|
||||
CHECK(sound.IsLooping());
|
||||
sound.Play();
|
||||
CHECK(sound.GetStatus() == Nz::SoundStatus::Playing);
|
||||
CHECK(sound.GetPlayingOffset() >= 8000);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
CHECK(sound.GetStatus() == Nz::SoundStatus::Playing);
|
||||
CHECK(sound.GetPlayingOffset() < 300);
|
||||
}
|
||||
}
|
||||
|
||||
Nz::Audio::Instance()->GetDefaultDevice()->SetGlobalVolume(100.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
tests/UnitTests/Engine/ClientModules.hpp
Normal file
8
tests/UnitTests/Engine/ClientModules.hpp
Normal file
@@ -0,0 +1,8 @@
|
||||
// used for precompiled headers generation
|
||||
#include <Engine/Modules.hpp>
|
||||
#include <Nazara/Audio.hpp>
|
||||
#include <Nazara/Graphics.hpp>
|
||||
#include <Nazara/OpenGLRenderer.hpp>
|
||||
#include <Nazara/Platform.hpp>
|
||||
#include <Nazara/VulkanRenderer.hpp>
|
||||
#include <Nazara/Widgets.hpp>
|
||||
28
tests/UnitTests/Engine/Core/AbstractHashTest.cpp
Normal file
28
tests/UnitTests/Engine/Core/AbstractHashTest.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include <Nazara/Core/AbstractHash.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <Nazara/Core/ByteArray.hpp>
|
||||
|
||||
#include <array>
|
||||
|
||||
SCENARIO("AbstractHash", "[CORE][ABSTRACTHASH]")
|
||||
{
|
||||
GIVEN("The hash SHA512")
|
||||
{
|
||||
std::unique_ptr<Nz::AbstractHash> SHA512 = Nz::AbstractHash::Get(Nz::HashType::SHA512);
|
||||
SHA512->Begin();
|
||||
|
||||
WHEN("We introduce data")
|
||||
{
|
||||
std::array<Nz::UInt8, 4> array{ { 0, 1, 2, 3 } };
|
||||
SHA512->Append(array.data(), array.size());
|
||||
|
||||
THEN("We ask for the bytearray")
|
||||
{
|
||||
Nz::ByteArray byteArray = SHA512->End();
|
||||
CHECK(byteArray.GetSize() == SHA512->GetDigestLength());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
99
tests/UnitTests/Engine/Core/AlgorithmCoreTest.cpp
Normal file
99
tests/UnitTests/Engine/Core/AlgorithmCoreTest.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include <Nazara/Core/Algorithm.hpp>
|
||||
#include <Nazara/Core/File.hpp>
|
||||
#include <Nazara/Core/StringExt.hpp>
|
||||
#include <Nazara/Math/Vector2.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
#include <variant>
|
||||
|
||||
std::filesystem::path GetAssetDir();
|
||||
|
||||
TEST_CASE("ComputeHash", "[CORE][ALGORITHM]")
|
||||
{
|
||||
std::filesystem::path testFilePath = GetAssetDir() / "Logo.png";
|
||||
|
||||
struct Test
|
||||
{
|
||||
Nz::HashType hashType;
|
||||
std::variant<const char*, std::filesystem::path> input;
|
||||
const char* expectedOutput;
|
||||
};
|
||||
|
||||
static_assert(Nz::CRC32("Nazara Engine") == 0x8A2F5235);
|
||||
static_assert(Nz::CRC32("The quick brown fox jumps over the lazy dog") == 0x414FA339);
|
||||
|
||||
CHECK(Nz::CRC32("Nazara Engine") == 0x8A2F5235);
|
||||
CHECK(Nz::CRC32("The quick brown fox jumps over the lazy dog") == 0x414FA339);
|
||||
|
||||
// https://defuse.ca/checksums.htm
|
||||
// https://toolslick.com/programming/hashing/crc-calculator
|
||||
|
||||
std::array tests{
|
||||
//Test{ Nz::HashType::CRC16, "Nazara Engine", "9412" },
|
||||
Test{ Nz::HashType::CRC32, "Nazara Engine", "8A2F5235" },
|
||||
Test{ Nz::HashType::CRC64, "Nazara Engine", "87211217C5FFCDDD" },
|
||||
Test{ Nz::HashType::Fletcher16, "Nazara Engine", "71D7" },
|
||||
Test{ Nz::HashType::MD5, "Nazara Engine", "71FF4EC3B56010ABC03E4B2C1C8A14B9" },
|
||||
Test{ Nz::HashType::SHA1, "Nazara Engine", "FCE7C077A1ED0881A8C60C4822BB1369B672CA5B" },
|
||||
Test{ Nz::HashType::SHA224, "Nazara Engine", "673677224E9B0D24C3828FE6CD36A37C2D43BA27C31B6E6E54756BD4" },
|
||||
Test{ Nz::HashType::SHA256, "Nazara Engine", "7E66722931B65BF780FB55D41834C3D67455029CC1CB497976D2D41A90D3FD4C" },
|
||||
Test{ Nz::HashType::SHA384, "Nazara Engine", "80064D11A4E4C2A44DE03406E03025C52641E04BA80DE78B1BB0BA6EA577B4B6914F2BDED5B95BB7285F8EA785B9B996" },
|
||||
Test{ Nz::HashType::SHA512, "Nazara Engine", "C3A8212B61B88D77E8C4B40884D49BA6A54202865CAA847F676D2EA20E60F43B1C8024DE982A214EB3670B752AF3EE37189F1EBDCA608DD0DD427D8C19371FA5" },
|
||||
Test{ Nz::HashType::Whirlpool, "Nazara Engine", "92113DC95C25057C4154E9A8B2A4C4C800D24DD22FA7D796F300AF9C4EFA4FAAB6030F66B0DC74B270A911DA18E007544B79B84440A1D58AA7C79A73C39C29F8" },
|
||||
|
||||
//Test{ Nz::HashType::CRC16, "The quick brown fox jumps over the lazy dog", "FCDF" },
|
||||
Test{ Nz::HashType::CRC32, "The quick brown fox jumps over the lazy dog", "414FA339" },
|
||||
Test{ Nz::HashType::CRC64, "The quick brown fox jumps over the lazy dog", "41E05242FFA9883B" },
|
||||
Test{ Nz::HashType::Fletcher16, "The quick brown fox jumps over the lazy dog", "FEE8" },
|
||||
Test{ Nz::HashType::MD5, "The quick brown fox jumps over the lazy dog", "9E107D9D372BB6826BD81D3542A419D6" },
|
||||
Test{ Nz::HashType::SHA1, "The quick brown fox jumps over the lazy dog", "2FD4E1C67A2D28FCED849EE1BB76E7391B93EB12" },
|
||||
Test{ Nz::HashType::SHA224, "The quick brown fox jumps over the lazy dog", "730E109BD7A8A32B1CB9D9A09AA2325D2430587DDBC0C38BAD911525" },
|
||||
Test{ Nz::HashType::SHA256, "The quick brown fox jumps over the lazy dog", "D7A8FBB307D7809469CA9ABCB0082E4F8D5651E46D3CDB762D02D0BF37C9E592" },
|
||||
Test{ Nz::HashType::SHA384, "The quick brown fox jumps over the lazy dog", "CA737F1014A48F4C0B6DD43CB177B0AFD9E5169367544C494011E3317DBF9A509CB1E5DC1E85A941BBEE3D7F2AFBC9B1" },
|
||||
Test{ Nz::HashType::SHA512, "The quick brown fox jumps over the lazy dog", "07E547D9586F6A73F73FBAC0435ED76951218FB7D0C8D788A309D785436BBB642E93A252A954F23912547D1E8A3B5ED6E1BFD7097821233FA0538F3DB854FEE6" },
|
||||
Test{ Nz::HashType::Whirlpool, "The quick brown fox jumps over the lazy dog", "B97DE512E91E3828B40D2B0FDCE9CEB3C4A71F9BEA8D88E75C4FA854DF36725FD2B52EB6544EDCACD6F8BEDDFEA403CB55AE31F03AD62A5EF54E42EE82C3FB35" },
|
||||
|
||||
//Test{ Nz::HashType::CRC16, testFilePath, "30A6" },
|
||||
Test{ Nz::HashType::CRC32, testFilePath, "5A2024CD" },
|
||||
Test{ Nz::HashType::CRC64, testFilePath, "F92157F70C713EEC" },
|
||||
Test{ Nz::HashType::Fletcher16, testFilePath, "9152" },
|
||||
Test{ Nz::HashType::MD5, testFilePath, "75B35EDB2DB8B4ED5020821E401B1FA3" },
|
||||
Test{ Nz::HashType::SHA1, testFilePath, "FD6B51F6E176AA91BDDCFAFB6D65AF97116C3981" },
|
||||
Test{ Nz::HashType::SHA224, testFilePath, "77F8536DB2AEDDB0B18747EF7907AEFFE019C3EB6C9E64CC31D2E4DA" },
|
||||
Test{ Nz::HashType::SHA256, testFilePath, "5C4B9387327C039A6CE9ED51983D6C2ADA9F9DD01D024C2D5D588237ADFC7423" },
|
||||
Test{ Nz::HashType::SHA384, testFilePath, "BB11F442F14AED77C116C6CA05103C006D40F861D23D272263BBD9F0D260FA5449B728A94F80BB807BD16778C558EF05" },
|
||||
Test{ Nz::HashType::SHA512, testFilePath, "1EA50E73C1D7D8DFCD51AC2718D7EB953E4B2D1D9EFA06F5B89DC1B0315C6C57A007D733DFA4DA41BFCE0E73446EFAF3413E8C1D38A0C327773AFF49C010F091" },
|
||||
Test{ Nz::HashType::Whirlpool, testFilePath, "A1598870C7E5C59004CD0A2C7E17248606E2DDD832EED1B0E2D52A9A72842A073CC2F889D2EC71C061A21A86879FF009D4FED1010B454FF8535C401BC9A60F64" },
|
||||
};
|
||||
|
||||
for (const Test& test : tests)
|
||||
{
|
||||
auto hash = Nz::AbstractHash::Get(test.hashType);
|
||||
|
||||
if (std::holds_alternative<const char*>(test.input))
|
||||
{
|
||||
const char* inputStr = std::get<const char*>(test.input);
|
||||
WHEN("We compute " << hash->GetHashName() << " of '" << inputStr << "'")
|
||||
{
|
||||
auto result = Nz::ComputeHash(*hash, inputStr);
|
||||
CHECK(result.GetSize() == hash->GetDigestLength());
|
||||
CHECK(Nz::ToUpper(result.ToHex()) == test.expectedOutput);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(std::holds_alternative<std::filesystem::path>(test.input));
|
||||
const std::filesystem::path& filePath = std::get<std::filesystem::path>(test.input);
|
||||
Nz::File file(filePath);
|
||||
|
||||
WHEN("We compute " << hash->GetHashName() << " of " << filePath << " file")
|
||||
{
|
||||
auto result = Nz::ComputeHash(*hash, file);
|
||||
CHECK(result.GetSize() == hash->GetDigestLength());
|
||||
CHECK(Nz::ToUpper(result.ToHex()) == test.expectedOutput);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
85
tests/UnitTests/Engine/Core/BufferingTest.cpp
Normal file
85
tests/UnitTests/Engine/Core/BufferingTest.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#include <Nazara/Core/MemoryView.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <cstring>
|
||||
|
||||
SCENARIO("Buffering", "[CORE][BUFFERING]")
|
||||
{
|
||||
const char mem[] = "abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
for (std::size_t bufferSize : { 1, 2, 3, 4, 6, 7, 0xFFFF })
|
||||
{
|
||||
Nz::MemoryView memView(mem, sizeof(mem));
|
||||
memView.EnableBuffering(true, bufferSize);
|
||||
|
||||
WHEN("Using a buffer size of " + std::to_string(bufferSize))
|
||||
{
|
||||
CHECK(memView.GetCursorPos() == 0);
|
||||
|
||||
std::vector<char> readBuffer;
|
||||
|
||||
WHEN("Reading the full buffer")
|
||||
{
|
||||
readBuffer.resize(sizeof(mem));
|
||||
REQUIRE(memView.Read(readBuffer.data(), readBuffer.size()) == readBuffer.size());
|
||||
CHECK(std::memcmp(readBuffer.data(), mem, readBuffer.size()) == 0);
|
||||
CHECK(memView.GetCursorPos() == sizeof(mem));
|
||||
}
|
||||
|
||||
WHEN("Reading 2 bytes")
|
||||
{
|
||||
readBuffer.resize(2);
|
||||
REQUIRE(memView.Read(readBuffer.data(), readBuffer.size()) == readBuffer.size());
|
||||
CHECK(std::memcmp(readBuffer.data(), "ab", readBuffer.size()) == 0);
|
||||
CHECK(memView.GetCursorPos() == 2);
|
||||
|
||||
REQUIRE(memView.Read(readBuffer.data(), readBuffer.size()) == readBuffer.size());
|
||||
CHECK(std::memcmp(readBuffer.data(), "cd", readBuffer.size()) == 0);
|
||||
CHECK(memView.GetCursorPos() == 4);
|
||||
|
||||
WHEN("Reading 4 bytes and reading")
|
||||
{
|
||||
readBuffer.resize(4);
|
||||
REQUIRE(memView.Read(readBuffer.data(), readBuffer.size()) == readBuffer.size());
|
||||
CHECK(std::memcmp(readBuffer.data(), "efgh", readBuffer.size()) == 0);
|
||||
CHECK(memView.GetCursorPos() == 8);
|
||||
|
||||
AND_WHEN("Seeking at 10")
|
||||
{
|
||||
memView.SetCursorPos(10);
|
||||
CHECK(memView.GetCursorPos() == 10);
|
||||
|
||||
readBuffer.resize(2);
|
||||
REQUIRE(memView.Read(readBuffer.data(), readBuffer.size()) == readBuffer.size());
|
||||
CHECK(std::memcmp(readBuffer.data(), "kl", readBuffer.size()) == 0);
|
||||
CHECK(memView.GetCursorPos() == 12);
|
||||
}
|
||||
|
||||
AND_WHEN("Seeking at 6 and reading")
|
||||
{
|
||||
memView.SetCursorPos(6);
|
||||
CHECK(memView.GetCursorPos() == 6);
|
||||
|
||||
readBuffer.resize(2);
|
||||
REQUIRE(memView.Read(readBuffer.data(), readBuffer.size()) == readBuffer.size());
|
||||
CHECK(std::memcmp(readBuffer.data(), "ghij", readBuffer.size()) == 0);
|
||||
CHECK(memView.GetCursorPos() == 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("Reading 6 then 2 bytes")
|
||||
{
|
||||
readBuffer.resize(6);
|
||||
REQUIRE(memView.Read(readBuffer.data(), readBuffer.size()) == readBuffer.size());
|
||||
CHECK(std::memcmp(readBuffer.data(), "abcdef", readBuffer.size()) == 0);
|
||||
CHECK(memView.GetCursorPos() == 6);
|
||||
|
||||
readBuffer.resize(2);
|
||||
REQUIRE(memView.Read(readBuffer.data(), readBuffer.size()) == readBuffer.size());
|
||||
CHECK(std::memcmp(readBuffer.data(), "gh", readBuffer.size()) == 0);
|
||||
CHECK(memView.GetCursorPos() == 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
237
tests/UnitTests/Engine/Core/ByteArrayTest.cpp
Normal file
237
tests/UnitTests/Engine/Core/ByteArrayTest.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
#include <Nazara/Core/ByteArray.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
SCENARIO("ByteArray", "[CORE][BYTEARRAY]")
|
||||
{
|
||||
GIVEN("Allocate and raw constructor")
|
||||
{
|
||||
Nz::ByteArray byteArray(3);
|
||||
|
||||
THEN("Capacity is 3 and size is 0")
|
||||
{
|
||||
REQUIRE(byteArray.GetSize() == 0);
|
||||
REQUIRE(byteArray.GetCapacity() >= 3);
|
||||
}
|
||||
|
||||
WHEN("We add 'data'")
|
||||
{
|
||||
byteArray.Append("data", 4);
|
||||
|
||||
THEN("We get 'data'")
|
||||
{
|
||||
REQUIRE(byteArray.GetSize() == 4);
|
||||
REQUIRE(byteArray.GetCapacity() >= 4);
|
||||
REQUIRE(byteArray == Nz::ByteArray("data", 4));
|
||||
REQUIRE(byteArray.ToString() == "data");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("Iterator and default constructor")
|
||||
{
|
||||
std::string anotherDataString("anotherData");
|
||||
Nz::ByteArray defaultByte;
|
||||
Nz::ByteArray anotherData(anotherDataString.begin(), anotherDataString.end());
|
||||
|
||||
WHEN("We assign 'anotherData' with iterator")
|
||||
{
|
||||
defaultByte.Assign(anotherData.begin(), anotherData.end());
|
||||
REQUIRE(anotherData == defaultByte);
|
||||
REQUIRE(defaultByte.GetSize() == 11);
|
||||
REQUIRE(defaultByte.GetCapacity() >= 11);
|
||||
REQUIRE(anotherData.GetSize() == 11);
|
||||
REQUIRE(anotherData.GetCapacity() >= 11);
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("Copy and Move constructor")
|
||||
{
|
||||
Nz::ByteArray originalArray(3, 64);
|
||||
|
||||
WHEN("We copy")
|
||||
{
|
||||
Nz::ByteArray copyByteArray(originalArray);
|
||||
|
||||
THEN("We get a copy")
|
||||
{
|
||||
REQUIRE(copyByteArray == originalArray);
|
||||
|
||||
AND_WHEN("We modify one")
|
||||
{
|
||||
for (Nz::ByteArray::size_type i = 0; i < copyByteArray.GetSize(); ++i)
|
||||
copyByteArray[i] = 46;
|
||||
|
||||
THEN("They are no more equal")
|
||||
{
|
||||
REQUIRE(copyByteArray != originalArray);
|
||||
REQUIRE(copyByteArray == Nz::ByteArray(3, 46));
|
||||
REQUIRE(copyByteArray.GetConstBuffer() != originalArray.GetConstBuffer());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We move")
|
||||
{
|
||||
Nz::ByteArray moveByteArray(std::move(originalArray));
|
||||
|
||||
THEN("These results are expected")
|
||||
{
|
||||
REQUIRE(moveByteArray == Nz::ByteArray(3, 64));
|
||||
CHECK(originalArray.IsEmpty());
|
||||
REQUIRE(originalArray.GetCapacity() == 0);
|
||||
REQUIRE(moveByteArray.GetConstBuffer() != originalArray.GetConstBuffer());
|
||||
|
||||
AND_WHEN("We modify the empty one")
|
||||
{
|
||||
originalArray.Prepend(Nz::ByteArray(3, 64));
|
||||
|
||||
THEN("They are no more equal")
|
||||
{
|
||||
REQUIRE(moveByteArray == originalArray);
|
||||
REQUIRE(moveByteArray.GetConstBuffer() != originalArray.GetConstBuffer());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("Two byte array (abc) and (cba)")
|
||||
{
|
||||
Nz::ByteArray abc("abc", 3);
|
||||
Nz::ByteArray cba;
|
||||
cba = Nz::ByteArray("cba", 3);
|
||||
|
||||
WHEN("We do some antagonists operations")
|
||||
{
|
||||
THEN("These results are expected")
|
||||
{
|
||||
REQUIRE(abc.Back() == cba.Front());
|
||||
|
||||
abc.Erase(abc.begin(), abc.begin() + 1);
|
||||
abc.Erase(abc.begin());
|
||||
|
||||
cba.Erase(cba.end() - 1, cba.end());
|
||||
cba.Erase(cba.end() - 1);
|
||||
|
||||
REQUIRE(abc == Nz::ByteArray("c", 1));
|
||||
REQUIRE(cba == Nz::ByteArray("c", 1));
|
||||
|
||||
std::string ab("ab");
|
||||
abc.Insert(abc.begin(), ab.begin(), ab.end());
|
||||
cba += Nz::ByteArray("ba", 2);
|
||||
|
||||
REQUIRE(abc == Nz::ByteArray("abc", 3));
|
||||
REQUIRE(cba == Nz::ByteArray("cba", 3));
|
||||
|
||||
abc.PopBack();
|
||||
cba.PopFront();
|
||||
|
||||
REQUIRE(abc == Nz::ByteArray("ab", 2));
|
||||
REQUIRE(cba == Nz::ByteArray("ba", 2));
|
||||
|
||||
abc.PushBack('c');
|
||||
cba.PushFront('c');
|
||||
|
||||
REQUIRE(abc == Nz::ByteArray("abc", 3));
|
||||
REQUIRE(cba == Nz::ByteArray("cba", 3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("One byte array of capacity 10")
|
||||
{
|
||||
Nz::ByteArray capacityArray(10);
|
||||
|
||||
WHEN("We reserve for 100")
|
||||
{
|
||||
capacityArray.Reserve(100);
|
||||
|
||||
THEN("Capacity is 100")
|
||||
{
|
||||
REQUIRE(capacityArray.GetCapacity() == 100);
|
||||
}
|
||||
|
||||
AND_WHEN("We add information and then shrink to fit")
|
||||
{
|
||||
capacityArray.Prepend("information", 11);
|
||||
capacityArray.ShrinkToFit();
|
||||
|
||||
THEN("Capacity is 11")
|
||||
{
|
||||
REQUIRE(capacityArray.GetCapacity() == 11);
|
||||
REQUIRE(capacityArray.GetSize() == 11);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Nz::ByteArray::const_pointer oldBuffer = capacityArray.GetConstBuffer();
|
||||
WHEN("We reserve for 5, add 'data' for 4 and then shrink to fit")
|
||||
{
|
||||
capacityArray.Reserve(5);
|
||||
THEN("Capacity is still 10")
|
||||
{
|
||||
REQUIRE(capacityArray.GetCapacity() == 10);
|
||||
REQUIRE(capacityArray.GetSize() == 0);
|
||||
REQUIRE(capacityArray.GetConstBuffer() == oldBuffer);
|
||||
}
|
||||
|
||||
capacityArray.Append("data", 4);
|
||||
capacityArray.ShrinkToFit();
|
||||
|
||||
THEN("Capacity is 4")
|
||||
{
|
||||
REQUIRE(capacityArray.GetConstBuffer() != oldBuffer);
|
||||
REQUIRE(capacityArray.GetCapacity() == 4);
|
||||
REQUIRE(capacityArray.GetSize() == 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("Three byte array")
|
||||
{
|
||||
Nz::ByteArray first("hello", 5);
|
||||
Nz::ByteArray second("world", 5);
|
||||
Nz::ByteArray third;
|
||||
|
||||
WHEN("We swap first and third, then second and third and finally third and first")
|
||||
{
|
||||
Nz::ByteArray oldFirst(first);
|
||||
Nz::ByteArray oldSecond(second);
|
||||
|
||||
first.Swap(third);
|
||||
std::swap(second, third);
|
||||
third.Swap(first);
|
||||
|
||||
THEN("First and second have been swapped and third is still empty.")
|
||||
{
|
||||
REQUIRE(oldFirst == second);
|
||||
REQUIRE(oldSecond == first);
|
||||
REQUIRE(third.IsEmpty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A default byte array")
|
||||
{
|
||||
Nz::ByteArray defaultByteArray;
|
||||
|
||||
WHEN("We resize")
|
||||
{
|
||||
Nz::UInt64 oldSize = defaultByteArray.GetSize();
|
||||
REQUIRE(oldSize == 0);
|
||||
defaultByteArray.Resize(10);
|
||||
|
||||
THEN("Capacity has increased")
|
||||
{
|
||||
REQUIRE(defaultByteArray[oldSize] == 0);
|
||||
|
||||
REQUIRE(defaultByteArray.GetCapacity() >= 10);
|
||||
REQUIRE(defaultByteArray.GetSize() == 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
74
tests/UnitTests/Engine/Core/ByteStreamTest.cpp
Normal file
74
tests/UnitTests/Engine/Core/ByteStreamTest.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#include <Nazara/Core/ByteStream.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <array>
|
||||
|
||||
SCENARIO("ByteStream", "[CORE][BYTESTREAM]")
|
||||
{
|
||||
GIVEN("A bytestream from a bunch of bytes")
|
||||
{
|
||||
const int numberOfBytes = 16;
|
||||
std::array<Nz::Int8, numberOfBytes> data;
|
||||
|
||||
Nz::ByteStream byteStream(data.data(), numberOfBytes);
|
||||
|
||||
WHEN("We write some data in it")
|
||||
{
|
||||
int value = 5;
|
||||
byteStream << value;
|
||||
std::string string = "string";
|
||||
byteStream << string;
|
||||
|
||||
byteStream.FlushBits();
|
||||
|
||||
THEN("We can retrieve them")
|
||||
{
|
||||
const void* const ptrData = data.data();
|
||||
Nz::ByteStream readStream;
|
||||
CHECK(readStream.GetSize() == 0);
|
||||
readStream = Nz::ByteStream(ptrData, byteStream.GetSize());
|
||||
int retrievedValue = 0;
|
||||
readStream >> retrievedValue;
|
||||
std::string retrievedString;
|
||||
readStream >> retrievedString;
|
||||
|
||||
CHECK(value == retrievedValue);
|
||||
CHECK(string == retrievedString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A bytestream with a byte array and a different endianness")
|
||||
{
|
||||
const int numberOfBytes = 16;
|
||||
Nz::ByteArray byteArray(numberOfBytes);
|
||||
Nz::ByteStream byteStream(&byteArray);
|
||||
|
||||
byteStream.SetDataEndianness(Nz::GetPlatformEndianness() == Nz::Endianness::BigEndian ? Nz::Endianness::LittleEndian : Nz::Endianness::BigEndian);
|
||||
|
||||
WHEN("We write an integer")
|
||||
{
|
||||
int value = 7;
|
||||
byteStream.Write(&value, sizeof(int));
|
||||
bool boolean = true;
|
||||
byteStream << boolean;
|
||||
byteStream.FlushBits();
|
||||
|
||||
THEN("We can retrieve it properly")
|
||||
{
|
||||
Nz::ByteStream tmpStream(&byteArray);
|
||||
tmpStream.SetDataEndianness(byteStream.GetDataEndianness());
|
||||
|
||||
int retrievedValue = 0;
|
||||
tmpStream.Read(&retrievedValue, sizeof(int));
|
||||
CHECK(value == retrievedValue);
|
||||
|
||||
Nz::ByteStream readStream(std::move(tmpStream));
|
||||
bool retrievedBoolean = false;
|
||||
readStream >> retrievedBoolean;
|
||||
CHECK(boolean == retrievedBoolean);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
51
tests/UnitTests/Engine/Core/ClockTest.cpp
Normal file
51
tests/UnitTests/Engine/Core/ClockTest.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#include <Nazara/Core/Clock.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
SCENARIO("Clock", "[CORE][CLOCK]")
|
||||
{
|
||||
GIVEN("A clock paused")
|
||||
{
|
||||
Nz::UInt64 initialTime = 100;
|
||||
Nz::Clock clock(initialTime, true);
|
||||
|
||||
WHEN("We get time since it is paused")
|
||||
{
|
||||
THEN("Time must be the initialTime")
|
||||
{
|
||||
CHECK(clock.GetMicroseconds() == initialTime);
|
||||
CHECK(clock.IsPaused());
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We unpause it")
|
||||
{
|
||||
clock.Unpause();
|
||||
REQUIRE(!clock.IsPaused());
|
||||
|
||||
THEN("Time must not be the initialTime")
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
Nz::UInt64 microSeconds = clock.GetMicroseconds();
|
||||
CHECK(microSeconds != initialTime);
|
||||
CHECK(microSeconds / 1000 <= clock.GetMilliseconds());
|
||||
CHECK(microSeconds / (1000.f * 1000.f) <= clock.GetSeconds());
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We restart it")
|
||||
{
|
||||
clock.Restart();
|
||||
|
||||
THEN("It is unpaused and we can pause it")
|
||||
{
|
||||
CHECK(!clock.IsPaused());
|
||||
clock.Pause();
|
||||
CHECK(clock.IsPaused());
|
||||
CHECK(clock.GetMicroseconds() != initialTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
152
tests/UnitTests/Engine/Core/ColorTest.cpp
Normal file
152
tests/UnitTests/Engine/Core/ColorTest.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
#include <Nazara/Core/Color.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
void CompareColor(const Nz::Color& lhs, const Nz::Color& rhs)
|
||||
{
|
||||
constexpr float epsilon = 0.1f;
|
||||
|
||||
REQUIRE(lhs.r == Catch::Approx(rhs.r).margin(epsilon));
|
||||
REQUIRE(lhs.g == Catch::Approx(rhs.g).margin(epsilon));
|
||||
REQUIRE(lhs.b == Catch::Approx(rhs.b).margin(epsilon));
|
||||
REQUIRE(lhs.a == Catch::Approx(rhs.a).margin(epsilon));
|
||||
}
|
||||
|
||||
constexpr float epsilon = 1.f;
|
||||
|
||||
void CompareCMY(const Nz::Color& color, float cyan, float magenta, float yellow)
|
||||
{
|
||||
float retrievedCyan = 0.f, retrievedMagenta = 0.f, retrievedYellow = 0.f;
|
||||
Nz::Color::ToCMY(color, &retrievedCyan, &retrievedMagenta, &retrievedYellow);
|
||||
CHECK(retrievedCyan == Catch::Approx(cyan).margin(epsilon));
|
||||
CHECK(retrievedMagenta == Catch::Approx(magenta).margin(epsilon));
|
||||
CHECK(retrievedYellow == Catch::Approx(yellow).margin(epsilon));
|
||||
}
|
||||
|
||||
void CompareCMYK(const Nz::Color& color, float cyan, float magenta, float yellow, float black)
|
||||
{
|
||||
float retrievedCyan = 0.f, retrievedMagenta = 0.f, retrievedYellow = 0.f, retrievedBlack = 0.f;
|
||||
Nz::Color::ToCMYK(color, &retrievedCyan, &retrievedMagenta, &retrievedYellow, &retrievedBlack);
|
||||
CHECK(retrievedCyan == Catch::Approx(cyan).margin(epsilon));
|
||||
CHECK(retrievedMagenta == Catch::Approx(magenta).margin(epsilon));
|
||||
CHECK(retrievedYellow == Catch::Approx(yellow).margin(epsilon));
|
||||
CHECK(retrievedBlack == Catch::Approx(black).margin(epsilon));
|
||||
}
|
||||
|
||||
void CompareHSL(const Nz::Color& color, float hue, float saturation, float luminosity)
|
||||
{
|
||||
float retrievedHue = 0.f, retrievedSaturation = 0.f, retrievedLuminosity = 0.f;
|
||||
Nz::Color::ToHSL(color, &retrievedHue, &retrievedSaturation, &retrievedLuminosity);
|
||||
CHECK(retrievedHue == Catch::Approx(hue).margin(epsilon));
|
||||
CHECK(retrievedSaturation == Catch::Approx(saturation).margin(epsilon));
|
||||
CHECK(retrievedLuminosity == Catch::Approx(luminosity).margin(epsilon));
|
||||
}
|
||||
|
||||
void CompareHSV(const Nz::Color& color, float hue, float saturation, float value)
|
||||
{
|
||||
float retrievedHue = 0.f, retrievedSaturation = 0.f, retrievedValue = 0.f;
|
||||
Nz::Color::ToHSV(color, &retrievedHue, &retrievedSaturation, &retrievedValue);
|
||||
CHECK(retrievedHue == Catch::Approx(hue).margin(epsilon));
|
||||
CHECK(retrievedSaturation == Catch::Approx(saturation).margin(epsilon));
|
||||
CHECK(retrievedValue == Catch::Approx(value).margin(epsilon));
|
||||
}
|
||||
|
||||
void CompareXYZ(const Nz::Color& color, float x, float y, float z)
|
||||
{
|
||||
Nz::Vector3f retrievedValues = Nz::Vector3f::Zero();
|
||||
Nz::Color::ToXYZ(color, &retrievedValues);
|
||||
CHECK(retrievedValues.x == Catch::Approx(x).margin(epsilon));
|
||||
CHECK(retrievedValues.y == Catch::Approx(y).margin(epsilon));
|
||||
CHECK(retrievedValues.z == Catch::Approx(z).margin(epsilon));
|
||||
}
|
||||
|
||||
SCENARIO("Color", "[CORE][COLOR]")
|
||||
{
|
||||
GIVEN("Two colors, one red (255) and one gray (128)")
|
||||
{
|
||||
Nz::Color red(1.f, 0.f, 0.f);
|
||||
Nz::Color grey(0.5f);
|
||||
|
||||
WHEN("We do operations")
|
||||
{
|
||||
THEN("These results are expected")
|
||||
{
|
||||
red += Nz::Color(0, 0, 0);
|
||||
grey *= Nz::Color(1.f);
|
||||
|
||||
CompareColor(red + grey, Nz::Color(1.5f, 0.5f, 0.5f, 3.f));
|
||||
CompareColor(red * grey, Nz::Color(0.5f, 0.f, 0.f, 2.f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A special color in different formats")
|
||||
{
|
||||
struct ColorData
|
||||
{
|
||||
std::string name;
|
||||
Nz::Color rgb;
|
||||
float cyan, magenta, yellow;
|
||||
float cyanK, magentaK, yellowK, black;
|
||||
float hue, saturation, luminosity;
|
||||
float hueV, saturationV, valueV;
|
||||
float x, y, z;
|
||||
};
|
||||
|
||||
std::vector<ColorData> colors;
|
||||
|
||||
colors.push_back({
|
||||
"blue",
|
||||
Nz::Color(0.f, 0.f, 1.f),
|
||||
1.f, 1.f, 0.f, // cmy
|
||||
1.f, 1.f, 0.f, 0.f, // cmyk
|
||||
240.f, 1.f, 0.5f, // hsl
|
||||
240.f, 1.f, 1.f, // hsv
|
||||
18.05f, 7.22f, 95.05f // xyz
|
||||
});
|
||||
|
||||
colors.push_back({
|
||||
"white",
|
||||
Nz::Color(1.f, 1.f, 1.f),
|
||||
0.f, 0.f, 0.f, // cmy
|
||||
0.f, 0.f, 0.f, 0.f, // cmyk
|
||||
0.f, 0.f, 1.f, // hsl
|
||||
0.f, 0.f, 1.f, // hsv
|
||||
95.05f, 100.f, 108.09f // xyz
|
||||
});
|
||||
|
||||
colors.push_back({
|
||||
"greenish",
|
||||
Nz::Color(5 / 255.f, 191.f / 255.f, 25.f / 255.f),
|
||||
0.980f, 0.251f, 0.902f, // cmy
|
||||
0.974f, 0.000f, 0.869f, 0.251f, // cmyk
|
||||
126.f, 0.95f, 0.38f, // hsl
|
||||
126.f, 0.97f, 0.75f, // hsv
|
||||
18.869f, 37.364f, 7.137f // xyz
|
||||
});
|
||||
|
||||
for (const ColorData& color : colors)
|
||||
{
|
||||
WHEN("We perform conversion for: " + color.name)
|
||||
{
|
||||
THEN("From other color spaces")
|
||||
{
|
||||
CompareColor(color.rgb, Nz::Color::FromCMY(color.cyan, color.magenta, color.yellow));
|
||||
CompareColor(color.rgb, Nz::Color::FromCMYK(color.cyanK, color.magentaK, color.yellowK, color.black));
|
||||
CompareColor(color.rgb, Nz::Color::FromHSL(color.hue, color.saturation, color.luminosity));
|
||||
CompareColor(color.rgb, Nz::Color::FromHSV(color.hueV, color.saturationV, color.valueV));
|
||||
CompareColor(color.rgb, Nz::Color::FromXYZ(Nz::Vector3f(color.x, color.y, color.z)));
|
||||
}
|
||||
|
||||
THEN("To other color spaces")
|
||||
{
|
||||
CompareCMY(color.rgb, color.cyan, color.magenta, color.yellow);
|
||||
CompareCMYK(color.rgb, color.cyanK, color.magentaK, color.yellowK, color.black);
|
||||
CompareHSL(color.rgb, color.hue, color.saturation, color.luminosity);
|
||||
CompareHSV(color.rgb, color.hueV, color.saturationV, color.valueV);
|
||||
CompareXYZ(color.rgb, color.x, color.y, color.z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
29
tests/UnitTests/Engine/Core/ErrorTest.cpp
Normal file
29
tests/UnitTests/Engine/Core/ErrorTest.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
SCENARIO("Error", "[CORE][ERROR]")
|
||||
{
|
||||
Nz::ErrorModeFlags oldFlags = Nz::Error::GetFlags();
|
||||
|
||||
GIVEN("Multiple errors")
|
||||
{
|
||||
WHEN("Calling to error")
|
||||
{
|
||||
THEN("These errors should be written in the log file")
|
||||
{
|
||||
Nz::Error::Trigger(Nz::ErrorType::Internal, "ErrorType::Internal");
|
||||
Nz::Error::Trigger(Nz::ErrorType::Internal, "ErrorType::Internal", 2, "Error.cpp", "2nd place Internal");
|
||||
REQUIRE("ErrorType::Internal" == Nz::Error::GetLastError());
|
||||
Nz::Error::Trigger(Nz::ErrorType::Normal, "ErrorType::Normal");
|
||||
Nz::Error::Trigger(Nz::ErrorType::Normal, "ErrorType::Normal", 2, "Error.cpp", "2nd place Normal");
|
||||
REQUIRE("ErrorType::Normal" == Nz::Error::GetLastError());
|
||||
Nz::Error::Trigger(Nz::ErrorType::Warning, "ErrorType::Warning");
|
||||
Nz::Error::Trigger(Nz::ErrorType::Warning, "ErrorType::Warning", 2, "Error.cpp", "2nd place Warning");
|
||||
REQUIRE("ErrorType::Warning" == Nz::Error::GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Nz::Error::SetFlags(oldFlags);
|
||||
}
|
||||
84
tests/UnitTests/Engine/Core/FileTest.cpp
Normal file
84
tests/UnitTests/Engine/Core/FileTest.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
#include <Nazara/Core/File.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
std::filesystem::path GetAssetDir();
|
||||
|
||||
SCENARIO("File", "[CORE][FILE]")
|
||||
{
|
||||
GIVEN("One file")
|
||||
{
|
||||
WHEN("We create a new file")
|
||||
{
|
||||
Nz::File file("Test File.txt", Nz::OpenMode_ReadWrite);
|
||||
REQUIRE(file.GetDirectory() == std::filesystem::current_path());
|
||||
REQUIRE(file.IsOpen());
|
||||
|
||||
THEN("We are allowed to write 3 times 'Test String'")
|
||||
{
|
||||
const char message[12] = "Test String"; // 11 + '\0'
|
||||
Nz::ByteArray byteArray(message, 11);
|
||||
file.Write("Test String");
|
||||
file.Write(byteArray);
|
||||
file.Write(message, 11);
|
||||
}
|
||||
|
||||
AND_THEN("We can retrieve 3 times 'Test String'")
|
||||
{
|
||||
char message[12];
|
||||
REQUIRE(file.Read(message, 11) == 11);
|
||||
message[11] = '\0';
|
||||
REQUIRE(std::string(message) == "Test String");
|
||||
|
||||
REQUIRE(file.Read(message, 11) == 11);
|
||||
message[11] = '\0';
|
||||
REQUIRE(std::string(message) == "Test String");
|
||||
}
|
||||
|
||||
AND_THEN("We can get its size")
|
||||
{
|
||||
REQUIRE(file.GetSize() == 33U);
|
||||
}
|
||||
|
||||
AND_THEN("We close it")
|
||||
{
|
||||
file.Close();
|
||||
CHECK(!file.IsOpen());
|
||||
}
|
||||
|
||||
AND_THEN("Change its size")
|
||||
{
|
||||
file.SetSize(50U);
|
||||
REQUIRE(file.GetSize() == 50U);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We delete this file")
|
||||
{
|
||||
std::filesystem::remove("Test File.txt");
|
||||
|
||||
THEN("It doesn't exist anymore")
|
||||
{
|
||||
CHECK(!std::filesystem::exists("Test File.txt"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("The test file")
|
||||
{
|
||||
REQUIRE(std::filesystem::exists(GetAssetDir() / "Core/FileTest.txt"));
|
||||
|
||||
Nz::File fileTest(GetAssetDir() / "Core/FileTest.txt", Nz::OpenMode::ReadOnly | Nz::OpenMode::Text);
|
||||
|
||||
WHEN("We read the first line of the file")
|
||||
{
|
||||
REQUIRE(fileTest.IsOpen());
|
||||
std::string content = fileTest.ReadLine();
|
||||
|
||||
THEN("The content must be 'Test'")
|
||||
{
|
||||
REQUIRE(content == "Test");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
141
tests/UnitTests/Engine/Core/ObjectHandleTest.cpp
Normal file
141
tests/UnitTests/Engine/Core/ObjectHandleTest.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
#include <Nazara/Core/HandledObject.hpp>
|
||||
#include <Nazara/Core/ObjectHandle.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
struct ObjectHandle_Test : public Nz::HandledObject<ObjectHandle_Test>
|
||||
{
|
||||
ObjectHandle_Test(int value) :
|
||||
i(value)
|
||||
{
|
||||
}
|
||||
|
||||
int i;
|
||||
};
|
||||
|
||||
SCENARIO("Handle", "[CORE][HandledObject][ObjectHandle]")
|
||||
{
|
||||
GIVEN("One test with two handles")
|
||||
{
|
||||
int defaultValue = 1;
|
||||
ObjectHandle_Test test(defaultValue);
|
||||
|
||||
Nz::ObjectHandle<ObjectHandle_Test> handle1 = test.CreateHandle();
|
||||
Nz::ObjectHandle<ObjectHandle_Test> handle2 = test.CreateHandle();
|
||||
|
||||
WHEN("We modify from one")
|
||||
{
|
||||
const int newI = 2;
|
||||
handle1->i = newI;
|
||||
|
||||
THEN("The other one should also be modified")
|
||||
{
|
||||
REQUIRE(handle2->i == newI);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We copy construct")
|
||||
{
|
||||
ObjectHandle_Test other(test);
|
||||
Nz::ObjectHandle<ObjectHandle_Test> otherHandle = other.CreateHandle();
|
||||
|
||||
THEN("Handles should point to 1")
|
||||
{
|
||||
CHECK(handle1->i == defaultValue);
|
||||
CHECK(handle2->i == defaultValue);
|
||||
CHECK(otherHandle->i == defaultValue);
|
||||
CHECK(handle2.GetObject() == &test);
|
||||
CHECK(otherHandle.GetObject() == &other);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We move construct")
|
||||
{
|
||||
ObjectHandle_Test other(std::move(test));
|
||||
Nz::ObjectHandle<ObjectHandle_Test> otherHandle = other.CreateHandle();
|
||||
|
||||
THEN("Handles should point to 1")
|
||||
{
|
||||
CHECK(handle1->i == defaultValue);
|
||||
CHECK(handle2->i == defaultValue);
|
||||
CHECK(otherHandle->i == defaultValue);
|
||||
CHECK(handle1.GetObject() == &other);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We copy assign")
|
||||
{
|
||||
int copyValue = 3;
|
||||
ObjectHandle_Test other(copyValue);
|
||||
Nz::ObjectHandle<ObjectHandle_Test> otherHandle = other.CreateHandle();
|
||||
test = other;
|
||||
|
||||
THEN("Handles should point to 3")
|
||||
{
|
||||
CHECK(handle1->i == copyValue);
|
||||
CHECK(handle2->i == copyValue);
|
||||
CHECK(otherHandle->i == copyValue);
|
||||
CHECK(handle1.GetObject() == &test);
|
||||
CHECK(otherHandle.GetObject() == &other);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We move assign")
|
||||
{
|
||||
int moveValue = 4;
|
||||
ObjectHandle_Test other(moveValue);
|
||||
Nz::ObjectHandle<ObjectHandle_Test> otherHandle = other.CreateHandle();
|
||||
test = std::move(other);
|
||||
|
||||
THEN("Handles to previous objects should be invalid")
|
||||
{
|
||||
CHECK_FALSE(handle1.IsValid());
|
||||
CHECK_FALSE(handle2.IsValid());
|
||||
}
|
||||
|
||||
THEN("Handles should point to 4")
|
||||
{
|
||||
CHECK(otherHandle.GetObject() == &test);
|
||||
CHECK(otherHandle->i == moveValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("One handle pointing to a default test")
|
||||
{
|
||||
ObjectHandle_Test test(1);
|
||||
Nz::ObjectHandle<ObjectHandle_Test> invalidHandle(&test);
|
||||
|
||||
WHEN("We bind it to a HandledObject which is going to die")
|
||||
{
|
||||
{
|
||||
ObjectHandle_Test dyingTest(5);
|
||||
invalidHandle.Reset(&dyingTest);
|
||||
}
|
||||
|
||||
THEN("It should not be valid")
|
||||
{
|
||||
REQUIRE(!invalidHandle.IsValid());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("Two handle pointing to two different tests")
|
||||
{
|
||||
ObjectHandle_Test test1(1);
|
||||
Nz::ObjectHandle<ObjectHandle_Test> test1Handle = test1.CreateHandle();
|
||||
ObjectHandle_Test test2(2);
|
||||
Nz::ObjectHandle<ObjectHandle_Test> test2Handle = test2.CreateHandle();
|
||||
|
||||
WHEN("We swap their content")
|
||||
{
|
||||
test1Handle.Swap(test2Handle);
|
||||
|
||||
THEN("They should be pointing to the correct one")
|
||||
{
|
||||
CHECK(test1Handle.GetObject() == &test2);
|
||||
CHECK(test2Handle.GetObject() == &test1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
44
tests/UnitTests/Engine/Core/ObjectRefTest.cpp
Normal file
44
tests/UnitTests/Engine/Core/ObjectRefTest.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include <Nazara/Core/ObjectRef.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
class Test : public Nz::RefCounted
|
||||
{
|
||||
};
|
||||
|
||||
SCENARIO("ObjectRef", "[CORE][OBJECTREF]")
|
||||
{
|
||||
GIVEN("A ObjectRef")
|
||||
{
|
||||
Nz::ObjectRef<Test> objectRef;
|
||||
|
||||
WHEN("We have two objectRef handling the same object")
|
||||
{
|
||||
Test test;
|
||||
|
||||
objectRef = &test;
|
||||
Nz::ObjectRef<Test> otherRef(&test);
|
||||
|
||||
THEN("Pointers the same")
|
||||
{
|
||||
REQUIRE(objectRef.IsValid());
|
||||
REQUIRE(otherRef.IsValid());
|
||||
}
|
||||
|
||||
objectRef.Reset(nullptr);
|
||||
}
|
||||
|
||||
WHEN("We assign it to a simple font")
|
||||
{
|
||||
Test test;
|
||||
|
||||
THEN("Release suppress the reference to the object")
|
||||
{
|
||||
objectRef.Reset(&test);
|
||||
objectRef.Release();
|
||||
|
||||
REQUIRE(!objectRef.IsValid());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
247
tests/UnitTests/Engine/Core/ParameterListTest.cpp
Normal file
247
tests/UnitTests/Engine/Core/ParameterListTest.cpp
Normal file
@@ -0,0 +1,247 @@
|
||||
#include <Nazara/Core/ParameterList.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
void nullAction(void*)
|
||||
{
|
||||
}
|
||||
|
||||
SCENARIO("ParameterList", "[CORE][PARAMETERLIST]")
|
||||
{
|
||||
GIVEN("An empty ParameterList")
|
||||
{
|
||||
Nz::ParameterList parameterList;
|
||||
|
||||
WHEN("We add Bool 'true' and analogous")
|
||||
{
|
||||
bool boolean = true;
|
||||
parameterList.SetParameter("bool", boolean);
|
||||
|
||||
long long intTrue = 1;
|
||||
parameterList.SetParameter("intTrue", intTrue);
|
||||
long long intFalse = 0;
|
||||
parameterList.SetParameter("intFalse", intFalse);
|
||||
|
||||
std::string strTrue = "true";
|
||||
parameterList.SetParameter("strTrue", strTrue);
|
||||
std::string strFalse = "false";
|
||||
parameterList.SetParameter("strFalse", strFalse);
|
||||
|
||||
THEN("We can get it back")
|
||||
{
|
||||
CHECK(parameterList.GetBooleanParameter("bool").GetValue() == true);
|
||||
}
|
||||
|
||||
THEN("Conversion from int to bool should also work if strict mode is disabled")
|
||||
{
|
||||
CHECK(parameterList.GetBooleanParameter("intTrue", false).GetValue() == true);
|
||||
CHECK(parameterList.GetBooleanParameter("intFalse", false).GetValue() == false);
|
||||
}
|
||||
|
||||
THEN("Conversion from str to bool should also work if strict mode is disabled")
|
||||
{
|
||||
CHECK(parameterList.GetBooleanParameter("strTrue", false).GetValue() == true);
|
||||
CHECK(parameterList.GetBooleanParameter("strFalse", false).GetValue() == false);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We add Color 'rgb(1, 2, 3)'")
|
||||
{
|
||||
Nz::Color rgb(1, 2, 3);
|
||||
parameterList.SetParameter("color", rgb);
|
||||
|
||||
THEN("We can get it back")
|
||||
{
|
||||
CHECK(parameterList.GetColorParameter("color").GetValue() == rgb);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We add Double '3.0' and analogous")
|
||||
{
|
||||
double fl = 3.0;
|
||||
parameterList.SetParameter("double", fl);
|
||||
|
||||
long long intDouble = 3;
|
||||
parameterList.SetParameter("intDouble", intDouble);
|
||||
|
||||
std::string strDouble = "3.0";
|
||||
parameterList.SetParameter("strDouble", strDouble);
|
||||
|
||||
THEN("We can get it back")
|
||||
{
|
||||
CHECK(parameterList.GetDoubleParameter("double").GetValue() == fl);
|
||||
}
|
||||
|
||||
THEN("Conversion from int to double should also work if strict mode is disabled")
|
||||
{
|
||||
CHECK(parameterList.GetDoubleParameter("intDouble", false).GetValue() == fl);
|
||||
}
|
||||
|
||||
THEN("Conversion from string to double should also work if strict mode is disabled")
|
||||
{
|
||||
CHECK(parameterList.GetDoubleParameter("strDouble", false).GetValue() == fl);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We add Int '3' and analogous")
|
||||
{
|
||||
long long i = 3;
|
||||
parameterList.SetParameter("int", i);
|
||||
|
||||
bool trueInt = 1;
|
||||
parameterList.SetParameter("trueInt", trueInt);
|
||||
bool falseInt = 0;
|
||||
parameterList.SetParameter("falseInt", falseInt);
|
||||
|
||||
double doubleInt = 3;
|
||||
parameterList.SetParameter("doubleInt", doubleInt);
|
||||
|
||||
std::string strInt = "3";
|
||||
parameterList.SetParameter("strInt", strInt);
|
||||
|
||||
THEN("We can get it back")
|
||||
{
|
||||
CHECK(parameterList.GetIntegerParameter("int").GetValue() == i);
|
||||
}
|
||||
|
||||
THEN("Conversion from bool to int should also work if strict mode is disabled")
|
||||
{
|
||||
CHECK(parameterList.GetIntegerParameter("trueInt", false).GetValue() == trueInt);
|
||||
CHECK(parameterList.GetIntegerParameter("falseInt", false).GetValue() == falseInt);
|
||||
}
|
||||
|
||||
THEN("Conversion from double to int should also work if strict mode is disabled")
|
||||
{
|
||||
CHECK(parameterList.GetIntegerParameter("doubleInt", false).GetValue() == i);
|
||||
}
|
||||
|
||||
THEN("Conversion from string to int should also work if strict mode is disabled")
|
||||
{
|
||||
CHECK(parameterList.GetIntegerParameter("strInt", false).GetValue() == i);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We add String 'string' and analogous")
|
||||
{
|
||||
std::string string("string");
|
||||
parameterList.SetParameter("string", string);
|
||||
|
||||
bool trueString = 1;
|
||||
parameterList.SetParameter("trueString", trueString);
|
||||
bool falseString = 0;
|
||||
parameterList.SetParameter("falseString", falseString);
|
||||
|
||||
Nz::Color colorString(1, 2, 3);
|
||||
parameterList.SetParameter("colorString", colorString);
|
||||
|
||||
double doubleString = 3.0;
|
||||
parameterList.SetParameter("doubleString", doubleString);
|
||||
|
||||
long long intString = 3;
|
||||
parameterList.SetParameter("intString", intString);
|
||||
|
||||
THEN("We can get it back")
|
||||
{
|
||||
CHECK(parameterList.GetStringParameter("string").GetValue() == string);
|
||||
CHECK(parameterList.GetStringViewParameter("string").GetValue() == string);
|
||||
}
|
||||
|
||||
THEN("Conversion from bool to str should also work if strict mode is disabled")
|
||||
{
|
||||
CHECK(parameterList.GetStringParameter("trueString", false).GetValue() == "true");
|
||||
CHECK(parameterList.GetStringParameter("falseString", false).GetValue() == "false");
|
||||
CHECK(parameterList.GetStringViewParameter("trueString", false).GetValue() == "true");
|
||||
CHECK(parameterList.GetStringViewParameter("falseString", false).GetValue() == "false");
|
||||
}
|
||||
|
||||
THEN("Conversion from color to string should also work if strict mode is disabled")
|
||||
{
|
||||
CHECK(parameterList.GetStringParameter("colorString", false).GetValue() == colorString.ToString());
|
||||
}
|
||||
|
||||
THEN("Conversion from string to double should also work if strict mode is disabled")
|
||||
{
|
||||
CHECK(parameterList.GetStringParameter("doubleString", false).GetValue() == "3.000000");
|
||||
}
|
||||
|
||||
THEN("Conversion from string to int should also work if strict mode is disabled")
|
||||
{
|
||||
CHECK(parameterList.GetStringParameter("intString", false).GetValue() == "3");
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We add Pointer to stack value")
|
||||
{
|
||||
int stackValue = 3;
|
||||
void* ptrToStackValue = &stackValue; // Ugly conversion
|
||||
parameterList.SetParameter("ptr", ptrToStackValue);
|
||||
|
||||
THEN("We can get it back")
|
||||
{
|
||||
CHECK(parameterList.GetPointerParameter("ptr").GetValue() == ptrToStackValue);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We set our own data")
|
||||
{
|
||||
struct Data {
|
||||
int i;
|
||||
float f;
|
||||
};
|
||||
|
||||
Data data{ 1, 3.f };
|
||||
parameterList.SetParameter("userData", &data, nullAction);
|
||||
|
||||
THEN("We can get it back")
|
||||
{
|
||||
void* ptrToData;
|
||||
|
||||
CHECK_NOTHROW(ptrToData = parameterList.GetUserdataParameter("userData").GetValue());
|
||||
Data* dataPtr = static_cast<Data*>(ptrToData);
|
||||
CHECK(dataPtr->i == data.i);
|
||||
CHECK(dataPtr->f == data.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A parameter list with some values")
|
||||
{
|
||||
Nz::ParameterList parameterList;
|
||||
|
||||
long long i = 3;
|
||||
parameterList.SetParameter("i", i);
|
||||
double d = 1.0;
|
||||
parameterList.SetParameter("d", d);
|
||||
|
||||
parameterList.SetParameter("toaster");
|
||||
parameterList.SetParameter("str", "ing");
|
||||
|
||||
WHEN("We remove two elements")
|
||||
{
|
||||
CHECK(parameterList.HasParameter("i"));
|
||||
CHECK(parameterList.HasParameter("toaster"));
|
||||
|
||||
parameterList.RemoveParameter("i");
|
||||
parameterList.RemoveParameter("toaster");
|
||||
|
||||
THEN("They do not exist anymore")
|
||||
{
|
||||
CHECK(!parameterList.HasParameter("i"));
|
||||
CHECK(!parameterList.HasParameter("toaster"));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We copy this list")
|
||||
{
|
||||
Nz::ParameterList copy = parameterList;
|
||||
|
||||
THEN("It has the same elements")
|
||||
{
|
||||
CHECK(parameterList.HasParameter("i"));
|
||||
CHECK(parameterList.HasParameter("d"));
|
||||
CHECK(parameterList.HasParameter("toaster"));
|
||||
CHECK(parameterList.HasParameter("str"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
44
tests/UnitTests/Engine/Core/PrimitiveListTest.cpp
Normal file
44
tests/UnitTests/Engine/Core/PrimitiveListTest.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include <Nazara/Core/PrimitiveList.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
SCENARIO("PrimitiveList", "[CORE][PRIMITIVELIST]")
|
||||
{
|
||||
GIVEN("An empty PrimitiveList")
|
||||
{
|
||||
Nz::PrimitiveList primitiveList;
|
||||
|
||||
WHEN("We add two primitives")
|
||||
{
|
||||
float size = 1.f;
|
||||
unsigned int subdivision = 1;
|
||||
Nz::Matrix4f identity = Nz::Matrix4f::Identity();
|
||||
|
||||
primitiveList.AddCubicSphere(size, subdivision, identity);
|
||||
primitiveList.AddBox(Nz::Vector3f(size), Nz::Vector3ui(subdivision), identity);
|
||||
primitiveList.AddIcoSphere(size, subdivision, identity);
|
||||
|
||||
THEN("There must be two items")
|
||||
{
|
||||
REQUIRE(primitiveList.GetSize() == 3);
|
||||
}
|
||||
|
||||
THEN("The first one is the cubic sphere")
|
||||
{
|
||||
REQUIRE(primitiveList(0).type == Nz::PrimitiveType::Sphere);
|
||||
REQUIRE(primitiveList(0).sphere.type == Nz::SphereType::Cubic);
|
||||
}
|
||||
|
||||
THEN("The second one is the box")
|
||||
{
|
||||
REQUIRE(primitiveList(1).type == Nz::PrimitiveType::Box);
|
||||
}
|
||||
|
||||
THEN("The third one is the ico sphere")
|
||||
{
|
||||
REQUIRE(primitiveList(2).type == Nz::PrimitiveType::Sphere);
|
||||
REQUIRE(primitiveList(2).sphere.type == Nz::SphereType::Ico);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
tests/UnitTests/Engine/Core/RefCountedTest.cpp
Normal file
30
tests/UnitTests/Engine/Core/RefCountedTest.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include <Nazara/Core/RefCounted.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
SCENARIO("RefCounted", "[CORE][REFCOUNTED]")
|
||||
{
|
||||
GIVEN("A refcounted persistent")
|
||||
{
|
||||
Nz::RefCounted refCounted;
|
||||
REQUIRE(refCounted.IsPersistent() == true);
|
||||
|
||||
WHEN("We add a reference to this persistent object")
|
||||
{
|
||||
THEN("Number of references should be one")
|
||||
{
|
||||
refCounted.AddReference();
|
||||
REQUIRE(refCounted.GetReferenceCount() == 1);
|
||||
REQUIRE(refCounted.RemoveReference() == false);
|
||||
}
|
||||
|
||||
AND_THEN("We suppress the reference, object is still alive")
|
||||
{
|
||||
refCounted.AddReference();
|
||||
REQUIRE(refCounted.IsPersistent());
|
||||
REQUIRE(refCounted.RemoveReference() == false);
|
||||
REQUIRE(refCounted.GetReferenceCount() == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
263
tests/UnitTests/Engine/Core/SerializationTest.cpp
Normal file
263
tests/UnitTests/Engine/Core/SerializationTest.cpp
Normal file
@@ -0,0 +1,263 @@
|
||||
#include <Nazara/Core/SerializationContext.hpp>
|
||||
|
||||
#include <Nazara/Core/Color.hpp>
|
||||
#include <Nazara/Core/MemoryView.hpp>
|
||||
#include <Nazara/Math/BoundingVolume.hpp>
|
||||
#include <Nazara/Math/Frustum.hpp>
|
||||
#include <Nazara/Math/Ray.hpp>
|
||||
#include <array>
|
||||
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
SCENARIO("Serialization", "[CORE][SERIALIZATION]")
|
||||
{
|
||||
GIVEN("A context of serialization")
|
||||
{
|
||||
std::array<char, 256> datas; // The array must be bigger than any of the serializable classes
|
||||
Nz::MemoryView stream(datas.data(), datas.size());
|
||||
|
||||
Nz::SerializationContext context;
|
||||
context.stream = &stream;
|
||||
|
||||
WHEN("We serialize basic types")
|
||||
{
|
||||
THEN("Arithmetical types")
|
||||
{
|
||||
context.stream->SetCursorPos(0);
|
||||
REQUIRE(Serialize(context, 3));
|
||||
int value = 0;
|
||||
context.stream->SetCursorPos(0);
|
||||
REQUIRE(Unserialize(context, &value));
|
||||
REQUIRE(value == 3);
|
||||
}
|
||||
|
||||
THEN("Boolean type")
|
||||
{
|
||||
context.stream->SetCursorPos(0);
|
||||
REQUIRE(Serialize(context, true));
|
||||
context.FlushBits(); //< Don't forget to flush bits (it is NOT done by the stream)
|
||||
context.stream->SetCursorPos(0);
|
||||
bool value = false;
|
||||
REQUIRE(Unserialize(context, &value));
|
||||
REQUIRE(value == true);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We serialize mathematical classes")
|
||||
{
|
||||
THEN("BoudingVolume")
|
||||
{
|
||||
context.stream->SetCursorPos(0);
|
||||
Nz::BoundingVolumef nullVolume = Nz::BoundingVolumef::Null();
|
||||
Nz::BoundingVolumef copy(nullVolume);
|
||||
REQUIRE(Serialize(context, nullVolume));
|
||||
nullVolume = Nz::BoundingVolumef::Infinite();
|
||||
REQUIRE(nullVolume != copy);
|
||||
context.stream->SetCursorPos(0);
|
||||
REQUIRE(Unserialize(context, &nullVolume));
|
||||
REQUIRE(nullVolume == copy);
|
||||
}
|
||||
|
||||
THEN("Box")
|
||||
{
|
||||
context.stream->SetCursorPos(0);
|
||||
Nz::Boxf zeroBox = Nz::Boxf::Zero();
|
||||
Nz::Boxf copy(zeroBox);
|
||||
REQUIRE(Serialize(context, zeroBox));
|
||||
zeroBox = Nz::Boxf(1, 1, 1, 1, 1, 1);
|
||||
REQUIRE(zeroBox != copy);
|
||||
context.stream->SetCursorPos(0);
|
||||
REQUIRE(Unserialize(context, &zeroBox));
|
||||
REQUIRE(zeroBox == copy);
|
||||
}
|
||||
|
||||
THEN("EulerAngles")
|
||||
{
|
||||
context.stream->SetCursorPos(0);
|
||||
Nz::EulerAnglesf zeroEuler = Nz::EulerAnglesf::Zero();
|
||||
Nz::EulerAnglesf copy(zeroEuler);
|
||||
REQUIRE(Serialize(context, zeroEuler));
|
||||
zeroEuler = Nz::EulerAnglesf(10, 24, 6); // Random values
|
||||
REQUIRE(zeroEuler != copy);
|
||||
context.stream->SetCursorPos(0);
|
||||
REQUIRE(Unserialize(context, &zeroEuler));
|
||||
REQUIRE(zeroEuler == copy);
|
||||
}
|
||||
|
||||
THEN("Frustum")
|
||||
{
|
||||
context.stream->SetCursorPos(0);
|
||||
Nz::Frustumf frustum = Nz::Frustumf::Build(10, 10, 10, 100, Nz::Vector3f::UnitX(), Nz::Vector3f::UnitZ()); // Random values
|
||||
Nz::Frustumf copy(frustum);
|
||||
REQUIRE(Serialize(context, frustum));
|
||||
frustum = Nz::Frustumf::Build(50, 40, 20, 100, Nz::Vector3f::UnitX(), Nz::Vector3f::UnitZ());
|
||||
for (std::size_t i = 0; i < Nz::FrustumPlaneCount; ++i)
|
||||
REQUIRE(frustum.GetPlane(static_cast<Nz::FrustumPlane>(i)) != copy.GetPlane(static_cast<Nz::FrustumPlane>(i)));
|
||||
context.stream->SetCursorPos(0);
|
||||
REQUIRE(Unserialize(context, &frustum));
|
||||
for (std::size_t i = 0; i < Nz::FrustumPlaneCount; ++i)
|
||||
REQUIRE(frustum.GetPlane(static_cast<Nz::FrustumPlane>(i)) == copy.GetPlane(static_cast<Nz::FrustumPlane>(i)));
|
||||
}
|
||||
|
||||
THEN("Matrix4")
|
||||
{
|
||||
context.stream->SetCursorPos(0);
|
||||
Nz::Matrix4f zeroMatrix = Nz::Matrix4f::Zero();
|
||||
Nz::Matrix4f copy(zeroMatrix);
|
||||
REQUIRE(Serialize(context, zeroMatrix));
|
||||
zeroMatrix = Nz::Matrix4f::Identity(); // Random values
|
||||
REQUIRE(zeroMatrix != copy);
|
||||
context.stream->SetCursorPos(0);
|
||||
REQUIRE(Unserialize(context, &zeroMatrix));
|
||||
REQUIRE(zeroMatrix == copy);
|
||||
}
|
||||
|
||||
THEN("OrientedBox")
|
||||
{
|
||||
context.stream->SetCursorPos(0);
|
||||
Nz::OrientedBoxf zeroOBB = Nz::OrientedBoxf::Zero();
|
||||
Nz::OrientedBoxf copy(zeroOBB);
|
||||
REQUIRE(Serialize(context, zeroOBB));
|
||||
zeroOBB = Nz::OrientedBoxf(Nz::Boxf(1, 1, 1, 1, 1, 1)); // Random values
|
||||
REQUIRE(zeroOBB != copy);
|
||||
context.stream->SetCursorPos(0);
|
||||
REQUIRE(Unserialize(context, &zeroOBB));
|
||||
REQUIRE(zeroOBB == copy);
|
||||
}
|
||||
|
||||
THEN("Plane")
|
||||
{
|
||||
context.stream->SetCursorPos(0);
|
||||
Nz::Planef planeXY = Nz::Planef::XY();
|
||||
Nz::Planef copy(planeXY);
|
||||
REQUIRE(Serialize(context, planeXY));
|
||||
planeXY = Nz::Planef::YZ();
|
||||
REQUIRE(planeXY != copy);
|
||||
context.stream->SetCursorPos(0);
|
||||
REQUIRE(Unserialize(context, &planeXY));
|
||||
REQUIRE(planeXY == copy);
|
||||
}
|
||||
|
||||
THEN("Quaternion")
|
||||
{
|
||||
context.stream->SetCursorPos(0);
|
||||
Nz::Quaternionf quaternionIdentity = Nz::Quaternionf::Identity();
|
||||
Nz::Quaternionf copy(quaternionIdentity);
|
||||
REQUIRE(Serialize(context, quaternionIdentity));
|
||||
quaternionIdentity = Nz::Quaternionf::Zero();
|
||||
REQUIRE(quaternionIdentity != copy);
|
||||
context.stream->SetCursorPos(0);
|
||||
REQUIRE(Unserialize(context, &quaternionIdentity));
|
||||
REQUIRE(quaternionIdentity == copy);
|
||||
}
|
||||
|
||||
THEN("Ray")
|
||||
{
|
||||
context.stream->SetCursorPos(0);
|
||||
Nz::Rayf axisX = Nz::Rayf::AxisX();
|
||||
Nz::Rayf copy(axisX);
|
||||
REQUIRE(Serialize(context, axisX));
|
||||
axisX = Nz::Rayf::AxisY();
|
||||
REQUIRE(axisX != copy);
|
||||
context.stream->SetCursorPos(0);
|
||||
REQUIRE(Unserialize(context, &axisX));
|
||||
REQUIRE(axisX == copy);
|
||||
}
|
||||
|
||||
THEN("Rect")
|
||||
{
|
||||
context.stream->SetCursorPos(0);
|
||||
Nz::Rectf zeroRect = Nz::Rectf::Zero();
|
||||
Nz::Rectf copy(zeroRect);
|
||||
REQUIRE(Serialize(context, zeroRect));
|
||||
zeroRect = Nz::Rectf(1, 1, 1, 1); // Random values
|
||||
REQUIRE(zeroRect != copy);
|
||||
context.stream->SetCursorPos(0);
|
||||
REQUIRE(Unserialize(context, &zeroRect));
|
||||
REQUIRE(zeroRect == copy);
|
||||
}
|
||||
|
||||
THEN("Sphere")
|
||||
{
|
||||
context.stream->SetCursorPos(0);
|
||||
Nz::Spheref zeroSphere = Nz::Spheref::Zero();
|
||||
Nz::Spheref copy(zeroSphere);
|
||||
REQUIRE(Serialize(context, zeroSphere));
|
||||
zeroSphere = Nz::Spheref::Unit();
|
||||
REQUIRE(zeroSphere != copy);
|
||||
context.stream->SetCursorPos(0);
|
||||
REQUIRE(Unserialize(context, &zeroSphere));
|
||||
REQUIRE(zeroSphere == copy);
|
||||
}
|
||||
|
||||
THEN("Vector2")
|
||||
{
|
||||
context.stream->SetCursorPos(0);
|
||||
Nz::Vector2f unitX = Nz::Vector2f::UnitX();
|
||||
Nz::Vector2f copy(unitX);
|
||||
REQUIRE(Serialize(context, unitX));
|
||||
unitX = Nz::Vector2f::UnitY();
|
||||
REQUIRE(unitX != copy);
|
||||
context.stream->SetCursorPos(0);
|
||||
REQUIRE(Unserialize(context, &unitX));
|
||||
REQUIRE(unitX == copy);
|
||||
}
|
||||
|
||||
THEN("Vector3")
|
||||
{
|
||||
context.stream->SetCursorPos(0);
|
||||
Nz::Vector3f unitX = Nz::Vector3f::UnitX();
|
||||
Nz::Vector3f copy(unitX);
|
||||
REQUIRE(Serialize(context, unitX));
|
||||
unitX = Nz::Vector3f::UnitY();
|
||||
REQUIRE(unitX != copy);
|
||||
context.stream->SetCursorPos(0);
|
||||
REQUIRE(Unserialize(context, &unitX));
|
||||
REQUIRE(unitX == copy);
|
||||
}
|
||||
|
||||
THEN("Vector4")
|
||||
{
|
||||
context.stream->SetCursorPos(0);
|
||||
Nz::Vector4f unitX = Nz::Vector4f::UnitX();
|
||||
Nz::Vector4f copy(unitX);
|
||||
REQUIRE(Serialize(context, unitX));
|
||||
unitX = Nz::Vector4f::UnitY();
|
||||
REQUIRE(unitX != copy);
|
||||
context.stream->SetCursorPos(0);
|
||||
REQUIRE(Unserialize(context, &unitX));
|
||||
REQUIRE(unitX == copy);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We serialize core classes")
|
||||
{
|
||||
THEN("Color")
|
||||
{
|
||||
context.stream->SetCursorPos(0);
|
||||
Nz::Color red = Nz::Color::Red;
|
||||
Nz::Color copy(red);
|
||||
REQUIRE(Serialize(context, red));
|
||||
red = Nz::Color::Black;
|
||||
REQUIRE(red != copy);
|
||||
context.stream->SetCursorPos(0);
|
||||
REQUIRE(Unserialize(context, &red));
|
||||
REQUIRE(red == copy);
|
||||
}
|
||||
|
||||
THEN("String")
|
||||
{
|
||||
context.stream->SetCursorPos(0);
|
||||
std::string string = "string";
|
||||
std::string copy(string);
|
||||
REQUIRE(Serialize(context, string));
|
||||
string = "another";
|
||||
REQUIRE(string != copy);
|
||||
context.stream->SetCursorPos(0);
|
||||
REQUIRE(Unserialize(context, &string));
|
||||
REQUIRE(string == copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
154
tests/UnitTests/Engine/Core/StringExtTest.cpp
Normal file
154
tests/UnitTests/Engine/Core/StringExtTest.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
#include <Nazara/Core/StringExt.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
SCENARIO("String", "[CORE][STRING]")
|
||||
{
|
||||
std::string unicodeString(u8"\u00E0\u00E9\u00E7\u0153\u00C2\u5B98\u46E1");
|
||||
|
||||
WHEN("Checking if string ends with")
|
||||
{
|
||||
CHECK(Nz::EndsWith("", ""));
|
||||
CHECK(Nz::EndsWith("Nazara Engine", ""));
|
||||
CHECK(Nz::EndsWith("Nazara Engine", "Engine"));
|
||||
CHECK_FALSE(Nz::EndsWith("Nazara Engine", "engine"));
|
||||
CHECK_FALSE(Nz::EndsWith("Nazara Engine", " ngine"));
|
||||
CHECK_FALSE(Nz::EndsWith("Nazara Engine", "NazaraEngine"));
|
||||
CHECK_FALSE(Nz::EndsWith("Nazara Engine", "Nazara"));
|
||||
CHECK_FALSE(Nz::EndsWith("Nazara Engine", "Sir Nazara van Engine"));
|
||||
|
||||
CHECK(Nz::EndsWith("", "", Nz::CaseIndependent{}));
|
||||
CHECK(Nz::EndsWith("Nazara Engine", "", Nz::CaseIndependent{}));
|
||||
CHECK(Nz::EndsWith("Nazara Engine", "Engine", Nz::CaseIndependent{}));
|
||||
CHECK(Nz::EndsWith("Nazara Engine", "engine", Nz::CaseIndependent{}));
|
||||
CHECK(Nz::EndsWith("Nazara engine", "EnGiNe", Nz::CaseIndependent{}));
|
||||
CHECK_FALSE(Nz::EndsWith("Nazara Engine", " ngine", Nz::CaseIndependent{}));
|
||||
CHECK_FALSE(Nz::EndsWith("Nazara Engine", "NazaraEngine", Nz::CaseIndependent{}));
|
||||
CHECK_FALSE(Nz::EndsWith("Nazara Engine", "Nazara", Nz::CaseIndependent{}));
|
||||
CHECK_FALSE(Nz::EndsWith("Nazara Engine", "Sir Nazara van Engine", Nz::CaseIndependent{}));
|
||||
|
||||
CHECK(Nz::EndsWith(u8"L'\u00CEle de R\u00E9", u8"", Nz::UnicodeAware{}));
|
||||
CHECK(Nz::EndsWith(u8"L'\u00CEle de R\u00E9", u8"R\u00E9", Nz::UnicodeAware{}));
|
||||
CHECK_FALSE(Nz::EndsWith(u8"L'\u00CEle de R\u00E9", u8"Long long j\u00F4hnson", Nz::UnicodeAware{}));
|
||||
|
||||
CHECK(Nz::EndsWith(u8"L'\u00CEle de R\u00E9", u8"", Nz::CaseIndependent{}, Nz::UnicodeAware{}));
|
||||
CHECK(Nz::EndsWith(u8"L'\u00CEle de R\u00E9", u8"R\u00C9", Nz::CaseIndependent{}, Nz::UnicodeAware{}));
|
||||
CHECK_FALSE(Nz::EndsWith(u8"L'\u00CEle de R\u00E9", u8"Long long j\u00F4hnson", Nz::CaseIndependent{}, Nz::UnicodeAware{}));
|
||||
}
|
||||
|
||||
WHEN("Converting string back and forth")
|
||||
{
|
||||
CHECK(Nz::FromUtf16String(Nz::ToUtf16String(unicodeString)) == unicodeString);
|
||||
CHECK(Nz::FromUtf32String(Nz::ToUtf32String(unicodeString)) == unicodeString);
|
||||
}
|
||||
|
||||
WHEN("Fetching words")
|
||||
{
|
||||
CHECK(Nz::GetWord({}, 0).empty());
|
||||
CHECK(Nz::GetWord(" ", 0).empty());
|
||||
|
||||
std::string sentence = u8"\nSay hello\tto Nazara\u00A0Engine\n\t! "; //< \u00A0 is a No-Break Space
|
||||
CHECK(Nz::GetWord(sentence, 0) == "Say");
|
||||
CHECK(Nz::GetWord(sentence, 1) == "hello");
|
||||
CHECK(Nz::GetWord(sentence, 2) == "to");
|
||||
CHECK(Nz::GetWord(sentence, 3) == u8"Nazara\u00A0Engine");
|
||||
CHECK(Nz::GetWord(sentence, 4) == "!");
|
||||
CHECK(Nz::GetWord(sentence, 5).empty());
|
||||
|
||||
// Try the same using Unicode aware overloads
|
||||
CHECK(Nz::GetWord(sentence, 0, Nz::UnicodeAware{}) == "Say");
|
||||
CHECK(Nz::GetWord(sentence, 1, Nz::UnicodeAware{}) == "hello");
|
||||
CHECK(Nz::GetWord(sentence, 2, Nz::UnicodeAware{}) == "to");
|
||||
CHECK(Nz::GetWord(sentence, 3, Nz::UnicodeAware{}) == "Nazara");
|
||||
CHECK(Nz::GetWord(sentence, 4, Nz::UnicodeAware{}) == "Engine");
|
||||
CHECK(Nz::GetWord(sentence, 5, Nz::UnicodeAware{}) == "!");
|
||||
CHECK(Nz::GetWord(sentence, 6, Nz::UnicodeAware{}).empty());
|
||||
}
|
||||
|
||||
WHEN("Checking if string is number")
|
||||
{
|
||||
CHECK(Nz::IsNumber("123456"));
|
||||
CHECK(Nz::IsNumber("-123456"));
|
||||
CHECK_FALSE(Nz::IsNumber("123 "));
|
||||
CHECK_FALSE(Nz::IsNumber("Nazara Engine"));
|
||||
CHECK_FALSE(Nz::IsNumber("12345Nazara Engine"));
|
||||
}
|
||||
|
||||
WHEN("Matching patterns")
|
||||
{
|
||||
CHECK(Nz::MatchPattern("Lynix", "?????"));
|
||||
CHECK(Nz::MatchPattern("Lynix", "*Lynix"));
|
||||
CHECK(Nz::MatchPattern("Lynox", "*Lyn?x"));
|
||||
CHECK_FALSE(Nz::MatchPattern("", "?"));
|
||||
CHECK_FALSE(Nz::MatchPattern("", "*"));
|
||||
|
||||
const char* pattern = "Naz?ra *gine";
|
||||
CHECK(Nz::MatchPattern("Nazara Engine", pattern));
|
||||
CHECK(Nz::MatchPattern("Nazora engine", pattern));
|
||||
CHECK(Nz::MatchPattern("Nazora Biggine", pattern));
|
||||
CHECK(Nz::MatchPattern("Nazora gine", pattern));
|
||||
|
||||
CHECK_FALSE(Nz::MatchPattern("Nazaragine", pattern));
|
||||
CHECK_FALSE(Nz::MatchPattern("Nazorra Engine", pattern));
|
||||
CHECK_FALSE(Nz::MatchPattern("Nazra Engine", pattern));
|
||||
CHECK_FALSE(Nz::MatchPattern("NazaraEngine", pattern));
|
||||
}
|
||||
|
||||
WHEN("Converting pointers to string")
|
||||
{
|
||||
CHECK(Nz::TrimRight(Nz::PointerToString(nullptr), '0') == "0x");
|
||||
|
||||
const void* ptr = reinterpret_cast<const void*>(static_cast<std::uintptr_t>(0xDEADBEEF));
|
||||
CHECK(Nz::MatchPattern(Nz::PointerToString(ptr), "0x*DEADBEEF"));
|
||||
}
|
||||
|
||||
WHEN("Replacing strings")
|
||||
{
|
||||
std::string str = "Nazara Engine";
|
||||
REQUIRE(Nz::ReplaceStr(str, "Nazara", "Unreal") == "Unreal Engine");
|
||||
REQUIRE(Nz::ReplaceStr(str, "Engine", "Reality") == "Unreal Reality");
|
||||
REQUIRE(Nz::ReplaceStr(str, "Unreal Reality", "Ungine") == "Ungine");
|
||||
}
|
||||
|
||||
WHEN("Checking if string starts with")
|
||||
{
|
||||
CHECK(Nz::StartsWith("Nazara Engine", ""));
|
||||
CHECK(Nz::StartsWith("Nazara Engine", "Nazara"));
|
||||
CHECK_FALSE(Nz::StartsWith("Nazara Engine", "Navara"));
|
||||
CHECK_FALSE(Nz::StartsWith("Nazara Engine", "NaZaRa"));
|
||||
CHECK_FALSE(Nz::StartsWith("Nazara Engine", "Long long johnson"));
|
||||
|
||||
CHECK(Nz::StartsWith("NAZARA Engine", "", Nz::CaseIndependent{}));
|
||||
CHECK(Nz::StartsWith("NAZARA Engine", "Nazara", Nz::CaseIndependent{}));
|
||||
CHECK(Nz::StartsWith("NAZARA Engine", "NaZaRa", Nz::CaseIndependent{}));
|
||||
CHECK_FALSE(Nz::StartsWith("NAZARA Engine", "NavaRa", Nz::CaseIndependent{}));
|
||||
CHECK_FALSE(Nz::StartsWith("NAZARA Engine", "Long long johnson", Nz::CaseIndependent{}));
|
||||
|
||||
CHECK(Nz::StartsWith(u8"L'\u00CEle de R\u00E9", u8"", Nz::UnicodeAware{}));
|
||||
CHECK(Nz::StartsWith(u8"L'\u00CEle de R\u00E9", u8"L'\u00CEle", Nz::UnicodeAware{}));
|
||||
CHECK_FALSE(Nz::StartsWith(u8"L'\u00CEle de R\u00E9", u8"Long long j\u00F4hnson", Nz::UnicodeAware{}));
|
||||
|
||||
CHECK(Nz::StartsWith(u8"L'\u00CEle de R\u00E9", u8"", Nz::CaseIndependent{}, Nz::UnicodeAware{}));
|
||||
CHECK(Nz::StartsWith(u8"L'\u00CEle de R\u00E9", u8"l'\u00EEle", Nz::CaseIndependent{}, Nz::UnicodeAware{}));
|
||||
CHECK_FALSE(Nz::StartsWith(u8"L'\u00CEle de R\u00E9", u8"Long long j\u00F4hnson", Nz::CaseIndependent{}, Nz::UnicodeAware{}));
|
||||
}
|
||||
|
||||
WHEN("Converting between lower and upper")
|
||||
{
|
||||
CHECK(Nz::ToLower("Nazara Engine") == "nazara engine");
|
||||
CHECK(Nz::ToLower(u8"L'\u00CELE DE R\u00C9", Nz::UnicodeAware{}) == u8"l'\u00EEle de r\u00E9");
|
||||
CHECK(Nz::ToUpper("Nazara Engine") == "NAZARA ENGINE");
|
||||
CHECK(Nz::ToUpper(u8"l'\u00EEle de r\u00E9", Nz::UnicodeAware{}) == u8"L'\u00CELE DE R\u00C9");
|
||||
}
|
||||
|
||||
WHEN("Trimming strings")
|
||||
{
|
||||
CHECK(Nz::Trim(" \n Hello World\t") == "Hello World");
|
||||
CHECK(Nz::Trim("Nazara Engin", 'n') == "Nazara Engi");
|
||||
CHECK(Nz::Trim("Nazara Engin", 'n', Nz::CaseIndependent{}) == "azara Engi");
|
||||
CHECK(Nz::Trim(unicodeString, Nz::UnicodeAware{}) == unicodeString);
|
||||
CHECK(Nz::Trim("\n\t" + unicodeString + "\t ", Nz::UnicodeAware{}) == unicodeString);
|
||||
CHECK(Nz::Trim(unicodeString, U'\u46E1', Nz::UnicodeAware{}) == u8"\u00E0\u00E9\u00E7\u0153\u00C2\u5B98");
|
||||
CHECK(Nz::Trim(unicodeString, Nz::Unicode::Category_Letter, Nz::UnicodeAware{}) == "");
|
||||
}
|
||||
}
|
||||
85
tests/UnitTests/Engine/Core/UuidTest.cpp
Normal file
85
tests/UnitTests/Engine/Core/UuidTest.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#include <Nazara/Core/Uuid.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
|
||||
SCENARIO("Uuid", "[CORE][UUID]")
|
||||
{
|
||||
WHEN("Parsing UUID")
|
||||
{
|
||||
CHECK(Nz::Uuid::FromString("00000000-0000-0000-0000-000000000000") == Nz::Uuid());
|
||||
CHECK(Nz::Uuid::FromString("1b0e29af-fd4a-43e0-ba4c-e9334183b1f1") == Nz::Uuid({ 0x1B, 0x0E, 0x29, 0xAF, 0xFD, 0x4A, 0x43, 0xE0, 0xBA, 0x4C, 0xE9, 0x33, 0x41, 0x83, 0xB1, 0xF1 }));
|
||||
|
||||
// Testing some invalid cases
|
||||
CHECK(Nz::Uuid::FromString("Nazara Engine") == Nz::Uuid());
|
||||
CHECK(Nz::Uuid::FromString("1b0e29af_fd4a_43e0_ba4c_e9334183b1f1") == Nz::Uuid());
|
||||
CHECK(Nz::Uuid::FromString("1b0e29af-fd4a_43e0_ba4c_e93341-3b1f1") == Nz::Uuid());
|
||||
CHECK(Nz::Uuid::FromString("Zb0e29af-fd4a-43e0-ba4c-e9334183b1f1") == Nz::Uuid());
|
||||
CHECK(Nz::Uuid::FromString("1b0e29a\t-fd4a-43e0-ba4c-e9334183b1f1") == Nz::Uuid());
|
||||
CHECK(Nz::Uuid::FromString("1b0e29af-fd4\v-43e0-ba4c-e9334183b1f1") == Nz::Uuid());
|
||||
CHECK(Nz::Uuid::FromString("1b0e29af-fd4a-\r3e0-ba4c-e9334183b1f1") == Nz::Uuid());
|
||||
CHECK(Nz::Uuid::FromString("1b0e29af-fd4a-43e\n-ba4c-e9334183b1f1") == Nz::Uuid());
|
||||
CHECK(Nz::Uuid::FromString("1b0e29af-fd4a-43e0-\0a4c-e9334183b1f1") == Nz::Uuid());
|
||||
CHECK(Nz::Uuid::FromString("1b0e29af-fd4a-43e0-ba4\n-e9334183b1f1") == Nz::Uuid());
|
||||
CHECK(Nz::Uuid::FromString("1b0e29af-fd4a-43e0-ba4c-g9334183b1f1") == Nz::Uuid());
|
||||
CHECK(Nz::Uuid::FromString("1b0e29af-fd4a-43e0-ba4c-e9334183b1fG") == Nz::Uuid());
|
||||
CHECK(Nz::Uuid::FromString("1b0e29af-HELL-OWOR-LDDD-e9334183b1f1") == Nz::Uuid());
|
||||
}
|
||||
|
||||
WHEN("Generating a null UUID")
|
||||
{
|
||||
Nz::Uuid nullUuid;
|
||||
CHECK(nullUuid.IsNull());
|
||||
CHECK(nullUuid.ToArray() == std::array<Nz::UInt8, 16>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
|
||||
CHECK(nullUuid.ToString() == "00000000-0000-0000-0000-000000000000");
|
||||
CHECK(nullUuid.ToStringArray() == std::array<char, 37>{"00000000-0000-0000-0000-000000000000"});
|
||||
CHECK(nullUuid == Nz::Uuid{});
|
||||
CHECK(nullUuid >= Nz::Uuid{});
|
||||
CHECK(nullUuid <= Nz::Uuid{});
|
||||
CHECK_FALSE(nullUuid > Nz::Uuid{});
|
||||
CHECK_FALSE(nullUuid < Nz::Uuid{});
|
||||
CHECK(nullUuid != Nz::Uuid::Generate());
|
||||
CHECK(Nz::Uuid::FromString(nullUuid.ToString()) == nullUuid);
|
||||
}
|
||||
|
||||
WHEN("Generating a UUID")
|
||||
{
|
||||
// https://stackoverflow.com/questions/136505/searching-for-uuids-in-text-with-regex
|
||||
std::regex uuidRegex(R"(^\b[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}\b$)");
|
||||
|
||||
Nz::Uuid uuid = Nz::Uuid::Generate();
|
||||
INFO(uuid);
|
||||
CHECK_FALSE(uuid.IsNull());
|
||||
CHECK(std::regex_match(uuid.ToString(), uuidRegex));
|
||||
CHECK(uuid == uuid);
|
||||
CHECK(uuid == Nz::Uuid{uuid.ToArray()});
|
||||
CHECK(uuid >= uuid);
|
||||
CHECK(uuid <= uuid);
|
||||
CHECK_FALSE(uuid > uuid);
|
||||
CHECK_FALSE(uuid < uuid);
|
||||
CHECK(Nz::Uuid::FromString(uuid.ToString()) == uuid);
|
||||
}
|
||||
|
||||
WHEN("Generating multiple UUID, they are unique")
|
||||
{
|
||||
std::set<Nz::Uuid> uuidSet;
|
||||
std::unordered_set<Nz::Uuid> uuidUnorderedset;
|
||||
|
||||
auto InsertUniqueUuid = [](auto& container, const Nz::Uuid& uuid)
|
||||
{
|
||||
auto it = container.find(uuid);
|
||||
REQUIRE(it == container.end());
|
||||
container.insert(uuid);
|
||||
};
|
||||
|
||||
for (std::size_t i = 0; i < 1'000; ++i)
|
||||
{
|
||||
auto uuid = Nz::Uuid::Generate();
|
||||
|
||||
InsertUniqueUuid(uuidSet, uuid);
|
||||
InsertUniqueUuid(uuidUnorderedset, uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
322
tests/UnitTests/Engine/Core/VirtualDirectoryTest.cpp
Normal file
322
tests/UnitTests/Engine/Core/VirtualDirectoryTest.cpp
Normal file
@@ -0,0 +1,322 @@
|
||||
#include <Nazara/Core/Algorithm.hpp>
|
||||
#include <Nazara/Core/File.hpp>
|
||||
#include <Nazara/Core/StringExt.hpp>
|
||||
#include <Nazara/Core/VirtualDirectory.hpp>
|
||||
#include <Nazara/Core/Hash/SHA256.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <random>
|
||||
|
||||
std::filesystem::path GetAssetDir();
|
||||
|
||||
TEST_CASE("VirtualDirectory", "[Core][VirtualDirectory]")
|
||||
{
|
||||
SECTION("Creating a virtual directory")
|
||||
{
|
||||
std::shared_ptr<Nz::VirtualDirectory> virtualDir = std::make_shared<Nz::VirtualDirectory>();
|
||||
WHEN("Iterating it, it only has . and ..")
|
||||
{
|
||||
bool dot = false;
|
||||
bool dotDot = false;
|
||||
virtualDir->Foreach([&](std::string_view name, const Nz::VirtualDirectory::Entry& /*entry*/)
|
||||
{
|
||||
if (name == ".")
|
||||
{
|
||||
CHECK_FALSE(dot);
|
||||
dot = true;
|
||||
}
|
||||
else if (name == "..")
|
||||
{
|
||||
CHECK_FALSE(dotDot);
|
||||
dotDot = true;
|
||||
}
|
||||
if (name != "." && name != "..")
|
||||
FAIL("Got file " << name);
|
||||
}, true);
|
||||
|
||||
CHECK(dot);
|
||||
CHECK(dotDot);
|
||||
}
|
||||
AND_WHEN("Iterating it without dots, directory it empty")
|
||||
{
|
||||
virtualDir->Foreach([&](std::string_view name, const Nz::VirtualDirectory::Entry& /*entry*/)
|
||||
{
|
||||
FAIL("There should be nothing here, got " << name);
|
||||
});
|
||||
}
|
||||
AND_WHEN("We try to retrieve a non-existing file, it fails")
|
||||
{
|
||||
CHECK_FALSE(virtualDir->GetEntry("File.bin", [](const Nz::VirtualDirectory::Entry& /*entry*/)
|
||||
{
|
||||
return true;
|
||||
}));
|
||||
|
||||
CHECK_FALSE(virtualDir->GetEntry("Foo/File.bin", [](const Nz::VirtualDirectory::Entry& /*entry*/)
|
||||
{
|
||||
return true;
|
||||
}));
|
||||
|
||||
CHECK_FALSE(virtualDir->GetEntry("Foo/Bar/File.bin", [](const Nz::VirtualDirectory::Entry& /*entry*/)
|
||||
{
|
||||
return true;
|
||||
}));
|
||||
|
||||
virtualDir->StoreDirectory("Foo/Bar", std::make_shared<Nz::VirtualDirectory>());
|
||||
|
||||
CHECK(virtualDir->GetEntry("Foo", [](const Nz::VirtualDirectory::Entry& entry)
|
||||
{
|
||||
return std::holds_alternative<Nz::VirtualDirectory::VirtualDirectoryEntry>(entry);
|
||||
}));
|
||||
|
||||
CHECK(virtualDir->GetEntry("Foo/Bar", [](const Nz::VirtualDirectory::Entry& entry)
|
||||
{
|
||||
return std::holds_alternative<Nz::VirtualDirectory::VirtualDirectoryEntry>(entry);
|
||||
}));
|
||||
|
||||
CHECK_FALSE(virtualDir->GetEntry("Foo/Bar/File.bin", [](const Nz::VirtualDirectory::Entry& /*entry*/)
|
||||
{
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
std::mt19937 randGen(std::random_device{}());
|
||||
auto GenerateRandomData = [&]
|
||||
{
|
||||
std::vector<Nz::UInt8> randomData;
|
||||
for (std::size_t i = 0; i < 256; ++i)
|
||||
{
|
||||
unsigned int data = randGen();
|
||||
randomData.push_back(Nz::SafeCast<Nz::UInt8>((data & 0x000000FF) >> 0));
|
||||
randomData.push_back(Nz::SafeCast<Nz::UInt8>((data & 0x0000FF00) >> 8));
|
||||
randomData.push_back(Nz::SafeCast<Nz::UInt8>((data & 0x00FF0000) >> 16));
|
||||
randomData.push_back(Nz::SafeCast<Nz::UInt8>((data & 0xFF000000) >> 24));
|
||||
}
|
||||
|
||||
return randomData;
|
||||
};
|
||||
|
||||
auto CheckFile = [&](std::string_view path, const std::vector<Nz::UInt8>& expectedData)
|
||||
{
|
||||
return virtualDir->GetEntry(path, [&](const Nz::VirtualDirectory::Entry& entry)
|
||||
{
|
||||
if (!std::holds_alternative<Nz::VirtualDirectory::FileContentEntry>(entry))
|
||||
{
|
||||
FAIL("Target is not a file");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& contentEntry = std::get<Nz::VirtualDirectory::FileContentEntry>(entry);
|
||||
return std::equal(expectedData.begin(), expectedData.end(), contentEntry.data.begin(), contentEntry.data.end());
|
||||
});
|
||||
};
|
||||
|
||||
auto CheckFileContent = [&](std::string_view path, const std::vector<Nz::UInt8>& expectedData)
|
||||
{
|
||||
return virtualDir->GetFileContent(path, [&](const void* data, std::size_t size)
|
||||
{
|
||||
if (expectedData.size() != size)
|
||||
{
|
||||
FAIL("size doesn't match");
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::memcmp(expectedData.data(), data, expectedData.size()) == 0;
|
||||
});
|
||||
};
|
||||
|
||||
WHEN("Storing a file")
|
||||
{
|
||||
auto randomData = GenerateRandomData();
|
||||
virtualDir->StoreFile("File.bin", randomData);
|
||||
|
||||
WHEN("We retrieve it")
|
||||
{
|
||||
CHECK(CheckFile("File.bin", randomData));
|
||||
CHECK(CheckFileContent("File.bin", randomData));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("Storing multiples files")
|
||||
{
|
||||
std::array paths = {
|
||||
"Abc",
|
||||
"Ab/cd",
|
||||
"po/mme\\de/terre.o",
|
||||
"Nazara",
|
||||
"Engine.exe",
|
||||
"Un/Deux/Trois",
|
||||
"Gnocchi.fromage",
|
||||
"Karmeliet.triple",
|
||||
"Mogwai.gremlins"
|
||||
};
|
||||
|
||||
struct File
|
||||
{
|
||||
std::string path;
|
||||
std::vector<Nz::UInt8> data;
|
||||
};
|
||||
|
||||
std::vector<File> files;
|
||||
std::unordered_map<std::string, std::size_t> filePathToIndex;
|
||||
|
||||
for (const char* path : paths)
|
||||
{
|
||||
auto& file = files.emplace_back();
|
||||
file.data = GenerateRandomData();
|
||||
file.path = path;
|
||||
|
||||
filePathToIndex[file.path] = files.size() - 1;
|
||||
}
|
||||
|
||||
// Insert files into the virtual directory
|
||||
for (const File& file : files)
|
||||
{
|
||||
INFO("Storing " << file.path);
|
||||
CHECK_NOTHROW(virtualDir->StoreFile(file.path, file.data));
|
||||
}
|
||||
|
||||
// Try to retrieve them
|
||||
for (const File& file : files)
|
||||
{
|
||||
INFO("Retrieving " << file.path);
|
||||
CHECK(CheckFile(file.path, file.data));
|
||||
INFO("Retrieving " << file.path << " using GetFileContent");
|
||||
CHECK(CheckFileContent(file.path, file.data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Accessing filesystem using a VirtualDirectory")
|
||||
{
|
||||
std::shared_ptr<Nz::VirtualDirectory> resourceDir = std::make_shared<Nz::VirtualDirectory>(GetAssetDir());
|
||||
|
||||
WHEN("Iterating, it's not empty")
|
||||
{
|
||||
bool empty = true;
|
||||
resourceDir->Foreach([&](std::string_view name, const Nz::VirtualDirectory::Entry& entry)
|
||||
{
|
||||
CHECK_FALSE(name == ".");
|
||||
CHECK_FALSE(name == "..");
|
||||
|
||||
INFO("Only physical files and directories are expected");
|
||||
CHECK((std::holds_alternative<Nz::VirtualDirectory::PhysicalDirectoryEntry>(entry) || std::holds_alternative<Nz::VirtualDirectory::PhysicalFileEntry>(entry)));
|
||||
empty = false;
|
||||
});
|
||||
|
||||
REQUIRE_FALSE(empty);
|
||||
}
|
||||
|
||||
auto CheckFileHash = [](const Nz::VirtualDirectoryPtr& dir, const char* filepath, const char* expectedHash)
|
||||
{
|
||||
return dir->GetEntry(filepath, [&](const Nz::VirtualDirectory::Entry& entry)
|
||||
{
|
||||
REQUIRE(std::holds_alternative<Nz::VirtualDirectory::PhysicalFileEntry>(entry));
|
||||
|
||||
const auto& physFileEntry = std::get<Nz::VirtualDirectory::PhysicalFileEntry>(entry);
|
||||
|
||||
Nz::File file(physFileEntry.filePath);
|
||||
|
||||
Nz::SHA256Hash hash;
|
||||
WHEN("We compute " << hash.GetHashName() << " of " << physFileEntry.filePath << " file")
|
||||
{
|
||||
CHECK(Nz::ToUpper(Nz::ComputeHash(hash, file).ToHex()) == expectedHash);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
auto CheckFileContentHash = [](const Nz::VirtualDirectoryPtr& dir, const char* filepath, const char* expectedHash)
|
||||
{
|
||||
return dir->GetFileContent(filepath, [&](const void* data, std::size_t size)
|
||||
{
|
||||
Nz::SHA256Hash hash;
|
||||
WHEN("We compute " << hash.GetHashName() << " of " << filepath << " file")
|
||||
{
|
||||
hash.Begin();
|
||||
hash.Append(static_cast<const Nz::UInt8*>(data), size);
|
||||
CHECK(Nz::ToUpper(hash.End().ToHex()) == expectedHash);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
WHEN("Accessing files")
|
||||
{
|
||||
CHECK(CheckFileHash(resourceDir, "Logo.png", "5C4B9387327C039A6CE9ED51983D6C2ADA9F9DD01D024C2D5D588237ADFC7423"));
|
||||
CHECK(CheckFileHash(resourceDir, "./Logo.png", "5C4B9387327C039A6CE9ED51983D6C2ADA9F9DD01D024C2D5D588237ADFC7423"));
|
||||
CHECK(CheckFileHash(resourceDir, "Audio/The_Brabanconne.ogg", "E07706E0BEEC7770CDE36008826743AF9EEE5C80CA0BD83C37771CBC8B52E738"));
|
||||
CHECK(CheckFileHash(resourceDir, "Audio/./The_Brabanconne.ogg", "E07706E0BEEC7770CDE36008826743AF9EEE5C80CA0BD83C37771CBC8B52E738"));
|
||||
CHECK_FALSE(CheckFileHash(resourceDir, "The_Brabanconne.ogg", "E07706E0BEEC7770CDE36008826743AF9EEE5C80CA0BD83C37771CBC8B52E738"));
|
||||
CHECK_FALSE(CheckFileHash(resourceDir, "The_Brabanconne.ogg", "E07706E0BEEC7770CDE36008826743AF9EEE5C80CA0BD83C37771CBC8B52E738"));
|
||||
CHECK_FALSE(CheckFileHash(resourceDir, "Audio/../The_Brabanconne.ogg", "E07706E0BEEC7770CDE36008826743AF9EEE5C80CA0BD83C37771CBC8B52E738"));
|
||||
|
||||
// We can't escape the virtual directory
|
||||
CHECK(CheckFileHash(resourceDir, "../Logo.png", "5C4B9387327C039A6CE9ED51983D6C2ADA9F9DD01D024C2D5D588237ADFC7423"));
|
||||
CHECK(CheckFileHash(resourceDir, "../../Logo.png", "5C4B9387327C039A6CE9ED51983D6C2ADA9F9DD01D024C2D5D588237ADFC7423"));
|
||||
CHECK(CheckFileHash(resourceDir, "../../Audio/The_Brabanconne.ogg", "E07706E0BEEC7770CDE36008826743AF9EEE5C80CA0BD83C37771CBC8B52E738"));
|
||||
CHECK(CheckFileHash(resourceDir, ".././Audio/The_Brabanconne.ogg", "E07706E0BEEC7770CDE36008826743AF9EEE5C80CA0BD83C37771CBC8B52E738"));
|
||||
CHECK_FALSE(CheckFileHash(resourceDir, "../Tests/Audio/The_Brabanconne.ogg", "E07706E0BEEC7770CDE36008826743AF9EEE5C80CA0BD83C37771CBC8B52E738"));
|
||||
|
||||
auto CheckOurselves = [&](const auto& entry)
|
||||
{
|
||||
REQUIRE(std::holds_alternative<Nz::VirtualDirectory::VirtualDirectoryEntry>(entry));
|
||||
const auto& dirEntry = std::get<Nz::VirtualDirectory::VirtualDirectoryEntry>(entry);
|
||||
return dirEntry.directory == resourceDir;
|
||||
};
|
||||
|
||||
CHECK(resourceDir->GetEntry("..", CheckOurselves));
|
||||
CHECK(resourceDir->GetEntry("../..", CheckOurselves));
|
||||
CHECK(resourceDir->GetEntry("./..", CheckOurselves));
|
||||
CHECK(resourceDir->GetEntry("./..", CheckOurselves));
|
||||
CHECK(resourceDir->GetEntry("Audio/../..", CheckOurselves));
|
||||
CHECK(resourceDir->GetEntry("Core/../Audio/../../..", CheckOurselves));
|
||||
}
|
||||
AND_THEN("Overriding the physical file with another one")
|
||||
{
|
||||
resourceDir->StoreFile("Logo.png", GetAssetDir() / "Audio/ambience.ogg");
|
||||
|
||||
CHECK(CheckFileHash(resourceDir, "Audio/ambience.ogg", "49C486F44E43F023D54C9F375D902C21375DDB2748D3FA1863C9581D30E17F94"));
|
||||
CHECK(CheckFileHash(resourceDir, "Logo.png", "49C486F44E43F023D54C9F375D902C21375DDB2748D3FA1863C9581D30E17F94"));
|
||||
CHECK(CheckFileContentHash(resourceDir, "Audio/ambience.ogg", "49C486F44E43F023D54C9F375D902C21375DDB2748D3FA1863C9581D30E17F94"));
|
||||
CHECK(CheckFileContentHash(resourceDir, "Logo.png", "49C486F44E43F023D54C9F375D902C21375DDB2748D3FA1863C9581D30E17F94"));
|
||||
}
|
||||
|
||||
WHEN("Accessing physical folder as a virtual folder")
|
||||
{
|
||||
CHECK(resourceDir->GetDirectoryEntry("Utility", [&](const Nz::VirtualDirectory::DirectoryEntry& directoryEntry)
|
||||
{
|
||||
bool found = false;
|
||||
directoryEntry.directory->Foreach([&](std::string_view entryName, const Nz::VirtualDirectory::Entry& entry)
|
||||
{
|
||||
if (entryName == "GIF")
|
||||
{
|
||||
CHECK(std::holds_alternative<Nz::VirtualDirectory::PhysicalDirectoryEntry>(entry));
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
|
||||
return found;
|
||||
}));
|
||||
}
|
||||
|
||||
WHEN("Testing uproot escape")
|
||||
{
|
||||
std::shared_ptr<Nz::VirtualDirectory> engineDir = std::make_shared<Nz::VirtualDirectory>(GetAssetDir() / "Audio");
|
||||
|
||||
CHECK_FALSE(engineDir->IsUprootAllowed());
|
||||
|
||||
// We can't escape the virtual directory
|
||||
CHECK_FALSE(engineDir->Exists("../Logo.png"));
|
||||
CHECK_FALSE(engineDir->Exists("../../Logo.png"));
|
||||
CHECK_FALSE(engineDir->Exists("../Tests/Audio/Audio/The_Brabanconne.ogg"));
|
||||
CHECK(CheckFileHash(engineDir, "../../The_Brabanconne.ogg", "E07706E0BEEC7770CDE36008826743AF9EEE5C80CA0BD83C37771CBC8B52E738"));
|
||||
CHECK(CheckFileHash(engineDir, ".././The_Brabanconne.ogg", "E07706E0BEEC7770CDE36008826743AF9EEE5C80CA0BD83C37771CBC8B52E738"));
|
||||
|
||||
engineDir->AllowUproot(true);
|
||||
CHECK(engineDir->IsUprootAllowed());
|
||||
|
||||
// Now we're able to access the asset folder beneath
|
||||
CHECK(CheckFileHash(engineDir, "../Logo.png", "5C4B9387327C039A6CE9ED51983D6C2ADA9F9DD01D024C2D5D588237ADFC7423"));
|
||||
CHECK(CheckFileHash(engineDir, "../Audio/The_Brabanconne.ogg", "E07706E0BEEC7770CDE36008826743AF9EEE5C80CA0BD83C37771CBC8B52E738"));
|
||||
}
|
||||
}
|
||||
}
|
||||
359
tests/UnitTests/Engine/Math/AlgorithmMathTest.cpp
Normal file
359
tests/UnitTests/Engine/Math/AlgorithmMathTest.cpp
Normal file
@@ -0,0 +1,359 @@
|
||||
#include <Nazara/Math/Algorithm.hpp>
|
||||
#include <Nazara/Math/Angle.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <limits>
|
||||
|
||||
TEST_CASE("Approach", "[MATH][ALGORITHM]")
|
||||
{
|
||||
SECTION("Approach 8 with 5 by 2")
|
||||
{
|
||||
REQUIRE(Nz::Approach(5, 8, 2) == 7);
|
||||
}
|
||||
|
||||
SECTION("Approach 5 with 8 by 2")
|
||||
{
|
||||
REQUIRE(Nz::Approach(8, 5, 2) == 6);
|
||||
}
|
||||
|
||||
SECTION("Approach 8 with 8 by 2")
|
||||
{
|
||||
REQUIRE(Nz::Approach(8, 8, 2) == 8);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Clamp", "[MATH][ALGORITHM]")
|
||||
{
|
||||
SECTION("Clamp 8 between 5 and 10")
|
||||
{
|
||||
REQUIRE(Nz::Clamp(8, 5, 10) == 8);
|
||||
}
|
||||
|
||||
SECTION("Clamp 4 between 5 and 10")
|
||||
{
|
||||
REQUIRE(Nz::Clamp(4, 5, 10) == 5);
|
||||
}
|
||||
|
||||
SECTION("Clamp 12 between 5 and 10")
|
||||
{
|
||||
REQUIRE(Nz::Clamp(12, 5, 10) == 10);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("CountBits", "[MATH][ALGORITHM]")
|
||||
{
|
||||
SECTION("Number 10 has 2 bits set to 1")
|
||||
{
|
||||
REQUIRE(Nz::CountBits(10) == 2);
|
||||
}
|
||||
|
||||
SECTION("Number 0 has 0 bit set to 1")
|
||||
{
|
||||
REQUIRE(Nz::CountBits(0) == 0);
|
||||
}
|
||||
|
||||
SECTION("Number 0xFFFFFFFF has 32 bit set to 1")
|
||||
{
|
||||
REQUIRE(Nz::CountBits(0xFFFFFFFF) == 32);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("DegreeToRadian", "[MATH][ALGORITHM]")
|
||||
{
|
||||
SECTION("Convert 45.f degree to radian")
|
||||
{
|
||||
REQUIRE(Nz::DegreeToRadian(45.f) == Catch::Approx(Nz::Pi<float> / 4.f));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("GetNearestPowerOfTwo", "[MATH][ALGORITHM]")
|
||||
{
|
||||
SECTION("Nearest power of two of 0 = 1")
|
||||
{
|
||||
REQUIRE(Nz::GetNearestPowerOfTwo(0) == 1);
|
||||
}
|
||||
|
||||
SECTION("Nearest power of two of 16 = 16")
|
||||
{
|
||||
REQUIRE(Nz::GetNearestPowerOfTwo(16) == 16);
|
||||
}
|
||||
|
||||
SECTION("Nearest power of two of 17 = 32")
|
||||
{
|
||||
REQUIRE(Nz::GetNearestPowerOfTwo(17) == 32);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("GetNumberLength", "[MATH][ALGORITHM]")
|
||||
{
|
||||
SECTION("GetNumberLength of -127 signed char")
|
||||
{
|
||||
signed char minus127 = -127;
|
||||
REQUIRE(Nz::GetNumberLength(minus127) == 4);
|
||||
}
|
||||
|
||||
SECTION("GetNumberLength of 255 unsigned char")
|
||||
{
|
||||
unsigned char plus255 = 255;
|
||||
REQUIRE(Nz::GetNumberLength(plus255) == 3);
|
||||
}
|
||||
|
||||
SECTION("GetNumberLength of -1270 signed int")
|
||||
{
|
||||
signed int minus1270 = -1270;
|
||||
REQUIRE(Nz::GetNumberLength(minus1270) == 5);
|
||||
}
|
||||
|
||||
SECTION("GetNumberLength of 2550 unsigned int")
|
||||
{
|
||||
unsigned int plus2550 = 2550;
|
||||
REQUIRE(Nz::GetNumberLength(plus2550) == 4);
|
||||
}
|
||||
|
||||
SECTION("GetNumberLength of -1270 signed long long")
|
||||
{
|
||||
signed long long minus12700 = -12700;
|
||||
REQUIRE(Nz::GetNumberLength(minus12700) == 6);
|
||||
}
|
||||
|
||||
SECTION("GetNumberLength of 2550 unsigned long long")
|
||||
{
|
||||
unsigned long long plus25500 = 25500;
|
||||
REQUIRE(Nz::GetNumberLength(plus25500) == 5);
|
||||
}
|
||||
|
||||
SECTION("GetNumberLength of -2.456f float")
|
||||
{
|
||||
float minus2P456 = -2.456f;
|
||||
REQUIRE(Nz::GetNumberLength(minus2P456, 3) == 6);
|
||||
}
|
||||
|
||||
SECTION("GetNumberLength of -2.456 double")
|
||||
{
|
||||
double minus2P456 = -2.456;
|
||||
REQUIRE(Nz::GetNumberLength(minus2P456, 3) == 6);
|
||||
}
|
||||
|
||||
SECTION("GetNumberLength of -2.456 long double")
|
||||
{
|
||||
long double minus2P456 = -2.456L;
|
||||
REQUIRE(Nz::GetNumberLength(minus2P456, 3) == 6);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("IntegralLog2", "[MATH][ALGORITHM]")
|
||||
{
|
||||
SECTION("According to implementation, log in base 2 of 0 = 0")
|
||||
{
|
||||
REQUIRE(Nz::IntegralLog2(0) == 0);
|
||||
}
|
||||
|
||||
SECTION("Log in base 2 of 1 = 0")
|
||||
{
|
||||
REQUIRE(Nz::IntegralLog2(1) == 0);
|
||||
}
|
||||
|
||||
SECTION("Log in base 2 of 4 = 2")
|
||||
{
|
||||
REQUIRE(Nz::IntegralLog2(4) == 2);
|
||||
}
|
||||
|
||||
SECTION("Log in base 2 of 5 = 2")
|
||||
{
|
||||
REQUIRE(Nz::IntegralLog2(5) == 2);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("IntegralLog2Pot", "[MATH][ALGORITHM]")
|
||||
{
|
||||
SECTION("According to implementation, log in base 2 of 0 = 0")
|
||||
{
|
||||
REQUIRE(Nz::IntegralLog2Pot(0) == 0);
|
||||
}
|
||||
|
||||
SECTION("Log in base 2 of 1 = 0")
|
||||
{
|
||||
REQUIRE(Nz::IntegralLog2Pot(1) == 0);
|
||||
}
|
||||
|
||||
SECTION("Log in base 2 of 4 = 2")
|
||||
{
|
||||
REQUIRE(Nz::IntegralLog2Pot(4) == 2);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("IntegralPow", "[MATH][ALGORITHM]")
|
||||
{
|
||||
SECTION("2 to power 4")
|
||||
{
|
||||
REQUIRE(Nz::IntegralPow(2, 4) == 16);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Lerp", "[MATH][ALGORITHM]")
|
||||
{
|
||||
SECTION("Lerp 2 to 6 with 0.5")
|
||||
{
|
||||
REQUIRE(Nz::Lerp(2, 6, 0.5) == 4);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("MultiplyAdd", "[MATH][ALGORITHM]")
|
||||
{
|
||||
SECTION("2 * 3 + 1")
|
||||
{
|
||||
REQUIRE(Nz::MultiplyAdd(2, 3, 1) == 7);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("NormalizeAngle", "[MATH][ALGORITHM]")
|
||||
{
|
||||
SECTION("-90 should be normalized to +270")
|
||||
{
|
||||
REQUIRE(Nz::DegreeAnglef(-90.f).Normalize() == Nz::DegreeAnglef(270.f));
|
||||
}
|
||||
|
||||
SECTION("-540 should be normalized to +180")
|
||||
{
|
||||
REQUIRE(Nz::DegreeAnglef(-540.f).Normalize() == Nz::DegreeAnglef(180.f));
|
||||
}
|
||||
|
||||
SECTION("0 should remain 0")
|
||||
{
|
||||
REQUIRE(Nz::DegreeAnglef(0.f).Normalize() == Nz::DegreeAnglef(0.f));
|
||||
}
|
||||
|
||||
SECTION("90 should remain 90")
|
||||
{
|
||||
REQUIRE(Nz::DegreeAnglef(90.f).Normalize() == Nz::DegreeAnglef(90.f));
|
||||
}
|
||||
|
||||
SECTION("360 should be normalized to 0")
|
||||
{
|
||||
REQUIRE(Nz::DegreeAnglef(360.f).Normalize() == Nz::DegreeAnglef(0.f));
|
||||
}
|
||||
|
||||
SECTION("450 should be normalized to 90")
|
||||
{
|
||||
REQUIRE(Nz::DegreeAnglef(450.f).Normalize() == Nz::DegreeAnglef(90.f));
|
||||
}
|
||||
|
||||
SECTION("-90 should be normalized to +270")
|
||||
{
|
||||
REQUIRE(Nz::DegreeAnglef(-90).Normalize() == Nz::DegreeAnglef(270));
|
||||
}
|
||||
|
||||
SECTION("-540 should be normalized to +180")
|
||||
{
|
||||
REQUIRE(Nz::DegreeAnglef(-540).Normalize() == Nz::DegreeAnglef(180));
|
||||
}
|
||||
|
||||
SECTION("0 should remain 0")
|
||||
{
|
||||
REQUIRE(Nz::DegreeAnglef(0).Normalize() == Nz::DegreeAnglef(0));
|
||||
}
|
||||
|
||||
SECTION("90 should remain 90")
|
||||
{
|
||||
REQUIRE(Nz::DegreeAnglef(90).Normalize() == Nz::DegreeAnglef(90));
|
||||
}
|
||||
|
||||
SECTION("360 should be normalized to 0")
|
||||
{
|
||||
REQUIRE(Nz::DegreeAnglef(360).Normalize() == Nz::DegreeAnglef(0));
|
||||
}
|
||||
|
||||
SECTION("450 should be normalized to 90")
|
||||
{
|
||||
REQUIRE(Nz::DegreeAnglef(450).Normalize() == Nz::DegreeAnglef(90));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("NumberEquals", "[MATH][ALGORITHM]")
|
||||
{
|
||||
SECTION("2.35 and 2.351 should be the same at 0.01")
|
||||
{
|
||||
CHECK(Nz::NumberEquals(2.35, 2.35, 0.01));
|
||||
}
|
||||
|
||||
SECTION("0 and 4 unsigned should be the same at 4")
|
||||
{
|
||||
CHECK(Nz::NumberEquals(0U, 4U, 4U));
|
||||
}
|
||||
|
||||
SECTION("1 and -1 signed should be the same at 2")
|
||||
{
|
||||
CHECK(Nz::NumberEquals(1, -1, 2));
|
||||
}
|
||||
|
||||
SECTION("Maximum integer and -1 should not be equal")
|
||||
{
|
||||
CHECK_FALSE(Nz::NumberEquals(std::numeric_limits<int>::max(), -1));
|
||||
}
|
||||
|
||||
SECTION("Maximum integer and minimum integer should not be equal")
|
||||
{
|
||||
CHECK_FALSE(Nz::NumberEquals(std::numeric_limits<int>::max(), std::numeric_limits<int>::min()));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("NumberToString", "[MATH][ALGORITHM]")
|
||||
{
|
||||
SECTION("0 to string")
|
||||
{
|
||||
REQUIRE(Nz::NumberToString(0) == "0");
|
||||
}
|
||||
|
||||
SECTION("235 to string")
|
||||
{
|
||||
REQUIRE(Nz::NumberToString(235) == "235");
|
||||
}
|
||||
|
||||
SECTION("-235 to string")
|
||||
{
|
||||
REQUIRE(Nz::NumberToString(-235) == "-235");
|
||||
}
|
||||
|
||||
SECTION("16 in base 16 to string")
|
||||
{
|
||||
REQUIRE(Nz::NumberToString(16, 16) == "10");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("RadianToDegree", "[MATH][ALGORITHM]")
|
||||
{
|
||||
SECTION("PI / 4 to degree")
|
||||
{
|
||||
REQUIRE(Nz::RadianToDegree(Nz::Pi<float> / 4.f) == Catch::Approx(45.f));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("StringToNumber", "[MATH][ALGORITHM]")
|
||||
{
|
||||
SECTION("235 in string")
|
||||
{
|
||||
REQUIRE(Nz::StringToNumber("235") == 235);
|
||||
}
|
||||
|
||||
SECTION("-235 in string")
|
||||
{
|
||||
REQUIRE(Nz::StringToNumber("-235") == -235);
|
||||
}
|
||||
|
||||
SECTION("235 157 in string")
|
||||
{
|
||||
REQUIRE(Nz::StringToNumber("235 157") == 235157);
|
||||
}
|
||||
|
||||
SECTION("16 in base 16 in string")
|
||||
{
|
||||
REQUIRE(Nz::StringToNumber("10", 16) == 16);
|
||||
}
|
||||
|
||||
SECTION("8 in base 4 in string should not be valid")
|
||||
{
|
||||
bool ok = true;
|
||||
REQUIRE(Nz::StringToNumber("8", 4, &ok) == 0);
|
||||
REQUIRE(!ok);
|
||||
}
|
||||
}
|
||||
237
tests/UnitTests/Engine/Math/AngleTest.cpp
Normal file
237
tests/UnitTests/Engine/Math/AngleTest.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
#include <Nazara/Math/Angle.hpp>
|
||||
#include <Nazara/Math/EulerAngles.hpp>
|
||||
#include <Nazara/Math/Quaternion.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
SCENARIO("Angle", "[MATH][ANGLE]")
|
||||
{
|
||||
GIVEN("A degree angle of 90deg")
|
||||
{
|
||||
Nz::DegreeAnglef angle(90.f);
|
||||
|
||||
WHEN("We convert it to degrees")
|
||||
{
|
||||
Nz::DegreeAnglef copyAngle = angle.ToDegreeAngle();
|
||||
|
||||
THEN("It should compare to itself")
|
||||
{
|
||||
CHECK(angle == copyAngle);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We convert it to radians")
|
||||
{
|
||||
Nz::RadianAnglef radAngle(angle);
|
||||
|
||||
THEN("It should be equal to pi/2")
|
||||
{
|
||||
Nz::RadianAnglef expectedResult(Nz::HalfPi<float>);
|
||||
|
||||
CHECK(radAngle == expectedResult);
|
||||
CHECK(angle.ToRadianAngle() == expectedResult);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We convert it to turns")
|
||||
{
|
||||
Nz::TurnAnglef turnAngle(angle);
|
||||
|
||||
THEN("It should be equal to pi/2")
|
||||
{
|
||||
Nz::TurnAnglef expectedResult(1.f / 4.f);
|
||||
|
||||
CHECK(turnAngle == expectedResult);
|
||||
CHECK(angle.ToTurnAngle() == expectedResult);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We compute its sinus/cosinus separatly")
|
||||
{
|
||||
THEN("It should be equal to 1 and 0")
|
||||
{
|
||||
CHECK(angle.GetSin() == Catch::Approx(1.f).margin(0.0001f));
|
||||
CHECK(angle.GetCos() == Catch::Approx(0.f).margin(0.0001f));
|
||||
}
|
||||
AND_WHEN("We compute sin/cos at the same time")
|
||||
{
|
||||
auto sincos = angle.GetSinCos();
|
||||
|
||||
THEN("It should also be equal to 1 and 0")
|
||||
{
|
||||
CHECK(sincos.first == Catch::Approx(1.f).margin(0.0001f));
|
||||
CHECK(sincos.second == Catch::Approx(0.f).margin(0.0001f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We get the Euler Angles representation of this angle")
|
||||
{
|
||||
Nz::EulerAnglesf eulerAngles = angle;
|
||||
THEN("It should be equivalent to a 2D rotation by this angle")
|
||||
{
|
||||
CHECK(eulerAngles == Nz::EulerAnglesf(0.f, 0.f, 90.f));
|
||||
}
|
||||
AND_WHEN("We get the Quaternion representation of this angle")
|
||||
{
|
||||
Nz::Quaternionf quat = angle;
|
||||
|
||||
THEN("It should be equivalent to a 2D rotation by this angle")
|
||||
{
|
||||
CHECK(quat == eulerAngles.ToQuaternion());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A degree angle of 480deg")
|
||||
{
|
||||
Nz::DegreeAnglef angle(480.f);
|
||||
|
||||
WHEN("We normalize it")
|
||||
{
|
||||
angle.Normalize();
|
||||
|
||||
THEN("It should be equal to a normalized version of itself")
|
||||
{
|
||||
Nz::DegreeAnglef expectedResult(120.f);
|
||||
|
||||
CHECK(angle == expectedResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A degree angle of -300deg")
|
||||
{
|
||||
Nz::DegreeAnglef angle(-300.f);
|
||||
|
||||
WHEN("We normalize it")
|
||||
{
|
||||
angle.Normalize();
|
||||
|
||||
THEN("It should be equal to a normalized version of itself")
|
||||
{
|
||||
Nz::DegreeAnglef expectedResult(60.f);
|
||||
|
||||
CHECK(angle == expectedResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A radian angle of -Pi")
|
||||
{
|
||||
Nz::RadianAnglef angle(-Nz::Pi<float>);
|
||||
|
||||
WHEN("We convert it to radians")
|
||||
{
|
||||
Nz::RadianAnglef copyAngle = angle.ToRadianAngle();
|
||||
|
||||
THEN("It should compare to itself")
|
||||
{
|
||||
CHECK(angle == copyAngle);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We convert it to degrees")
|
||||
{
|
||||
Nz::DegreeAnglef degAngle(angle);
|
||||
|
||||
THEN("It should be equal to pi/2")
|
||||
{
|
||||
Nz::DegreeAnglef expectedResult(-180.f);
|
||||
|
||||
CHECK(degAngle == expectedResult);
|
||||
CHECK(angle.ToDegreeAngle() == expectedResult);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We compute its sinus/cosinus separatly")
|
||||
{
|
||||
THEN("It should be equal to 0 and -1")
|
||||
{
|
||||
CHECK(angle.GetSin() == Catch::Approx(0.f).margin(0.0001f));
|
||||
CHECK(angle.GetCos() == Catch::Approx(-1.f).margin(0.0001f));
|
||||
}
|
||||
|
||||
}
|
||||
AND_WHEN("We compute it at the same time")
|
||||
{
|
||||
auto sincos = angle.GetSinCos();
|
||||
|
||||
THEN("It should also be equal to 0 and -1")
|
||||
{
|
||||
CHECK(sincos.first == Catch::Approx(0.f).margin(0.0001f));
|
||||
CHECK(sincos.second == Catch::Approx(-1.f).margin(0.0001f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We get the Euler Angles representation of this angle")
|
||||
{
|
||||
Nz::EulerAnglesf eulerAngles = angle;
|
||||
THEN("It should be equivalent to a 2D rotation by this angle")
|
||||
{
|
||||
CHECK(eulerAngles == Nz::EulerAnglesf(0.f, 0.f, -180.f));
|
||||
}
|
||||
AND_WHEN("We get the Quaternion representation of this angle")
|
||||
{
|
||||
Nz::Quaternionf quat = angle;
|
||||
|
||||
THEN("It should be equivalent to a 2D rotation by this angle")
|
||||
{
|
||||
CHECK(quat == eulerAngles.ToQuaternion());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A radian angle of 7pi")
|
||||
{
|
||||
Nz::RadianAnglef angle(7.f * Nz::Pi<float>);
|
||||
|
||||
WHEN("We normalize it")
|
||||
{
|
||||
angle.Normalize();
|
||||
|
||||
THEN("It should be equal to a normalized version of itself")
|
||||
{
|
||||
Nz::RadianAnglef expectedResult(Nz::Pi<float>);
|
||||
|
||||
CHECK(angle == expectedResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A radian angle of -4pi")
|
||||
{
|
||||
Nz::RadianAnglef angle(-4.f * Nz::Pi<float>);
|
||||
|
||||
WHEN("We normalize it")
|
||||
{
|
||||
angle.Normalize();
|
||||
|
||||
THEN("It should be equal to a normalized version of itself")
|
||||
{
|
||||
Nz::RadianAnglef expectedResult(0.f);
|
||||
|
||||
CHECK(angle == expectedResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A turn angle of 1.5f")
|
||||
{
|
||||
Nz::TurnAnglef angle(1.5f);
|
||||
|
||||
WHEN("We normalize it")
|
||||
{
|
||||
angle.Normalize();
|
||||
|
||||
THEN("It should be equal to a normalized version of itself")
|
||||
{
|
||||
Nz::TurnAnglef expectedResult(0.5f);
|
||||
|
||||
CHECK(angle == expectedResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
146
tests/UnitTests/Engine/Math/BoundingVolumeTest.cpp
Normal file
146
tests/UnitTests/Engine/Math/BoundingVolumeTest.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
#include <Nazara/Math/BoundingVolume.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
SCENARIO("BoundingVolume", "[MATH][BOUNDINGVOLUME]")
|
||||
{
|
||||
GIVEN("With a null bounding volume and an infinite")
|
||||
{
|
||||
Nz::BoundingVolumef nullVolume = Nz::BoundingVolumef::Null();
|
||||
Nz::BoundingVolumef infiniteVolume = Nz::BoundingVolumef::Infinite();
|
||||
|
||||
WHEN("We compare them")
|
||||
{
|
||||
THEN("They should be different")
|
||||
{
|
||||
REQUIRE(nullVolume != infiniteVolume);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We ask for the characteristic")
|
||||
{
|
||||
THEN("They should be respectively null and infinite")
|
||||
{
|
||||
CHECK(nullVolume.IsNull());
|
||||
CHECK(infiniteVolume.IsInfinite());
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We compare two null or two infinite")
|
||||
{
|
||||
THEN("Everything should be ok")
|
||||
{
|
||||
REQUIRE(Nz::BoundingVolumef::Null() == Nz::BoundingVolumef::Null());
|
||||
REQUIRE(Nz::BoundingVolumef::Infinite() == Nz::BoundingVolumef::Infinite());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("Two same bounding volume with different constructor")
|
||||
{
|
||||
Nz::BoundingVolumef firstCenterAndUnit(Nz::Boxf(0.f, 0.f, 0.f, 1.f, 1.f, 1.f));
|
||||
Nz::BoundingVolumef secondCenterAndUnit{ Nz::Boxf(Nz::Vector3f::Unit()) };
|
||||
firstCenterAndUnit.Update(Nz::Matrix4f::Identity());
|
||||
secondCenterAndUnit.Update(Nz::Matrix4f::Identity());
|
||||
|
||||
WHEN("We compare them")
|
||||
{
|
||||
THEN("Then the should be equal")
|
||||
{
|
||||
REQUIRE(firstCenterAndUnit == secondCenterAndUnit);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We ask for the characteristic")
|
||||
{
|
||||
THEN("They should be finite")
|
||||
{
|
||||
CHECK(firstCenterAndUnit.IsFinite());
|
||||
CHECK(secondCenterAndUnit.IsFinite());
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We use a constructor of conversion")
|
||||
{
|
||||
THEN("There's no problem")
|
||||
{
|
||||
Nz::BoundingVolume<int> intVolumeCenterAndUnit(Nz::Boxi(Nz::Vector3i::Zero(), Nz::Vector3i::Unit()));
|
||||
Nz::BoundingVolumef thirdCenterAndUnit(intVolumeCenterAndUnit);
|
||||
REQUIRE(thirdCenterAndUnit == secondCenterAndUnit);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We make one twice bigger with a matrix")
|
||||
{
|
||||
firstCenterAndUnit.Update(Nz::Matrix4f::Scale(Nz::Vector3f::Unit() * 2.f));
|
||||
|
||||
THEN("The local box should be the same but the aabb different")
|
||||
{
|
||||
REQUIRE(firstCenterAndUnit.obb.localBox == secondCenterAndUnit.obb.localBox);
|
||||
REQUIRE(firstCenterAndUnit.aabb != secondCenterAndUnit.aabb);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We try to lerp")
|
||||
{
|
||||
THEN("Compilation should be fine")
|
||||
{
|
||||
Nz::BoundingVolumef nullBoundingVolume = Nz::BoundingVolumef(Nz::Boxf::Zero());
|
||||
Nz::BoundingVolumef centerAndUnit = firstCenterAndUnit;
|
||||
nullBoundingVolume.Update(Nz::Matrix4f::Identity());
|
||||
centerAndUnit.Update(Nz::Matrix4f::Identity());
|
||||
Nz::BoundingVolumef result(Nz::Boxf(Nz::Vector3f::Zero(), Nz::Vector3f::Unit() * 0.5f));
|
||||
result.Update(Nz::Matrix4f::Identity());
|
||||
|
||||
REQUIRE(Nz::BoundingVolumef::Lerp(nullBoundingVolume, centerAndUnit, 0.5f) == result);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We lerp with special cases")
|
||||
{
|
||||
Nz::OrientedBoxf centerAndUnitOBB(Nz::Boxf(0.f, 0.f, 0.f, 1.f, 1.f, 1.f));
|
||||
centerAndUnitOBB.Update(Nz::Matrix4f::Identity());
|
||||
|
||||
Nz::BoundingVolumef centerAndUnit(centerAndUnitOBB);
|
||||
|
||||
Nz::BoundingVolumef nullBoundingVolume(Nz::Extend::Null);
|
||||
Nz::BoundingVolumef infiniteBoundingVolume(Nz::Extend::Infinite);
|
||||
|
||||
THEN("Normal to null should give a smaller volume")
|
||||
{
|
||||
Nz::BoundingVolumef result(Nz::Boxf(Nz::Vector3f::Zero(), Nz::Vector3f::Unit() * 0.5f));
|
||||
result.Update(Nz::Matrix4f::Identity());
|
||||
|
||||
REQUIRE(Nz::BoundingVolumef::Lerp(centerAndUnit, nullBoundingVolume, 0.5f) == result);
|
||||
}
|
||||
|
||||
THEN("Normal to infinite should give an infinite volume")
|
||||
{
|
||||
REQUIRE(Nz::BoundingVolumef::Lerp(centerAndUnit, infiniteBoundingVolume, 0.5f) == infiniteBoundingVolume);
|
||||
}
|
||||
|
||||
THEN("Null to normal should give a small volume")
|
||||
{
|
||||
Nz::BoundingVolumef result(Nz::Boxf(Nz::Vector3f::Zero(), Nz::Vector3f::Unit() * 0.5f));
|
||||
result.Update(Nz::Matrix4f::Identity());
|
||||
|
||||
REQUIRE(Nz::BoundingVolumef::Lerp(nullBoundingVolume, centerAndUnit, 0.5f) == result);
|
||||
}
|
||||
|
||||
THEN("Infinite to normal should give an infinite volume")
|
||||
{
|
||||
REQUIRE(Nz::BoundingVolumef::Lerp(infiniteBoundingVolume, centerAndUnit, 0.5f) == infiniteBoundingVolume);
|
||||
}
|
||||
|
||||
THEN("Infinite to null should give an infinite volume")
|
||||
{
|
||||
REQUIRE(Nz::BoundingVolumef::Lerp(infiniteBoundingVolume, nullBoundingVolume, 0.5f) == infiniteBoundingVolume);
|
||||
}
|
||||
|
||||
THEN("Null to null should give a null volume")
|
||||
{
|
||||
REQUIRE(Nz::BoundingVolumef::Lerp(nullBoundingVolume, nullBoundingVolume, 0.5f) == nullBoundingVolume);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
138
tests/UnitTests/Engine/Math/BoxTest.cpp
Normal file
138
tests/UnitTests/Engine/Math/BoxTest.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
#include <Nazara/Math/Box.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
SCENARIO("Box", "[MATH][BOX]")
|
||||
{
|
||||
GIVEN("Two zero boxes")
|
||||
{
|
||||
Nz::Boxf firstZero(Nz::Boxf::Zero());
|
||||
Nz::Boxf secondZero(Nz::Vector3f::Zero(), Nz::Vector3f::Zero());
|
||||
|
||||
WHEN("We scale them")
|
||||
{
|
||||
firstZero.Scale(1.f);
|
||||
secondZero.Scale(Nz::Vector3f::Unit() * 3.f);
|
||||
|
||||
THEN("They should stay the same")
|
||||
{
|
||||
REQUIRE(firstZero == secondZero);
|
||||
CHECK(firstZero.IsValid());
|
||||
CHECK(firstZero.IsNull());
|
||||
CHECK(secondZero.IsValid());
|
||||
CHECK(secondZero.IsNull());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("Two unit and center boxes")
|
||||
{
|
||||
Nz::Boxf firstCenterAndUnit(Nz::Vector3f::Zero(), Nz::Vector3f::Unit());
|
||||
Nz::Boxf secondCenterAndUnit(1.f, 1.f, 1.f);
|
||||
|
||||
WHEN("We ask for some informations")
|
||||
{
|
||||
THEN("These results are expected")
|
||||
{
|
||||
REQUIRE(firstCenterAndUnit.GetBoundingSphere() == Nz::Spheref(Nz::Vector3f::Unit() * 0.5f, std::sqrt(3.f * 0.5f * 0.5f)));
|
||||
REQUIRE(firstCenterAndUnit.GetCenter() == (Nz::Vector3f::Unit() * 0.5f));
|
||||
REQUIRE(firstCenterAndUnit.GetCorner(Nz::BoxCorner::FarLeftTop) == Nz::Vector3f::UnitY());
|
||||
REQUIRE(firstCenterAndUnit.GetLengths() == Nz::Vector3f::Unit());
|
||||
REQUIRE(firstCenterAndUnit.GetMaximum() == Nz::Vector3f::Unit());
|
||||
REQUIRE(firstCenterAndUnit.GetMinimum() == Nz::Vector3f::Zero());
|
||||
REQUIRE(firstCenterAndUnit.GetNegativeVertex(Nz::Vector3f::Unit()) == Nz::Vector3f::Zero());
|
||||
REQUIRE(firstCenterAndUnit.GetPosition() == Nz::Vector3f::Zero());
|
||||
REQUIRE(firstCenterAndUnit.GetPositiveVertex(Nz::Vector3f::Unit()) == Nz::Vector3f::Unit());
|
||||
REQUIRE(firstCenterAndUnit.GetRadius() == Catch::Approx(std::sqrt(3.f * 0.5f * 0.5f)));
|
||||
REQUIRE(firstCenterAndUnit.GetSquaredBoundingSphere() == Nz::Spheref(Nz::Vector3f::Unit() * 0.5f, 3.f * 0.5f * 0.5f));
|
||||
REQUIRE(firstCenterAndUnit.GetSquaredRadius() == Catch::Approx(3.f * 0.5f * 0.5f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We ask for the intersection between the two")
|
||||
{
|
||||
THEN("We should have a center and unit")
|
||||
{
|
||||
Nz::Boxf thirdCenterAndUnit;
|
||||
CHECK(firstCenterAndUnit.Intersect(secondCenterAndUnit, &thirdCenterAndUnit));
|
||||
REQUIRE(firstCenterAndUnit == secondCenterAndUnit);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We ask for the intersection when there are none")
|
||||
{
|
||||
firstCenterAndUnit.Translate(Nz::Vector3f::UnitZ() * 5.f);
|
||||
THEN("We should have a center and unit")
|
||||
{
|
||||
Nz::Boxf thirdCenterAndUnit;
|
||||
CHECK(!firstCenterAndUnit.Intersect(secondCenterAndUnit, &thirdCenterAndUnit));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We use the constructor of conversion")
|
||||
{
|
||||
THEN("Shouldn't be a problem")
|
||||
{
|
||||
Nz::Boxf tmp(Nz::Boxi(0, 0, 0, 1, 1, 1));
|
||||
REQUIRE(tmp == firstCenterAndUnit);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We try to lerp")
|
||||
{
|
||||
THEN("Compilation should be fine")
|
||||
{
|
||||
Nz::Boxf nullBox = Nz::Boxf::Zero();
|
||||
Nz::Boxf centerAndUnit = firstCenterAndUnit;
|
||||
Nz::Boxf result(Nz::Vector3f::Zero(), Nz::Vector3f::Unit() * 0.5f);
|
||||
|
||||
REQUIRE(Nz::Boxf::Lerp(nullBox, centerAndUnit, 0.5f) == result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("Two wrong box (negative width, height and depth")
|
||||
{
|
||||
Nz::Boxf firstWrongBox(-Nz::Vector3f::Unit());
|
||||
Nz::Boxf secondWrongBox(-Nz::Vector3f::Unit());
|
||||
|
||||
WHEN("We check if valid")
|
||||
{
|
||||
THEN("Result if false")
|
||||
{
|
||||
CHECK(!firstWrongBox.IsValid());
|
||||
CHECK(!secondWrongBox.IsValid());
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We correct them")
|
||||
{
|
||||
firstWrongBox.ExtendTo(Nz::Vector3f::Unit());
|
||||
secondWrongBox.Transform(Nz::Matrix4f::Scale(-Nz::Vector3f::Unit()));
|
||||
|
||||
THEN("They should be valid")
|
||||
{
|
||||
CHECK(firstWrongBox.IsValid());
|
||||
CHECK(secondWrongBox.IsValid());
|
||||
}
|
||||
|
||||
AND_WHEN("We ask if they contain boxes")
|
||||
{
|
||||
THEN("These results are expected")
|
||||
{
|
||||
CHECK(firstWrongBox.Contains(0.f, 0.f, 0.f));
|
||||
CHECK(secondWrongBox.Contains(0.f, 0.f, 0.f));
|
||||
|
||||
secondWrongBox = Nz::Boxf::Lerp(Nz::Boxf::Zero(), secondWrongBox, 0.f); // Zeroed
|
||||
secondWrongBox.ExtendTo(Nz::Boxf::FromExtends(Nz::Vector3f(0.1f, 0.1f, 0.1f), Nz::Vector3f(0.9f, 0.9f, 0.9f)));
|
||||
secondWrongBox.Translate(Nz::Vector3f(0.05f, 0.05f, 0.05f)); // Box 0.15 to 0.95
|
||||
CHECK(firstWrongBox.Contains(secondWrongBox));
|
||||
|
||||
Nz::Boxf test(1.f, -500.f, -500.f, 1000.f, 1000.f, 1000.f);
|
||||
CHECK(test.Contains(Nz::Boxf(500.f, -0.5f, -0.5f, 1.f, 1.f, 1.f)));
|
||||
CHECK(test.Contains(500.f, 0.f, 0.f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
123
tests/UnitTests/Engine/Math/EulerAnglesTest.cpp
Normal file
123
tests/UnitTests/Engine/Math/EulerAnglesTest.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#include <Nazara/Math/Angle.hpp>
|
||||
#include <Nazara/Math/EulerAngles.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
SCENARIO("EulerAngles", "[MATH][EULERANGLES]")
|
||||
{
|
||||
GIVEN("Two zero euler angles")
|
||||
{
|
||||
Nz::EulerAnglesf firstZero(0.f, 0.f, 0.f);
|
||||
Nz::EulerAnglesf secondZero(Nz::EulerAngles<int>::Zero());
|
||||
|
||||
THEN("They should be equal")
|
||||
{
|
||||
REQUIRE(firstZero == secondZero);
|
||||
}
|
||||
|
||||
WHEN("We do some operations")
|
||||
{
|
||||
Nz::EulerAnglesf euler90(Nz::DegreeAnglef(90.f), Nz::DegreeAnglef(90.f), Nz::DegreeAnglef(90.f));
|
||||
Nz::EulerAnglesf euler270(Nz::DegreeAnglef(270.f), Nz::DegreeAnglef(270.f), Nz::DegreeAnglef(270.f));
|
||||
|
||||
Nz::EulerAnglesf euler360 = euler90 + euler270;
|
||||
euler360.Normalize();
|
||||
Nz::EulerAnglesf euler0 = euler270 - euler90;
|
||||
euler0 -= euler90;
|
||||
euler0 -= euler90;
|
||||
|
||||
THEN("They should still be equal")
|
||||
{
|
||||
CHECK(euler360 == firstZero);
|
||||
CHECK(euler0 == secondZero);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We ask for conversion to quaternion")
|
||||
{
|
||||
THEN("They are the same")
|
||||
{
|
||||
CHECK(firstZero.ToQuaternion() == secondZero.ToQuaternion());
|
||||
CHECK(firstZero.ToQuaternion() == Nz::EulerAnglesf(Nz::Quaternionf(1.f, 0.f, 0.f, 0.f)));
|
||||
CHECK(secondZero.ToQuaternion() == Nz::EulerAnglesf(Nz::Quaternionf(1.f, 0.f, 0.f, 0.f)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("Three rotation of 90 on each axis")
|
||||
{
|
||||
Nz::EulerAnglesf euler90P(Nz::DegreeAnglef(90.f), 0.f, 0.f);
|
||||
Nz::EulerAnglesf euler90Y(0.f, Nz::DegreeAnglef(90.f), 0.f);
|
||||
Nz::EulerAnglesf euler90R(0.f, 0.f, Nz::DegreeAnglef(90.f));
|
||||
|
||||
WHEN("We transform the axis")
|
||||
{
|
||||
THEN("This is supposed to be left-handed")
|
||||
{
|
||||
Nz::Vector3f rotation90P = euler90P.ToQuaternion() * Nz::Vector3f::UnitY();
|
||||
Nz::Vector3f rotation90Y = euler90Y.ToQuaternion() * Nz::Vector3f::UnitZ();
|
||||
Nz::Vector3f rotation90R = euler90R.ToQuaternion() * Nz::Vector3f::UnitX();
|
||||
|
||||
CHECK(rotation90P == Nz::Vector3f::UnitZ());
|
||||
CHECK(rotation90Y == Nz::Vector3f::UnitX());
|
||||
CHECK(rotation90R == Nz::Vector3f::UnitY());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("Euler angles with rotation 45 on each axis")
|
||||
{
|
||||
WHEN("We convert to quaternion")
|
||||
{
|
||||
THEN("These results are expected")
|
||||
{
|
||||
CHECK(Nz::EulerAnglesf(Nz::DegreeAnglef(45.f), 0.f, 0.f) == Nz::EulerAnglesf(Nz::Quaternionf(0.923879504204f, 0.382683455944f, 0.f, 0.f).ToEulerAngles()));
|
||||
CHECK(Nz::EulerAnglesf(0.f, Nz::DegreeAnglef(45.f), 0.f) == Nz::EulerAnglesf(Nz::Quaternionf(0.923879504204f, 0.f, 0.382683455944f, 0.f).ToEulerAngles()));
|
||||
CHECK(Nz::EulerAnglesf(0.f, 0.f, Nz::DegreeAnglef(45.f)) == Nz::EulerAnglesf(Nz::Quaternionf(0.923879504204f, 0.f, 0.f, 0.382683455944f).ToEulerAngles()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("Three euler angles: (0, 22.5, 22.5), (90, 90, 0) and (30, 0, 30)")
|
||||
{
|
||||
Nz::EulerAnglesf euler45(Nz::DegreeAnglef(0.f), Nz::DegreeAnglef(22.5f), Nz::DegreeAnglef(22.5f));
|
||||
Nz::EulerAnglesf euler90(Nz::DegreeAnglef(90.f), Nz::DegreeAnglef(90.f), Nz::DegreeAnglef(0.f));
|
||||
Nz::EulerAnglesf euler30(Nz::DegreeAnglef(30.f), Nz::DegreeAnglef(0.f), Nz::DegreeAnglef(30.f));
|
||||
|
||||
WHEN("We convert them to quaternion")
|
||||
{
|
||||
THEN("And then convert to euler angles, we have identity")
|
||||
{
|
||||
Nz::EulerAnglesf tmp = Nz::Quaternionf(euler45.ToQuaternion()).ToEulerAngles();
|
||||
CHECK(tmp.pitch.ToDegrees() == Catch::Approx(0.f));
|
||||
CHECK(tmp.yaw.ToDegrees() == Catch::Approx(22.5f));
|
||||
CHECK(tmp.roll.ToDegrees() == Catch::Approx(22.5f));
|
||||
|
||||
tmp = Nz::Quaternionf(euler90.ToQuaternion()).ToEulerAngles();
|
||||
CHECK(tmp.pitch.ToDegrees() == Catch::Approx(90.f));
|
||||
CHECK(tmp.yaw.ToDegrees() == Catch::Approx(90.f));
|
||||
CHECK(tmp.roll.ToDegrees() == Catch::Approx(0.f));
|
||||
|
||||
tmp = Nz::Quaternionf(euler30.ToQuaternion()).ToEulerAngles();
|
||||
CHECK(tmp.pitch.ToDegrees() == Catch::Approx(30.f));
|
||||
CHECK(tmp.yaw.ToDegrees() == Catch::Approx(0.f).margin(0.0001f));
|
||||
CHECK(tmp.roll.ToDegrees() == Catch::Approx(30.f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("An angle of 45 degrees")
|
||||
{
|
||||
Nz::DegreeAnglef angle(45.f);
|
||||
|
||||
WHEN("We convert it to Euler angles")
|
||||
{
|
||||
Nz::EulerAnglesf eulerAngles(angle);
|
||||
|
||||
THEN("It should be equal to a 2D rotation of 45 degrees")
|
||||
{
|
||||
CHECK(eulerAngles == Nz::EulerAnglesf(0.f, 0.f, 45.f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
95
tests/UnitTests/Engine/Math/FrustumTest.cpp
Normal file
95
tests/UnitTests/Engine/Math/FrustumTest.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include <Nazara/Math/Frustum.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
SCENARIO("Frustum", "[MATH][FRUSTUM]")
|
||||
{
|
||||
GIVEN("One frustum (90, 1, 1, 1000, (0, 0, 0), (1, 0, 0))")
|
||||
{
|
||||
Nz::Frustumf frustum = Nz::Frustumf::Build(Nz::DegreeAnglef(90.f), 1.f, 1.f, 1000.f, Nz::Vector3f::Zero(), Nz::Vector3f::UnitX());
|
||||
|
||||
WHEN("We ask for intersection with objects outside the frustum")
|
||||
{
|
||||
THEN("These results are expected")
|
||||
{
|
||||
Nz::BoundingVolumef bv(Nz::Boxf(Nz::Vector3f::Zero(), Nz::Vector3f::Unit()));
|
||||
bv.Update(Nz::Matrix4f::Identity());
|
||||
REQUIRE(Nz::IntersectionSide::Outside == frustum.Intersect(bv));
|
||||
REQUIRE(Nz::IntersectionSide::Outside == frustum.Intersect(Nz::Boxf(Nz::Vector3f::Zero(), Nz::Vector3f::Unit() * 0.9f)));
|
||||
Nz::OrientedBoxf obb(Nz::Boxf(Nz::Vector3f::Zero(), Nz::Vector3f::Unit() * 0.9f));
|
||||
obb.Update(Nz::Matrix4f::Identity());
|
||||
REQUIRE(Nz::IntersectionSide::Outside == frustum.Intersect(obb));
|
||||
REQUIRE(Nz::IntersectionSide::Outside == frustum.Intersect(Nz::Spheref(Nz::Vector3f::Zero(), 0.5f)));
|
||||
Nz::Vector3f tmp = Nz::Vector3f::Zero();
|
||||
REQUIRE(Nz::IntersectionSide::Outside == frustum.Intersect(&tmp, 1));
|
||||
tmp = Nz::Vector3f::UnitX() * -10.f;
|
||||
REQUIRE(Nz::IntersectionSide::Outside == frustum.Intersect(&tmp, 1));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We ask for intersection with objects inside the frustum")
|
||||
{
|
||||
THEN("These results are expected")
|
||||
{
|
||||
Nz::BoundingVolumef bv(Nz::Boxf(500.f, -0.5f, -0.5f, 1.f, 1.f, 1.f));
|
||||
bv.Update(Nz::Matrix4f::Identity());
|
||||
|
||||
REQUIRE(Nz::IntersectionSide::Inside == frustum.Intersect(bv));
|
||||
REQUIRE(Nz::IntersectionSide::Inside == frustum.Intersect(Nz::Boxf(Nz::Vector3f::UnitX() * 500.f, Nz::Vector3f::Unit())));
|
||||
Nz::OrientedBoxf obb(Nz::Boxf(Nz::Vector3f::UnitX() * 100.f, Nz::Vector3f::Unit()));
|
||||
obb.Update(Nz::Matrix4f::Identity());
|
||||
REQUIRE(Nz::IntersectionSide::Inside == frustum.Intersect(obb));
|
||||
REQUIRE(Nz::IntersectionSide::Inside == frustum.Intersect(Nz::Spheref(Nz::Vector3f::UnitX() * 100.f, 0.5f)));
|
||||
Nz::Vector3f tmp = Nz::Vector3f::UnitX() * 100.f;
|
||||
REQUIRE(Nz::IntersectionSide::Inside == frustum.Intersect(&tmp, 1));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We ask for contains with objects outside the frustum")
|
||||
{
|
||||
THEN("These results are expected")
|
||||
{
|
||||
Nz::BoundingVolumef bv(Nz::Boxf(0.f, -0.25f, -0.25f, 0.5f, 0.5f, 0.5f));
|
||||
bv.Update(Nz::Matrix4f::Identity());
|
||||
CHECK(!frustum.Contains(bv));
|
||||
CHECK(!frustum.Contains(Nz::Boxf(0.f, -0.25f, -0.25f, 0.5f, 0.5f, 0.5f)));
|
||||
Nz::OrientedBoxf obb(Nz::Boxf(0.f, -0.25f, -0.25f, 0.5f, 0.5f, 0.5f));
|
||||
obb.Update(Nz::Matrix4f::Identity());
|
||||
CHECK(!frustum.Contains(obb));
|
||||
CHECK(!frustum.Contains(Nz::Spheref(Nz::Vector3f::Zero(), 0.5f)));
|
||||
Nz::Vector3f tmp = Nz::Vector3f::Zero();
|
||||
CHECK(!frustum.Contains(&tmp, 1));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We ask for contains with objects inside the frustum")
|
||||
{
|
||||
THEN("These results are expected")
|
||||
{
|
||||
Nz::BoundingVolumef bv(Nz::Boxf(500.f, -0.5f, -0.5f, 1.f, 1.f, 1.f));
|
||||
bv.Update(Nz::Matrix4f::Identity());
|
||||
CHECK(frustum.Contains(bv));
|
||||
CHECK(frustum.Contains(Nz::Boxf(500.f, -0.5f, -0.5f, 1.f, 1.f, 1.f)));
|
||||
Nz::OrientedBoxf obb(Nz::Boxf(500.f, -0.5f, -0.5f, 1.f, 1.f, 1.f));
|
||||
obb.Update(Nz::Matrix4f::Identity());
|
||||
CHECK(frustum.Contains(obb));
|
||||
CHECK(frustum.Contains(Nz::Spheref(Nz::Vector3f::UnitX() * 500.f, 1.f)));
|
||||
Nz::Vector3f tmp = Nz::Vector3f::UnitX() * 500.f;
|
||||
CHECK(frustum.Contains(&tmp, 1));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We test for edge cases")
|
||||
{
|
||||
THEN("Implementation defined these")
|
||||
{
|
||||
Nz::BoundingVolumef nullVolume = Nz::BoundingVolumef::Null();
|
||||
CHECK(!frustum.Contains(nullVolume));
|
||||
Nz::BoundingVolumef infiniteVolume = Nz::BoundingVolumef::Infinite();
|
||||
CHECK(frustum.Contains(infiniteVolume));
|
||||
REQUIRE(frustum.Intersect(nullVolume) == Nz::IntersectionSide::Outside);
|
||||
REQUIRE(frustum.Intersect(infiniteVolume) == Nz::IntersectionSide::Intersecting);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
309
tests/UnitTests/Engine/Math/Matrix4Test.cpp
Normal file
309
tests/UnitTests/Engine/Math/Matrix4Test.cpp
Normal file
@@ -0,0 +1,309 @@
|
||||
#include <Nazara/Math/Matrix4.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <array>
|
||||
|
||||
SCENARIO("Matrix4", "[MATH][MATRIX4]")
|
||||
{
|
||||
GIVEN("Two identity matrix")
|
||||
{
|
||||
Nz::Matrix4f firstIdentity(Nz::Matrix4<int>::Identity());
|
||||
Nz::Matrix4f secondIdentity(1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f);
|
||||
|
||||
WHEN("We compare them")
|
||||
{
|
||||
THEN("They are equal")
|
||||
{
|
||||
REQUIRE(firstIdentity == secondIdentity);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We multiply the first with a Nz::Vector")
|
||||
{
|
||||
THEN("Nz::Vector stay the same")
|
||||
{
|
||||
CHECK(firstIdentity * Nz::Vector2f::Unit() == Nz::Vector2f::Unit());
|
||||
CHECK(firstIdentity * Nz::Vector3f::Unit() == Nz::Vector3f::Unit());
|
||||
CHECK(firstIdentity * Nz::Vector4f(1.f, 1.f, 1.f, 1.f) == Nz::Vector4f(1.f, 1.f, 1.f, 1.f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We multiply them")
|
||||
{
|
||||
THEN("It keeps being a identity")
|
||||
{
|
||||
CHECK(firstIdentity.Concatenate(secondIdentity) == firstIdentity);
|
||||
CHECK(firstIdentity.ConcatenateTransform(secondIdentity) == firstIdentity);
|
||||
CHECK((firstIdentity * secondIdentity) == firstIdentity);
|
||||
CHECK((1.f * firstIdentity) == firstIdentity);
|
||||
CHECK(firstIdentity.Inverse() == secondIdentity.InverseTransform());
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We transpose one of this matrix")
|
||||
{
|
||||
THEN("Identity transposed is the same than identity")
|
||||
{
|
||||
Nz::Matrix4f transposedIdentity;
|
||||
firstIdentity.GetTransposed(&transposedIdentity);
|
||||
REQUIRE(firstIdentity == transposedIdentity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("Two different matrix")
|
||||
{
|
||||
Nz::Matrix4f matrix1(1.0f, 0.0f, 0.0f, 0.0f,
|
||||
7.0f, 2.0f, 0.0f, 0.0f,
|
||||
1.0f, 5.0f, 3.0f, 0.0f,
|
||||
8.0f, 9.0f, 2.0f, 4.0f);
|
||||
|
||||
Nz::Matrix4f matrix2(1.0f, 1.0f, 2.0f, -1.0f,
|
||||
-2.0f, -1.0f, -2.0f, 2.0f,
|
||||
4.0f, 2.0f, 5.0f, -4.0f,
|
||||
5.0f, -3.0f, -7.0f, -6.0f);
|
||||
|
||||
WHEN("We ask for determinant")
|
||||
{
|
||||
THEN("These results are expected")
|
||||
{
|
||||
CHECK(matrix1.GetDeterminant() == Catch::Approx(24.f));
|
||||
CHECK(matrix2.GetDeterminant() == Catch::Approx(-1.f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We multiply the matrix and its inverse")
|
||||
{
|
||||
Nz::Matrix4f invMatrix1;
|
||||
matrix1.GetInverse(&invMatrix1);
|
||||
|
||||
Nz::Matrix4f invMatrix2;
|
||||
matrix2.GetInverse(&invMatrix2);
|
||||
|
||||
THEN("We get the identity")
|
||||
{
|
||||
Nz::Matrix4f tmp = matrix1 * invMatrix1;
|
||||
CHECK(tmp.m32 == Catch::Approx(0.f).margin(0.0001f));
|
||||
CHECK(tmp.m42 == Catch::Approx(0.f).margin(0.0001f));
|
||||
tmp.m32 = 0.f;
|
||||
tmp.m42 = 0.f;
|
||||
CHECK(tmp == Nz::Matrix4f::Identity());
|
||||
CHECK((matrix2 * invMatrix2) == Nz::Matrix4f::Identity());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("One transformed matrix from rotation 45 and translation 0")
|
||||
{
|
||||
Nz::Matrix4f transformedMatrix = Nz::Matrix4f::Transform(Nz::Vector3f::Zero(), Nz::Quaternionf::Identity());
|
||||
REQUIRE(transformedMatrix == Nz::Matrix4f::Identity());
|
||||
|
||||
WHEN("We compare with the right matrix")
|
||||
{
|
||||
THEN("Rotation around X")
|
||||
{
|
||||
transformedMatrix.MakeTransform(Nz::Vector3f::Zero(), Nz::EulerAnglesf(Nz::DegreeAnglef(45.f), 0.f, 0.f).ToQuaternion());
|
||||
Nz::Matrix4f rotation45X(1.f, 0.f, 0.f, 0.f,
|
||||
0.f, std::sqrt(2.f) / 2.f, std::sqrt(2.f) / 2.f, 0.f,
|
||||
0.f, -std::sqrt(2.f) / 2.f, std::sqrt(2.f) / 2.f, 0.f,
|
||||
0.f, 0.f, 0.f, 1.f);
|
||||
|
||||
CHECK(transformedMatrix == rotation45X);
|
||||
transformedMatrix.MakeTransform(Nz::Vector3f::Unit(), Nz::EulerAnglesf(Nz::DegreeAnglef(45.f), 0.f, 0.f).ToQuaternion());
|
||||
rotation45X.ApplyTranslation(Nz::Vector3f::Unit());
|
||||
CHECK(transformedMatrix == rotation45X);
|
||||
}
|
||||
|
||||
THEN("Rotation around Y")
|
||||
{
|
||||
transformedMatrix.MakeTransform(Nz::Vector3f::Zero(), Nz::EulerAnglesf(0.f, Nz::DegreeAnglef(45.f), 0.f).ToQuaternion());
|
||||
Nz::Matrix4f rotation45Y(std::sqrt(2.f) / 2.f, 0.f, -std::sqrt(2.f) / 2.f, 0.f,
|
||||
0.f, 1.f, 0.f, 0.f,
|
||||
std::sqrt(2.f) / 2.f, 0.f, std::sqrt(2.f) / 2.f, 0.f,
|
||||
0.f, 0.f, 0.f, 1.f);
|
||||
|
||||
CHECK(transformedMatrix == rotation45Y);
|
||||
transformedMatrix.MakeTransform(Nz::Vector3f::Unit(), Nz::EulerAnglesf(0.f, Nz::DegreeAnglef(45.f), 0.f).ToQuaternion());
|
||||
rotation45Y.ApplyTranslation(Nz::Vector3f::Unit());
|
||||
CHECK(transformedMatrix == rotation45Y);
|
||||
}
|
||||
|
||||
THEN("Rotation around Z")
|
||||
{
|
||||
transformedMatrix.MakeTransform(Nz::Vector3f::Zero(), Nz::EulerAnglesf(0.f, 0.f, Nz::DegreeAnglef(45.f)).ToQuaternion());
|
||||
Nz::Matrix4f rotation45Z( std::sqrt(2.f) / 2.f, std::sqrt(2.f) / 2.f, 0.f, 0.f,
|
||||
-std::sqrt(2.f) / 2.f, std::sqrt(2.f) / 2.f, 0.f, 0.f,
|
||||
0.f, 0.f, 1.f, 0.f,
|
||||
0.f, 0.f, 0.f, 1.f);
|
||||
|
||||
CHECK(transformedMatrix == rotation45Z);
|
||||
transformedMatrix.MakeTransform(Nz::Vector3f::Unit(), Nz::EulerAnglesf(Nz::EulerAnglesf(0.f, 0.f, Nz::DegreeAnglef(45.f)).ToQuaternion()));
|
||||
rotation45Z.ApplyTranslation(Nz::Vector3f::Unit());
|
||||
CHECK(transformedMatrix == rotation45Z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("An identity matrix")
|
||||
{
|
||||
std::array<float, 16> content{{ 1.f, 0.f, 0.f, 0.f,
|
||||
0.f, 1.f, 0.f, 0.f,
|
||||
0.f, 0.f, 1.f, 0.f,
|
||||
0.f, 0.f, 0.f, 1.f
|
||||
}};
|
||||
|
||||
Nz::Matrix4f identity(content.data());
|
||||
REQUIRE(identity.IsIdentity());
|
||||
|
||||
WHEN("We rotate it from pitch 30")
|
||||
{
|
||||
Nz::Quaternionf rotation(Nz::EulerAnglesf(Nz::DegreeAnglef(30.f), 0.f, 0.f));
|
||||
identity.ApplyRotation(rotation);
|
||||
|
||||
THEN("We should retrieve it")
|
||||
{
|
||||
REQUIRE(identity.GetRotation() == rotation);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We rotate it from yaw 30")
|
||||
{
|
||||
Nz::Quaternionf rotation(Nz::EulerAnglesf(0.f, Nz::DegreeAnglef(30.f), 0.f));
|
||||
identity.ApplyRotation(rotation);
|
||||
|
||||
THEN("We should retrieve it")
|
||||
{
|
||||
REQUIRE(identity.GetRotation() == rotation);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We rotate it from roll 30")
|
||||
{
|
||||
Nz::Quaternionf rotation(Nz::EulerAnglesf(0.f, 0.f, Nz::DegreeAnglef(30.f)));
|
||||
identity.ApplyRotation(rotation);
|
||||
|
||||
THEN("We should retrieve it")
|
||||
{
|
||||
REQUIRE(identity.GetRotation() == rotation);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We rotate it from a strange rotation")
|
||||
{
|
||||
Nz::Quaternionf rotation(Nz::EulerAnglesf(Nz::DegreeAnglef(10.f), Nz::DegreeAnglef(20.f), Nz::DegreeAnglef(30.f)));
|
||||
identity.ApplyRotation(rotation);
|
||||
|
||||
THEN("We should retrieve it")
|
||||
{
|
||||
REQUIRE(identity.GetRotation() == rotation);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We scale it")
|
||||
{
|
||||
Nz::Vector3f scale(1.f, 2.f, 3.f);
|
||||
Nz::Vector3f squaredScale(scale.x * scale.x, scale.y * scale.y, scale.z * scale.z);
|
||||
identity.ApplyScale(scale);
|
||||
|
||||
THEN("We should retrieve it")
|
||||
{
|
||||
CHECK(identity.GetScale() == scale);
|
||||
CHECK(identity.GetSquaredScale() == squaredScale);
|
||||
}
|
||||
|
||||
AND_THEN("With a rotation")
|
||||
{
|
||||
identity.ApplyRotation(Nz::EulerAnglesf(Nz::DegreeAnglef(10.f), Nz::DegreeAnglef(20.f), Nz::DegreeAnglef(30.f)));
|
||||
Nz::Vector3f retrievedScale = identity.GetScale();
|
||||
CHECK(retrievedScale.x == Catch::Approx(scale.x));
|
||||
CHECK(retrievedScale.y == Catch::Approx(scale.y));
|
||||
CHECK(retrievedScale.z == Catch::Approx(scale.z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A matrix with a negative determinant")
|
||||
{
|
||||
Nz::Matrix4f negativeDeterminant( -1.f, 0.f, 0.f, 0.f,
|
||||
0.f, 1.f, 0.f, 0.f,
|
||||
0.f, 0.f, 1.f, 0.f,
|
||||
0.f, 0.f, 0.f, 1.f);
|
||||
|
||||
WHEN("We ask information about determinant")
|
||||
{
|
||||
THEN("We expect those to be true")
|
||||
{
|
||||
CHECK(negativeDeterminant.GetDeterminant() == Catch::Approx(-1.f));
|
||||
CHECK(!negativeDeterminant.HasScale());
|
||||
CHECK(negativeDeterminant.HasNegativeScale());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("Some transformed matrices")
|
||||
{
|
||||
Nz::Vector3f simpleTranslation = Nz::Vector3f::Zero();
|
||||
Nz::Quaternionf simpleRotation = Nz::Quaternionf::Identity();
|
||||
Nz::Vector3f simpleScale = Nz::Vector3f::Unit();
|
||||
Nz::Matrix4f simple = Nz::Matrix4f::Transform(simpleTranslation, simpleRotation, simpleScale);
|
||||
|
||||
Nz::Vector3f complexTranslation = Nz::Vector3f(-5.f, 7.f, 3.5f);
|
||||
Nz::Quaternionf complexRotation = Nz::EulerAnglesf(Nz::DegreeAnglef(-22.5f), Nz::DegreeAnglef(30.f), Nz::DegreeAnglef(15.f));
|
||||
Nz::Vector3f complexScale = Nz::Vector3f(1.f, 2.f, 0.5f);
|
||||
Nz::Matrix4f complex = Nz::Matrix4f::Transform(complexTranslation, complexRotation, complexScale);
|
||||
|
||||
Nz::Vector3f oppositeTranslation = Nz::Vector3f(-5.f, 7.f, 3.5f);
|
||||
Nz::Quaternionf oppositeRotation = Nz::EulerAnglesf(Nz::DegreeAnglef(-90.f), Nz::DegreeAnglef(0.f), Nz::DegreeAnglef(0.f));
|
||||
Nz::Vector3f oppositeScale = Nz::Vector3f(1.f, 2.f, 0.5f);
|
||||
Nz::Matrix4f opposite = Nz::Matrix4f::Transform(oppositeTranslation, oppositeRotation, oppositeScale);
|
||||
|
||||
WHEN("We retrieve the different components")
|
||||
{
|
||||
THEN("It should be the original ones")
|
||||
{
|
||||
CHECK(simple.GetTranslation() == simpleTranslation);
|
||||
CHECK(simple.GetRotation() == simpleRotation);
|
||||
CHECK(simple.GetScale() == simpleScale);
|
||||
|
||||
/*CHECK(complex.GetTranslation() == complexTranslation);
|
||||
CHECK(complex.GetRotation() == complexRotation);
|
||||
CHECK(complex.GetScale() == complexScale);
|
||||
|
||||
CHECK(opposite.GetTranslation() == oppositeTranslation);
|
||||
CHECK(opposite.GetRotation() == oppositeRotation);
|
||||
CHECK(opposite.GetScale() == oppositeScale);*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("Some defined matrix and its opposite")
|
||||
{
|
||||
Nz::Vector3f translation(-5.f, 3.f, 0.5);
|
||||
Nz::Matrix4f initial = Nz::Matrix4f::Translate(translation);
|
||||
Nz::Quaternionf rotation = Nz::EulerAnglesf(Nz::DegreeAnglef(30.f), Nz::DegreeAnglef(-90.f), 0.f);
|
||||
initial.ApplyRotation(rotation);
|
||||
|
||||
Nz::Matrix4f simple = Nz::Matrix4f::Transform(-translation, rotation.GetInverse(), Nz::Vector3f::Unit());
|
||||
|
||||
WHEN("We multiply them together")
|
||||
{
|
||||
Nz::Matrix4f result = Nz::Matrix4f::Concatenate(simple, initial);
|
||||
|
||||
THEN("We should get the identity")
|
||||
{
|
||||
Nz::Matrix4f identity = Nz::Matrix4f::Identity();
|
||||
for (int i = 0; i != 4; ++i)
|
||||
{
|
||||
Nz::Vector4f row = result.GetRow(i);
|
||||
Nz::Vector4f column = result.GetColumn(i);
|
||||
for (int j = 0; j != 4; ++j)
|
||||
{
|
||||
CHECK(Nz::NumberEquals(row[j], identity(i, j), 0.00001f));
|
||||
CHECK(Nz::NumberEquals(column[j], identity(i, j), 0.00001f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
63
tests/UnitTests/Engine/Math/OrientedBoxTest.cpp
Normal file
63
tests/UnitTests/Engine/Math/OrientedBoxTest.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#include <Nazara/Math/OrientedBox.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
SCENARIO("OrientedBox", "[MATH][ORIENTEDBOX]")
|
||||
{
|
||||
GIVEN("Two center and unit oriented boxes")
|
||||
{
|
||||
Nz::OrientedBoxf firstCenterAndUnit(Nz::Boxf(0.f, 0.f, 0.f, 1.f, 1.f, 1.f));
|
||||
Nz::OrientedBoxf secondCenterAndUnit(Nz::OrientedBox<int>(Nz::Boxi(Nz::Vector3i::Zero(), Nz::Vector3i::Unit())));
|
||||
|
||||
firstCenterAndUnit.Update(Nz::Matrix4f::Identity());
|
||||
secondCenterAndUnit.Update(Nz::Matrix4f::Identity());
|
||||
|
||||
WHEN("We compare them")
|
||||
{
|
||||
THEN("They are the same")
|
||||
{
|
||||
REQUIRE(firstCenterAndUnit == secondCenterAndUnit);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We ask if they are valid")
|
||||
{
|
||||
THEN("They are valid")
|
||||
{
|
||||
CHECK(firstCenterAndUnit.IsValid());
|
||||
CHECK(secondCenterAndUnit.IsValid());
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We multiply them")
|
||||
{
|
||||
THEN("Results are different between operator * and update(ScaleMatrix) but corners are the same")
|
||||
{
|
||||
firstCenterAndUnit.localBox.Scale(2.f);
|
||||
firstCenterAndUnit.Update(Nz::Matrix4f::Identity());
|
||||
secondCenterAndUnit.Update(Nz::Matrix4f::Scale(Nz::Vector3f::Unit() * 2.f));
|
||||
|
||||
REQUIRE(firstCenterAndUnit != secondCenterAndUnit);
|
||||
for (unsigned int i = 0; i < Nz::BoxCornerCount; ++i)
|
||||
{
|
||||
REQUIRE(firstCenterAndUnit(i) == secondCenterAndUnit(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We try to lerp")
|
||||
{
|
||||
THEN("Compilation should be fine")
|
||||
{
|
||||
Nz::OrientedBoxf nullOrientedBox = Nz::OrientedBoxf::Zero();
|
||||
Nz::OrientedBoxf centerAndUnit = firstCenterAndUnit;
|
||||
nullOrientedBox.Update(Nz::Matrix4f::Identity());
|
||||
centerAndUnit.Update(Nz::Matrix4f::Identity());
|
||||
Nz::OrientedBoxf result(Nz::Boxf(Nz::Vector3f::Zero(), Nz::Vector3f::Unit() * 0.5f));
|
||||
result.Update(Nz::Matrix4f::Identity());
|
||||
|
||||
REQUIRE(Nz::OrientedBoxf::Lerp(nullOrientedBox, centerAndUnit, 0.5f) == result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
tests/UnitTests/Engine/Math/PlaneTest.cpp
Normal file
88
tests/UnitTests/Engine/Math/PlaneTest.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#include <Nazara/Math/Plane.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
SCENARIO("Plane", "[MATH][PLANE]")
|
||||
{
|
||||
GIVEN("Two planes normal(1, 1, 1), distance 1")
|
||||
{
|
||||
Nz::Planef firstPlane(Nz::Vector3f::Unit().Normalize(), 1.f);
|
||||
Nz::Planef secondPlane(Nz::Planed(Nz::Vector3d::Unit().Normalize(), 1.0));
|
||||
|
||||
WHEN("We compare them")
|
||||
{
|
||||
THEN("They are equal")
|
||||
{
|
||||
REQUIRE(firstPlane == secondPlane);
|
||||
}
|
||||
|
||||
AND_THEN("We compare with normal(-1, -1, -1), distance -1")
|
||||
{
|
||||
REQUIRE(firstPlane == Nz::Planef(-Nz::Vector3f::Unit().Normalize(), -1.f));
|
||||
}
|
||||
|
||||
AND_THEN("They have the same distance from the same point")
|
||||
{
|
||||
Nz::Vector3f point(-2.f, 3.f, 1.f);
|
||||
REQUIRE(firstPlane.Distance(point) == Catch::Approx(secondPlane.Distance(point)));
|
||||
REQUIRE(firstPlane.Distance(-2.f, 3.f, 1.f) == Catch::Approx(0.1547f));
|
||||
}
|
||||
|
||||
AND_THEN("Distance between Plane (0, 1, 0), distance 1 and point (0, 2, 0) should be 1")
|
||||
{
|
||||
REQUIRE(Nz::Planef(Nz::Vector3f::UnitY(), 1.f).Distance(Nz::Vector3f::UnitY() * 2.f) == Catch::Approx(1.f));
|
||||
}
|
||||
|
||||
AND_THEN("Distance between Plane (0, 1, 0), distance 5 and point (0, 2, 0) should be -3")
|
||||
{
|
||||
REQUIRE(Nz::Planef(Nz::Vector3f::UnitY(), 5.f).Distance(Nz::Vector3f::UnitY() * 2.f) == Catch::Approx(-3.f));
|
||||
}
|
||||
|
||||
AND_THEN("Distance between Plane (0, 1, 0), distance 1000 and point (0, 500, 0) and (0, 1500, 0)")
|
||||
{
|
||||
REQUIRE(Nz::Planef(Nz::Vector3f::UnitY(), 1000.f).Distance(Nz::Vector3f::UnitY() * 500.f) == Catch::Approx(-500.f));
|
||||
REQUIRE(Nz::Planef(Nz::Vector3f::UnitY(), 1000.f).Distance(Nz::Vector3f::UnitY() * 1500.f) == Catch::Approx(500.f));
|
||||
}
|
||||
|
||||
AND_THEN("Distance between Plane (0, -1, 0), distance -1000 and point (0, 500, 0) and (0, 1500, 0)")
|
||||
{
|
||||
REQUIRE(Nz::Planef(-Nz::Vector3f::UnitY(), -1000.f).Distance(Nz::Vector3f::UnitY() * 500.f) == Catch::Approx(500.f));
|
||||
REQUIRE(Nz::Planef(-Nz::Vector3f::UnitY(), -1000.f).Distance(Nz::Vector3f::UnitY() * 1500.f) == Catch::Approx(-500.f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We try to lerp")
|
||||
{
|
||||
THEN("Compilation should be fine")
|
||||
{
|
||||
Nz::Planef planeXY = Nz::Planef::XY();
|
||||
Nz::Planef planeXZ = Nz::Planef::XZ();
|
||||
Nz::Vector3f result = Nz::Vector3f(0.f, 1.f, 1.f) * 0.5f;
|
||||
result.Normalize();
|
||||
REQUIRE(Nz::Planef::Lerp(planeXY, planeXZ, 0.5f) == Nz::Planef(result, 0.f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("The plane XZ, distance 1 with 3 points (0, 1, 0), (1, 1, 1), (-1, 1, 0)")
|
||||
{
|
||||
WHEN("We do a positive plane")
|
||||
{
|
||||
Nz::Planef xy(Nz::Vector3f(2.f, 1.f, 0.f), Nz::Vector3f(-1.f, 1.f, -1.f), Nz::Vector3f(-1.f, 1.f, 0.f));
|
||||
|
||||
THEN("It must be equal to XZ distance 1")
|
||||
{
|
||||
REQUIRE(xy == Nz::Planef(Nz::Vector3f::UnitY(), 1.f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We do a negative plane")
|
||||
{
|
||||
Nz::Planef xy(Nz::Vector3f(0.f, 1.f, 0.f), Nz::Vector3f(1.f, 1.f, 1.f), Nz::Vector3f(-1.f, 1.f, 0.f));
|
||||
THEN("It must be equal to XZ distance 1")
|
||||
{
|
||||
REQUIRE(xy == Nz::Planef(-Nz::Vector3f::UnitY(), -1.f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
246
tests/UnitTests/Engine/Math/QuaternionTest.cpp
Normal file
246
tests/UnitTests/Engine/Math/QuaternionTest.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
#include <Nazara/Math/Quaternion.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
SCENARIO("Quaternion", "[MATH][QUATERNION]")
|
||||
{
|
||||
GIVEN("Two quaternions (0, 1, 0, 0)")
|
||||
{
|
||||
Nz::Quaternionf firstQuaternion(Nz::DegreeAnglef(180.f), Nz::Vector3f::UnitX());
|
||||
Nz::Quaternionf secondQuaternion(0.f, 1.f, 0.f, 0.f);
|
||||
|
||||
WHEN("We compare them")
|
||||
{
|
||||
THEN("They are the same and the proprieties of quaternions are respected")
|
||||
{
|
||||
REQUIRE(firstQuaternion == secondQuaternion);
|
||||
REQUIRE(firstQuaternion.ComputeW() == secondQuaternion.Normalize());
|
||||
REQUIRE(firstQuaternion.Conjugate() == secondQuaternion.Inverse());
|
||||
REQUIRE(firstQuaternion.DotProduct(secondQuaternion) == Catch::Approx(1.f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We do some operations")
|
||||
{
|
||||
THEN("Multiply with a vectorX is identity")
|
||||
{
|
||||
REQUIRE((firstQuaternion * Nz::Vector3f::UnitX()) == Nz::Vector3f::UnitX());
|
||||
}
|
||||
|
||||
AND_THEN("Multiply with a vectorY or Z is opposite")
|
||||
{
|
||||
REQUIRE((firstQuaternion * Nz::Vector3f::UnitY()) == -Nz::Vector3f::UnitY());
|
||||
REQUIRE((firstQuaternion * Nz::Vector3f::UnitZ()) == -Nz::Vector3f::UnitZ());
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We invert or normalize Zero quaternion")
|
||||
{
|
||||
Nz::Quaternionf zero = Nz::Quaternionf::Zero();
|
||||
|
||||
THEN("It's meant not to be changed")
|
||||
{
|
||||
Nz::Quaternionf inverted = zero.GetInverse();
|
||||
float tmp = -1.f;
|
||||
Nz::Quaternionf normalized = zero.GetNormal(&tmp);
|
||||
|
||||
REQUIRE(inverted == zero);
|
||||
REQUIRE(normalized == zero);
|
||||
REQUIRE(tmp == Catch::Approx(0.f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("The four unit quaternions")
|
||||
{
|
||||
Nz::Quaternionf w(1.f, 0.f, 0.f, 0.f);
|
||||
Nz::Quaternionf x(0.f, 1.f, 0.f, 0.f);
|
||||
Nz::Quaternionf y(0.f, 0.f, 1.f, 0.f);
|
||||
Nz::Quaternionf z(0.f, 0.f, 0.f, 1.f);
|
||||
|
||||
Nz::Quaternionf xyzw = x * y * z * w;
|
||||
|
||||
WHEN("We ask for the norm")
|
||||
{
|
||||
THEN("They are all equal to 1")
|
||||
{
|
||||
REQUIRE(w.Magnitude() == Catch::Approx(1.f));
|
||||
REQUIRE(x.Magnitude() == Catch::Approx(1.f));
|
||||
REQUIRE(y.Magnitude() == Catch::Approx(1.f));
|
||||
REQUIRE(z.Magnitude() == Catch::Approx(1.f));
|
||||
REQUIRE(xyzw.Magnitude() == Catch::Approx(1.f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We multiply them")
|
||||
{
|
||||
THEN("Results shoud follow")
|
||||
{
|
||||
Nz::Quaternionf oppositeOfW(-1.f, 0.f, 0.f, 0.f);
|
||||
Nz::Quaternionf oppositeOfX = x.GetConjugate();
|
||||
Nz::Quaternionf oppositeOfY = y.GetConjugate();
|
||||
Nz::Quaternionf oppositeOfZ = z.GetConjugate();
|
||||
|
||||
REQUIRE((x * x) == oppositeOfW);
|
||||
REQUIRE((y * y) == oppositeOfW);
|
||||
REQUIRE((z * z) == oppositeOfW);
|
||||
REQUIRE((x * y * z) == oppositeOfW);
|
||||
|
||||
REQUIRE((x * y) == z);
|
||||
REQUIRE((y * x) == oppositeOfZ);
|
||||
REQUIRE((y * z) == x);
|
||||
REQUIRE((z * y) == oppositeOfX);
|
||||
REQUIRE((z * x) == y);
|
||||
REQUIRE((x * z) == oppositeOfY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("Two different quaternions (10, (1, 0, 0) and (20, (1, 0, 0))")
|
||||
{
|
||||
Nz::Quaternionf x10 = Nz::Quaternionf(Nz::DegreeAnglef(10.f), Nz::Vector3f::UnitX());
|
||||
Nz::Quaternionf x20 = x10 * x10;
|
||||
|
||||
Nz::Quaternionf x30a = x10 * x20;
|
||||
Nz::Quaternionf x30b = x20 * x10;
|
||||
|
||||
WHEN("We multiply them")
|
||||
{
|
||||
THEN("These results are expected")
|
||||
{
|
||||
REQUIRE(x20 == Nz::Quaternionf(Nz::DegreeAnglef(20.f), Nz::Vector3f::UnitX()));
|
||||
REQUIRE(x30a == x30b);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("Convert euler to quaternion")
|
||||
{
|
||||
Nz::Quaternionf X45(Nz::EulerAnglesf(Nz::DegreeAnglef(45.f), 0.f, 0.f));
|
||||
Nz::Quaternionf Y45(Nz::EulerAnglesf(0.f, Nz::DegreeAnglef(45.f), 0.f));
|
||||
Nz::Quaternionf Z45(Nz::EulerAnglesf(0.f, 0.f, Nz::DegreeAnglef(45.f)));
|
||||
|
||||
THEN("They must be equal")
|
||||
{
|
||||
REQUIRE(X45 == Nz::Quaternionf(0.9238795f, 0.38268346f, 0.f, 0.f));
|
||||
REQUIRE(Y45 == Nz::Quaternionf(0.9238795f, 0.f, 0.38268346f, 0.f));
|
||||
REQUIRE(Z45 == Nz::Quaternionf(0.9238795f, 0.f, 0.f, 0.38268346f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We convert to euler angles and then to quaternions")
|
||||
{
|
||||
THEN("These results are expected")
|
||||
{
|
||||
REQUIRE(x30a.ToEulerAngles() == x30b.ToEulerAngles());
|
||||
REQUIRE(x30a.ToEulerAngles().ToQuaternion() == x30b.ToEulerAngles().ToQuaternion());
|
||||
|
||||
Nz::Quaternionf tmp(1.f, 1.f, 0.f, 0.f);
|
||||
tmp.Normalize();
|
||||
REQUIRE(tmp == tmp.ToEulerAngles().ToQuaternion());
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We slerp")
|
||||
{
|
||||
THEN("The half of 10 and 30 is 20")
|
||||
{
|
||||
Nz::Quaternionf slerpx10x30a = Nz::Quaternionf::Slerp(x10, x30a, 0.5f);
|
||||
REQUIRE(slerpx10x30a.w == Catch::Approx(x20.w));
|
||||
REQUIRE(slerpx10x30a.x == Catch::Approx(x20.x));
|
||||
REQUIRE(slerpx10x30a.y == Catch::Approx(x20.y));
|
||||
REQUIRE(slerpx10x30a.z == Catch::Approx(x20.z));
|
||||
Nz::Quaternionf slerpx10x30b = Nz::Quaternionf::Slerp(x10, x30b, 0.5f);
|
||||
REQUIRE(slerpx10x30b.w == Catch::Approx(x20.w));
|
||||
REQUIRE(slerpx10x30b.x == Catch::Approx(x20.x));
|
||||
REQUIRE(slerpx10x30b.y == Catch::Approx(x20.y));
|
||||
REQUIRE(slerpx10x30b.z == Catch::Approx(x20.z));
|
||||
REQUIRE(Nz::Quaternionf::Slerp(x10, x30a, 0.f) == x10);
|
||||
REQUIRE(Nz::Quaternionf::Slerp(x10, x30a, 1.f) == x30a);
|
||||
}
|
||||
|
||||
AND_THEN("The half of 45 is 22.5")
|
||||
{
|
||||
Nz::Quaternionf quaterionA(Nz::DegreeAnglef(0.f), Nz::Vector3f::UnitZ());
|
||||
Nz::Quaternionf quaterionB(Nz::DegreeAnglef(45.f), Nz::Vector3f::UnitZ());
|
||||
Nz::Quaternionf quaternionC = Nz::Quaternionf::Slerp(quaterionA, quaterionB, 0.5f);
|
||||
|
||||
Nz::Quaternionf unitZ225(Nz::DegreeAnglef(22.5f), Nz::Vector3f::UnitZ());
|
||||
REQUIRE(quaternionC.w == Catch::Approx(unitZ225.w));
|
||||
REQUIRE(quaternionC.x == Catch::Approx(unitZ225.x));
|
||||
REQUIRE(quaternionC.y == Catch::Approx(unitZ225.y));
|
||||
REQUIRE(quaternionC.z == Catch::Approx(unitZ225.z));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We get the rotation between two vectors")
|
||||
{
|
||||
THEN("The rotation in right-handed is 90 degree on z")
|
||||
{
|
||||
Nz::Quaternionf rotationBetweenXY = Nz::Quaternionf::RotationBetween(Nz::Vector3f::UnitX(), Nz::Vector3f::UnitY());
|
||||
Nz::Quaternionf rotation90Z(Nz::DegreeAnglef(90.f), Nz::Vector3f::UnitZ());
|
||||
REQUIRE(rotation90Z == rotationBetweenXY);
|
||||
}
|
||||
|
||||
THEN("The rotation in right-handed is 90 degree on y")
|
||||
{
|
||||
Nz::Quaternionf rotationBetweenXZ = Nz::Quaternionf::RotationBetween(Nz::Vector3f::UnitX(), Nz::Vector3f::UnitZ());
|
||||
Nz::Quaternionf rotation90Y(Nz::DegreeAnglef(-90.f), Nz::Vector3f::UnitY());
|
||||
REQUIRE(rotation90Y == rotationBetweenXZ);
|
||||
}
|
||||
|
||||
THEN("The rotation in right-handed is 90 degree on x")
|
||||
{
|
||||
Nz::Quaternionf rotationBetweenYZ = Nz::Quaternionf::RotationBetween(Nz::Vector3f::UnitY(), Nz::Vector3f::UnitZ());
|
||||
Nz::Quaternionf rotation90X(Nz::DegreeAnglef(90.f), Nz::Vector3f::UnitX());
|
||||
REQUIRE(rotation90X == rotationBetweenYZ);
|
||||
}
|
||||
|
||||
THEN("The rotation in right-handed is 90 degree on y with non-unit vectors")
|
||||
{
|
||||
Nz::Vector3f origin(1.f, 0.f, 1.f);
|
||||
Nz::Vector3f extremity(-1.f, 0.f, 1.f);
|
||||
Nz::Quaternionf rotation = Nz::Quaternionf::RotationBetween(origin, extremity);
|
||||
REQUIRE(rotation * origin == extremity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("Different angles")
|
||||
{
|
||||
Nz::Quaternionf rotation90X(0.707f, 0.707f, 0.f, 0.f);
|
||||
Nz::Quaternionf rotation90Y(0.707f, 0.f, 0.707f, 0.f);
|
||||
Nz::Quaternionf rotation90Z(0.707f, 0.f, 0.f, 0.707f);
|
||||
|
||||
Nz::Quaternionf rotation180X(0.f, 1.f, 0.f, 0.f);
|
||||
Nz::Quaternionf rotation180Y(0.f, 0.f, 1.f, 0.f);
|
||||
Nz::Quaternionf rotation180Z(0.f, 0.f, 0.f, 1.f);
|
||||
|
||||
Nz::Quaternionf rotation270X(-0.707f, 0.707f, 0.f, 0.f);
|
||||
Nz::Quaternionf rotation270Y(-0.707f, 0.f, 0.707f, 0.f);
|
||||
Nz::Quaternionf rotation270Z(-0.707f, 0.f, 0.f, 0.707f);
|
||||
|
||||
Nz::Quaternionf special(0.707f, 0.006f, 0.006f, 0.707f);
|
||||
|
||||
WHEN("We convert them to euler angles")
|
||||
{
|
||||
THEN("Those are equal to")
|
||||
{
|
||||
CHECK(Nz::NumberEquals(rotation90X.ToEulerAngles().pitch.ToDegrees(), 90.f, 0.1f));
|
||||
CHECK(Nz::NumberEquals(rotation90Y.ToEulerAngles().yaw.ToDegrees(), 90.f, 0.1f));
|
||||
CHECK(Nz::NumberEquals(rotation90Z.ToEulerAngles().roll.ToDegrees(), 90.f, 0.1f));
|
||||
|
||||
CHECK(rotation180X == Nz::EulerAnglesf(180.f, 0.f, 0.f));
|
||||
CHECK(rotation180Y == Nz::EulerAnglesf(0.f, 180.f, 0.f));
|
||||
CHECK(rotation180Z == Nz::EulerAnglesf(0.f, 0.f, 180.f));
|
||||
|
||||
CHECK(Nz::NumberEquals(rotation270X.ToEulerAngles().pitch.ToDegrees(), -90.f, 0.1f));
|
||||
CHECK(Nz::NumberEquals(rotation270Y.ToEulerAngles().yaw.ToDegrees(), -90.f, 0.1f));
|
||||
CHECK(Nz::NumberEquals(rotation270Z.ToEulerAngles().roll.ToDegrees(), -90.f, 0.1f));
|
||||
|
||||
CHECK(Nz::NumberEquals(special.ToEulerAngles().pitch.ToDegrees(), 0.f, 0.1f));
|
||||
CHECK(Nz::NumberEquals(special.ToEulerAngles().yaw.ToDegrees(), 1.f, 0.1f));
|
||||
CHECK(Nz::NumberEquals(special.ToEulerAngles().roll.ToDegrees(), 90.f, 0.1f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
106
tests/UnitTests/Engine/Math/RayTest.cpp
Normal file
106
tests/UnitTests/Engine/Math/RayTest.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
#include <Nazara/Math/Ray.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
SCENARIO("Ray", "[MATH][RAY]")
|
||||
{
|
||||
GIVEN("Two same Rays (0, 0, 0) -> (0, 1, 0)")
|
||||
{
|
||||
Nz::Rayf ray(Nz::Ray<int>(Nz::Plane<int>::XY(), Nz::Plane<int>::YZ()));
|
||||
Nz::Rayf secondRay(0.f, 0.f, 0.f, 0.f, 1.f, 0.f);
|
||||
|
||||
WHEN("We compare them")
|
||||
{
|
||||
THEN("They are the same and Y axis")
|
||||
{
|
||||
REQUIRE(ray == secondRay);
|
||||
REQUIRE(ray == Nz::Rayf::AxisY());
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We ask for the closest point")
|
||||
{
|
||||
THEN("The point that is multiple on the Nz::Ray, is at multiple")
|
||||
{
|
||||
REQUIRE(ray.ClosestPoint(secondRay.GetPoint(1.f)) == Catch::Approx(1.f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We ask for intersection")
|
||||
{
|
||||
THEN("For the Box collision's")
|
||||
{
|
||||
float tmpClosest;
|
||||
float tmpFurthest;
|
||||
|
||||
REQUIRE(ray.Intersect(Nz::Boxf(-0.5f, 1.f, -0.5f, 1.f, 1.f, 1.f), &tmpClosest, &tmpFurthest));
|
||||
CHECK(ray.GetPoint(tmpClosest) == Nz::Vector3f::UnitY());
|
||||
CHECK(ray.GetPoint(tmpFurthest) == (Nz::Vector3f::UnitY() * 2.f));
|
||||
REQUIRE(!ray.Intersect(Nz::Boxf(-10.f, 1.f, -10.f, 1.f, 1.f, 1.f), &tmpClosest, &tmpFurthest));
|
||||
}
|
||||
|
||||
THEN("For the Nz::Plane collision's")
|
||||
{
|
||||
float tmpHit = -1.f;
|
||||
|
||||
REQUIRE(ray.Intersect(Nz::Planef(Nz::Vector3f::UnitY(), 1.f), &tmpHit));
|
||||
CHECK(ray.GetPoint(tmpHit) == Nz::Vector3f::UnitY());
|
||||
REQUIRE(ray.Intersect(Nz::Planef::XZ(), &tmpHit));
|
||||
CHECK(ray.GetPoint(tmpHit) == Nz::Vector3f::Zero());
|
||||
REQUIRE(ray.Intersect(Nz::Planef(Nz::Vector3f::UnitY(), 2.f), &tmpHit));
|
||||
CHECK(ray.GetPoint(tmpHit) == 2.f * Nz::Vector3f::UnitY());
|
||||
|
||||
CHECK(!ray.Intersect(Nz::Planef(Nz::Vector3f::UnitX(), 1.f)));
|
||||
}
|
||||
|
||||
THEN("For the Sphere collision's")
|
||||
{
|
||||
float tmpClosest;
|
||||
float tmpFurthest;
|
||||
|
||||
CHECK(ray.Intersect(Nz::Spheref(Nz::Vector3f::UnitY(), 0.1f), &tmpClosest, &tmpFurthest));
|
||||
REQUIRE(ray.GetPoint(tmpClosest) == Nz::Vector3f::UnitY() * 0.9f);
|
||||
REQUIRE(ray.GetPoint(tmpFurthest) == (Nz::Vector3f::UnitY() * 1.1f));
|
||||
|
||||
CHECK(!ray.Intersect(Nz::Spheref(Nz::Vector3f::UnitX(), 0.9f)));
|
||||
}
|
||||
|
||||
THEN("For the bounding volume collision's")
|
||||
{
|
||||
Nz::BoundingVolumef nullVolume(Nz::Extend::Null);
|
||||
CHECK(!ray.Intersect(nullVolume));
|
||||
|
||||
float tmpClosest = -1.f;
|
||||
float tmpFurthest = -1.f;
|
||||
Nz::BoundingVolumef infiniteVolume(Nz::Extend::Infinite);
|
||||
CHECK(ray.Intersect(infiniteVolume, &tmpClosest, &tmpFurthest));
|
||||
CHECK(tmpClosest == Catch::Approx(0.f));
|
||||
CHECK(tmpFurthest == std::numeric_limits<float>::infinity());
|
||||
}
|
||||
|
||||
THEN("For the triangle collision's")
|
||||
{
|
||||
Nz::Vector3f firstPoint(0.f, 1.f, 1.f);
|
||||
Nz::Vector3f secondPoint(-1.f, 1.f, -1.f);
|
||||
Nz::Vector3f thidPoint(1.f, 1.f, -1.f);
|
||||
float tmpHit = -1.f;
|
||||
|
||||
CHECK(ray.Intersect(firstPoint, secondPoint, thidPoint, &tmpHit));
|
||||
REQUIRE(ray.GetPoint(tmpHit) == Nz::Vector3f::UnitY());
|
||||
|
||||
Nz::Vector3f offset = Nz::Vector3f(10.f, 0.f, 10.f);
|
||||
CHECK(!ray.Intersect(firstPoint + offset, secondPoint + offset, thidPoint + offset, &tmpHit));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We try to lerp")
|
||||
{
|
||||
THEN("Compilation should be fine")
|
||||
{
|
||||
Nz::Rayf AxisX = Nz::Rayf::AxisX();
|
||||
Nz::Rayf AxisY = Nz::Rayf::AxisY();
|
||||
REQUIRE(Nz::Rayf::Lerp(AxisX, AxisY, 0.5f) == (Nz::Rayf(Nz::Vector3f::Zero(), Nz::Vector3f(0.5f, 0.5f, 0.f))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
77
tests/UnitTests/Engine/Math/RectTest.cpp
Normal file
77
tests/UnitTests/Engine/Math/RectTest.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include <Nazara/Math/Rect.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
SCENARIO("Rect", "[MATH][RECT]")
|
||||
{
|
||||
GIVEN("Two same Nz::Rectangles center and unit lengths")
|
||||
{
|
||||
Nz::Rectf firstCenterAndUnit(0.f, 0.f, 1.f, 1.f);
|
||||
Nz::Rectf secondCenterAndUnit(Nz::Recti::FromExtends(Nz::Vector2i::Unit(), Nz::Vector2i::Zero()));
|
||||
|
||||
WHEN("We ask if they are the same")
|
||||
{
|
||||
THEN("They should be")
|
||||
{
|
||||
CHECK(firstCenterAndUnit == secondCenterAndUnit);
|
||||
CHECK(firstCenterAndUnit.GetCenter() == secondCenterAndUnit.GetCenter());
|
||||
CHECK(firstCenterAndUnit.GetCorner(Nz::RectCorner::LeftBottom) == secondCenterAndUnit.GetCorner(Nz::RectCorner::LeftBottom));
|
||||
CHECK(firstCenterAndUnit.IsValid());
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We move one from (0.5, 0.5)")
|
||||
{
|
||||
firstCenterAndUnit.Translate(Nz::Vector2f(0.5f, 0.5f));
|
||||
|
||||
THEN("The collision should be (0.5, 0.5) -> (0.5, 0.5)")
|
||||
{
|
||||
Nz::Rectf tmp;
|
||||
CHECK(firstCenterAndUnit.Intersect(secondCenterAndUnit, &tmp));
|
||||
REQUIRE(tmp == Nz::Rectf(0.5f, 0.5f, 0.5f, 0.5f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We make it empty")
|
||||
{
|
||||
THEN("It's not valid")
|
||||
{
|
||||
CHECK(firstCenterAndUnit.Scale(0.f).IsNull());
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We ask for infos")
|
||||
{
|
||||
THEN("These results are expected")
|
||||
{
|
||||
CHECK(firstCenterAndUnit.GetLengths() == Nz::Vector2f::Unit());
|
||||
CHECK(firstCenterAndUnit.GetMaximum() == Nz::Vector2f::Unit());
|
||||
CHECK(firstCenterAndUnit.GetMinimum() == Nz::Vector2f::Zero());
|
||||
CHECK(firstCenterAndUnit.GetNegativeVertex(Nz::Vector2f::Unit()) == Nz::Vector2f::Zero());
|
||||
CHECK(firstCenterAndUnit.GetPosition() == Nz::Vector2f::Zero());
|
||||
CHECK(firstCenterAndUnit.GetPositiveVertex(Nz::Vector2f::Unit()) == Nz::Vector2f::Unit());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We ask for intersection")
|
||||
{
|
||||
Nz::Rectf intersection;
|
||||
CHECK(firstCenterAndUnit.Intersect(secondCenterAndUnit, &intersection));
|
||||
CHECK(intersection == Nz::Rectf(1.f, 1.f));
|
||||
CHECK(intersection == Nz::Rectf(Nz::Vector2f(1.f, 1.f)));
|
||||
}
|
||||
|
||||
WHEN("We try to lerp")
|
||||
{
|
||||
THEN("Compilation should be fine")
|
||||
{
|
||||
Nz::Rectf nullRect = Nz::Rectf::Zero();
|
||||
Nz::Rectf centerAndUnit = firstCenterAndUnit;
|
||||
Nz::Rectf result(Nz::Vector2f::Zero(), Nz::Vector2f::Unit() * 0.5f);
|
||||
|
||||
REQUIRE(Nz::Rectf::Lerp(nullRect, centerAndUnit, 0.5f) == result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
104
tests/UnitTests/Engine/Math/SphereTest.cpp
Normal file
104
tests/UnitTests/Engine/Math/SphereTest.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#include <Nazara/Math/Sphere.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
SCENARIO("Sphere", "[MATH][SPHERE]")
|
||||
{
|
||||
GIVEN("Two same sphere center and unit")
|
||||
{
|
||||
Nz::Spheref firstCenterAndUnit(0.f, 0.f, 0.f, 1.f);
|
||||
Nz::Spheref secondCenterAndUnit(Nz::Sphere<int>(Nz::Vector3i::Zero(), 1));
|
||||
|
||||
WHEN("We compare them")
|
||||
{
|
||||
THEN("They are the same")
|
||||
{
|
||||
REQUIRE(firstCenterAndUnit == secondCenterAndUnit);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We ask if they intersect or contain")
|
||||
{
|
||||
THEN("These results are expected for Contains")
|
||||
{
|
||||
CHECK(firstCenterAndUnit.Contains(0.5f, 0.5f, 0.5f));
|
||||
CHECK(firstCenterAndUnit.Contains(Nz::Boxf(Nz::Vector3f::Zero(), Nz::Vector3f::Unit() * 0.5f)));
|
||||
CHECK(!firstCenterAndUnit.Contains(Nz::Boxf(Nz::Vector3f::Zero(), Nz::Vector3f::Unit() * 5.f)));
|
||||
}
|
||||
|
||||
THEN("There are for Intersect")
|
||||
{
|
||||
CHECK(firstCenterAndUnit.Intersect(Nz::Boxf(Nz::Vector3f::Zero(), Nz::Vector3f::Unit() * 0.5f)));
|
||||
CHECK(firstCenterAndUnit.Intersect(Nz::Boxf(Nz::Vector3f::Zero(), Nz::Vector3f::Unit() * 5.f)));
|
||||
CHECK(!firstCenterAndUnit.Intersect(Nz::Boxf(Nz::Vector3f::Unit() * 5.f, Nz::Vector3f::Unit())));
|
||||
|
||||
CHECK(firstCenterAndUnit.Intersect(Nz::Spheref(Nz::Vector3f::Zero(), 0.5f)));
|
||||
CHECK(firstCenterAndUnit.Intersect(Nz::Spheref(Nz::Vector3f::Zero(), 5.f)));
|
||||
CHECK(!firstCenterAndUnit.Intersect(Nz::Spheref(Nz::Vector3f::Unit() * 5.f, 1.f)));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We ask for distance")
|
||||
{
|
||||
THEN("These results are expected because we don't take into account the border")
|
||||
{
|
||||
CHECK(firstCenterAndUnit.Distance(Nz::Vector3f::UnitX() * 2.f) == Catch::Approx(1.f));
|
||||
|
||||
Nz::Spheref tmp(Nz::Vector3f::UnitX(), 1.f);
|
||||
CHECK(tmp.Distance(Nz::Vector3f::UnitX() * 4.f) == Catch::Approx(2.f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We get sphere from box unit and center")
|
||||
{
|
||||
Nz::Boxf centerUnitBox = Nz::Boxf::FromExtends(Nz::Vector3f::Unit() * -0.5f, Nz::Vector3f::Unit() * 0.5f);
|
||||
|
||||
THEN("This is equal to sphere center and radius 0.75")
|
||||
{
|
||||
CHECK(centerUnitBox.GetSquaredBoundingSphere() == Nz::Spheref(Nz::Vector3f::Zero(), 0.75f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We ask for positive and negative vertex")
|
||||
{
|
||||
Nz::Vector3f positiveVector = Nz::Vector3f::UnitY();
|
||||
|
||||
THEN("Positive vertex should be the same with centered and unit sphere")
|
||||
{
|
||||
CHECK(positiveVector == firstCenterAndUnit.GetPositiveVertex(positiveVector));
|
||||
}
|
||||
|
||||
AND_THEN("Negative vertex should be the opposite")
|
||||
{
|
||||
CHECK(-positiveVector == firstCenterAndUnit.GetNegativeVertex(positiveVector));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We extend the unit sphere to one point")
|
||||
{
|
||||
Nz::Vector3f point = Nz::Vector3f::UnitY() * 2.f;
|
||||
|
||||
firstCenterAndUnit.ExtendTo(point);
|
||||
|
||||
REQUIRE(firstCenterAndUnit.radius == Catch::Approx(2.f));
|
||||
|
||||
THEN("Sphere must contain it and distance should be good")
|
||||
{
|
||||
CHECK(firstCenterAndUnit.Contains(point));
|
||||
CHECK(firstCenterAndUnit.Distance(point) == Catch::Approx(0.f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We try to lerp")
|
||||
{
|
||||
THEN("Compilation should be fine")
|
||||
{
|
||||
Nz::Spheref nullRect = Nz::Spheref::Zero();
|
||||
Nz::Spheref centerAndUnit = firstCenterAndUnit;
|
||||
Nz::Spheref result(Nz::Vector3f::Zero(), 0.5f);
|
||||
|
||||
REQUIRE(Nz::Spheref::Lerp(nullRect, centerAndUnit, 0.5f) == result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
97
tests/UnitTests/Engine/Math/Vector2Test.cpp
Normal file
97
tests/UnitTests/Engine/Math/Vector2Test.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <Nazara/Math/Vector2.hpp>
|
||||
#include <Nazara/Math/Vector4.hpp>
|
||||
|
||||
SCENARIO("Vector2", "[MATH][VECTOR2]")
|
||||
{
|
||||
GIVEN("Two same vectors (1, 1)")
|
||||
{
|
||||
Nz::Vector2f firstUnit(1.f);
|
||||
Nz::Vector2f secondUnit(Nz::Vector2i(Nz::Vector4i(1, 1, 3, 5)));
|
||||
|
||||
WHEN("We compare them")
|
||||
{
|
||||
THEN("They are the same")
|
||||
{
|
||||
REQUIRE(firstUnit == secondUnit);
|
||||
REQUIRE(firstUnit <= secondUnit);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We test the dot product")
|
||||
{
|
||||
Nz::Vector2f tmp(-1.f, 1.f);
|
||||
|
||||
THEN("These are perpendicular")
|
||||
{
|
||||
REQUIRE(firstUnit.AbsDotProduct(tmp) == Catch::Approx(2.f));
|
||||
REQUIRE(firstUnit.DotProduct(tmp) == Catch::Approx(0.f));
|
||||
REQUIRE(firstUnit.AngleBetween(tmp) == Nz::DegreeAnglef(90.f));
|
||||
Nz::Vector2f negativeUnitX = -Nz::Vector2f::UnitX();
|
||||
REQUIRE(negativeUnitX.AngleBetween(negativeUnitX + Nz::Vector2f(0, 0.0000001f)) == Nz::DegreeAnglef(360.f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We ask for distance from (-2, -3)")
|
||||
{
|
||||
Nz::Vector2f tmp(-2.f, -3.f);
|
||||
Nz::Vector2f tmp2(-1.f, -1.f);
|
||||
|
||||
THEN("These are expected")
|
||||
{
|
||||
REQUIRE(firstUnit.Distance(tmp2) == Catch::Approx(2.f * std::sqrt(2.f)));
|
||||
REQUIRE(firstUnit.Distance(tmp) == Catch::Approx(5.f));
|
||||
REQUIRE(firstUnit.SquaredDistance(tmp) == Catch::Approx(25.f));
|
||||
|
||||
REQUIRE(firstUnit.GetSquaredLength() == Catch::Approx(2.f));
|
||||
REQUIRE(firstUnit.GetLength() == Catch::Approx(std::sqrt(2.f)));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We nomalize the vectors")
|
||||
{
|
||||
float ratio = 0.f;
|
||||
THEN("For normal cases should be normal")
|
||||
{
|
||||
Nz::Vector2f normalized = firstUnit.GetNormal(&ratio);
|
||||
REQUIRE(normalized == (Nz::Vector2f::Unit() / std::sqrt(2.f)));
|
||||
REQUIRE(ratio == Catch::Approx(std::sqrt(2.f)));
|
||||
}
|
||||
|
||||
THEN("For null vector")
|
||||
{
|
||||
Nz::Vector2f zero = Nz::Vector2f::Zero();
|
||||
REQUIRE(zero.GetNormal(&ratio) == Nz::Vector2f::Zero());
|
||||
REQUIRE(ratio == Catch::Approx(0.f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We try to maximize and minimize")
|
||||
{
|
||||
Nz::Vector2f maximize(2.f, 1.f);
|
||||
Nz::Vector2f minimize(1.f, 2.f);
|
||||
|
||||
THEN("The minimised and maximised should be (1, 1) and (2, 2)")
|
||||
{
|
||||
Nz::Vector2f maximized = maximize;
|
||||
Nz::Vector2f minimized = minimize;
|
||||
REQUIRE(minimized.Minimize(maximized) == Nz::Vector2f::Unit());
|
||||
REQUIRE(maximize.Maximize(minimize) == (2.f * Nz::Vector2f::Unit()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
WHEN("We try to lerp")
|
||||
{
|
||||
THEN("Compilation should be fine")
|
||||
{
|
||||
Nz::Vector2f zero = Nz::Vector2f::Zero();
|
||||
Nz::Vector2f unit = Nz::Vector2f::Unit();
|
||||
REQUIRE(Nz::Vector2f::Lerp(zero, unit, 0.5f) == (Nz::Vector2f::Unit() * 0.5f));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
120
tests/UnitTests/Engine/Math/Vector3Test.cpp
Normal file
120
tests/UnitTests/Engine/Math/Vector3Test.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <Nazara/Math/Vector2.hpp>
|
||||
#include <Nazara/Math/Vector3.hpp>
|
||||
#include <Nazara/Math/Vector4.hpp>
|
||||
|
||||
SCENARIO("Vector3", "[MATH][VECTOR3]")
|
||||
{
|
||||
GIVEN("Two same unit vector")
|
||||
{
|
||||
Nz::Vector3f firstUnit(1.f, 1.f, 1.f);
|
||||
Nz::Vector3f secondUnit(Nz::Vector3i(Nz::Vector4i(1, 1, 1, 5)));
|
||||
|
||||
WHEN("We compare them")
|
||||
{
|
||||
THEN("They are the same")
|
||||
{
|
||||
REQUIRE(firstUnit == secondUnit);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We test the dot product")
|
||||
{
|
||||
Nz::Vector3f tmp(-1.f, 0.f, 1.f);
|
||||
|
||||
THEN("These results are expected")
|
||||
{
|
||||
REQUIRE(firstUnit.AbsDotProduct(tmp) == Catch::Approx(2.f));
|
||||
REQUIRE(firstUnit.DotProduct(tmp) == Catch::Approx(0.f));
|
||||
REQUIRE(firstUnit.AngleBetween(tmp) == Nz::DegreeAnglef(90.f));
|
||||
REQUIRE(firstUnit.AngleBetween(-firstUnit) == Nz::DegreeAnglef(180.f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We test the cross product")
|
||||
{
|
||||
THEN("These results are expected")
|
||||
{
|
||||
REQUIRE(Nz::Vector3f::CrossProduct(Nz::Vector3f::UnitX(), Nz::Vector3f::UnitY()) == Nz::Vector3f::UnitZ());
|
||||
REQUIRE(Nz::Vector3f::CrossProduct(Nz::Vector3f(1.f, 2.f, 3.f), Nz::Vector3f(3.f, 2.f, 1.f)) == Nz::Vector3f(-4.f, 8.f, -4.f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We ask for distance")
|
||||
{
|
||||
Nz::Vector3f tmp(-1.f, -5.f, -8.f);
|
||||
|
||||
THEN("These are expected")
|
||||
{
|
||||
REQUIRE(firstUnit.Distance(tmp) == Catch::Approx(11.f));
|
||||
REQUIRE(firstUnit.SquaredDistance(tmp) == Catch::Approx(121.f));
|
||||
|
||||
REQUIRE(firstUnit.GetSquaredLength() == Catch::Approx(3.f));
|
||||
REQUIRE(firstUnit.GetLength() == Catch::Approx(std::sqrt(3.f)));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We nomalize the vectors")
|
||||
{
|
||||
float ratio = 0.f;
|
||||
THEN("For normal cases should be normal")
|
||||
{
|
||||
Nz::Vector3f normalized = firstUnit.GetNormal(&ratio);
|
||||
REQUIRE(normalized == (Nz::Vector3f::Unit() / std::sqrt(3.f)));
|
||||
REQUIRE(ratio == Catch::Approx(std::sqrt(3.f)));
|
||||
}
|
||||
|
||||
THEN("For null vector")
|
||||
{
|
||||
Nz::Vector3f zero = Nz::Vector3f::Zero();
|
||||
REQUIRE(zero.GetNormal(&ratio) == Nz::Vector3f::Zero());
|
||||
REQUIRE(ratio == Catch::Approx(0.f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We try to maximize and minimize")
|
||||
{
|
||||
Nz::Vector3f maximize(2.f, 1.f, 2.f);
|
||||
Nz::Vector3f minimize(1.f, 2.f, 1.f);
|
||||
|
||||
THEN("The minimised and maximised should be (1, 1, 1) and (2, 2, 2)")
|
||||
{
|
||||
Nz::Vector3f maximized = maximize;
|
||||
Nz::Vector3f minimized = minimize;
|
||||
REQUIRE(minimized.Minimize(maximized) == Nz::Vector3f::Unit());
|
||||
REQUIRE(maximize.Maximize(minimize) == (2.f * Nz::Vector3f::Unit()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
WHEN("We try to lerp")
|
||||
{
|
||||
THEN("Compilation should be fine")
|
||||
{
|
||||
Nz::Vector3f zero = Nz::Vector3f::Zero();
|
||||
Nz::Vector3f unit = Nz::Vector3f::Unit();
|
||||
REQUIRE(Nz::Vector3f::Lerp(zero, unit, 0.5f) == (Nz::Vector3f::Unit() * 0.5f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("Two vectors")
|
||||
{
|
||||
Nz::Vector2f unit = Nz::Vector2f::Unit();
|
||||
Nz::Vector3f smaller(-1.f, unit);
|
||||
|
||||
Nz::Vector3f bigger(1.f, unit.x, unit.y);
|
||||
|
||||
WHEN("We combine divisions and multiplications")
|
||||
{
|
||||
Nz::Vector3f result = smaller / bigger;
|
||||
result *= bigger;
|
||||
|
||||
THEN("We should get the identity")
|
||||
{
|
||||
REQUIRE(result == smaller);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
54
tests/UnitTests/Engine/Math/Vector4Test.cpp
Normal file
54
tests/UnitTests/Engine/Math/Vector4Test.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include <Nazara/Math/Vector4.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <Nazara/Math/Vector3.hpp>
|
||||
|
||||
SCENARIO("Vector4", "[MATH][VECTOR4]")
|
||||
{
|
||||
GIVEN("Two same unit vector")
|
||||
{
|
||||
Nz::Vector4f firstUnit(1.f, 1.f, 1.f);
|
||||
Nz::Vector4f secondUnit(Nz::Vector4i(1, 1, 1, 1));
|
||||
|
||||
WHEN("We compare them")
|
||||
{
|
||||
THEN("They are the same")
|
||||
{
|
||||
REQUIRE(firstUnit == secondUnit);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We test the dot product")
|
||||
{
|
||||
Nz::Vector4f tmp(-1.f, 0.f, 1.f, 0.f);
|
||||
|
||||
THEN("These results are expected")
|
||||
{
|
||||
REQUIRE(firstUnit.AbsDotProduct(tmp) == Catch::Approx(2.f));
|
||||
REQUIRE(firstUnit.DotProduct(tmp) == Catch::Approx(0.f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We normalize")
|
||||
{
|
||||
Nz::Vector4f tmp(1.f, 1.f, 1.f, 3.f);
|
||||
|
||||
THEN("These results are expected")
|
||||
{
|
||||
REQUIRE(firstUnit.Normalize() == Nz::Vector4f(1.f, Nz::Vector3f::Unit()));
|
||||
REQUIRE(tmp.Normalize() == Nz::Vector4f(Nz::Vector3f::Unit() * (1.f / 3.f), 1.f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We try to lerp")
|
||||
{
|
||||
THEN("Compilation should be fine")
|
||||
{
|
||||
Nz::Vector4f zero = Nz::Vector4f::Zero();
|
||||
Nz::Vector4f unitX = Nz::Vector4f::UnitX();
|
||||
REQUIRE(Nz::Vector4f::Lerp(zero, unitX, 0.5f) == Nz::Vector4f(Nz::Vector3f::UnitX() * 0.5f, 1.f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
tests/UnitTests/Engine/Modules.hpp
Normal file
7
tests/UnitTests/Engine/Modules.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
// used for precompiled headers generation
|
||||
#include <Nazara/Core.hpp>
|
||||
#include <Nazara/Math.hpp>
|
||||
#include <Nazara/Network.hpp>
|
||||
#include <Nazara/Physics2D.hpp>
|
||||
#include <Nazara/Physics3D.hpp>
|
||||
#include <Nazara/Utility.hpp>
|
||||
156
tests/UnitTests/Engine/Network/IpAddressTest.cpp
Normal file
156
tests/UnitTests/Engine/Network/IpAddressTest.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
#include <Nazara/Network/Algorithm.hpp>
|
||||
#include <Nazara/Network/IpAddress.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
SCENARIO("IpAddress", "[NETWORK][IPADDRESS]")
|
||||
{
|
||||
WHEN("Parsing IP")
|
||||
{
|
||||
// IPv4
|
||||
CHECK(Nz::IpAddress("0.0.0.0") == Nz::IpAddress::AnyIpV4);
|
||||
CHECK(Nz::IpAddress("0.0.0.0:0") == Nz::IpAddress::AnyIpV4);
|
||||
CHECK(Nz::IpAddress("0.0.0.0:12345") == Nz::IpAddress(0, 0, 0, 0, 12345));
|
||||
|
||||
Nz::IpAddress loopbackIpV4("127.0.0.1");
|
||||
CHECK(loopbackIpV4 == Nz::IpAddress::LoopbackIpV4);
|
||||
CHECK(static_cast<bool>(loopbackIpV4));
|
||||
CHECK(loopbackIpV4.IsValid());
|
||||
CHECK(loopbackIpV4.IsLoopback());
|
||||
CHECK(loopbackIpV4.GetPort() == 0);
|
||||
CHECK(loopbackIpV4.GetProtocol() == Nz::NetProtocol::IPv4);
|
||||
CHECK(loopbackIpV4.ToString() == "127.0.0.1");
|
||||
CHECK(loopbackIpV4.ToUInt32() == 0x7F000001);
|
||||
|
||||
Nz::IpAddress loopbackIpV4WithPort("127.0.0.1:80");
|
||||
CHECK(loopbackIpV4WithPort != Nz::IpAddress::LoopbackIpV4);
|
||||
CHECK(static_cast<bool>(loopbackIpV4WithPort));
|
||||
CHECK(loopbackIpV4WithPort.IsValid());
|
||||
CHECK(loopbackIpV4WithPort.IsLoopback());
|
||||
CHECK(loopbackIpV4WithPort.GetPort() == 80);
|
||||
CHECK(loopbackIpV4WithPort.GetProtocol() == Nz::NetProtocol::IPv4);
|
||||
CHECK(loopbackIpV4WithPort.ToString() == "127.0.0.1:80");
|
||||
CHECK(loopbackIpV4WithPort.ToUInt32() == 0x7F000001);
|
||||
|
||||
Nz::IpAddress nonregularLoopbackIpV4("127.147.254.15:41235");
|
||||
CHECK(nonregularLoopbackIpV4 != Nz::IpAddress::LoopbackIpV4);
|
||||
CHECK(static_cast<bool>(nonregularLoopbackIpV4));
|
||||
CHECK(nonregularLoopbackIpV4.IsValid());
|
||||
CHECK(nonregularLoopbackIpV4.IsLoopback());
|
||||
CHECK(nonregularLoopbackIpV4.GetPort() == 41235);
|
||||
CHECK(nonregularLoopbackIpV4.GetProtocol() == Nz::NetProtocol::IPv4);
|
||||
CHECK(nonregularLoopbackIpV4.ToString() == "127.147.254.15:41235");
|
||||
CHECK(nonregularLoopbackIpV4.ToUInt32() == 0x7F93FE0F);
|
||||
|
||||
nonregularLoopbackIpV4.SetPort(14738);
|
||||
CHECK(nonregularLoopbackIpV4.GetPort() == 14738);
|
||||
CHECK(nonregularLoopbackIpV4.ToString() == "127.147.254.15:14738");
|
||||
|
||||
// IPv6
|
||||
CHECK(Nz::IpAddress("::") == Nz::IpAddress::AnyIpV6);
|
||||
CHECK(Nz::IpAddress("[::]:0") == Nz::IpAddress::AnyIpV6);
|
||||
CHECK(Nz::IpAddress("[::]:80") == Nz::IpAddress(0, 0, 0, 0, 0, 0, 0, 0, 80));
|
||||
|
||||
CHECK(Nz::IpAddress("1:2:3:4:5:6:7:8") == Nz::IpAddress(1, 2, 3, 4, 5, 6, 7, 8));
|
||||
CHECK(Nz::IpAddress("1:2:3:4:5::7:8") == Nz::IpAddress(1, 2, 3, 4, 5, 0, 7, 8));
|
||||
CHECK(Nz::IpAddress("1:2:3:4::7:8") == Nz::IpAddress(1, 2, 3, 4, 0, 0, 7, 8));
|
||||
CHECK(Nz::IpAddress("1:2:3::7:8") == Nz::IpAddress(1, 2, 3, 0, 0, 0, 7, 8));
|
||||
CHECK(Nz::IpAddress("1:2::5:6:7:8") == Nz::IpAddress(1, 2, 0, 0, 5, 6, 7, 8));
|
||||
CHECK(Nz::IpAddress("1::7:8") == Nz::IpAddress(1, 0, 0, 0, 0, 0, 7, 8));
|
||||
CHECK(Nz::IpAddress("1::8") == Nz::IpAddress(1, 0, 0, 0, 0, 0, 0, 8));
|
||||
CHECK(Nz::IpAddress("::8") == Nz::IpAddress(0, 0, 0, 0, 0, 0, 0, 8));
|
||||
CHECK(Nz::IpAddress("1::") == Nz::IpAddress(1, 0, 0, 0, 0, 0, 0, 0));
|
||||
CHECK(Nz::IpAddress("1:2::8") == Nz::IpAddress(1, 2, 0, 0, 0, 0, 0, 8));
|
||||
|
||||
CHECK(Nz::IpAddress("2001:41d0:2:d4cd::") == Nz::IpAddress(0x2001, 0x41D0, 0x2, 0xD4CD, 0, 0, 0, 0));
|
||||
CHECK(Nz::IpAddress("[2001:41D0:2:D4CD::]:80") == Nz::IpAddress(0x2001, 0x41D0, 0x2, 0xD4CD, 0, 0, 0, 0, 80));
|
||||
|
||||
CHECK(Nz::IpAddress(0x2001, 0x41D0, 0x2, 0, 0, 0, 0, 0xD4CD).ToString() == "2001:41d0:2::d4cd");
|
||||
CHECK(Nz::IpAddress(0x2001, 0x41D0, 0x2, 0, 0, 0, 0, 0xD4CD, 443).ToString() == "[2001:41d0:2::d4cd]:443");
|
||||
CHECK(Nz::IpAddress(0x2001, 0x41D0, 0x2, 0xDEAD, 0xBEEF, 0x42, 0x2022, 0xD4CD, 443).ToString() == "[2001:41d0:2:dead:beef:42:2022:d4cd]:443");
|
||||
|
||||
// All of theses are differents representations of the same IPv6 address
|
||||
std::array<std::string, 8> testAddresses {
|
||||
"2001:db8:0:0:1:0:0:1",
|
||||
"2001:0db8:0:0:1:0:0:1",
|
||||
"2001:db8::1:0:0:1",
|
||||
"2001:db8::0:1:0:0:1",
|
||||
"2001:0db8::1:0:0:1",
|
||||
"2001:db8:0:0:1::1",
|
||||
"2001:db8:0000:0:1::1",
|
||||
"2001:DB8:0:0:1::1"
|
||||
};
|
||||
|
||||
Nz::IpAddress referenceAddress(0x2001, 0xDB8, 0, 0, 1, 0, 0, 1);
|
||||
for (const std::string& str : testAddresses)
|
||||
{
|
||||
INFO(str);
|
||||
CHECK(Nz::IpAddress(str) == referenceAddress);
|
||||
}
|
||||
|
||||
Nz::IpAddress loopbackIpV6("::1");
|
||||
CHECK(loopbackIpV6 == Nz::IpAddress::LoopbackIpV6);
|
||||
CHECK(loopbackIpV6.IsValid());
|
||||
CHECK(loopbackIpV6.IsLoopback());
|
||||
CHECK(loopbackIpV6.GetPort() == 0);
|
||||
CHECK(loopbackIpV6.GetProtocol() == Nz::NetProtocol::IPv6);
|
||||
CHECK(loopbackIpV6.ToString() == "::1");
|
||||
|
||||
Nz::IpAddress loopbackIpV6WithPort("[::1]:443");
|
||||
CHECK(loopbackIpV6WithPort != Nz::IpAddress::LoopbackIpV6);
|
||||
CHECK(loopbackIpV6WithPort.IsValid());
|
||||
CHECK(loopbackIpV6WithPort.IsLoopback());
|
||||
CHECK(loopbackIpV6WithPort.GetPort() == 443);
|
||||
CHECK(loopbackIpV6WithPort.GetProtocol() == Nz::NetProtocol::IPv6);
|
||||
CHECK(loopbackIpV6WithPort.ToString() == "[::1]:443");
|
||||
|
||||
// IPv4-mapped IPv6
|
||||
Nz::IpAddress ipv4MappedIPv6("::ffff:192.168.173.22");
|
||||
CHECK(ipv4MappedIPv6.IsValid());
|
||||
CHECK_FALSE(ipv4MappedIPv6.IsLoopback());
|
||||
CHECK(ipv4MappedIPv6.GetProtocol() == Nz::NetProtocol::IPv6);
|
||||
CHECK(ipv4MappedIPv6.ToString() == "::ffff:192.168.173.22");
|
||||
CHECK(ipv4MappedIPv6 == Nz::IpAddress(0, 0, 0, 0, 0, 0xFFFF, 0xC0A8, 0xAD16));
|
||||
|
||||
Nz::IpAddress ipv4MappedIPv6WithPort("::ffff:192.168.173.22");
|
||||
CHECK(ipv4MappedIPv6WithPort.IsValid());
|
||||
CHECK_FALSE(ipv4MappedIPv6WithPort.IsLoopback());
|
||||
CHECK(ipv4MappedIPv6WithPort.GetProtocol() == Nz::NetProtocol::IPv6);
|
||||
CHECK(ipv4MappedIPv6WithPort.ToString() == "::ffff:192.168.173.22");
|
||||
CHECK(ipv4MappedIPv6WithPort == Nz::IpAddress(0, 0, 0, 0, 0, 0xFFFF, 0xC0A8, 0xAD16));
|
||||
|
||||
// Invalid addresses
|
||||
CHECK_FALSE(Nz::IpAddress("000.123.456.789").IsValid());
|
||||
CHECK_FALSE(Nz::IpAddress("0.0.0.0SomeGarbage").IsValid());
|
||||
CHECK_FALSE(Nz::IpAddress("0.0.0.0:Hell0").IsValid());
|
||||
CHECK_FALSE(Nz::IpAddress("Hey0.0.0.0:12345").IsValid());
|
||||
CHECK_FALSE(Nz::IpAddress("::1:ffff:192.168.173.22").IsValid());
|
||||
CHECK_FALSE(Nz::IpAddress("::ffff:123.412.210.230").IsValid());
|
||||
CHECK_FALSE(Nz::IpAddress("::ffff:127.0.0.0.1").IsValid());
|
||||
CHECK_FALSE(Nz::IpAddress("::ffff:0:255.255.255.255").IsValid());
|
||||
}
|
||||
|
||||
GIVEN("No IpAddress")
|
||||
{
|
||||
WHEN("We get the IP of Nazara")
|
||||
{
|
||||
std::vector<Nz::HostnameInfo> hostnameInfos = Nz::IpAddress::ResolveHostname(Nz::NetProtocol::Any, "nazara.digitalpulsesoftware.net");
|
||||
|
||||
THEN("Result is not null")
|
||||
{
|
||||
CHECK_FALSE(hostnameInfos.empty());
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We convert IP to hostname")
|
||||
{
|
||||
Nz::IpAddress google(8, 8, 8, 8);
|
||||
THEN("Google (DNS) is 8.8.8.8")
|
||||
{
|
||||
std::string dnsAddress = Nz::IpAddress::ResolveAddress(google);
|
||||
bool dnsCheck = dnsAddress == "google-public-dns-a.google.com" || dnsAddress == "dns.google";
|
||||
CHECK(dnsCheck);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
85
tests/UnitTests/Engine/Network/SocketPollerTest.cpp
Normal file
85
tests/UnitTests/Engine/Network/SocketPollerTest.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#include <Nazara/Math/Vector3.hpp>
|
||||
#include <Nazara/Network/NetPacket.hpp>
|
||||
#include <Nazara/Network/SocketPoller.hpp>
|
||||
#include <Nazara/Network/TcpClient.hpp>
|
||||
#include <Nazara/Network/TcpServer.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <random>
|
||||
|
||||
SCENARIO("SocketPoller", "[NETWORK][SOCKETPOLLER]")
|
||||
{
|
||||
GIVEN("A TcpServer and a TcpClient in a selector")
|
||||
{
|
||||
std::random_device rd;
|
||||
std::uniform_int_distribution<Nz::UInt16> dis(1025, 65535);
|
||||
|
||||
Nz::UInt16 port = dis(rd);
|
||||
Nz::TcpServer server;
|
||||
server.EnableBlocking(false);
|
||||
|
||||
REQUIRE(server.Listen(Nz::NetProtocol::IPv4, port) == Nz::SocketState::Bound);
|
||||
|
||||
Nz::IpAddress serverIP(Nz::IpAddress::LoopbackIpV4.ToIPv4(), port);
|
||||
REQUIRE(serverIP.IsValid());
|
||||
|
||||
Nz::SocketPoller serverPoller;
|
||||
Nz::TcpClient clientToServer;
|
||||
|
||||
WHEN("We register the server socket to the poller")
|
||||
{
|
||||
REQUIRE(serverPoller.RegisterSocket(server, Nz::SocketPollEvent::Read));
|
||||
|
||||
THEN("The poller should have registered our socket")
|
||||
{
|
||||
REQUIRE(serverPoller.IsRegistered(server));
|
||||
}
|
||||
AND_THEN("We connect using a TcpClient")
|
||||
{
|
||||
Nz::SocketState state = clientToServer.Connect(serverIP);
|
||||
|
||||
CHECK(state != Nz::SocketState::NotConnected);
|
||||
|
||||
AND_THEN("We wait on our selector, it should return true")
|
||||
{
|
||||
REQUIRE(serverPoller.Wait(1000));
|
||||
|
||||
Nz::TcpClient serverToClient;
|
||||
REQUIRE(server.AcceptClient(&serverToClient));
|
||||
|
||||
WHEN("We register the client socket to the poller")
|
||||
{
|
||||
REQUIRE(serverPoller.RegisterSocket(serverToClient, Nz::SocketPollEvent::Read));
|
||||
|
||||
THEN("The poller should have registered our socket")
|
||||
{
|
||||
REQUIRE(serverPoller.IsRegistered(serverToClient));
|
||||
}
|
||||
|
||||
AND_WHEN("We test sending data from the client to the server and checking the poller")
|
||||
{
|
||||
std::array<char, 5> buffer = {"Data"};
|
||||
|
||||
std::size_t sent;
|
||||
REQUIRE(clientToServer.Send(buffer.data(), buffer.size(), &sent));
|
||||
REQUIRE(sent == buffer.size());
|
||||
|
||||
REQUIRE(serverPoller.Wait(1000));
|
||||
|
||||
CHECK(serverPoller.IsReadyToRead(serverToClient));
|
||||
|
||||
CHECK(serverToClient.Read(buffer.data(), buffer.size()) == sent);
|
||||
|
||||
AND_THEN("Our selector should report no socket ready")
|
||||
{
|
||||
REQUIRE_FALSE(serverPoller.Wait(100));
|
||||
|
||||
REQUIRE_FALSE(serverPoller.IsReadyToRead(serverToClient));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
65
tests/UnitTests/Engine/Network/TCPTest.cpp
Normal file
65
tests/UnitTests/Engine/Network/TCPTest.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include <Nazara/Math/Vector3.hpp>
|
||||
#include <Nazara/Network/NetPacket.hpp>
|
||||
#include <Nazara/Network/TcpClient.hpp>
|
||||
#include <Nazara/Network/TcpServer.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
SCENARIO("TCP", "[NETWORK][TCP]")
|
||||
{
|
||||
GIVEN("Two TCP, one client, one server")
|
||||
{
|
||||
Nz::TcpServer server;
|
||||
server.EnableBlocking(false);
|
||||
|
||||
REQUIRE(server.Listen(Nz::NetProtocol::IPv4, 0) == Nz::SocketState::Bound);
|
||||
|
||||
Nz::IpAddress serverIP(Nz::IpAddress::LoopbackIpV4.ToIPv4(), server.GetBoundPort());
|
||||
REQUIRE(serverIP.IsValid());
|
||||
|
||||
Nz::TcpClient client;
|
||||
CHECK(client.WaitForConnected(100) == Nz::SocketState::NotConnected);
|
||||
REQUIRE(client.Connect(serverIP) == Nz::SocketState::Connecting);
|
||||
|
||||
Nz::IpAddress clientIP = client.GetRemoteAddress();
|
||||
CHECK(clientIP.IsValid());
|
||||
|
||||
REQUIRE(client.WaitForConnected(100) == Nz::SocketState::Connected);
|
||||
|
||||
CHECK(client.IsBlockingEnabled());
|
||||
CHECK_FALSE(client.IsKeepAliveEnabled());
|
||||
CHECK_FALSE(client.IsLowDelayEnabled());
|
||||
CHECK(client.QueryReceiveBufferSize() > 0);
|
||||
CHECK(client.QuerySendBufferSize() > 0);
|
||||
|
||||
Nz::TcpClient serverToClient;
|
||||
REQUIRE(server.AcceptClient(&serverToClient));
|
||||
|
||||
WHEN("We send data from client")
|
||||
{
|
||||
CHECK(serverToClient.EndOfStream());
|
||||
|
||||
Nz::NetPacket packet(1);
|
||||
Nz::Vector3f vector123(1.f, 2.f, 3.f);
|
||||
packet << vector123;
|
||||
REQUIRE(client.SendPacket(packet));
|
||||
|
||||
std::this_thread::yield();
|
||||
|
||||
THEN("We should get it on the server")
|
||||
{
|
||||
CHECK(!serverToClient.EndOfStream());
|
||||
|
||||
Nz::NetPacket resultPacket;
|
||||
REQUIRE(serverToClient.ReceivePacket(&resultPacket));
|
||||
|
||||
Nz::Vector3f result;
|
||||
resultPacket >> result;
|
||||
|
||||
CHECK(result == vector123);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
42
tests/UnitTests/Engine/Network/UdpSocketTest.cpp
Normal file
42
tests/UnitTests/Engine/Network/UdpSocketTest.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include <Nazara/Math/Vector3.hpp>
|
||||
#include <Nazara/Network/UdpSocket.hpp>
|
||||
#include <Nazara/Network/NetPacket.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
SCENARIO("UdpSocket", "[NETWORK][UDPSOCKET]")
|
||||
{
|
||||
GIVEN("Two UdpSocket, one client, one server")
|
||||
{
|
||||
Nz::UdpSocket server(Nz::NetProtocol::IPv4);
|
||||
REQUIRE(server.Bind(0) == Nz::SocketState::Bound);
|
||||
|
||||
Nz::UInt16 port = server.GetBoundPort();
|
||||
|
||||
Nz::IpAddress serverIP(Nz::IpAddress::LoopbackIpV4.ToIPv4(), port);
|
||||
REQUIRE(serverIP.IsValid());
|
||||
|
||||
Nz::UdpSocket client(Nz::NetProtocol::IPv4);
|
||||
CHECK_FALSE(client.IsBroadcastingEnabled());
|
||||
CHECK(client.QueryMaxDatagramSize() > 1500);
|
||||
|
||||
WHEN("We send data from client")
|
||||
{
|
||||
Nz::NetPacket packet(1);
|
||||
Nz::Vector3f vector123(1.f, 2.f, 3.f);
|
||||
packet << vector123;
|
||||
REQUIRE(client.SendPacket(serverIP, packet));
|
||||
|
||||
THEN("We should get it on the server")
|
||||
{
|
||||
Nz::NetPacket resultPacket;
|
||||
Nz::IpAddress fromIp;
|
||||
REQUIRE(server.ReceivePacket(&resultPacket, &fromIp));
|
||||
|
||||
Nz::Vector3f result;
|
||||
resultPacket >> result;
|
||||
REQUIRE(result == vector123);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
134
tests/UnitTests/Engine/Physics2D/Collider2DTest.cpp
Normal file
134
tests/UnitTests/Engine/Physics2D/Collider2DTest.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
#include <Nazara/Physics2D/Collider2D.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
SCENARIO("Collider2D", "[PHYSICS2D][COLLIDER2D]")
|
||||
{
|
||||
GIVEN("No particular elements")
|
||||
{
|
||||
WHEN("We construct a box with Rect")
|
||||
{
|
||||
Nz::Rectf aabb(5.f, 3.f, 10.f, 6.f);
|
||||
Nz::BoxCollider2D box(aabb);
|
||||
|
||||
THEN("We expect those to be true")
|
||||
{
|
||||
CHECK(box.GetRect() == aabb);
|
||||
CHECK(box.GetSize() == aabb.GetLengths());
|
||||
CHECK(box.GetType() == Nz::ColliderType2D::Box);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We construct a box with Vector2D")
|
||||
{
|
||||
Nz::Vector2f vec(5.f, 3.f);
|
||||
Nz::Rectf aabb(-2.5f, -1.5f, 5.f, 3.f);
|
||||
Nz::BoxCollider2D box(vec);
|
||||
|
||||
THEN("We expect those to be true")
|
||||
{
|
||||
CHECK(box.GetRect() == aabb);
|
||||
CHECK(box.GetSize() == vec);
|
||||
CHECK(box.GetType() == Nz::ColliderType2D::Box);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We construct a circle")
|
||||
{
|
||||
Nz::Vector2f position(5.f, 3.f);
|
||||
float radius = 7.f;
|
||||
Nz::CircleCollider2D circle(radius, position);
|
||||
|
||||
THEN("We expect those to be true")
|
||||
{
|
||||
CHECK(circle.GetRadius() == Catch::Approx(radius));
|
||||
CHECK(circle.GetType() == Nz::ColliderType2D::Circle);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We construct a compound")
|
||||
{
|
||||
Nz::Rectf aabb(0.f, 0.f, 1.f, 1.f);
|
||||
std::shared_ptr<Nz::BoxCollider2D> box1 = std::make_shared<Nz::BoxCollider2D>(aabb);
|
||||
aabb.Translate(Nz::Vector2f::Unit());
|
||||
std::shared_ptr<Nz::BoxCollider2D> box2 = std::make_shared<Nz::BoxCollider2D>(aabb);
|
||||
|
||||
std::vector<std::shared_ptr<Nz::Collider2D>> colliders;
|
||||
colliders.push_back(box1);
|
||||
colliders.push_back(box2);
|
||||
Nz::CompoundCollider2D compound(colliders);
|
||||
|
||||
THEN("We expect those to be true")
|
||||
{
|
||||
CHECK(compound.GetType() == Nz::ColliderType2D::Compound);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We construct a convex")
|
||||
{
|
||||
std::vector<Nz::Vector2f> vertices;
|
||||
vertices.push_back(Nz::Vector2f(0.f, 0.f));
|
||||
vertices.push_back(Nz::Vector2f(0.f, 1.f));
|
||||
vertices.push_back(Nz::Vector2f(1.f, 1.f));
|
||||
vertices.push_back(Nz::Vector2f(1.f, 0.f));
|
||||
|
||||
Nz::ConvexCollider2D convex(Nz::SparsePtr<const Nz::Vector2f>(vertices.data()), vertices.size());
|
||||
|
||||
THEN("We expect those to be true")
|
||||
{
|
||||
CHECK(convex.GetType() == Nz::ColliderType2D::Convex);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We construct a null")
|
||||
{
|
||||
Nz::NullCollider2D null;
|
||||
|
||||
THEN("We expect those to be true")
|
||||
{
|
||||
CHECK(null.GetType() == Nz::ColliderType2D::Null);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We construct a segment")
|
||||
{
|
||||
Nz::Vector2f firstPoint(2.f, 1.f);
|
||||
Nz::Vector2f secondPoint(-4.f, -3.f);
|
||||
Nz::SegmentCollider2D segment(firstPoint, secondPoint);
|
||||
|
||||
THEN("We expect those to be true")
|
||||
{
|
||||
CHECK(segment.GetFirstPoint() == firstPoint);
|
||||
CHECK(segment.GetLength() == Catch::Approx(firstPoint.Distance(secondPoint)));
|
||||
CHECK(segment.GetSecondPoint() == secondPoint);
|
||||
CHECK(segment.GetType() == Nz::ColliderType2D::Segment);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We verify general purpose methods")
|
||||
{
|
||||
Nz::Rectf aabb(5.f, 3.f, 10.f, 6.f);
|
||||
Nz::BoxCollider2D box(aabb);
|
||||
|
||||
Nz::UInt32 categoryMask = 1;
|
||||
Nz::UInt32 groupId = 2;
|
||||
Nz::UInt32 typeId = 3;
|
||||
Nz::UInt32 mask = 4;
|
||||
bool trigger = true;
|
||||
box.SetCategoryMask(categoryMask);
|
||||
box.SetCollisionGroup(groupId);
|
||||
box.SetCollisionId(typeId);
|
||||
box.SetCollisionMask(mask);
|
||||
box.SetTrigger(trigger);
|
||||
|
||||
THEN("We expect those to be true")
|
||||
{
|
||||
CHECK(box.GetCategoryMask() == categoryMask);
|
||||
CHECK(box.GetCollisionGroup() == groupId);
|
||||
CHECK(box.GetCollisionId() == typeId);
|
||||
CHECK(box.GetCollisionMask() == mask);
|
||||
CHECK(box.IsTrigger() == trigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
204
tests/UnitTests/Engine/Physics2D/PhysWorld2DTest.cpp
Normal file
204
tests/UnitTests/Engine/Physics2D/PhysWorld2DTest.cpp
Normal file
@@ -0,0 +1,204 @@
|
||||
#include <Nazara/Physics2D/PhysWorld2D.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
Nz::RigidBody2D CreateBody(Nz::PhysWorld2D& world, const Nz::Vector2f& position, bool isMoving = true, const Nz::Vector2f& lengths = Nz::Vector2f::Unit());
|
||||
|
||||
Nz::UInt32 collisionGroup = 1;
|
||||
Nz::UInt32 categoryMask = 2;
|
||||
Nz::UInt32 collisionMask = 3;
|
||||
|
||||
SCENARIO("PhysWorld2D", "[PHYSICS2D][PHYSWORLD2D]")
|
||||
{
|
||||
GIVEN("A physic world and a bunch of entities on a grid")
|
||||
{
|
||||
Nz::PhysWorld2D world;
|
||||
|
||||
std::vector<Nz::RigidBody2D> bodies;
|
||||
const int numberOfBodiesPerLign = 3;
|
||||
for (int i = 0; i != numberOfBodiesPerLign; ++i)
|
||||
{
|
||||
for (int j = 0; j != numberOfBodiesPerLign; ++j)
|
||||
{
|
||||
bodies.push_back(CreateBody(world, Nz::Vector2f(10.f * i, 10.f * j)));
|
||||
}
|
||||
}
|
||||
|
||||
world.Step(1.f);
|
||||
|
||||
WHEN("We ask for the nearest body")
|
||||
{
|
||||
Nz::PhysWorld2D::NearestQueryResult result;
|
||||
REQUIRE(world.NearestBodyQuery(-Nz::Vector2f::UnitY() * 1.f, 2.f, collisionGroup, categoryMask, collisionMask, &result));
|
||||
|
||||
THEN("It should be the one on the origin")
|
||||
{
|
||||
CHECK(result.nearestBody == &bodies[0]);
|
||||
CHECK(result.closestPoint == Nz::Vector2f::Zero());
|
||||
CHECK(result.fraction == -Nz::Vector2f::UnitY());
|
||||
CHECK(result.distance == Catch::Approx(1.f));
|
||||
}
|
||||
|
||||
REQUIRE(world.NearestBodyQuery(Nz::Vector2f::UnitY() * 2.f, 2.f, collisionGroup, categoryMask, collisionMask, &result));
|
||||
|
||||
THEN("It should be the one on the origin")
|
||||
{
|
||||
CHECK(result.nearestBody == &bodies[0]);
|
||||
CHECK(result.closestPoint == Nz::Vector2f::UnitY());
|
||||
CHECK(result.fraction == Nz::Vector2f::UnitY());
|
||||
CHECK(result.distance == Catch::Approx(1.f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We ask for the first ray collision")
|
||||
{
|
||||
Nz::Vector2f origin = -Nz::Vector2f::UnitY() * 2.f;
|
||||
Nz::Vector2f end = (numberOfBodiesPerLign + 1) * 10.f * Nz::Vector2f::UnitY();
|
||||
Nz::PhysWorld2D::RaycastHit result;
|
||||
REQUIRE(world.RaycastQueryFirst(origin, end, 1.f, collisionGroup, categoryMask, collisionMask, &result));
|
||||
|
||||
THEN("It should be the one on the origin")
|
||||
{
|
||||
CHECK(result.nearestBody == &bodies[0]);
|
||||
CHECK(result.fraction == Catch::Approx(1.f / 42.f));
|
||||
CHECK(result.hitPos == Nz::Vector2f::Zero());
|
||||
CHECK(result.hitNormal == -Nz::Vector2f::UnitY());
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We ask for the ray collisions")
|
||||
{
|
||||
Nz::Vector2f origin = -Nz::Vector2f::UnitY() * 2.f;
|
||||
Nz::Vector2f end = (numberOfBodiesPerLign + 1) * 10.f * Nz::Vector2f::UnitY();
|
||||
std::vector<Nz::PhysWorld2D::RaycastHit> results;
|
||||
REQUIRE(world.RaycastQuery(origin, end, 1.f, collisionGroup, categoryMask, collisionMask, &results));
|
||||
|
||||
THEN("It should be the first lign")
|
||||
{
|
||||
REQUIRE(results.size() == numberOfBodiesPerLign);
|
||||
|
||||
for (int i = 0; i != numberOfBodiesPerLign; ++i)
|
||||
{
|
||||
const Nz::PhysWorld2D::RaycastHit& result = results[i];
|
||||
CHECK(result.nearestBody == &bodies[i]);
|
||||
CHECK(result.fraction == Catch::Approx(i / 4.f).margin(0.1f));
|
||||
CHECK(result.hitPos == Nz::Vector2f(0.f, i * 10.f));
|
||||
CHECK(result.hitNormal == -Nz::Vector2f::UnitY());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We ask for a region")
|
||||
{
|
||||
std::vector<Nz::RigidBody2D*> results;
|
||||
world.RegionQuery(Nz::Rectf(-5.f, -5.f, 5.f, 5.f), collisionGroup, categoryMask, collisionMask, &results);
|
||||
|
||||
THEN("It should be the one on the origin")
|
||||
{
|
||||
REQUIRE(results.size() == 1);
|
||||
CHECK(results[0] == &bodies[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("Three entities, a character, a wall and a trigger zone")
|
||||
{
|
||||
unsigned int CHARACTER_COLLISION_ID = 1;
|
||||
unsigned int WALL_COLLISION_ID = 2;
|
||||
unsigned int TRIGGER_COLLISION_ID = 3;
|
||||
|
||||
int statusTriggerCollision = 0;
|
||||
|
||||
Nz::PhysWorld2D world;
|
||||
|
||||
Nz::Rectf characterAABB(0.f, 0.f, 1.f, 1.f);
|
||||
std::shared_ptr<Nz::Collider2D> characterBox = std::make_shared<Nz::BoxCollider2D>(characterAABB);
|
||||
characterBox->SetCollisionId(CHARACTER_COLLISION_ID);
|
||||
Nz::RigidBody2D character(&world, 1.f, characterBox);
|
||||
character.SetPosition(Nz::Vector2f::Zero());
|
||||
|
||||
Nz::Rectf wallAABB(0.f, 0.f, 1.f, 2.f);
|
||||
std::shared_ptr<Nz::Collider2D> wallBox = std::make_shared<Nz::BoxCollider2D>(wallAABB);
|
||||
wallBox->SetCollisionId(WALL_COLLISION_ID);
|
||||
Nz::RigidBody2D wall(&world, 0.f, wallBox);
|
||||
wall.SetPosition(Nz::Vector2f(5.f, 0.f));
|
||||
|
||||
Nz::Rectf triggerAABB(0.f, 0.f, 1.f, 1.f);
|
||||
std::shared_ptr<Nz::Collider2D> triggerBox = std::make_shared<Nz::BoxCollider2D>(triggerAABB);
|
||||
triggerBox->SetTrigger(true);
|
||||
triggerBox->SetCollisionId(TRIGGER_COLLISION_ID);
|
||||
Nz::RigidBody2D trigger(&world, 0.f, triggerBox);
|
||||
trigger.SetPosition(Nz::Vector2f(2.f, 0.f));
|
||||
|
||||
world.Step(0.f);
|
||||
|
||||
Nz::PhysWorld2D::Callback characterTriggerCallback;
|
||||
characterTriggerCallback.startCallback = [&](Nz::PhysWorld2D&, Nz::Arbiter2D&, Nz::RigidBody2D&, Nz::RigidBody2D&, void*) -> bool {
|
||||
statusTriggerCollision = statusTriggerCollision | 1 << 0;
|
||||
return true;
|
||||
};
|
||||
characterTriggerCallback.preSolveCallback = [&](Nz::PhysWorld2D&, Nz::Arbiter2D&, Nz::RigidBody2D&, Nz::RigidBody2D&, void*) -> bool {
|
||||
statusTriggerCollision = statusTriggerCollision | 1 << 1;
|
||||
return true;
|
||||
};
|
||||
characterTriggerCallback.postSolveCallback = [&](Nz::PhysWorld2D&, Nz::Arbiter2D&, Nz::RigidBody2D&, Nz::RigidBody2D&, void*) {
|
||||
statusTriggerCollision = statusTriggerCollision | 1 << 2;
|
||||
};
|
||||
characterTriggerCallback.endCallback = [&](Nz::PhysWorld2D&, Nz::Arbiter2D&, Nz::RigidBody2D&, Nz::RigidBody2D&, void*) {
|
||||
statusTriggerCollision = statusTriggerCollision | 1 << 3;
|
||||
};
|
||||
world.RegisterCallbacks(CHARACTER_COLLISION_ID, TRIGGER_COLLISION_ID, characterTriggerCallback);
|
||||
|
||||
int statusWallCollision = 0;
|
||||
Nz::PhysWorld2D::Callback characterWallCallback;
|
||||
characterWallCallback.startCallback = [&](Nz::PhysWorld2D&, Nz::Arbiter2D&, Nz::RigidBody2D&, Nz::RigidBody2D&, void*) -> bool {
|
||||
statusWallCollision = statusWallCollision | 1 << 0;
|
||||
return true;
|
||||
};
|
||||
characterWallCallback.endCallback = [&](Nz::PhysWorld2D&, Nz::Arbiter2D&, Nz::RigidBody2D&, Nz::RigidBody2D&, void*) {
|
||||
statusWallCollision = statusWallCollision | 1 << 1;
|
||||
};
|
||||
world.RegisterCallbacks(CHARACTER_COLLISION_ID, WALL_COLLISION_ID, characterWallCallback);
|
||||
|
||||
WHEN("We make our character go towards the wall")
|
||||
{
|
||||
character.SetVelocity(Nz::Vector2f(1.f, 0.f));
|
||||
for (int i = 0; i != 11; ++i)
|
||||
world.Step(0.1f);
|
||||
|
||||
THEN("It should trigger several collisions")
|
||||
{
|
||||
CHECK(statusTriggerCollision == 3);
|
||||
for (int i = 0; i != 20; ++i)
|
||||
world.Step(0.1f);
|
||||
CHECK(statusTriggerCollision == 11);
|
||||
|
||||
CHECK(character.GetPosition().x == Catch::Approx(3.1f).margin(0.01f));
|
||||
|
||||
for (int i = 0; i != 9; ++i)
|
||||
world.Step(0.1f);
|
||||
CHECK(character.GetPosition().x == Catch::Approx(4.f).margin(0.01f));
|
||||
world.Step(0.1f);
|
||||
CHECK(character.GetPosition().x == Catch::Approx(4.f).margin(0.01f));
|
||||
CHECK(statusWallCollision == 1); // It should be close to the wall
|
||||
|
||||
character.SetVelocity(Nz::Vector2f(-2.f, 0.f));
|
||||
for (int i = 0; i != 10; ++i)
|
||||
world.Step(0.1f);
|
||||
CHECK(statusWallCollision == 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Nz::RigidBody2D CreateBody(Nz::PhysWorld2D& world, const Nz::Vector2f& position, bool isMoving, const Nz::Vector2f& lengths)
|
||||
{
|
||||
Nz::Rectf aabb(0.f, 0.f, lengths.x, lengths.y);
|
||||
std::shared_ptr<Nz::Collider2D> box = std::make_shared<Nz::BoxCollider2D>(aabb);
|
||||
box->SetCategoryMask(categoryMask);
|
||||
box->SetCollisionMask(collisionMask);
|
||||
float mass = isMoving ? 1.f : 0.f;
|
||||
Nz::RigidBody2D rigidBody(&world, mass, box);
|
||||
rigidBody.SetPosition(position);
|
||||
return rigidBody;
|
||||
}
|
||||
353
tests/UnitTests/Engine/Physics2D/RigidBody2DTest.cpp
Normal file
353
tests/UnitTests/Engine/Physics2D/RigidBody2DTest.cpp
Normal file
@@ -0,0 +1,353 @@
|
||||
#include <Nazara/Physics2D/RigidBody2D.hpp>
|
||||
#include <Nazara/Physics2D/PhysWorld2D.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
|
||||
Nz::RigidBody2D CreateBody(Nz::PhysWorld2D& world);
|
||||
void EQUALITY(const Nz::RigidBody2D& left, const Nz::RigidBody2D& right);
|
||||
void EQUALITY(const Nz::Rectf& left, const Nz::Rectf& right);
|
||||
|
||||
SCENARIO("RigidBody2D", "[PHYSICS2D][RIGIDBODY2D]")
|
||||
{
|
||||
GIVEN("A physic world and a rigid body")
|
||||
{
|
||||
Nz::PhysWorld2D world;
|
||||
world.SetMaxStepCount(std::numeric_limits<std::size_t>::max());
|
||||
|
||||
Nz::Vector2f positionAABB(3.f, 4.f);
|
||||
Nz::Rectf aabb(positionAABB.x, positionAABB.y, 1.f, 2.f);
|
||||
std::shared_ptr<Nz::Collider2D> box = std::make_shared<Nz::BoxCollider2D>(aabb);
|
||||
float mass = 1.f;
|
||||
Nz::RigidBody2D body(&world, mass, box);
|
||||
float angularVelocity = 0.2f;
|
||||
body.SetAngularVelocity(angularVelocity);
|
||||
Nz::Vector2f massCenter(5.f, 7.f);
|
||||
body.SetMassCenter(massCenter);
|
||||
Nz::Vector2f position(9.f, 1.f);
|
||||
body.SetPosition(position);
|
||||
float rotation = 0.1f;
|
||||
body.SetRotation(rotation);
|
||||
Nz::Vector2f velocity(-4.f, -2.f);
|
||||
body.SetVelocity(velocity);
|
||||
bool userdata = false;
|
||||
body.SetUserdata(&userdata);
|
||||
|
||||
world.Step(1.f);
|
||||
|
||||
WHEN("We copy construct the body")
|
||||
{
|
||||
body.AddForce(Nz::Vector2f(3.f, 5.f));
|
||||
Nz::RigidBody2D copiedBody(body);
|
||||
EQUALITY(copiedBody, body);
|
||||
world.Step(1.f);
|
||||
EQUALITY(copiedBody, body);
|
||||
}
|
||||
|
||||
WHEN("We move construct the body")
|
||||
{
|
||||
Nz::RigidBody2D copiedBody(body);
|
||||
Nz::RigidBody2D movedBody(std::move(body));
|
||||
EQUALITY(movedBody, copiedBody);
|
||||
}
|
||||
|
||||
WHEN("We copy assign the body")
|
||||
{
|
||||
Nz::RigidBody2D copiedBody(&world, 0.f);
|
||||
copiedBody = body;
|
||||
EQUALITY(copiedBody, body);
|
||||
}
|
||||
|
||||
WHEN("We move assign the body")
|
||||
{
|
||||
Nz::RigidBody2D copiedBody(body);
|
||||
Nz::RigidBody2D movedBody(&world, 0.f);
|
||||
movedBody = std::move(body);
|
||||
EQUALITY(movedBody, copiedBody);
|
||||
}
|
||||
|
||||
WHEN("We set a new geometry")
|
||||
{
|
||||
float radius = 5.f;
|
||||
body.SetGeom(std::make_shared<Nz::CircleCollider2D>(radius));
|
||||
|
||||
world.Step(1.f);
|
||||
|
||||
THEN("The aabb should be updated")
|
||||
{
|
||||
position = body.GetPosition();
|
||||
Nz::Rectf circleAABB(position.x - radius, position.y - radius, 2.f * radius, 2.f* radius);
|
||||
EQUALITY(body.GetAABB(), circleAABB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A physic world")
|
||||
{
|
||||
Nz::PhysWorld2D world;
|
||||
world.SetMaxStepCount(std::numeric_limits<std::size_t>::max());
|
||||
|
||||
Nz::Rectf aabb(3.f, 4.f, 1.f, 2.f);
|
||||
|
||||
WHEN("We get a rigid body from a function")
|
||||
{
|
||||
std::vector<Nz::RigidBody2D> tmp;
|
||||
tmp.push_back(CreateBody(world));
|
||||
tmp.push_back(CreateBody(world));
|
||||
|
||||
world.Step(1.f);
|
||||
|
||||
THEN("They should be valid")
|
||||
{
|
||||
CHECK(tmp[0].GetAABB() == aabb);
|
||||
CHECK(tmp[1].GetAABB() == aabb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A physic world and a rigid body")
|
||||
{
|
||||
Nz::PhysWorld2D world;
|
||||
world.SetMaxStepCount(std::numeric_limits<std::size_t>::max());
|
||||
|
||||
Nz::Vector2f positionAABB(3.f, 4.f);
|
||||
Nz::Rectf aabb(positionAABB.x, positionAABB.y, 1.f, 2.f);
|
||||
std::shared_ptr<Nz::Collider2D> box = std::make_shared<Nz::BoxCollider2D>(aabb);
|
||||
float mass = 1.f;
|
||||
Nz::RigidBody2D body(&world, mass);
|
||||
body.SetGeom(box, true, false);
|
||||
|
||||
bool userData = false;
|
||||
body.SetUserdata(&userData);
|
||||
|
||||
Nz::Vector2f position = Nz::Vector2f::Zero();
|
||||
body.SetPosition(position);
|
||||
|
||||
world.Step(1.f);
|
||||
|
||||
WHEN("We retrieve standard information")
|
||||
{
|
||||
THEN("We expect those to be true")
|
||||
{
|
||||
CHECK(body.GetAABB() == aabb);
|
||||
CHECK(body.GetAngularVelocity() == 0.f);
|
||||
CHECK(body.GetMassCenter(Nz::CoordSys::Global) == position);
|
||||
CHECK(body.GetGeom() == box);
|
||||
CHECK(body.GetMass() == Catch::Approx(mass));
|
||||
CHECK(body.GetPosition() == position);
|
||||
CHECK(body.GetRotation().value == Catch::Approx(0.f));
|
||||
CHECK(body.GetUserdata() == &userData);
|
||||
CHECK(body.GetVelocity() == Nz::Vector2f::Zero());
|
||||
|
||||
CHECK(body.IsKinematic() == false);
|
||||
CHECK(body.IsSleeping() == false);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We set a velocity")
|
||||
{
|
||||
Nz::Vector2f velocity(Nz::Vector2f::Unit());
|
||||
body.SetVelocity(velocity);
|
||||
position += velocity;
|
||||
world.Step(1.f);
|
||||
|
||||
THEN("We expect those to be true")
|
||||
{
|
||||
aabb.Translate(velocity);
|
||||
CHECK(body.GetAABB() == aabb);
|
||||
CHECK(body.GetMassCenter(Nz::CoordSys::Global) == position);
|
||||
CHECK(body.GetPosition() == position);
|
||||
CHECK(body.GetVelocity() == velocity);
|
||||
}
|
||||
|
||||
AND_THEN("We apply an impulse in the opposite direction")
|
||||
{
|
||||
body.AddImpulse(-velocity);
|
||||
world.Step(1.f);
|
||||
|
||||
REQUIRE(body.GetVelocity() == Nz::Vector2f::Zero());
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We set an angular velocity")
|
||||
{
|
||||
Nz::RadianAnglef angularSpeed = Nz::RadianAnglef::FromDegrees(90.f);
|
||||
body.SetAngularVelocity(angularSpeed);
|
||||
world.Step(1.f);
|
||||
|
||||
THEN("We expect those to be true")
|
||||
{
|
||||
CHECK(body.GetAngularVelocity() == angularSpeed);
|
||||
CHECK(body.GetRotation() == angularSpeed);
|
||||
CHECK(body.GetAABB() == Nz::Rectf(-6.f, 3.f, 2.f, 1.f));
|
||||
|
||||
world.Step(1.f);
|
||||
CHECK(body.GetRotation() == 2.f * angularSpeed);
|
||||
CHECK(body.GetAABB() == Nz::Rectf(-4.f, -6.f, 1.f, 2.f));
|
||||
|
||||
world.Step(1.f);
|
||||
CHECK(body.GetRotation() == 3.f * angularSpeed);
|
||||
CHECK(body.GetAABB() == Nz::Rectf(4.f, -4.f, 2.f, 1.f));
|
||||
|
||||
world.Step(1.f);
|
||||
CHECK(body.GetRotation() == 4.f * angularSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We apply a torque")
|
||||
{
|
||||
body.AddTorque(Nz::DegreeAnglef(90.f));
|
||||
world.Step(1.f);
|
||||
|
||||
THEN("It is also counter-clockwise")
|
||||
{
|
||||
CHECK(body.GetAngularVelocity().value >= 0.f);
|
||||
CHECK(body.GetRotation().value >= 0.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A physic world and a rigid body of circle")
|
||||
{
|
||||
Nz::PhysWorld2D world;
|
||||
world.SetMaxStepCount(std::numeric_limits<std::size_t>::max());
|
||||
|
||||
Nz::Vector2f position(3.f, 4.f);
|
||||
float radius = 5.f;
|
||||
std::shared_ptr<Nz::Collider2D> circle = std::make_shared<Nz::CircleCollider2D>(radius, position);
|
||||
float mass = 1.f;
|
||||
Nz::RigidBody2D body(&world, mass);
|
||||
body.SetGeom(circle, true, false);
|
||||
|
||||
world.Step(1.f);
|
||||
|
||||
WHEN("We ask for the aabb of the circle")
|
||||
{
|
||||
THEN("We expect this to be true")
|
||||
{
|
||||
Nz::Rectf circleAABB(position.x - radius, position.y - radius, 2.f * radius, 2.f* radius);
|
||||
REQUIRE(body.GetAABB() == circleAABB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A physic world and a rigid body of compound")
|
||||
{
|
||||
Nz::PhysWorld2D world;
|
||||
world.SetMaxStepCount(std::numeric_limits<std::size_t>::max());
|
||||
|
||||
Nz::Rectf aabb(0.f, 0.f, 1.f, 1.f);
|
||||
std::shared_ptr<Nz::BoxCollider2D> box1 = std::make_shared<Nz::BoxCollider2D>(aabb);
|
||||
aabb.Translate(Nz::Vector2f::Unit());
|
||||
std::shared_ptr<Nz::BoxCollider2D> box2 = std::make_shared<Nz::BoxCollider2D>(aabb);
|
||||
|
||||
std::vector<std::shared_ptr<Nz::Collider2D>> colliders;
|
||||
colliders.push_back(box1);
|
||||
colliders.push_back(box2);
|
||||
std::shared_ptr<Nz::CompoundCollider2D> compound = std::make_shared<Nz::CompoundCollider2D>(colliders);
|
||||
|
||||
float mass = 1.f;
|
||||
Nz::RigidBody2D body(&world, mass);
|
||||
body.SetGeom(compound, true, false);
|
||||
|
||||
world.Step(1.f);
|
||||
|
||||
WHEN("We ask for the aabb of the compound")
|
||||
{
|
||||
THEN("We expect this to be true")
|
||||
{
|
||||
Nz::Rectf compoundAABB(0.f, 0.f, 2.f, 2.f);
|
||||
REQUIRE(body.GetAABB() == compoundAABB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A physic world and a rigid body of circle")
|
||||
{
|
||||
Nz::PhysWorld2D world;
|
||||
world.SetMaxStepCount(std::numeric_limits<std::size_t>::max());
|
||||
|
||||
std::vector<Nz::Vector2f> vertices;
|
||||
vertices.emplace_back(0.f, 0.f);
|
||||
vertices.emplace_back(0.f, 1.f);
|
||||
vertices.emplace_back(1.f, 1.f);
|
||||
vertices.emplace_back(1.f, 0.f);
|
||||
|
||||
Nz::SparsePtr<const Nz::Vector2f> sparsePtr(vertices.data());
|
||||
std::shared_ptr<Nz::ConvexCollider2D> convex = std::make_shared<Nz::ConvexCollider2D>(sparsePtr, vertices.size());
|
||||
float mass = 1.f;
|
||||
Nz::RigidBody2D body(&world, mass);
|
||||
body.SetGeom(convex, true, false);
|
||||
|
||||
world.Step(1.f);
|
||||
|
||||
WHEN("We ask for the aabb of the convex")
|
||||
{
|
||||
THEN("We expect this to be true")
|
||||
{
|
||||
Nz::Rectf convexAABB(0.f, 0.f, 1.f, 1.f);
|
||||
REQUIRE(body.GetAABB() == convexAABB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A physic world and a rigid body of segment")
|
||||
{
|
||||
Nz::PhysWorld2D world;
|
||||
world.SetMaxStepCount(std::numeric_limits<std::size_t>::max());
|
||||
|
||||
Nz::Vector2f positionA(3.f, 4.f);
|
||||
Nz::Vector2f positionB(1.f, -4.f);
|
||||
std::shared_ptr<Nz::Collider2D> segment = std::make_shared<Nz::SegmentCollider2D>(positionA, positionB, 0.f);
|
||||
float mass = 1.f;
|
||||
Nz::RigidBody2D body(&world, mass);
|
||||
body.SetGeom(segment, true, false);
|
||||
|
||||
world.Step(1.f);
|
||||
|
||||
WHEN("We ask for the aabb of the segment")
|
||||
{
|
||||
THEN("We expect this to be true")
|
||||
{
|
||||
Nz::Rectf segmentAABB = Nz::Rectf::FromExtends(positionA, positionB);
|
||||
REQUIRE(body.GetAABB() == segmentAABB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Nz::RigidBody2D CreateBody(Nz::PhysWorld2D& world)
|
||||
{
|
||||
Nz::Vector2f positionAABB(3.f, 4.f);
|
||||
Nz::Rectf aabb(positionAABB.x, positionAABB.y, 1.f, 2.f);
|
||||
std::shared_ptr<Nz::Collider2D> box = std::make_shared<Nz::BoxCollider2D>(aabb);
|
||||
float mass = 1.f;
|
||||
|
||||
Nz::RigidBody2D body(&world, mass, box);
|
||||
body.SetPosition(Nz::Vector2f::Zero());
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
void EQUALITY(const Nz::RigidBody2D& left, const Nz::RigidBody2D& right)
|
||||
{
|
||||
CHECK(left.GetAABB() == right.GetAABB());
|
||||
CHECK(left.GetAngularVelocity() == right.GetAngularVelocity());
|
||||
CHECK(left.GetMassCenter() == right.GetMassCenter());
|
||||
CHECK(left.GetGeom() == right.GetGeom());
|
||||
CHECK(left.GetHandle() != right.GetHandle());
|
||||
CHECK(left.GetMass() == Catch::Approx(right.GetMass()));
|
||||
CHECK(left.GetPosition() == right.GetPosition());
|
||||
CHECK(left.GetRotation().value == Catch::Approx(right.GetRotation().value));
|
||||
CHECK(left.GetUserdata() == right.GetUserdata());
|
||||
CHECK(left.GetVelocity() == right.GetVelocity());
|
||||
}
|
||||
|
||||
void EQUALITY(const Nz::Rectf& left, const Nz::Rectf& right)
|
||||
{
|
||||
CHECK(left.x == Catch::Approx(right.x));
|
||||
CHECK(left.y == Catch::Approx(right.y));
|
||||
CHECK(left.width == Catch::Approx(right.width));
|
||||
CHECK(left.height == Catch::Approx(right.height));
|
||||
}
|
||||
152
tests/UnitTests/Engine/Utility/FontLoading.cpp
Normal file
152
tests/UnitTests/Engine/Utility/FontLoading.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
#include <Nazara/Utility/AbstractImage.hpp>
|
||||
#include <Nazara/Utility/Font.hpp>
|
||||
#include <Nazara/Utility/GuillotineImageAtlas.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
|
||||
std::filesystem::path GetAssetDir();
|
||||
|
||||
SCENARIO("Fonts", "[Utility][Font]")
|
||||
{
|
||||
WHEN("Loading default font")
|
||||
{
|
||||
std::shared_ptr<Nz::Font> font = Nz::Font::GetDefault();
|
||||
|
||||
std::shared_ptr<Nz::GuillotineImageAtlas> imageAtlas = std::make_shared<Nz::GuillotineImageAtlas>();
|
||||
imageAtlas->SetMaxLayerSize(1024);
|
||||
|
||||
font->SetAtlas(imageAtlas);
|
||||
|
||||
CHECK(font->GetFamilyName() == "Open Sans");
|
||||
CHECK(font->GetStyleName() == "Regular");
|
||||
|
||||
for (std::size_t i = 0; i < 3; ++i)
|
||||
{
|
||||
WHEN("Inspecting size info")
|
||||
{
|
||||
const auto& sizeInfo24 = font->GetSizeInfo(24);
|
||||
CHECK(sizeInfo24.lineHeight == 33);
|
||||
CHECK(sizeInfo24.spaceAdvance == 6);
|
||||
CHECK(sizeInfo24.underlinePosition == Catch::Approx(-2.40625f));
|
||||
CHECK(sizeInfo24.underlineThickness == Catch::Approx(1.20312f));
|
||||
|
||||
const auto& sizeInfo72 = font->GetSizeInfo(72);
|
||||
CHECK(sizeInfo72.lineHeight == 98);
|
||||
CHECK(sizeInfo72.spaceAdvance == 19);
|
||||
CHECK(sizeInfo72.underlinePosition == Catch::Approx(-7.20312f));
|
||||
CHECK(sizeInfo72.underlineThickness == Catch::Approx(3.59375f));
|
||||
}
|
||||
|
||||
WHEN("Retrieving kerning")
|
||||
{
|
||||
struct Test
|
||||
{
|
||||
unsigned int characterSize;
|
||||
char firstChar;
|
||||
char secondChar;
|
||||
int expectedKerning;
|
||||
};
|
||||
|
||||
std::array<Test, 16> tests{
|
||||
{
|
||||
Test { 16, 'A', 'A', 0 },
|
||||
Test { 24, 'A', 'A', 0 },
|
||||
Test { 32, 'A', 'A', 0 },
|
||||
Test { 48, 'A', 'A', 0 },
|
||||
|
||||
Test { 16, 'A', 'M', 0 },
|
||||
Test { 24, 'A', 'M', 0 },
|
||||
Test { 32, 'A', 'M', 0 },
|
||||
Test { 48, 'A', 'M', 0 },
|
||||
|
||||
Test { 16, 'A', 'T', -1 },
|
||||
Test { 24, 'A', 'T', -2 },
|
||||
Test { 32, 'A', 'T', -2 },
|
||||
Test { 48, 'A', 'T', -3 },
|
||||
|
||||
Test { 16, 'A', 'V', 0 },
|
||||
Test { 24, 'A', 'V', -1 },
|
||||
Test { 32, 'A', 'V', -1 },
|
||||
Test { 48, 'A', 'V', -2 },
|
||||
}
|
||||
};
|
||||
|
||||
for (const Test& test : tests)
|
||||
{
|
||||
INFO("between " << test.firstChar << " and " << test.secondChar << " at character size " << test.characterSize);
|
||||
CHECK(font->GetKerning(test.characterSize, test.firstChar, test.secondChar) == test.expectedKerning);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("Retrieving glyphs")
|
||||
{
|
||||
const auto& glyph1 = font->GetGlyph(72, Nz::TextStyle_Regular, 0.f, 'L');
|
||||
REQUIRE(glyph1.valid);
|
||||
CHECK(glyph1.advance == 37);
|
||||
CHECK(glyph1.aabb.IsValid());
|
||||
CHECK(glyph1.atlasRect.IsValid());
|
||||
CHECK(glyph1.fauxOutlineThickness == 0.f);
|
||||
CHECK_FALSE(glyph1.requireFauxBold);
|
||||
CHECK_FALSE(glyph1.requireFauxItalic);
|
||||
|
||||
const auto& glyphYItalic = font->GetGlyph(72, Nz::TextStyle::Italic, 0.f, 'y');
|
||||
REQUIRE(glyphYItalic.valid);
|
||||
CHECK(glyphYItalic.advance == 36);
|
||||
CHECK(glyphYItalic.aabb.IsValid());
|
||||
CHECK(glyphYItalic.atlasRect.IsValid());
|
||||
CHECK(glyphYItalic.fauxOutlineThickness == 0.f);
|
||||
CHECK_FALSE(glyphYItalic.requireFauxBold);
|
||||
CHECK(glyphYItalic.requireFauxItalic);
|
||||
|
||||
const auto& glyphYRegular = font->GetGlyph(72, Nz::TextStyle_Regular, 0.f, 'y');
|
||||
REQUIRE(glyphYRegular.valid);
|
||||
CHECK(glyphYRegular.advance == 36);
|
||||
CHECK(glyphYRegular.aabb == glyphYItalic.aabb);
|
||||
CHECK(glyphYRegular.atlasRect == glyphYItalic.atlasRect);
|
||||
CHECK(glyphYRegular.fauxOutlineThickness == 0.f);
|
||||
CHECK_FALSE(glyphYRegular.requireFauxBold);
|
||||
CHECK_FALSE(glyphYRegular.requireFauxItalic);
|
||||
|
||||
CHECK(font->GetCachedGlyphCount() == 3);
|
||||
CHECK(font->GetAtlas()->GetLayerCount() == 1);
|
||||
CHECK(font->GetAtlas()->GetLayer(0)->GetLevelCount() == 1);
|
||||
}
|
||||
|
||||
WHEN("Precaching a lot of glyphs")
|
||||
{
|
||||
std::string characterSet;
|
||||
for (char c = 'a'; c <= 'z'; ++c)
|
||||
characterSet += c;
|
||||
|
||||
for (char c = 'A'; c <= 'Z'; ++c)
|
||||
characterSet += c;
|
||||
|
||||
for (char c = '0'; c <= '9'; ++c)
|
||||
characterSet += c;
|
||||
|
||||
for (unsigned int fontSize : {24, 36, 48, 72, 140})
|
||||
{
|
||||
for (float outlineThickness : { 0.f, 1.f, 2.f, 5.f })
|
||||
{
|
||||
font->Precache(fontSize, Nz::TextStyle_Regular, outlineThickness, characterSet);
|
||||
}
|
||||
}
|
||||
|
||||
CHECK(font->GetAtlas()->GetLayerCount() > 1);
|
||||
for (std::size_t layerIndex = 0; layerIndex < font->GetAtlas()->GetLayerCount(); ++layerIndex)
|
||||
{
|
||||
CHECK(font->GetAtlas()->GetLayer(layerIndex)->GetLevelCount() == 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 1)
|
||||
{
|
||||
font->ClearGlyphCache();
|
||||
font->ClearKerningCache();
|
||||
font->ClearSizeInfoCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
41
tests/UnitTests/Engine/Utility/ImageLoading.cpp
Normal file
41
tests/UnitTests/Engine/Utility/ImageLoading.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <Nazara/Utility/Image.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <filesystem>
|
||||
|
||||
std::filesystem::path GetAssetDir();
|
||||
|
||||
SCENARIO("Images", "[Utility][Image]")
|
||||
{
|
||||
WHEN("Loading PNG files")
|
||||
{
|
||||
GIVEN("Logo.png")
|
||||
{
|
||||
std::shared_ptr<Nz::Image> logo = Nz::Image::LoadFromFile(GetAssetDir() / "Logo.png");
|
||||
REQUIRE(logo);
|
||||
|
||||
CHECK(logo->GetWidth() == 765);
|
||||
CHECK(logo->GetHeight() == 212);
|
||||
CHECK(logo->GetLevelCount() == 1);
|
||||
CHECK(logo->GetPixelColor(165, 139) == Nz::Color::FromRGB8(51, 58, 100));
|
||||
CHECK(logo->GetFormat() == Nz::PixelFormat::RGBA8);
|
||||
CHECK(logo->HasAlpha());
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("Loading JPG files")
|
||||
{
|
||||
GIVEN("stars-background.jpg")
|
||||
{
|
||||
std::shared_ptr<Nz::Image> background = Nz::Image::LoadFromFile(GetAssetDir() / "Utility/stars-background.jpg");
|
||||
REQUIRE(background);
|
||||
|
||||
CHECK(background->GetWidth() == 1920);
|
||||
CHECK(background->GetHeight() == 1200);
|
||||
CHECK(background->GetLevelCount() == 1);
|
||||
CHECK(background->GetPixelColor(1377, 372) == Nz::Color::FromRGB8(171, 152, 233));
|
||||
CHECK(background->GetFormat() == Nz::PixelFormat::RGBA8);
|
||||
CHECK(!background->HasAlpha());
|
||||
}
|
||||
}
|
||||
}
|
||||
166
tests/UnitTests/Engine/Utility/ImageStreamLoading.cpp
Normal file
166
tests/UnitTests/Engine/Utility/ImageStreamLoading.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
#include <Nazara/Utility/Image.hpp>
|
||||
#include <Nazara/Utility/ImageStream.hpp>
|
||||
#include <Nazara/Utility/PixelFormat.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <filesystem>
|
||||
|
||||
std::filesystem::path GetAssetDir();
|
||||
|
||||
void CompareFrames(const Nz::ImageStream& gif, std::vector<Nz::UInt8>& frameData, const Nz::Image& referenceImage)
|
||||
{
|
||||
Nz::Vector2ui size = gif.GetSize();
|
||||
REQUIRE(referenceImage.GetSize() == Nz::Vector3ui(size, 1));
|
||||
REQUIRE(referenceImage.GetFormat() == gif.GetPixelFormat()); //< TODO: Convert?
|
||||
|
||||
REQUIRE(frameData.size() == Nz::PixelFormatInfo::ComputeSize(gif.GetPixelFormat(), size.x, size.y, 1));
|
||||
REQUIRE(std::memcmp(frameData.data(), referenceImage.GetConstPixels(), frameData.size()) == 0);
|
||||
}
|
||||
|
||||
SCENARIO("Streamed images", "[Utility][ImageStream]")
|
||||
{
|
||||
std::vector<Nz::UInt8> frameData;
|
||||
|
||||
struct ExpectedFrame
|
||||
{
|
||||
std::shared_ptr<Nz::Image> referenceImage;
|
||||
Nz::UInt64 time;
|
||||
};
|
||||
|
||||
std::filesystem::path resourcePath = GetAssetDir();
|
||||
|
||||
WHEN("Loading GIF files")
|
||||
{
|
||||
GIVEN("canvas_bgnd.gif")
|
||||
{
|
||||
std::array expectedFrames = {
|
||||
ExpectedFrame{
|
||||
Nz::Image::LoadFromFile(resourcePath / "Utility/GIF/canvas_bgnd/0.png"),
|
||||
0
|
||||
},
|
||||
ExpectedFrame{
|
||||
Nz::Image::LoadFromFile(resourcePath / "Utility/GIF/canvas_bgnd/1.png"),
|
||||
1000
|
||||
},
|
||||
ExpectedFrame{
|
||||
Nz::Image::LoadFromFile(resourcePath / "Utility/GIF/canvas_bgnd/2.png"),
|
||||
2000
|
||||
},
|
||||
ExpectedFrame{
|
||||
Nz::Image::LoadFromFile(resourcePath / "Utility/GIF/canvas_bgnd/3.png"),
|
||||
3000
|
||||
},
|
||||
ExpectedFrame{
|
||||
Nz::Image::LoadFromFile(resourcePath / "Utility/GIF/canvas_bgnd/4.png"),
|
||||
4000
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<Nz::ImageStream> gif = Nz::ImageStream::OpenFromFile(resourcePath / "Utility/GIF/canvas_bgnd.gif");
|
||||
REQUIRE(gif);
|
||||
|
||||
Nz::Vector2ui size = gif->GetSize();
|
||||
CHECK(size == Nz::Vector2ui(100, 100));
|
||||
CHECK(gif->GetFrameCount() == expectedFrames.size());
|
||||
CHECK(gif->GetPixelFormat() == Nz::PixelFormat::RGBA8);
|
||||
|
||||
frameData.resize(Nz::PixelFormatInfo::ComputeSize(gif->GetPixelFormat(), size.x, size.y, 1));
|
||||
|
||||
// Decode all frames in order
|
||||
Nz::UInt64 frameTime;
|
||||
for (ExpectedFrame& expectedFrame : expectedFrames)
|
||||
{
|
||||
REQUIRE(expectedFrame.referenceImage);
|
||||
REQUIRE(gif->DecodeNextFrame(frameData.data(), &frameTime));
|
||||
|
||||
CHECK(frameTime == expectedFrame.time);
|
||||
|
||||
CompareFrames(*gif, frameData, *expectedFrame.referenceImage);
|
||||
}
|
||||
|
||||
// Decoding the post-the-end frame fails but gives the end frametime
|
||||
REQUIRE_FALSE(gif->DecodeNextFrame(frameData.data(), &frameTime));
|
||||
CHECK(frameTime == 5000);
|
||||
|
||||
// Decode frames in arbitrary order, to ensure results are corrects
|
||||
for (std::size_t frameIndex : { 2, 0, 3, 1, 4 })
|
||||
{
|
||||
INFO("Decoding frame " << frameIndex);
|
||||
|
||||
ExpectedFrame& expectedFrame = expectedFrames[frameIndex];
|
||||
gif->Seek(frameIndex);
|
||||
|
||||
REQUIRE(gif->DecodeNextFrame(frameData.data(), &frameTime));
|
||||
CHECK(frameTime == expectedFrame.time);
|
||||
|
||||
CompareFrames(*gif, frameData, *expectedFrame.referenceImage);
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("canvas_prev.gif")
|
||||
{
|
||||
std::array expectedFrames = {
|
||||
ExpectedFrame{
|
||||
Nz::Image::LoadFromFile(resourcePath / "Utility/GIF/canvas_prev/0.png"),
|
||||
0
|
||||
},
|
||||
ExpectedFrame{
|
||||
Nz::Image::LoadFromFile(resourcePath / "Utility/GIF/canvas_prev/1.png"),
|
||||
100
|
||||
},
|
||||
ExpectedFrame{
|
||||
Nz::Image::LoadFromFile(resourcePath / "Utility/GIF/canvas_prev/2.png"),
|
||||
1100
|
||||
},
|
||||
ExpectedFrame{
|
||||
Nz::Image::LoadFromFile(resourcePath / "Utility/GIF/canvas_prev/3.png"),
|
||||
2100
|
||||
},
|
||||
ExpectedFrame{
|
||||
Nz::Image::LoadFromFile(resourcePath / "Utility/GIF/canvas_prev/4.png"),
|
||||
3100
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<Nz::ImageStream> gif = Nz::ImageStream::OpenFromFile(resourcePath / "Utility/GIF/canvas_prev.gif");
|
||||
REQUIRE(gif);
|
||||
|
||||
Nz::Vector2ui size = gif->GetSize();
|
||||
CHECK(size == Nz::Vector2ui(100, 100));
|
||||
CHECK(gif->GetFrameCount() == expectedFrames.size());
|
||||
CHECK(gif->GetPixelFormat() == Nz::PixelFormat::RGBA8);
|
||||
|
||||
frameData.resize(Nz::PixelFormatInfo::ComputeSize(gif->GetPixelFormat(), size.x, size.y, 1));
|
||||
|
||||
// Decode all frames in order
|
||||
Nz::UInt64 frameTime;
|
||||
for (ExpectedFrame& expectedFrame : expectedFrames)
|
||||
{
|
||||
REQUIRE(expectedFrame.referenceImage);
|
||||
REQUIRE(gif->DecodeNextFrame(frameData.data(), &frameTime));
|
||||
|
||||
CHECK(frameTime == expectedFrame.time);
|
||||
|
||||
CompareFrames(*gif, frameData, *expectedFrame.referenceImage);
|
||||
}
|
||||
|
||||
// Decoding the post-the-end frame fails but gives the end frametime
|
||||
REQUIRE_FALSE(gif->DecodeNextFrame(frameData.data(), &frameTime));
|
||||
CHECK(frameTime == 4100);
|
||||
|
||||
// Decode frames in arbitrary order, to ensure results are corrects
|
||||
for (std::size_t frameIndex : { 2, 0, 3, 1, 4 })
|
||||
{
|
||||
INFO("Decoding frame " << frameIndex);
|
||||
|
||||
ExpectedFrame& expectedFrame = expectedFrames[frameIndex];
|
||||
gif->Seek(frameIndex);
|
||||
|
||||
REQUIRE(gif->DecodeNextFrame(frameData.data(), &frameTime));
|
||||
CHECK(frameTime == expectedFrame.time);
|
||||
|
||||
CompareFrames(*gif, frameData, *expectedFrame.referenceImage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
51
tests/UnitTests/Engine/Utility/MeshLoading.cpp
Normal file
51
tests/UnitTests/Engine/Utility/MeshLoading.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#include <Nazara/Utility/Mesh.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <filesystem>
|
||||
|
||||
std::filesystem::path GetAssetDir();
|
||||
|
||||
SCENARIO("Meshes", "[Utility][Mesh]")
|
||||
{
|
||||
WHEN("Loading OJB files")
|
||||
{
|
||||
GIVEN("Spaceship/spaceship.obj")
|
||||
{
|
||||
std::shared_ptr<Nz::Mesh> spaceship = Nz::Mesh::LoadFromFile(GetAssetDir() / "Utility/Spaceship/spaceship.obj");
|
||||
REQUIRE(spaceship);
|
||||
|
||||
CHECK(!spaceship->IsAnimable());
|
||||
CHECK(spaceship->GetSubMeshCount() == 2);
|
||||
CHECK(spaceship->GetMaterialCount() == 2);
|
||||
CHECK(spaceship->GetTriangleCount() == 6814);
|
||||
CHECK(spaceship->GetVertexCount() == 8713);
|
||||
}
|
||||
|
||||
GIVEN("SpaceStation/space_station.obj")
|
||||
{
|
||||
std::shared_ptr<Nz::Mesh> spacestation = Nz::Mesh::LoadFromFile(GetAssetDir() / "Utility/SpaceStation/space_station.obj");
|
||||
REQUIRE(spacestation);
|
||||
|
||||
CHECK(!spacestation->IsAnimable());
|
||||
CHECK(spacestation->GetSubMeshCount() == 1);
|
||||
CHECK(spacestation->GetMaterialCount() == 1);
|
||||
CHECK(spacestation->GetTriangleCount() == 422);
|
||||
CHECK(spacestation->GetVertexCount() == 516);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("Loading MD2 files")
|
||||
{
|
||||
GIVEN("drfreak.md2")
|
||||
{
|
||||
std::shared_ptr<Nz::Mesh> drfreak = Nz::Mesh::LoadFromFile(GetAssetDir() / "Utility/drfreak/drfreak.md2");
|
||||
CHECK(drfreak);
|
||||
|
||||
CHECK(!drfreak->IsAnimable()); //< non-skeletal animations are not supported
|
||||
CHECK(drfreak->GetSubMeshCount() == 1);
|
||||
CHECK(drfreak->GetMaterialCount() == 1);
|
||||
CHECK(drfreak->GetTriangleCount() == 593);
|
||||
CHECK(drfreak->GetVertexCount() == 496);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user