diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/external_dependencies/openmpt-trunk/test | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz |
Initial community commit
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/test')
24 files changed, 4794 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/test/TestTools.h b/Src/external_dependencies/openmpt-trunk/test/TestTools.h new file mode 100644 index 00000000..3e4b1052 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/TestTools.h @@ -0,0 +1,32 @@ +/* + * TestTools.h + * ----------- + * Purpose: Unit test framework. + * Notes : + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#include "openmpt/all/BuildSettings.hpp" + +#include "TestToolsTracker.h" +#include "TestToolsLib.h" + +#include "../common/mptPathString.h" + +OPENMPT_NAMESPACE_BEGIN + +#ifdef ENABLE_TESTS + +namespace Test +{ + +mpt::PathString GetPathPrefix(); + +} // namespace Test + +OPENMPT_NAMESPACE_END + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/test/TestToolsLib.cpp b/Src/external_dependencies/openmpt-trunk/test/TestToolsLib.cpp new file mode 100644 index 00000000..b60ad6c1 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/TestToolsLib.cpp @@ -0,0 +1,309 @@ +/* + * TestToolsLib.cpp + * ---------------- + * Purpose: Unit test framework for libopenmpt. + * Notes : Currently somewhat unreadable :/ + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "TestToolsLib.h" + + +#ifdef ENABLE_TESTS +#ifndef MODPLUG_TRACKER + + +#include <exception> +#include <iostream> + +#include <cstdlib> + + +OPENMPT_NAMESPACE_BEGIN + + +namespace Test { + + + +void mpt_test_reporter::case_run(const mpt::source_location& loc) +{ + #if !MPT_OS_DJGPP + std::cout << "TEST..: " << MPT_AFORMAT("{}({}):")(loc.file_name() ? loc.file_name() : "", loc.line()) << ": " << std::endl; + #else + MPT_UNUSED(loc); + #endif +} + +void mpt_test_reporter::case_run(const mpt::source_location& loc, const char* text_e) +{ + #if !MPT_OS_DJGPP + std::cout << "TEST..: " << MPT_AFORMAT("{}({}): {}")(loc.file_name() ? loc.file_name() : "", loc.line(), text_e) << ": " << std::endl; + #else + MPT_UNUSED(loc); + MPT_UNUSED(text_e); + #endif +} + +void mpt_test_reporter::case_run(const mpt::source_location& loc, const char* text_ex, const char* text_e) +{ + if(text_ex) + { + #if !MPT_OS_DJGPP + std::cout << "TEST..: " << MPT_AFORMAT("{}({}): {} throws {}")(loc.file_name() ? loc.file_name() : "", loc.line(), text_e, text_ex) << ": " << std::endl; + #else + MPT_UNUSED(loc); + MPT_UNUSED(text_ex); + MPT_UNUSED(text_e); + #endif + } else + { + #if !MPT_OS_DJGPP + std::cout << "TEST..: " << MPT_AFORMAT("{}({}): {} throws")(loc.file_name() ? loc.file_name() : "", loc.line(), text_e) << ": " << std::endl; + #else + MPT_UNUSED(loc); + MPT_UNUSED(text_ex); + MPT_UNUSED(text_e); + #endif + } +} + +void mpt_test_reporter::case_run(const mpt::source_location& loc, const char* text_a, const char* text_cmp, const char* text_b) +{ + #if !MPT_OS_DJGPP + std::cout << "TEST..: " << MPT_AFORMAT("{}({}): {} {} {}")(loc.file_name() ? loc.file_name() : "", loc.line(), text_a, text_cmp, text_b) << ": " << std::endl; + #else + MPT_UNUSED(loc); + MPT_UNUSED(text_a); + MPT_UNUSED(text_cmp); + MPT_UNUSED(text_b); + #endif +} + +void mpt_test_reporter::case_result(const mpt::source_location& loc, const mpt::test::result& result) +{ + MPT_UNUSED(loc); + if(std::holds_alternative<mpt::test::result_success>(result.info)) + { + #if !MPT_OS_DJGPP + std::cout << "RESULT: PASS" << std::endl; + #endif + } else if(std::holds_alternative<mpt::test::result_failure>(result.info)) + { + fail_count++; + std::cout << "RESULT: FAIL" << std::endl; + std::cout.flush(); + std::cerr << "FAIL: " << "FAILURE: " << std::get<mpt::test::result_failure>(result.info).text << std::endl; + std::cerr.flush(); + } else if(std::holds_alternative<mpt::test::result_unexpected_exception>(result.info)) + { + fail_count++; + std::cout << "RESULT: FAIL" << std::endl; + std::cout.flush(); + std::cerr << "FAIL: " << "UNEXPECTED EXCEPTION: " << std::get<mpt::test::result_unexpected_exception>(result.info).text << std::endl; + std::cerr.flush(); + } else + { + fail_count++; + std::cout << "RESULT: FAIL" << std::endl; + std::cout.flush(); + std::cerr << "FAIL: " << "UNKOWN" << std::endl; + std::cerr.flush(); + } +} + + + +int fail_count = 0; + + +static std::string remove_newlines(std::string str) +{ + return mpt::replace(mpt::replace(str, std::string("\n"), std::string(" ")), std::string("\r"), std::string(" ")); +} + + +Testcase::Testcase(Fatality fatality, Verbosity verbosity, const char * const desc, const mpt::source_location &loc) + : fatality(fatality) + , verbosity(verbosity) + , desc(desc) + , loc(loc) +{ + return; +} + + +std::string Testcase::AsString() const +{ + return MPT_AFORMAT("{}({}): {}")(loc.file_name() ? loc.file_name() : "", loc.line(), remove_newlines(desc)); +} + + +void Testcase::ShowStart() const +{ + switch(verbosity) + { + case VerbosityQuiet: + break; + case VerbosityNormal: +#if !MPT_OS_DJGPP + std::cout << "TEST..: " << AsString() << ": " << std::endl; +#endif + break; + case VerbosityVerbose: +#if !MPT_OS_DJGPP + std::cout << "TEST..: " << AsString() << ": " << std::endl; +#endif + break; + } +} + + +void Testcase::ShowProgress(const char * text) const +{ + switch(verbosity) + { + case VerbosityQuiet: + break; + case VerbosityNormal: + break; + case VerbosityVerbose: +#if !MPT_OS_DJGPP + std::cout << "TEST..: " << AsString() << ": " << text << std::endl; +#else + MPT_UNUSED_VARIABLE(text); +#endif + break; + } +} + + +void Testcase::ShowPass() const +{ + switch(verbosity) + { + case VerbosityQuiet: + break; + case VerbosityNormal: +#if !MPT_OS_DJGPP + std::cout << "RESULT: PASS" << std::endl; +#endif + break; + case VerbosityVerbose: +#if !MPT_OS_DJGPP + std::cout << "PASS..: " << AsString() << std::endl; +#endif + break; + } +} + + +void Testcase::ShowFail(bool exception, const char * const text) const +{ + switch(verbosity) + { + case VerbosityQuiet: + break; + case VerbosityNormal: + std::cout << "RESULT: FAIL" << std::endl; + break; + case VerbosityVerbose: + std::cout << "FAIL..: " << AsString() << std::endl; + break; + } + std::cout.flush(); + if(!exception) + { + if(!text || (text && std::string(text).empty())) + { + std::cerr << "FAIL: " << AsString() << std::endl; + } else + { + std::cerr << "FAIL: " << AsString() << " : " << text << std::endl; + } + } else + { + if(!text || (text && std::string(text).empty())) + { + std::cerr << "FAIL: " << AsString() << " EXCEPTION!" << std::endl; + } else + { + std::cerr << "FAIL: " << AsString() << " EXCEPTION: " << text << std::endl; + } + } + std::cerr.flush(); +} + + +void Testcase::ReportPassed() +{ + ShowPass(); +} + + +void Testcase::ReportFailed() +{ + fail_count++; + ReportException(); +} + + +void Testcase::ReportException() +{ + try + { + throw; // get the exception + } catch(TestFailed & e) + { + ShowFail(false, e.values.c_str()); + if(fatality == FatalityStop) + { + throw; // rethrow + } + } catch(std::exception & e) + { + ShowFail(true, e.what()); + throw; // rethrow + } catch(...) + { + ShowFail(true); + throw; // rethrow + } +} + + +} // namespace Test + + +#if defined(MPT_ASSERT_HANDLER_NEEDED) + +MPT_NOINLINE void AssertHandler(const mpt::source_location &loc, const char *expr, const char *msg) +{ + Test::fail_count++; + if(msg) + { + mpt::log::GlobalLogger().SendLogMessage(loc, LogError, "ASSERT", + U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::Charset::ASCII, msg) + U_(" (") + mpt::ToUnicode(mpt::Charset::ASCII, expr) + U_(")") + ); + } else + { + mpt::log::GlobalLogger().SendLogMessage(loc, LogError, "ASSERT", + U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::Charset::ASCII, expr) + ); + } + #if defined(MPT_BUILD_FATAL_ASSERTS) + std::abort(); + #endif // MPT_BUILD_FATAL_ASSERTS +} + +#endif // MPT_ASSERT_HANDLER_NEEDED + + +OPENMPT_NAMESPACE_END + + +#endif // !MODPLUG_TRACKER +#endif // ENABLE_TESTS diff --git a/Src/external_dependencies/openmpt-trunk/test/TestToolsLib.h b/Src/external_dependencies/openmpt-trunk/test/TestToolsLib.h new file mode 100644 index 00000000..913f593e --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/TestToolsLib.h @@ -0,0 +1,362 @@ +/* + * TestToolsLib.h + * -------------- + * Purpose: Unit test framework for libopenmpt. + * Notes : This is more complex than the OpenMPT version because we cannot + * rely on a debugger and have to deal with exceptions ourselves. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "openmpt/all/BuildSettings.hpp" + + +#ifdef ENABLE_TESTS +#ifndef MODPLUG_TRACKER + + +//#define MPT_TEST_CXX11 + + +#include "mpt/test/test.hpp" + +#include <type_traits> + +#include "mpt/base/bit.hpp" +#include "openmpt/base/FlagSet.hpp" +#include "../soundlib/Snd_defs.h" + + +OPENMPT_NAMESPACE_BEGIN + + +namespace Test { + + + +class mpt_test_reporter + : public mpt::test::silent_reporter +{ +public: + mpt_test_reporter() = default; + ~mpt_test_reporter() override = default; +public: + void case_run(const mpt::source_location & loc) override; + void case_run(const mpt::source_location & loc, const char * text_e) override; + void case_run(const mpt::source_location & loc, const char * text_ex, const char * text_e) override; + void case_run(const mpt::source_location & loc, const char * text_a, const char * text_cmp, const char * text_b) override; + void case_result(const mpt::source_location & loc, const mpt::test::result & result) override; +}; + + + +extern int fail_count; + + +enum Verbosity +{ + VerbosityQuiet, + VerbosityNormal, + VerbosityVerbose, +}; + +enum Fatality +{ + FatalityContinue, + FatalityStop +}; + + +struct TestFailed +{ + std::string values; + TestFailed(const std::string &values) : values(values) { } + TestFailed() { } +}; + +} // namespace Test + +template<typename T> +struct ToStringHelper +{ + std::string operator () (const T &x) + { + return mpt::afmt::val(x); + } +}; + +#ifdef MPT_TEST_CXX11 + +template<> +struct ToStringHelper<mpt::endian> +{ + std::string operator () (const mpt::endian &x) + { + if(x == mpt::endian::big) return "big"; + if(x == mpt::endian::little) return "little"; + return "unknown"; + } +}; + +template<typename enum_t, typename store_t> +struct ToStringHelper<FlagSet<enum_t, store_t> > +{ + std::string operator () (const FlagSet<enum_t, store_t> &x) + { + return mpt::afmt::val(x.GetRaw()); + } +}; + +template<typename enum_t> +struct ToStringHelper<enum_value_type<enum_t> > +{ + std::string operator () (const enum_value_type<enum_t> &x) + { + return mpt::afmt::val(x.as_bits()); + } +}; + +template<typename Ta, typename Tb> +struct ToStringHelper<std::pair<Ta, Tb> > +{ + std::string operator () (const std::pair<Ta, Tb> &x) + { + return std::string("{") + mpt::afmt::val(x.first) + std::string(",") + mpt::afmt::val(x.second) + std::string("}"); + } +}; + +template<std::size_t FRACT, typename T> +struct ToStringHelper<FPInt<FRACT, T> > +{ + std::string operator () (const FPInt<FRACT, T> &x) + { + return std::string("FPInt<") + mpt::afmt::val(FRACT) + std::string(",") + mpt::afmt::val(typeid(T).name()) + std::string(">{") + mpt::afmt::val(x.GetInt()) + std::string(".") + mpt::afmt::val(x.GetFract()) + std::string("}"); + } +}; + +template<> +struct ToStringHelper<SamplePosition> +{ + std::string operator () (const SamplePosition &x) + { + return mpt::afmt::val(x.GetInt()) + std::string(".") + std::string("0x") + mpt::afmt::hex0<8>(x.GetFract()); + } +}; + +#endif // MPT_TEST_CXX11 + + +namespace Test { + +class Testcase +{ + +private: + + Fatality const fatality; + Verbosity const verbosity; + const char * const desc; + mpt::source_location const loc; + +public: + + Testcase(Fatality fatality, Verbosity verbosity, const char * const desc, const mpt::source_location &loc); + +public: + + std::string AsString() const; + + void ShowStart() const; + void ShowProgress(const char * text) const; + void ShowPass() const; + void ShowFail(bool exception = false, const char * const text = nullptr) const; + + void ReportPassed(); + void ReportFailed(); + + void ReportException(); + +private: + + template <typename Tx, typename Ty> + inline bool IsEqual(const Tx &x, const Ty &y, std::false_type, std::false_type) + { + return (x == y); + } + + template <typename Tx, typename Ty> + inline bool IsEqual(const Tx &x, const Ty &y, std::false_type, std::true_type) + { + return (x == y); + } + + template <typename Tx, typename Ty> + inline bool IsEqual(const Tx &x, const Ty &y, std::true_type, std::false_type) + { + return (x == y); + } + + template <typename Tx, typename Ty> + inline bool IsEqual(const Tx &x, const Ty &y, std::true_type /* is_integer */, std::true_type /* is_integer */ ) + { + // Avoid signed-unsigned-comparison warnings and test equivalence in case of either type conversion direction. + return ((x == static_cast<Tx>(y)) && (static_cast<Ty>(x) == y)); + } + + template <typename Tx, typename Ty, typename Teps> + inline bool IsEqualEpsilon(const Tx &x, const Ty &y, const Teps &eps) + { + return std::abs(x - y) <= eps; + } + +public: + +#ifdef MPT_TEST_CXX11 + +private: + + template <typename Tx, typename Ty> + MPT_NOINLINE void TypeCompareHelper(const Tx &x, const Ty &y) + { + if(!IsEqual(x, y, std::is_integral<Tx>(), std::is_integral<Ty>())) + { + throw TestFailed(MPT_AFORMAT("{} != {}")(ToStringHelper<Tx>()(x), ToStringHelper<Ty>()(y))); + //throw TestFailed(); + } + } + + template <typename Tx, typename Ty, typename Teps> + MPT_NOINLINE void TypeCompareHelper(const Tx &x, const Ty &y, const Teps &eps) + { + if(!IsEqualEpsilon(x, y, eps)) + { + throw TestFailed(MPT_AFORMAT("{} != {}")(ToStringHelper<Tx>()(x), ToStringHelper<Ty>()(y))); + //throw TestFailed(); + } + } + +public: + + template <typename Tfx, typename Tfy> + MPT_NOINLINE void operator () (const Tfx &fx, const Tfy &fy) + { + ShowStart(); + try + { + ShowProgress("Calculate x ..."); + const auto x = fx(); + ShowProgress("Calculate y ..."); + const auto y = fy(); + ShowProgress("Compare ..."); + TypeCompareHelper(x, y); + ReportPassed(); + } catch(...) + { + ReportFailed(); + } + } + + template <typename Tfx, typename Tfy, typename Teps> + MPT_NOINLINE void operator () (const Tfx &fx, const Tfy &fy, const Teps &eps) + { + ShowStart(); + try + { + ShowProgress("Calculate x ..."); + const auto x = fx(); + ShowProgress("Calculate y ..."); + const auto y = fy(); + ShowProgress("Compare ..."); + TypeCompareHelper(x, y, eps); + ReportPassed(); + } catch(...) + { + ReportFailed(); + } + } + + #define VERIFY_EQUAL(x,y) Test::Testcase(Test::FatalityContinue, Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( [&](){return (x) ;}, [&](){return (y) ;} ) + #define VERIFY_EQUAL_NONCONT(x,y) Test::Testcase(Test::FatalityStop , Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( [&](){return (x) ;}, [&](){return (y) ;} ) + #define VERIFY_EQUAL_QUIET_NONCONT(x,y) Test::Testcase(Test::FatalityStop , Test::VerbosityQuiet , #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( [&](){return (x) ;}, [&](){return (y) ;} ) + + #define VERIFY_EQUAL_EPS(x,y,eps) Test::Testcase(Test::FatalityContinue, Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( [&](){return (x) ;}, [&](){return (y) ;}, (eps) ) + +#else + +public: + + template <typename Tx, typename Ty> + MPT_NOINLINE void operator () (const Tx &x, const Ty &y) + { + ShowStart(); + try + { + if(!IsEqual(x, y, std::is_integral<Tx>(), std::is_integral<Ty>())) + { + //throw TestFailed(MPT_AFORMAT("{} != {}")(x, y)); + throw TestFailed(); + } + ReportPassed(); + } catch(...) + { + ReportFailed(); + } + } + + template <typename Tx, typename Ty, typename Teps> + MPT_NOINLINE void operator () (const Tx &x, const Ty &y, const Teps &eps) + { + ShowStart(); + try + { + if(!IsEqualEpsilon(x, y, eps)) + { + //throw TestFailed(MPT_AFORMAT("{} != {}")(x, y)); + throw TestFailed(); + } + ReportPassed(); + } catch(...) + { + ReportFailed(); + } + } + + #define VERIFY_EQUAL(x,y) Test::Testcase(Test::FatalityContinue, Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( (x) , (y) ) + #define VERIFY_EQUAL_NONCONT(x,y) Test::Testcase(Test::FatalityStop , Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( (x) , (y) ) + #define VERIFY_EQUAL_QUIET_NONCONT(x,y) Test::Testcase(Test::FatalityStop , Test::VerbosityQuiet , #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( (x) , (y) ) + + #define VERIFY_EQUAL_EPS(x,y,eps) Test::Testcase(Test::FatalityContinue, Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( (x) , (y), (eps) ) + +#endif + +}; + + +#define DO_TEST(func) \ +do { \ + Test::Testcase test(Test::FatalityStop, Test::VerbosityVerbose, #func , MPT_SOURCE_LOCATION_CURRENT() ); \ + try { \ + test.ShowStart(); \ + fail_count = 0; \ + func(); \ + if(fail_count > 0) { \ + throw Test::TestFailed(); \ + } \ + test.ReportPassed(); \ + } catch(...) { \ + test.ReportException(); \ + } \ +} while(0) + + +} // namespace Test + + +OPENMPT_NAMESPACE_END + + +#endif // !MODPLUG_TRACKER +#endif // ENABLE_TESTS diff --git a/Src/external_dependencies/openmpt-trunk/test/TestToolsTracker.h b/Src/external_dependencies/openmpt-trunk/test/TestToolsTracker.h new file mode 100644 index 00000000..91d36dec --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/TestToolsTracker.h @@ -0,0 +1,92 @@ +/* + * TestToolsTracker.h + * ------------------ + * Purpose: Unit test framework for OpenMPT. + * Notes : Really basic functionality that relies on a debugger that catches + * exceptions and breaks right at the spot where it gets thrown. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "openmpt/all/BuildSettings.hpp" + + +#ifdef ENABLE_TESTS +#ifdef MODPLUG_TRACKER + + +#include "mpt/test/test.hpp" + + +OPENMPT_NAMESPACE_BEGIN + + +namespace Test { + + +#if MPT_COMPILER_MSVC +// With MSVC, break directly using __debugbreak intrinsic instead of calling DebugBreak which breaks one stackframe deeper than we want +#define MyDebugBreak() __debugbreak() +#else +#define MyDebugBreak() DebugBreak() +#endif + + +class mpt_test_reporter + : public mpt::test::silent_reporter +{ +public: + mpt_test_reporter() = default; + ~mpt_test_reporter() override = default; +public: + inline void immediate_breakpoint() override { + MyDebugBreak(); + } +}; + + +// Verify that given parameters are 'equal'. Break directly into the debugger if not. +// The exact meaning of equality is based on operator== of the compared types. +#define VERIFY_EQUAL(x,y) \ + do { \ + if(!((x) == (y))) { \ + MyDebugBreak(); \ + } \ + } while(0) \ +/**/ + +// Like VERIFY_EQUAL, only differs for libopenmpt +#define VERIFY_EQUAL_NONCONT VERIFY_EQUAL + +// Like VERIFY_EQUAL, only differs for libopenmpt +#define VERIFY_EQUAL_QUIET_NONCONT VERIFY_EQUAL + +#define VERIFY_EQUAL_EPS(x,y,eps) \ + do { \ + if(std::abs((x) - (y)) > (eps)) { \ + MyDebugBreak(); \ + } \ + } while(0) \ +/**/ + + +#define DO_TEST(func) \ + do { \ + if(IsDebuggerPresent()) { \ + func(); \ + } \ + } while(0) \ +/**/ + + +} // namespace Test + + +OPENMPT_NAMESPACE_END + + +#endif // MODPLUG_TRACKER +#endif // ENABLE_TESTS diff --git a/Src/external_dependencies/openmpt-trunk/test/mpt_tests_base.cpp b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_base.cpp new file mode 100644 index 00000000..5041cf93 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_base.cpp @@ -0,0 +1,13 @@ + +#include "stdafx.h" + +#ifdef ENABLE_TESTS + +#include "mpt/base/tests/tests_base_arithmetic_shift.hpp" +#include "mpt/base/tests/tests_base_bit.hpp" +#include "mpt/base/tests/tests_base_math.hpp" +#include "mpt/base/tests/tests_base_saturate_cast.hpp" +#include "mpt/base/tests/tests_base_saturate_round.hpp" +#include "mpt/base/tests/tests_base_wrapping_divide.hpp" + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/test/mpt_tests_binary.cpp b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_binary.cpp new file mode 100644 index 00000000..a62afd6a --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_binary.cpp @@ -0,0 +1,8 @@ + +#include "stdafx.h" + +#ifdef ENABLE_TESTS + +#include "mpt/binary/tests/tests_binary.hpp" + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/test/mpt_tests_crc.cpp b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_crc.cpp new file mode 100644 index 00000000..021f6e0c --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_crc.cpp @@ -0,0 +1,8 @@ + +#include "stdafx.h" + +#ifdef ENABLE_TESTS + +#include "mpt/crc/tests/tests_crc.hpp" + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/test/mpt_tests_crypto.cpp b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_crypto.cpp new file mode 100644 index 00000000..ddc76bfc --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_crypto.cpp @@ -0,0 +1,10 @@ + +#include "stdafx.h" + +#ifdef ENABLE_TESTS + +#ifdef MODPLUG_TRACKER +#include "mpt/crypto/tests/tests_crypto.hpp" +#endif // MODPLUG_TRACKER + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/test/mpt_tests_endian.cpp b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_endian.cpp new file mode 100644 index 00000000..409329ab --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_endian.cpp @@ -0,0 +1,9 @@ + +#include "stdafx.h" + +#ifdef ENABLE_TESTS + +#include "mpt/endian/tests/tests_endian_floatingpoint.hpp" +#include "mpt/endian/tests/tests_endian_integer.hpp" + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/test/mpt_tests_format.cpp b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_format.cpp new file mode 100644 index 00000000..84de3e1e --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_format.cpp @@ -0,0 +1,9 @@ + +#include "stdafx.h" + +#ifdef ENABLE_TESTS + +#include "mpt/format/tests/tests_format_message.hpp" +#include "mpt/format/tests/tests_format_simple.hpp" + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/test/mpt_tests_io.cpp b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_io.cpp new file mode 100644 index 00000000..dc33c0de --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_io.cpp @@ -0,0 +1,8 @@ + +#include "stdafx.h" + +#ifdef ENABLE_TESTS + +#include "mpt/io/tests/tests_io.hpp" + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/test/mpt_tests_parse.cpp b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_parse.cpp new file mode 100644 index 00000000..80192bea --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_parse.cpp @@ -0,0 +1,8 @@ + +#include "stdafx.h" + +#ifdef ENABLE_TESTS + +#include "mpt/parse/tests/tests_parse.hpp" + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/test/mpt_tests_random.cpp b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_random.cpp new file mode 100644 index 00000000..d86d9ac8 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_random.cpp @@ -0,0 +1,8 @@ + +#include "stdafx.h" + +#ifdef ENABLE_TESTS + +#include "mpt/random/tests/tests_random.hpp" + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/test/mpt_tests_string.cpp b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_string.cpp new file mode 100644 index 00000000..eb9651da --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_string.cpp @@ -0,0 +1,9 @@ + +#include "stdafx.h" + +#ifdef ENABLE_TESTS + +#include "mpt/string/tests/tests_string_buffer.hpp" +#include "mpt/string/tests/tests_string_utility.hpp" + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/test/mpt_tests_string_transcode.cpp b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_string_transcode.cpp new file mode 100644 index 00000000..44fa7bf7 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_string_transcode.cpp @@ -0,0 +1,8 @@ + +#include "stdafx.h" + +#ifdef ENABLE_TESTS + +#include "mpt/string_transcode/tests/tests_string_transcode.hpp" + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/test/mpt_tests_uuid.cpp b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_uuid.cpp new file mode 100644 index 00000000..03f4bb65 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_uuid.cpp @@ -0,0 +1,8 @@ + +#include "stdafx.h" + +#ifdef ENABLE_TESTS + +#include "mpt/uuid/tests/tests_uuid.hpp" + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/test/mpt_tests_uuid_namespace.cpp b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_uuid_namespace.cpp new file mode 100644 index 00000000..1ae2e82c --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/mpt_tests_uuid_namespace.cpp @@ -0,0 +1,10 @@ + +#include "stdafx.h" + +#ifdef ENABLE_TESTS + +#ifdef MODPLUG_TRACKER +#include "mpt/uuid_namespace/tests/tests_uuid_namespace.hpp" +#endif // MODPLUG_TRACKER + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/test/test.cpp b/Src/external_dependencies/openmpt-trunk/test/test.cpp new file mode 100644 index 00000000..5f226885 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/test.cpp @@ -0,0 +1,3859 @@ +/* + * test.cpp + * -------- + * Purpose: Unit tests for OpenMPT. + * Notes : We need FAAAAAAAR more unit tests! + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "test.h" + + +#ifdef ENABLE_TESTS + +#include "mpt/base/numbers.hpp" +#include "mpt/crc/crc.hpp" +#include "mpt/environment/environment.hpp" +#include "mpt/io/base.hpp" +#include "mpt/io/io.hpp" +#include "mpt/io/io_stdstream.hpp" +#include "mpt/io_read/filecursor_stdstream.hpp" +#include "mpt/test/test.hpp" +#include "mpt/test/test_macros.hpp" +#include "mpt/uuid/uuid.hpp" + +#include "../common/version.h" +#include "../common/misc_util.h" +#include "../common/mptStringBuffer.h" +#include "../common/serialization_utils.h" +#include "../soundlib/Sndfile.h" +#include "../common/FileReader.h" +#include "../soundlib/mod_specifications.h" +#include "../soundlib/MIDIEvents.h" +#include "../soundlib/MIDIMacros.h" +#include "openmpt/soundbase/Copy.hpp" +#include "openmpt/soundbase/SampleConvert.hpp" +#include "openmpt/soundbase/SampleDecode.hpp" +#include "openmpt/soundbase/SampleEncode.hpp" +#include "openmpt/soundbase/SampleFormat.hpp" +#include "../soundlib/SampleCopy.h" +#include "../soundlib/SampleNormalize.h" +#include "../soundlib/ModSampleCopy.h" +#include "../soundlib/ITCompression.h" +#include "../soundlib/tuningcollection.h" +#include "../soundlib/tuning.h" +#include "openmpt/soundbase/Dither.hpp" +#include "../common/Dither.h" +#ifdef MODPLUG_TRACKER +#include "../mptrack/Mptrack.h" +#include "../mptrack/Moddoc.h" +#include "../mptrack/ModDocTemplate.h" +#include "../mptrack/Mainfrm.h" +#include "../mptrack/Settings.h" +#include "../mptrack/HTTP.h" +#endif // MODPLUG_TRACKER +#include "../common/mptFileIO.h" +#ifdef MODPLUG_TRACKER +#include "mpt/crypto/hash.hpp" +#include "mpt/crypto/jwk.hpp" +#include "mpt/uuid_namespace/uuid_namespace.hpp" +#endif // MODPLUG_TRACKER +#ifdef LIBOPENMPT_BUILD +#include "../libopenmpt/libopenmpt_version.h" +#endif // LIBOPENMPT_BUILD +#ifndef NO_PLUGINS +#include "../soundlib/plugins/PlugInterface.h" +#endif +#include <sstream> +#include <limits> +#ifdef LIBOPENMPT_BUILD +#include <iostream> +#endif // LIBOPENMPT_BUILD +#include <istream> +#include <ostream> +#include <stdexcept> +#if MPT_COMPILER_MSVC +#include <tchar.h> +#endif +#if MPT_OS_WINDOWS +#include <windows.h> +#endif +#if defined(MPT_WITH_ZLIB) +#include <zlib.h> +#elif defined(MPT_WITH_MINIZ) +#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES +#include <miniz/miniz.h> +#endif + +#define MPT_TEST_HAS_FILESYSTEM 1 +#if MPT_OS_DJGPP +#undef MPT_TEST_HAS_FILESYSTEM +#define MPT_TEST_HAS_FILESYSTEM 0 +#endif + +#include "TestTools.h" + + +// enable tests which may fail spuriously +//#define FLAKY_TESTS + + + +OPENMPT_NAMESPACE_BEGIN + + + +namespace Test { + + + +static MPT_NOINLINE void TestVersion(); +static MPT_NOINLINE void TestTypes(); +static MPT_NOINLINE void TestMisc1(); +static MPT_NOINLINE void TestMisc2(); +static MPT_NOINLINE void TestRandom(); +static MPT_NOINLINE void TestCharsets(); +static MPT_NOINLINE void TestStringFormatting(); +static MPT_NOINLINE void TestSettings(); +static MPT_NOINLINE void TestStringIO(); +static MPT_NOINLINE void TestMIDIEvents(); +static MPT_NOINLINE void TestSampleConversion(); +static MPT_NOINLINE void TestITCompression(); +static MPT_NOINLINE void TestPCnoteSerialization(); +static MPT_NOINLINE void TestLoadSaveFile(); +static MPT_NOINLINE void TestEditing(); + + + +static mpt::PathString *PathPrefix = nullptr; +static mpt::default_prng * s_PRNG = nullptr; + + + +mpt::PathString GetPathPrefix() +{ + if((*PathPrefix).empty()) + { + return P_(""); + } + return *PathPrefix + P_("/"); +} + + +void DoTests() +{ + + #ifdef LIBOPENMPT_BUILD + + std::cout << std::flush; + std::clog << std::flush; + std::cerr << std::flush; + + std::cout << "libopenmpt test suite" << std::endl; + + std::cout << "Version.: " << mpt::ToCharset(mpt::Charset::ASCII, Build::GetVersionString(Build::StringVersion | Build::StringRevision | Build::StringSourceInfo | Build::StringBuildFlags | Build::StringBuildFeatures)) << std::endl; + std::cout << "Compiler: " << mpt::ToCharset(mpt::Charset::ASCII, Build::GetBuildCompilerString()) << std::endl; + + std::cout << std::flush; + + #endif + + #if MPT_OS_WINDOWS + + // prefix for test suite + std::wstring pathprefix = std::wstring(); + + bool libopenmpt = false; + #ifdef LIBOPENMPT_BUILD + libopenmpt = true; + #endif + +#if !MPT_OS_WINDOWS_WINRT + // set path prefix for test files (if provided) + std::vector<WCHAR> buf(GetEnvironmentVariableW(L"srcdir", NULL, 0) + 1); + if(GetEnvironmentVariableW(L"srcdir", buf.data(), static_cast<DWORD>(buf.size())) > 0) + { + pathprefix = buf.data(); + } else +#endif + if(libopenmpt && IsDebuggerPresent()) + { + pathprefix = L"../../"; + } + + PathPrefix = new mpt::PathString(mpt::PathString::FromWide(pathprefix)); + + #else + + // prefix for test suite + mpt::ustring pathprefix = mpt::ustring(); + + // set path prefix for test files (if provided) + mpt::ustring env_srcdir = mpt::getenv( U_("srcdir") ).value_or( U_("") ); + if ( !env_srcdir.empty() ) { + pathprefix = env_srcdir; + } + + PathPrefix = new mpt::PathString(mpt::PathString::FromUnicode(pathprefix)); + + #endif + + void (*do_mpt_test)(void) = []() { + mpt_test_reporter reporter{}; + mpt::test::run_all(reporter); + }; + DO_TEST(do_mpt_test); + + mpt::random_device rd; + s_PRNG = new mpt::default_prng(mpt::make_prng<mpt::default_prng>(rd)); + + DO_TEST(TestVersion); + DO_TEST(TestTypes); + DO_TEST(TestMisc1); + DO_TEST(TestMisc2); + DO_TEST(TestRandom); + DO_TEST(TestCharsets); + DO_TEST(TestStringFormatting); + DO_TEST(TestSettings); + DO_TEST(TestStringIO); + DO_TEST(TestMIDIEvents); + DO_TEST(TestSampleConversion); + DO_TEST(TestITCompression); + + // slower tests, require opening a CModDoc + DO_TEST(TestPCnoteSerialization); + DO_TEST(TestLoadSaveFile); + DO_TEST(TestEditing); + + delete s_PRNG; + s_PRNG = nullptr; + + delete PathPrefix; + PathPrefix = nullptr; +} + + +static mpt::PathString GetTempFilenameBase(); + + +static void RemoveFile(const mpt::PathString &filename) +{ + #if MPT_OS_WINDOWS + for(int retry=0; retry<10; retry++) + { + if(DeleteFile(filename.AsNative().c_str()) != FALSE) + { + break; + } + // wait for windows virus scanners + Sleep(10); + } + #else + remove(filename.AsNative().c_str()); + #endif +} + + +// Test if functions related to program version data work +static MPT_NOINLINE void TestVersion() +{ + //Verify that macros and functions work. + { + VERIFY_EQUAL( Version::Parse(Version::Current().ToUString()), Version::Current() ); + VERIFY_EQUAL( Version::Parse(Version::Current().ToUString()).ToUString(), Version::Current().ToUString() ); + VERIFY_EQUAL( Version(18285096).ToUString(), U_("1.17.02.28") ); + VERIFY_EQUAL( Version::Parse(U_("1.17.02.28")), Version(18285096) ); + VERIFY_EQUAL( Version::Parse(U_("1.fe.02.28")), Version(0x01fe0228) ); + VERIFY_EQUAL( Version::Parse(U_("01.fe.02.28")), Version(0x01fe0228) ); + VERIFY_EQUAL( Version::Parse(U_("1.22")), Version(0x01220000) ); + VERIFY_EQUAL( MPT_V("1.17.02.28"), Version(18285096) ); + VERIFY_EQUAL( MPT_V("1.fe.02.28"), Version(0x01fe0228) ); + VERIFY_EQUAL( MPT_V("01.fe.02.28"), Version(0x01fe0228) ); + VERIFY_EQUAL( MPT_V("1.22"), Version(0x01220000) ); + VERIFY_EQUAL( MPT_V("1.19.02.00").WithoutTestNumber(), MPT_V("1.19.02.00")); + VERIFY_EQUAL( MPT_V("1.18.03.20").WithoutTestNumber(), MPT_V("1.18.03.00")); + VERIFY_EQUAL( MPT_V("1.18.01.13").IsTestVersion(), true); + VERIFY_EQUAL( MPT_V("1.19.01.00").IsTestVersion(), false); + VERIFY_EQUAL( MPT_V("1.17.02.54").IsTestVersion(), false); + VERIFY_EQUAL( MPT_V("1.18.00.00").IsTestVersion(), false); + VERIFY_EQUAL( MPT_V("1.18.02.00").IsTestVersion(), false); + VERIFY_EQUAL( MPT_V("1.18.02.01").IsTestVersion(), true); + + // Ensure that versions ending in .00.00 (which are ambiguous to truncated version numbers in certain file formats (e.g. S3M and IT) do not get qualified as test builds. + VERIFY_EQUAL( MPT_V("1.23.00.00").IsTestVersion(), false); + + static_assert( MPT_V("1.17.2.28").GetRawVersion() == 18285096 ); + static_assert( MPT_V("1.17.02.48").GetRawVersion() == 18285128 ); + static_assert( MPT_V("01.17.02.52").GetRawVersion() == 18285138 ); + // Ensure that bit-shifting works (used in some mod loaders for example) + static_assert( MPT_V("01.17.00.00").GetRawVersion() == 0x0117 << 16 ); + static_assert( MPT_V("01.17.03.00").GetRawVersion() >> 8 == 0x011703 ); + + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsNewerThan(VersionWithRevision::Parse(U_("1.30.00.05-r13100"))), false); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsNewerThan(VersionWithRevision::Parse(U_("1.30.00.05-r13099"))), false); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsNewerThan(VersionWithRevision::Parse(U_("1.30.00.05-r13098"))), true); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsNewerThan(VersionWithRevision::Parse(U_("1.30.00.05"))), false); + + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsOlderThan(VersionWithRevision::Parse(U_("1.30.00.05-r13100"))), true); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsOlderThan(VersionWithRevision::Parse(U_("1.30.00.05-r13099"))), false); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsOlderThan(VersionWithRevision::Parse(U_("1.30.00.05-r13098"))), false); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsOlderThan(VersionWithRevision::Parse(U_("1.30.00.05"))), false); + + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsNewerThan(VersionWithRevision::Parse(U_("1.30.00.05-r13100"))), false); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsNewerThan(VersionWithRevision::Parse(U_("1.30.00.05-r13099"))), false); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsNewerThan(VersionWithRevision::Parse(U_("1.30.00.05-r13098"))), false); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsNewerThan(VersionWithRevision::Parse(U_("1.30.00.05"))), false); + + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsOlderThan(VersionWithRevision::Parse(U_("1.30.00.05-r13100"))), false); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsOlderThan(VersionWithRevision::Parse(U_("1.30.00.05-r13099"))), false); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsOlderThan(VersionWithRevision::Parse(U_("1.30.00.05-r13098"))), false); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsOlderThan(VersionWithRevision::Parse(U_("1.30.00.05"))), false); + + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsEqualTo(VersionWithRevision::Parse(U_("1.30.00.05-r13100"))), false); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsEqualTo(VersionWithRevision::Parse(U_("1.30.00.05-r13099"))), true); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsEqualTo(VersionWithRevision::Parse(U_("1.30.00.05-r13098"))), false); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsEqualTo(VersionWithRevision::Parse(U_("1.30.00.05"))), false); + + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsEquivalentTo(VersionWithRevision::Parse(U_("1.30.00.05-r13100"))), false); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsEquivalentTo(VersionWithRevision::Parse(U_("1.30.00.05-r13099"))), true); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsEquivalentTo(VersionWithRevision::Parse(U_("1.30.00.05-r13098"))), false); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05-r13099")).IsEquivalentTo(VersionWithRevision::Parse(U_("1.30.00.05"))), true); + + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsEqualTo(VersionWithRevision::Parse(U_("1.30.00.05-r13100"))), false); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsEqualTo(VersionWithRevision::Parse(U_("1.30.00.05-r13099"))), false); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsEqualTo(VersionWithRevision::Parse(U_("1.30.00.05-r13098"))), false); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsEqualTo(VersionWithRevision::Parse(U_("1.30.00.05"))), true); + + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsEquivalentTo(VersionWithRevision::Parse(U_("1.30.00.05-r13100"))), true); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsEquivalentTo(VersionWithRevision::Parse(U_("1.30.00.05-r13099"))), true); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsEquivalentTo(VersionWithRevision::Parse(U_("1.30.00.05-r13098"))), true); + VERIFY_EQUAL(VersionWithRevision::Parse(U_("1.30.00.05")).IsEquivalentTo(VersionWithRevision::Parse(U_("1.30.00.05"))), true); + + } + +#ifdef MODPLUG_TRACKER + //Verify that the version obtained from the executable file is the same as + //defined in Version. + { + WCHAR szFullPath[MAX_PATH]; + DWORD dwVerHnd; + DWORD dwVerInfoSize; + + // Get version information from the application + ::GetModuleFileNameW(NULL, szFullPath, mpt::saturate_cast<DWORD>(std::size(szFullPath))); + dwVerInfoSize = ::GetFileVersionInfoSizeW(szFullPath, &dwVerHnd); + if (!dwVerInfoSize) + throw std::runtime_error("!dwVerInfoSize is true"); + + std::vector<TCHAR> pVersionInfo(dwVerInfoSize); + + WCHAR *szVer = nullptr; + UINT uVerLength; + if (!(::GetFileVersionInfoW(szFullPath, (DWORD)dwVerHnd, + (DWORD)dwVerInfoSize, pVersionInfo.data()))) + { + throw std::runtime_error("GetFileVersionInfo() returned false"); + } + if (!(::VerQueryValueW(pVersionInfo.data(), L"\\StringFileInfo\\040904b0\\FileVersion", + (LPVOID*)&szVer, &uVerLength))) { + throw std::runtime_error("VerQueryValue() returned false"); + } + + mpt::ustring version = mpt::ToUnicode(szVer); + + VERIFY_EQUAL( version, mpt::ufmt::val(Version::Current()) ); + VERIFY_EQUAL( Version::Parse(version), Version::Current() ); + } +#endif + +#ifdef LIBOPENMPT_BUILD +#if MPT_TEST_HAS_FILESYSTEM +#if !MPT_OS_DJGPP + mpt::PathString version_mk = GetPathPrefix() + P_("libopenmpt/libopenmpt_version.mk"); + mpt::ifstream f(version_mk, std::ios::in); + VERIFY_EQUAL(f ? true : false, true); + std::map<std::string, std::string> fields; + std::string line; + while(std::getline(f, line)) + { + line = mpt::trim(line); + if(line.empty()) + { + continue; + } + std::vector<std::string> line_fields = mpt::String::Split<std::string>(line, std::string("=")); + VERIFY_EQUAL_NONCONT(line_fields.size(), 2u); + line_fields[0] = mpt::trim(line_fields[0]); + line_fields[1] = mpt::trim(line_fields[1]); + VERIFY_EQUAL_NONCONT(line_fields[0].length() > 0, true); + fields[line_fields[0]] = line_fields[1]; + } + VERIFY_EQUAL(fields["LIBOPENMPT_VERSION_MAJOR"], mpt::afmt::val(OPENMPT_API_VERSION_MAJOR)); + VERIFY_EQUAL(fields["LIBOPENMPT_VERSION_MINOR"], mpt::afmt::val(OPENMPT_API_VERSION_MINOR)); + VERIFY_EQUAL(fields["LIBOPENMPT_VERSION_PATCH"], mpt::afmt::val(OPENMPT_API_VERSION_PATCH)); + VERIFY_EQUAL(fields["LIBOPENMPT_VERSION_PREREL"], mpt::afmt::val(OPENMPT_API_VERSION_PREREL)); + if(std::string(OPENMPT_API_VERSION_PREREL).length() > 0) + { + VERIFY_EQUAL(std::string(OPENMPT_API_VERSION_PREREL).substr(0, 1), "-"); + } + VERIFY_EQUAL(OPENMPT_API_VERSION_IS_PREREL, (std::string(OPENMPT_API_VERSION_PREREL).length() > 0) ? 1 : 0); + + VERIFY_EQUAL(fields["LIBOPENMPT_LTVER_CURRENT"].length() > 0, true); + VERIFY_EQUAL(fields["LIBOPENMPT_LTVER_REVISION"].length() > 0, true); + VERIFY_EQUAL(fields["LIBOPENMPT_LTVER_AGE"].length() > 0, true); +#endif // !MPT_OS_DJGPP +#endif // MPT_TEST_HAS_FILESYSTEM +#endif // LIBOPENMPT_BUILD + +} + + +// Test if data types are interpreted correctly +static MPT_NOINLINE void TestTypes() +{ + + static_assert(sizeof(std::uintptr_t) == sizeof(void*)); + #if defined(__SIZEOF_POINTER__) + static_assert(__SIZEOF_POINTER__ == mpt::pointer_size); + static_assert(__SIZEOF_POINTER__ == sizeof(void*)); + #endif + + VERIFY_EQUAL(int8_min, std::numeric_limits<int8>::min()); + VERIFY_EQUAL(int8_max, std::numeric_limits<int8>::max()); + VERIFY_EQUAL(uint8_max, std::numeric_limits<uint8>::max()); + + VERIFY_EQUAL(int16_min, std::numeric_limits<int16>::min()); + VERIFY_EQUAL(int16_max, std::numeric_limits<int16>::max()); + VERIFY_EQUAL(uint16_max, std::numeric_limits<uint16>::max()); + + VERIFY_EQUAL(int32_min, std::numeric_limits<int32>::min()); + VERIFY_EQUAL(int32_max, std::numeric_limits<int32>::max()); + VERIFY_EQUAL(uint32_max, std::numeric_limits<uint32>::max()); + + VERIFY_EQUAL(int64_min, std::numeric_limits<int64>::min()); + VERIFY_EQUAL(int64_max, std::numeric_limits<int64>::max()); + VERIFY_EQUAL(uint64_max, std::numeric_limits<uint64>::max()); + + + static_assert(int8_max == std::numeric_limits<int8>::max()); + static_assert(uint8_max == std::numeric_limits<uint8>::max()); + + static_assert(int16_max == std::numeric_limits<int16>::max()); + static_assert(uint16_max == std::numeric_limits<uint16>::max()); + + static_assert(int32_max == std::numeric_limits<int32>::max()); + static_assert(uint32_max == std::numeric_limits<uint32>::max()); + + static_assert(int64_max == std::numeric_limits<int64>::max()); + static_assert(uint64_max == std::numeric_limits<uint64>::max()); + +} + + + +#ifdef MODPLUG_TRACKER + +// In tracker debug builds, the sprintf-like function is retained in order to be able to validate our own formatting against sprintf. + +// There are 4 reasons why this is not available for library code: +// 1. printf-like functionality is not type-safe. +// 2. There are portability problems with char/wchar_t and the semantics of %s/%ls/%S . +// 3. There are portability problems with specifying format for 64bit integers. +// 4. Formatting of floating point values depends on the currently set C locale. +// A library is not allowed to mock with that and thus cannot influence the behavior in this case. + +template <typename T> +static std::string StringFormat(std::string format, T x) +{ + #if MPT_COMPILER_MSVC + // Count the needed array size. + const size_t nCount = _scprintf(format.c_str(), x); // null character not included. + std::vector<char> buf(nCount + 1); // + 1 is for null terminator. + sprintf_s(&(buf[0]), buf.size(), format.c_str(), x); + return &(buf[0]); + #else + int size = snprintf(NULL, 0, format.c_str(), x); // get required size, requires c99 compliant snprintf which msvc does not have + std::vector<char> temp(size + 1); + snprintf(&(temp[0]), size + 1, format.c_str(), x); + return &(temp[0]); + #endif +} + +#endif + +template <typename Tfloat> +static void TestFloatFormat(Tfloat x, std::string format, mpt::FormatFlags f, std::size_t width = 0, int precision = -1) +{ +#ifdef MODPLUG_TRACKER + std::string str_sprintf = StringFormat(format, x); +#endif + std::string str_iostreams = mpt::afmt::fmt(x, mpt::FormatSpec().SetFlags(f).SetWidth(width).SetPrecision(precision)); +#ifdef MODPLUG_TRACKER + //MPT_LOG_GLOBAL(LogDebug, "test", mpt::ToUnicode(mpt::Charset::ASCII, str_sprintf)); +#endif + //MPT_LOG_GLOBAL(LogDebug, "test", mpt::ToUnicode(mpt::Charset::ASCII, str_iostreams)); +#ifdef MODPLUG_TRACKER +#if MPT_MSVC_AT_LEAST(2019,4) || MPT_GCC_AT_LEAST(11,1,0) + // to_chars generates shortest form instead of 0-padded precision + if(precision != -1) +#endif + { + VERIFY_EQUAL(str_iostreams, str_sprintf); // this will fail with a set c locale (and there is nothing that can be done about that in libopenmpt) + } +#else + MPT_UNREFERENCED_PARAMETER(format); + MPT_UNUSED_VARIABLE(str_iostreams); +#endif +} + + +template <typename Tfloat> +static void TestFloatFormats(Tfloat x) +{ + + TestFloatFormat(x, "%.8g", mpt::afmt::NotaNrm | mpt::afmt::FillOff, 0, 8); + + TestFloatFormat(x, MPT_AFORMAT("%.{}g")(std::numeric_limits<Tfloat>::max_digits10), mpt::afmt::NotaNrm | mpt::afmt::FillOff); + TestFloatFormat(x, MPT_AFORMAT("%.{}f")(std::numeric_limits<Tfloat>::digits10), mpt::afmt::NotaFix | mpt::afmt::FillOff); + TestFloatFormat(x, MPT_AFORMAT("%.{}e")(std::numeric_limits<Tfloat>::max_digits10 - 1), mpt::afmt::NotaSci | mpt::afmt::FillOff); + + TestFloatFormat(x, "%.0f", mpt::afmt::NotaFix | mpt::afmt::FillOff, 0, 0); + TestFloatFormat(x, "%.1f", mpt::afmt::NotaFix | mpt::afmt::FillOff, 0, 1); + TestFloatFormat(x, "%.2f", mpt::afmt::NotaFix | mpt::afmt::FillOff, 0, 2); + TestFloatFormat(x, "%.3f", mpt::afmt::NotaFix | mpt::afmt::FillOff, 0, 3); + TestFloatFormat(x, "%0.1f", mpt::afmt::NotaFix | mpt::afmt::FillNul, 0, 1); + TestFloatFormat(x, "%02.0f", mpt::afmt::NotaFix | mpt::afmt::FillNul, 2, 0); +} + + + +static MPT_NOINLINE void TestStringFormatting() +{ + VERIFY_EQUAL(mpt::afmt::val(1.5f), "1.5"); + VERIFY_EQUAL(mpt::afmt::val(true), "1"); + VERIFY_EQUAL(mpt::afmt::val(false), "0"); + //VERIFY_EQUAL(mpt::afmt::val('A'), "A"); // deprecated + //VERIFY_EQUAL(mpt::afmt::val(L'A'), "A"); // deprecated + + VERIFY_EQUAL(mpt::afmt::val(0), "0"); + VERIFY_EQUAL(mpt::afmt::val(-23), "-23"); + VERIFY_EQUAL(mpt::afmt::val(42), "42"); + + VERIFY_EQUAL(mpt::afmt::hex0<3>((int32)-1), "-001"); + VERIFY_EQUAL(mpt::afmt::hex((int32)-1), "-1"); + VERIFY_EQUAL(mpt::afmt::hex(-0xabcde), "-abcde"); + VERIFY_EQUAL(mpt::afmt::hex(int32_min), "-80000000"); + VERIFY_EQUAL(mpt::afmt::hex(int32_min + 1), "-7fffffff"); + VERIFY_EQUAL(mpt::afmt::hex(0x123e), "123e"); + VERIFY_EQUAL(mpt::afmt::hex0<6>(0x123e), "00123e"); + VERIFY_EQUAL(mpt::afmt::hex0<2>(0x123e), "123e"); + + VERIFY_EQUAL(mpt::afmt::dec0<0>(1), "1"); + VERIFY_EQUAL(mpt::afmt::dec0<1>(1), "1"); + VERIFY_EQUAL(mpt::afmt::dec0<2>(1), "01"); + VERIFY_EQUAL(mpt::afmt::dec0<3>(1), "001"); + VERIFY_EQUAL(mpt::afmt::dec0<0>(11), "11"); + VERIFY_EQUAL(mpt::afmt::dec0<1>(11), "11"); + VERIFY_EQUAL(mpt::afmt::dec0<2>(11), "11"); + VERIFY_EQUAL(mpt::afmt::dec0<3>(11), "011"); + VERIFY_EQUAL(mpt::afmt::dec0<0>(-1), "-1"); + VERIFY_EQUAL(mpt::afmt::dec0<1>(-1), "-1"); + VERIFY_EQUAL(mpt::afmt::dec0<2>(-1), "-01"); + VERIFY_EQUAL(mpt::afmt::dec0<3>(-1), "-001"); + + VERIFY_EQUAL(mpt::ufmt::HEX0<7>(0xa2345678), U_("A2345678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<8>(0xa2345678), U_("A2345678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<9>(0xa2345678), U_("0A2345678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<10>(0xa2345678), U_("00A2345678")); + +#if MPT_WSTRING_FORMAT + VERIFY_EQUAL(mpt::wfmt::hex(0x123e), L"123e"); + VERIFY_EQUAL(mpt::wfmt::hex0<6>(0x123e), L"00123e"); + VERIFY_EQUAL(mpt::wfmt::hex0<2>(0x123e), L"123e"); +#endif + + VERIFY_EQUAL(mpt::afmt::val(-87.0f), "-87"); + if(mpt::afmt::val(-0.5e-6) != "-5e-007" + && mpt::afmt::val(-0.5e-6) != "-5e-07" + && mpt::afmt::val(-0.5e-6) != "-5e-7" + && mpt::afmt::val(-0.5e-6) != "-4.9999999999999998e-7" + && mpt::afmt::val(-0.5e-6) != "-4.9999999999999998e-07" + && mpt::afmt::val(-0.5e-6) != "-4.9999999999999998e-007" + ) + { + VERIFY_EQUAL(true, false); + } + if(mpt::afmt::val(-1.0 / 65536.0) != "-1.52587890625e-005" + && mpt::afmt::val(-1.0 / 65536.0) != "-1.52587890625e-05" + && mpt::afmt::val(-1.0 / 65536.0) != "-1.52587890625e-5" + ) + { + VERIFY_EQUAL(true, false); + } + if(mpt::afmt::val(-1.0f / 65536.0f) != "-1.52587891e-005" + && mpt::afmt::val(-1.0f / 65536.0f) != "-1.52587891e-05" + && mpt::afmt::val(-1.0f / 65536.0f) != "-1.52587891e-5" + && mpt::afmt::val(-1.0f / 65536.0f) != "-1.5258789e-005" + && mpt::afmt::val(-1.0f / 65536.0f) != "-1.5258789e-05" + && mpt::afmt::val(-1.0f / 65536.0f) != "-1.5258789e-5" + ) + { + VERIFY_EQUAL(true, false); + } + if(mpt::afmt::val(58.65403492763) != "58.654034927630001" + && mpt::afmt::val(58.65403492763) != "58.65403492763" + ) + { + VERIFY_EQUAL(true, false); + } + VERIFY_EQUAL(mpt::afmt::flt(58.65403492763, 6), "58.654"); + VERIFY_EQUAL(mpt::afmt::fix(23.42, 1), "23.4"); + VERIFY_EQUAL(mpt::afmt::fix(234.2, 1), "234.2"); + VERIFY_EQUAL(mpt::afmt::fix(2342.0, 1), "2342.0"); + + VERIFY_EQUAL(mpt::afmt::dec(2, ';', 2345678), std::string("2;34;56;78")); + VERIFY_EQUAL(mpt::afmt::dec(2, ';', 12345678), std::string("12;34;56;78")); + VERIFY_EQUAL(mpt::afmt::hex(3, ':', 0xa2345678), std::string("a2:345:678")); + + VERIFY_EQUAL(mpt::ufmt::dec(2, ';', 12345678), U_("12;34;56;78")); + VERIFY_EQUAL(mpt::ufmt::hex(3, ':', 0xa2345678), U_("a2:345:678")); + + VERIFY_EQUAL(mpt::ufmt::HEX0<7>(3, ':', 0xa2345678), U_("A2:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<8>(3, ':', 0xa2345678), U_("A2:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<9>(3, ':', 0xa2345678), U_("0A2:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<10>(3, ':', 0xa2345678), U_("0:0A2:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<11>(3, ':', 0xa2345678), U_("00:0A2:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<12>(3, ':', 0xa2345678), U_("000:0A2:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<7>(3, ':', -0x12345678), U_("-12:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<8>(3, ':', -0x12345678), U_("-12:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<9>(3, ':', -0x12345678), U_("-012:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<10>(3, ':', -0x12345678), U_("-0:012:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<11>(3, ':', -0x12345678), U_("-00:012:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<12>(3, ':', -0x12345678), U_("-000:012:345:678")); + + VERIFY_EQUAL(mpt::ufmt::HEX0<5>(3, ':', 0x345678), U_("345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<6>(3, ':', 0x345678), U_("345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<7>(3, ':', 0x345678), U_("0:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<5>(3, ':', -0x345678), U_("-345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<6>(3, ':', -0x345678), U_("-345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<7>(3, ':', -0x345678), U_("-0:345:678")); + + VERIFY_EQUAL(mpt::afmt::left(3, "a"), "a "); + VERIFY_EQUAL(mpt::afmt::right(3, "a"), " a"); + VERIFY_EQUAL(mpt::afmt::center(3, "a"), " a "); + VERIFY_EQUAL(mpt::afmt::center(4, "a"), " a "); + + #if defined(MPT_WITH_MFC) + VERIFY_EQUAL(mpt::cfmt::left(3, CString(_T("a"))), CString(_T("a "))); + VERIFY_EQUAL(mpt::cfmt::right(3, CString(_T("a"))), CString(_T(" a"))); + VERIFY_EQUAL(mpt::cfmt::center(3, CString(_T("a"))), CString(_T(" a "))); + VERIFY_EQUAL(mpt::cfmt::center(4, CString(_T("a"))), CString(_T(" a "))); + #endif // MPT_WITH_MFC + + VERIFY_EQUAL(ConvertStrTo<uint32>("586"), 586u); + VERIFY_EQUAL(ConvertStrTo<uint32>("2147483647"), (uint32)int32_max); + VERIFY_EQUAL(ConvertStrTo<uint32>("4294967295"), uint32_max); + + VERIFY_EQUAL(ConvertStrTo<int64>("-9223372036854775808"), int64_min); + VERIFY_EQUAL(ConvertStrTo<int64>("-159"), -159); + VERIFY_EQUAL(ConvertStrTo<int64>("9223372036854775807"), int64_max); + + VERIFY_EQUAL(ConvertStrTo<uint64>("85059"), 85059u); + VERIFY_EQUAL(ConvertStrTo<uint64>("9223372036854775807"), (uint64)int64_max); + VERIFY_EQUAL(ConvertStrTo<uint64>("18446744073709551615"), uint64_max); + + VERIFY_EQUAL(ConvertStrTo<float>("-87.0"), -87.0f); +#if !MPT_OS_DJGPP + VERIFY_EQUAL(ConvertStrTo<double>("-0.5e-6"), -0.5e-6); +#endif +#if !MPT_OS_DJGPP + VERIFY_EQUAL(ConvertStrTo<double>("58.65403492763"), 58.65403492763); +#else + VERIFY_EQUAL_EPS(ConvertStrTo<double>("58.65403492763"), 58.65403492763, 0.0001); +#endif + + VERIFY_EQUAL(ConvertStrTo<float>(mpt::afmt::val(-87.0)), -87.0f); +#if !MPT_OS_DJGPP + VERIFY_EQUAL(ConvertStrTo<double>(mpt::afmt::val(-0.5e-6)), -0.5e-6); +#endif + + VERIFY_EQUAL(mpt::String::Parse::Hex<unsigned char>("fe"), 254); +#if MPT_WSTRING_FORMAT + VERIFY_EQUAL(mpt::String::Parse::Hex<unsigned char>(L"fe"), 254); +#endif + VERIFY_EQUAL(mpt::String::Parse::Hex<unsigned int>(U_("ffff")), 65535); + + TestFloatFormats(0.0f); + TestFloatFormats(-0.0f); + TestFloatFormats(1.0f); + TestFloatFormats(-1.0f); + TestFloatFormats(0.1f); + TestFloatFormats(-0.1f); + TestFloatFormats(1000000000.0f); + TestFloatFormats(-1000000000.0f); + TestFloatFormats(0.0000000001f); + TestFloatFormats(-0.0000000001f); + TestFloatFormats(6.12345f); + + TestFloatFormats(0.0); + TestFloatFormats(-0.0); + TestFloatFormats(1.0); + TestFloatFormats(-1.0); + TestFloatFormats(0.1); + TestFloatFormats(-0.1); + TestFloatFormats(1000000000.0); + TestFloatFormats(-1000000000.0); + TestFloatFormats(0.0000000001); + TestFloatFormats(-0.0000000001); + TestFloatFormats(6.12345); + + TestFloatFormats(42.1234567890); + TestFloatFormats(0.1234567890); + TestFloatFormats(1234567890000000.0); + TestFloatFormats(0.0000001234567890); + + TestFloatFormats(mpt::numbers::pi); + TestFloatFormats(3.14159265358979323846); + TestFloatFormats(3.14159265358979323846f); + + VERIFY_EQUAL(mpt::afmt::flt(6.12345, 3), "6.12"); + VERIFY_EQUAL(mpt::afmt::fix(6.12345, 3), "6.123"); + VERIFY_EQUAL(mpt::afmt::flt(6.12345, 4), "6.123"); + VERIFY_EQUAL(mpt::afmt::fix(6.12345, 4), "6.1235"); + +#if MPT_WSTRING_FORMAT + VERIFY_EQUAL(mpt::wfmt::flt(6.12345, 3), L"6.12"); + VERIFY_EQUAL(mpt::wfmt::fix(6.12345, 3), L"6.123"); + VERIFY_EQUAL(mpt::wfmt::flt(6.12345, 4), L"6.123"); +#endif + + static_assert(mpt::parse_format_string_argument_count("") == 0); + static_assert(mpt::parse_format_string_argument_count("{{") == 0); + static_assert(mpt::parse_format_string_argument_count("}}") == 0); + static_assert(mpt::parse_format_string_argument_count("{}") == 1); + static_assert(mpt::parse_format_string_argument_count("{}{}") == 2); + static_assert(mpt::parse_format_string_argument_count("{0}{1}") == 2); + + // basic + VERIFY_EQUAL(MPT_AFORMAT("{}{}{}")(1,2,3), "123"); + VERIFY_EQUAL(MPT_AFORMAT("{2}{1}{0}")(1,2,3), "321"); + + VERIFY_EQUAL(MPT_AFORMAT("{2}{1}{0}{4}{3}{6}{5}{7}{10}{9}{8}")(0,1,2,3,4,5,6,7,8,9,"a"), "21043657a98"); + + //VERIFY_EQUAL(MPT_AFORMAT("{2}{1}{0}{2}{1}{0}{10}{9}{8}")(0,1,2,3,4,5,6,7,8,9,"a"), "210210a98"); + +#if MPT_WSTRING_FORMAT + VERIFY_EQUAL(MPT_WFORMAT("{}{}{}")(1,2,3), L"123"); +#endif + + // escaping behviour + VERIFY_EQUAL(MPT_AFORMAT("%")(), "%"); + VERIFY_EQUAL(MPT_AFORMAT("%")(), "%"); + VERIFY_EQUAL(MPT_AFORMAT("%%")(), "%%"); + VERIFY_EQUAL(MPT_AFORMAT("{}")("a"), "a"); + VERIFY_EQUAL(MPT_AFORMAT("{}%")("a"), "a%"); + VERIFY_EQUAL(MPT_AFORMAT("{}%")("a"), "a%"); + VERIFY_EQUAL(MPT_AFORMAT("{}%%")("a"), "a%%"); + VERIFY_EQUAL(MPT_AFORMAT("%1")(), "%1"); + VERIFY_EQUAL(MPT_AFORMAT("%{}")("a"), "%a"); + VERIFY_EQUAL(MPT_AFORMAT("%b")(), "%b"); + VERIFY_EQUAL(MPT_AFORMAT("{{}}")(), "{}"); + VERIFY_EQUAL(MPT_AFORMAT("{{{}}}")("a"), "{a}"); + +#if defined(MPT_WITH_MFC) + VERIFY_EQUAL(mpt::ufmt::val(CString(_T("foobar"))), U_("foobar")); + VERIFY_EQUAL(mpt::ufmt::val(CString(_T("foobar"))), U_("foobar")); + VERIFY_EQUAL(MPT_CFORMAT("{}{}{}")(1,2,3), _T("123")); + VERIFY_EQUAL(MPT_CFORMAT("{}{}{}")(1,mpt::cfmt::dec0<3>(2),3), _T("10023")); +#endif // MPT_WITH_MFC + + FlagSet<MODTYPE> foo; + foo.set(MOD_TYPE_MOD, true); + VERIFY_EQUAL(MPT_UFORMAT("{}")(foo), U_("00000000000000000000000000000001")); + +} + + +namespace { + +struct Gregorian { + int Y,M,D,h,m,s; + static Gregorian FromTM(tm t) { + Gregorian g; + g.Y = t.tm_year + 1900; + g.M = t.tm_mon + 1; + g.D = t.tm_mday; + g.h = t.tm_hour; + g.m = t.tm_min; + g.s = t.tm_sec; + return g; + } + static tm ToTM(Gregorian g) { + tm t; + MemsetZero(t); + t.tm_year = g.Y - 1900; + t.tm_mon = g.M - 1; + t.tm_mday = g.D; + t.tm_hour = g.h; + t.tm_min = g.m; + t.tm_sec = g.s; + return t; + } +}; + +inline bool operator ==(Gregorian a, Gregorian b) { + return a.Y == b.Y && a.M == b.M && a.D == b.D && a.h == b.h && a.m == b.m && a.s == b.s; +} + +} + +static int64 TestDate1(int s, int m, int h, int D, int M, int Y) { + return mpt::Date::Unix::FromUTC(Gregorian::ToTM(Gregorian{Y,M,D,h,m,s})); +} + +static Gregorian TestDate2(int s, int m, int h, int D, int M, int Y) { + return Gregorian{Y,M,D,h,m,s}; +} + + +static MPT_NOINLINE void TestMisc1() +{ + + VERIFY_EQUAL(ModCommand::IsPcNote(NOTE_MAX), false); + VERIFY_EQUAL(ModCommand::IsPcNote(NOTE_PC), true); + VERIFY_EQUAL(ModCommand::IsPcNote(NOTE_PCS), true); + + VERIFY_EQUAL(CModSpecifications::ExtensionToType(".mod"), MOD_TYPE_MOD); + VERIFY_EQUAL(CModSpecifications::ExtensionToType("mod"), MOD_TYPE_MOD); + VERIFY_EQUAL(CModSpecifications::ExtensionToType(".s3m"), MOD_TYPE_S3M); + VERIFY_EQUAL(CModSpecifications::ExtensionToType("s3m"), MOD_TYPE_S3M); + VERIFY_EQUAL(CModSpecifications::ExtensionToType(".xm"), MOD_TYPE_XM); + VERIFY_EQUAL(CModSpecifications::ExtensionToType("xm"), MOD_TYPE_XM); + VERIFY_EQUAL(CModSpecifications::ExtensionToType(".it"), MOD_TYPE_IT); + VERIFY_EQUAL(CModSpecifications::ExtensionToType("it"), MOD_TYPE_IT); + VERIFY_EQUAL(CModSpecifications::ExtensionToType(".itp"), MOD_TYPE_NONE); + VERIFY_EQUAL(CModSpecifications::ExtensionToType("itp"), MOD_TYPE_NONE); + VERIFY_EQUAL(CModSpecifications::ExtensionToType("mptm"), MOD_TYPE_MPT); + VERIFY_EQUAL(CModSpecifications::ExtensionToType("invalidExtension"), MOD_TYPE_NONE); + VERIFY_EQUAL(CModSpecifications::ExtensionToType("ita"), MOD_TYPE_NONE); + VERIFY_EQUAL(CModSpecifications::ExtensionToType("s2m"), MOD_TYPE_NONE); + VERIFY_EQUAL(CModSpecifications::ExtensionToType(""), MOD_TYPE_NONE); + + // invalid + VERIFY_EQUAL(SampleFormat::FromInt(0), SampleFormat::Invalid); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0000'11'0), SampleFormat::Invalid); + + // correct + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0001'00'1), SampleFormat::Unsigned8); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0001'00'0), SampleFormat::Int8); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0010'00'0), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0011'00'0), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0100'00'0), SampleFormat::Int32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0100'00'0), SampleFormat::Float32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1000'00'0), SampleFormat::Float64); + + // no size + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0000'00'0), SampleFormat::Invalid); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0000'00'1), SampleFormat::Unsigned8); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0000'00'0), SampleFormat::Float32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0000'00'1), SampleFormat::Invalid); + + // invalid unsigned + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0010'00'1), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0011'00'1), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0100'00'1), SampleFormat::Int32); + + // invalid float + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0001'00'0), SampleFormat::Int8); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0010'00'0), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0011'00'0), SampleFormat::Int24); + + // bogus size + + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0001'00'0), SampleFormat::Int8); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0010'00'0), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0011'00'0), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0100'00'0), SampleFormat::Int32); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0101'00'0), SampleFormat::Int32); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0110'00'0), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0111'00'0), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1000'00'0), SampleFormat::Float64); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1001'00'0), SampleFormat::Float64); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1010'00'0), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1011'00'0), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1100'00'0), SampleFormat::Int32); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1101'00'0), SampleFormat::Int32); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1110'00'0), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1111'00'0), SampleFormat::Int24); + + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0001'00'1), SampleFormat::Unsigned8); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0010'00'1), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0011'00'1), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0100'00'1), SampleFormat::Int32); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0101'00'1), SampleFormat::Int32); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0110'00'1), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0111'00'1), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1000'00'1), SampleFormat::Float64); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1001'00'1), SampleFormat::Float64); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1010'00'1), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1011'00'1), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1100'00'1), SampleFormat::Int32); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1101'00'1), SampleFormat::Int32); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1110'00'1), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1111'00'1), SampleFormat::Int24); + + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0001'00'0), SampleFormat::Int8); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0010'00'0), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0011'00'0), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0100'00'0), SampleFormat::Float32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0101'00'0), SampleFormat::Float32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0110'00'0), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0111'00'0), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1000'00'0), SampleFormat::Float64); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1001'00'0), SampleFormat::Float64); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1010'00'0), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1011'00'0), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1100'00'0), SampleFormat::Float32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1101'00'0), SampleFormat::Float32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1110'00'0), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1111'00'0), SampleFormat::Int24); + + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0001'00'1), SampleFormat::Unsigned8); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0010'00'1), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0011'00'1), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0100'00'1), SampleFormat::Float32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0101'00'1), SampleFormat::Float32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0110'00'1), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0111'00'1), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1000'00'1), SampleFormat::Float64); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1001'00'1), SampleFormat::Float64); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1010'00'1), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1011'00'1), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1100'00'1), SampleFormat::Float32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1101'00'1), SampleFormat::Float32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1110'00'1), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1111'00'1), SampleFormat::Int24); + +} + + +static MPT_NOINLINE void TestMisc2() +{ + + // Check for completeness of supported effect list in mod specifications + for(const auto &spec : ModSpecs::Collection) + { + VERIFY_EQUAL(strlen(spec->commands), (size_t)MAX_EFFECTS); + VERIFY_EQUAL(strlen(spec->volcommands), (size_t)MAX_VOLCMDS); + } + +#ifdef MODPLUG_TRACKER +#ifdef MPT_ENABLE_FILEIO + + { + std::vector<std::byte> data; + data.push_back(mpt::as_byte(0)); + data.push_back(mpt::as_byte(255)); + data.push_back(mpt::as_byte(1)); + data.push_back(mpt::as_byte(2)); + mpt::PathString fn = GetTempFilenameBase() + P_("lazy"); + RemoveFile(fn); + mpt::LazyFileRef f(fn); + f = data; + std::vector<std::byte> data2; + data2 = f; + VERIFY_EQUAL(data.size(), data2.size()); + for(std::size_t i = 0; i < data.size() && i < data2.size(); ++i) + { + VERIFY_EQUAL(data[i], data2[i]); + } + RemoveFile(fn); + } + +#endif +#endif // MODPLUG_TRACKER + +#ifdef MPT_WITH_ZLIB + VERIFY_EQUAL(crc32(0, mpt::byte_cast<const unsigned char*>(std::string("123456789").c_str()), 9), 0xCBF43926u); +#endif +#ifdef MPT_WITH_MINIZ + VERIFY_EQUAL(mz_crc32(0, mpt::byte_cast<const unsigned char*>(std::string("123456789").c_str()), 9), 0xCBF43926u); +#endif + + // Check floating-point accuracy in TransposeToFrequency + static constexpr int32 transposeToFrequency[] = + { + 5, 5, 5, 5, + 31, 32, 33, 34, + 196, 202, 207, 214, + 1243, 1280, 1317, 1356, + 7894, 8125, 8363, 8608, + 50121, 51590, 53102, 54658, + 318251, 327576, 337175, 347055, + 2020767, 2079980, 2140928, 2203663, + }; + + int freqIndex = 0; + for(int32 transpose = -128; transpose < 128; transpose += 32) + { + for(int32 finetune = -128; finetune < 128; finetune += 64, freqIndex++) + { + const auto freq = ModSample::TransposeToFrequency(transpose, finetune); + VERIFY_EQUAL_EPS(transposeToFrequency[freqIndex], static_cast<int32>(freq), 1); + if(transpose >= -96) + { + // Verify transpose+finetune <-> frequency roundtrip + // (not for transpose = -128 because it would require fractional precision that we don't have here) + ModSample smp; + smp.nC5Speed = freq; + smp.FrequencyToTranspose(); + smp.TransposeToFrequency(); + VERIFY_EQUAL(freq, smp.nC5Speed); + } + } + } + + { + ModSample smp; + smp.nC5Speed = 9999; + smp.Transpose(1.5); + VERIFY_EQUAL_EPS(28281, static_cast<int32>(smp.nC5Speed), 1); + smp.Transpose(-1.3); + VERIFY_EQUAL_EPS(11486, static_cast<int32>(smp.nC5Speed), 1); + } + + // Check SamplePosition fixed-point type + VERIFY_EQUAL(SamplePosition(1).GetRaw(), 1); + VERIFY_EQUAL(SamplePosition(2).Set(1), SamplePosition(1, 0)); + VERIFY_EQUAL(SamplePosition(2).SetInt(1), SamplePosition(1, 2)); + VERIFY_EQUAL(SamplePosition(1).IsPositive(), true); + VERIFY_EQUAL(SamplePosition(0).IsZero(), true); + VERIFY_EQUAL(SamplePosition(1, 0).IsUnity(), true); + VERIFY_EQUAL(SamplePosition(1, 1).IsUnity(), false); + VERIFY_EQUAL(SamplePosition(-1).IsNegative(), true); + VERIFY_EQUAL(SamplePosition(int64_max).GetRaw(), int64_max); + VERIFY_EQUAL(SamplePosition(2, SamplePosition::fractMax).GetInt(), 2); + VERIFY_EQUAL(SamplePosition(2, SamplePosition::fractMax).GetFract(), SamplePosition::fractMax); + VERIFY_EQUAL(SamplePosition(1, SamplePosition::fractMax).GetInvertedFract(), SamplePosition(0, 1)); + VERIFY_EQUAL(SamplePosition(1, 0).GetInvertedFract(), SamplePosition(1, 0)); + VERIFY_EQUAL(SamplePosition(2, 0).Negate(), SamplePosition(-2, 0)); + VERIFY_EQUAL(SamplePosition::Ratio(10, 5), SamplePosition(2, 0)); + VERIFY_EQUAL(SamplePosition(1, 1) + SamplePosition(2, 2), SamplePosition(3, 3)); + VERIFY_EQUAL(SamplePosition(1, 0) * 3, SamplePosition(3, 0)); + VERIFY_EQUAL((SamplePosition(6, 0) / SamplePosition(2, 0)), 3); + + VERIFY_EQUAL(srlztn::ID::FromInt(static_cast<uint32>(0x87654321u)).AsString(), srlztn::ID("\x21\x43\x65\x87").AsString()); + +#if defined(MODPLUG_TRACKER) + + VERIFY_EQUAL(mpt::OS::Wine::Version(U_("1.1.44" )).AsString() , U_("1.1.44")); + VERIFY_EQUAL(mpt::OS::Wine::Version(U_("1.6.2" )).AsString() , U_("1.6.2" )); + VERIFY_EQUAL(mpt::OS::Wine::Version(U_("1.8" )).AsString() , U_("1.8.0" )); + VERIFY_EQUAL(mpt::OS::Wine::Version(U_("2.0-rc" )).AsString() , U_("2.0.0" )); + VERIFY_EQUAL(mpt::OS::Wine::Version(U_("2.0-rc4")).AsString() , U_("2.0.0" )); + VERIFY_EQUAL(mpt::OS::Wine::Version(U_("2.0" )).AsString() , U_("2.0.0" )); + VERIFY_EQUAL(mpt::OS::Wine::Version(U_("2.4" )).AsString() , U_("2.4.0" )); + +#endif // MODPLUG_TRACKER + + // date + + VERIFY_EQUAL( 0, TestDate1( 0, 0, 0, 1, 1, 1970 )); + VERIFY_EQUAL( 3600, TestDate1( 0, 0, 1, 1, 1, 1970 )); + VERIFY_EQUAL( 86400, TestDate1( 0, 0, 0, 2, 1, 1970 )); + VERIFY_EQUAL( 31536000, TestDate1( 0, 0, 0, 1, 1, 1971 )); + VERIFY_EQUAL( 100000000, TestDate1( 40, 46, 9, 3, 3, 1973 )); + VERIFY_EQUAL( 951782400, TestDate1( 0, 0, 0, 29, 2, 2000 )); + VERIFY_EQUAL( 1000000000, TestDate1( 40, 46, 1, 9, 9, 2001 )); + VERIFY_EQUAL( 1044057600, TestDate1( 0, 0, 0, 1, 2, 2003 )); + VERIFY_EQUAL( 1044144000, TestDate1( 0, 0, 0, 2, 2, 2003 )); + VERIFY_EQUAL( 1046476800, TestDate1( 0, 0, 0, 1, 3, 2003 )); + VERIFY_EQUAL( 1064966400, TestDate1( 0, 0, 0, 1, 10, 2003 )); + VERIFY_EQUAL( 1077926399, TestDate1( 59, 59, 23, 27, 2, 2004 )); + VERIFY_EQUAL( 1077926400, TestDate1( 0, 0, 0, 28, 2, 2004 )); + VERIFY_EQUAL( 1077926410, TestDate1( 10, 0, 0, 28, 2, 2004 )); + VERIFY_EQUAL( 1078012799, TestDate1( 59, 59, 23, 28, 2, 2004 )); + VERIFY_EQUAL( 1078012800, TestDate1( 0, 0, 0, 29, 2, 2004 )); + VERIFY_EQUAL( 1078012820, TestDate1( 20, 0, 0, 29, 2, 2004 )); + VERIFY_EQUAL( 1078099199, TestDate1( 59, 59, 23, 29, 2, 2004 )); + VERIFY_EQUAL( 1078099200, TestDate1( 0, 0, 0, 1, 3, 2004 )); + VERIFY_EQUAL( 1078099230, TestDate1( 30, 0, 0, 1, 3, 2004 )); + VERIFY_EQUAL( 1078185599, TestDate1( 59, 59, 23, 1, 3, 2004 )); + VERIFY_EQUAL( 1096588800, TestDate1( 0, 0, 0, 1, 10, 2004 )); + VERIFY_EQUAL( 1413064016, TestDate1( 56, 46, 21, 11, 10, 2014 )); + VERIFY_EQUAL( 1413064100, TestDate1( 20, 48, 21, 11, 10, 2014 )); + + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 0).AsUTC()), TestDate2( 0, 0, 0, 1, 1, 1970 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 3600).AsUTC()), TestDate2( 0, 0, 1, 1, 1, 1970 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 86400).AsUTC()), TestDate2( 0, 0, 0, 2, 1, 1970 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 31536000).AsUTC()), TestDate2( 0, 0, 0, 1, 1, 1971 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 100000000).AsUTC()), TestDate2( 40, 46, 9, 3, 3, 1973 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 951782400).AsUTC()), TestDate2( 0, 0, 0, 29, 2, 2000 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1000000000).AsUTC()), TestDate2( 40, 46, 1, 9, 9, 2001 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1044057600).AsUTC()), TestDate2( 0, 0, 0, 1, 2, 2003 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1044144000).AsUTC()), TestDate2( 0, 0, 0, 2, 2, 2003 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1046476800).AsUTC()), TestDate2( 0, 0, 0, 1, 3, 2003 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1064966400).AsUTC()), TestDate2( 0, 0, 0, 1, 10, 2003 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1077926399).AsUTC()), TestDate2( 59, 59, 23, 27, 2, 2004 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1077926400).AsUTC()), TestDate2( 0, 0, 0, 28, 2, 2004 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1077926410).AsUTC()), TestDate2( 10, 0, 0, 28, 2, 2004 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1078012799).AsUTC()), TestDate2( 59, 59, 23, 28, 2, 2004 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1078012800).AsUTC()), TestDate2( 0, 0, 0, 29, 2, 2004 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1078012820).AsUTC()), TestDate2( 20, 0, 0, 29, 2, 2004 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1078099199).AsUTC()), TestDate2( 59, 59, 23, 29, 2, 2004 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1078099200).AsUTC()), TestDate2( 0, 0, 0, 1, 3, 2004 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1078099230).AsUTC()), TestDate2( 30, 0, 0, 1, 3, 2004 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1078185599).AsUTC()), TestDate2( 59, 59, 23, 1, 3, 2004 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1096588800).AsUTC()), TestDate2( 0, 0, 0, 1, 10, 2004 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1413064016).AsUTC()), TestDate2( 56, 46, 21, 11, 10, 2014 )); + VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1413064100).AsUTC()), TestDate2( 20, 48, 21, 11, 10, 2014 )); + + +#ifdef MODPLUG_TRACKER + + // URI & HTTP + + { + URI uri = ParseURI(U_("scheme://username:password@host:port/path?query#fragment")); + VERIFY_EQUAL(uri.scheme, U_("scheme")); + VERIFY_EQUAL(uri.username, U_("username")); + VERIFY_EQUAL(uri.password, U_("password")); + VERIFY_EQUAL(uri.host, U_("host")); + VERIFY_EQUAL(uri.port, U_("port")); + VERIFY_EQUAL(uri.path, U_("/path")); + VERIFY_EQUAL(uri.query, U_("query")); + VERIFY_EQUAL(uri.fragment, U_("fragment")); + } + { + URI uri = ParseURI(U_("scheme://host/path")); + VERIFY_EQUAL(uri.scheme, U_("scheme")); + VERIFY_EQUAL(uri.username, U_("")); + VERIFY_EQUAL(uri.password, U_("")); + VERIFY_EQUAL(uri.host, U_("host")); + VERIFY_EQUAL(uri.port, U_("")); + VERIFY_EQUAL(uri.path, U_("/path")); + VERIFY_EQUAL(uri.query, U_("")); + VERIFY_EQUAL(uri.fragment, U_("")); + } + { + URI uri = ParseURI(U_("scheme://host")); + VERIFY_EQUAL(uri.scheme, U_("scheme")); + VERIFY_EQUAL(uri.username, U_("")); + VERIFY_EQUAL(uri.password, U_("")); + VERIFY_EQUAL(uri.host, U_("host")); + VERIFY_EQUAL(uri.port, U_("")); + VERIFY_EQUAL(uri.path, U_("")); + VERIFY_EQUAL(uri.query, U_("")); + VERIFY_EQUAL(uri.fragment, U_("")); + } + { + URI uri = ParseURI(U_("scheme://host?query")); + VERIFY_EQUAL(uri.scheme, U_("scheme")); + VERIFY_EQUAL(uri.username, U_("")); + VERIFY_EQUAL(uri.password, U_("")); + VERIFY_EQUAL(uri.host, U_("host")); + VERIFY_EQUAL(uri.port, U_("")); + VERIFY_EQUAL(uri.path, U_("")); + VERIFY_EQUAL(uri.query, U_("query")); + VERIFY_EQUAL(uri.fragment, U_("")); + } + { + URI uri = ParseURI(U_("scheme://host#fragment")); + VERIFY_EQUAL(uri.scheme, U_("scheme")); + VERIFY_EQUAL(uri.username, U_("")); + VERIFY_EQUAL(uri.password, U_("")); + VERIFY_EQUAL(uri.host, U_("host")); + VERIFY_EQUAL(uri.port, U_("")); + VERIFY_EQUAL(uri.path, U_("")); + VERIFY_EQUAL(uri.query, U_("")); + VERIFY_EQUAL(uri.fragment, U_("fragment")); + } + { + URI uri = ParseURI(U_("scheme://host?#")); + VERIFY_EQUAL(uri.scheme, U_("scheme")); + VERIFY_EQUAL(uri.username, U_("")); + VERIFY_EQUAL(uri.password, U_("")); + VERIFY_EQUAL(uri.host, U_("host")); + VERIFY_EQUAL(uri.port, U_("")); + VERIFY_EQUAL(uri.path, U_("")); + VERIFY_EQUAL(uri.query, U_("")); + VERIFY_EQUAL(uri.fragment, U_("")); + } + { + URI uri = ParseURI(U_("scheme://host#?")); + VERIFY_EQUAL(uri.scheme, U_("scheme")); + VERIFY_EQUAL(uri.username, U_("")); + VERIFY_EQUAL(uri.password, U_("")); + VERIFY_EQUAL(uri.host, U_("host")); + VERIFY_EQUAL(uri.port, U_("")); + VERIFY_EQUAL(uri.path, U_("")); + VERIFY_EQUAL(uri.query, U_("")); + VERIFY_EQUAL(uri.fragment, U_("?")); + } + { + URI uri = ParseURI(U_("scheme://username:password@[2001:db8::1]:port/path?query#fragment")); + VERIFY_EQUAL(uri.scheme, U_("scheme")); + VERIFY_EQUAL(uri.username, U_("username")); + VERIFY_EQUAL(uri.password, U_("password")); + VERIFY_EQUAL(uri.host, U_("[2001:db8::1]")); + VERIFY_EQUAL(uri.port, U_("port")); + VERIFY_EQUAL(uri.path, U_("/path")); + VERIFY_EQUAL(uri.query, U_("query")); + VERIFY_EQUAL(uri.fragment, U_("fragment")); + } + + { + URI uri = ParseURI(U_("https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top")); + VERIFY_EQUAL(uri.scheme, U_("https")); + VERIFY_EQUAL(uri.username, U_("john.doe")); + VERIFY_EQUAL(uri.password, U_("")); + VERIFY_EQUAL(uri.host, U_("www.example.com")); + VERIFY_EQUAL(uri.port, U_("123")); + VERIFY_EQUAL(uri.path, U_("/forum/questions/")); + VERIFY_EQUAL(uri.query, U_("tag=networking&order=newest")); + VERIFY_EQUAL(uri.fragment, U_("top")); + } + { + URI uri = ParseURI(U_("ldap://[2001:db8::7]/c=GB?objectClass?one")); + VERIFY_EQUAL(uri.scheme, U_("ldap")); + VERIFY_EQUAL(uri.username, U_("")); + VERIFY_EQUAL(uri.password, U_("")); + VERIFY_EQUAL(uri.host, U_("[2001:db8::7]")); + VERIFY_EQUAL(uri.port, U_("")); + VERIFY_EQUAL(uri.path, U_("/c=GB")); + VERIFY_EQUAL(uri.query, U_("objectClass?one")); + VERIFY_EQUAL(uri.fragment, U_("")); + } + { + URI uri = ParseURI(U_("mailto:John.Doe@example.com")); + VERIFY_EQUAL(uri.scheme, U_("mailto")); + VERIFY_EQUAL(uri.username, U_("")); + VERIFY_EQUAL(uri.password, U_("")); + VERIFY_EQUAL(uri.host, U_("")); + VERIFY_EQUAL(uri.port, U_("")); + VERIFY_EQUAL(uri.path, U_("John.Doe@example.com")); + VERIFY_EQUAL(uri.query, U_("")); + VERIFY_EQUAL(uri.fragment, U_("")); + } + { + URI uri = ParseURI(U_("news:comp.infosystems.www.servers.unix")); + VERIFY_EQUAL(uri.scheme, U_("news")); + VERIFY_EQUAL(uri.username, U_("")); + VERIFY_EQUAL(uri.password, U_("")); + VERIFY_EQUAL(uri.host, U_("")); + VERIFY_EQUAL(uri.port, U_("")); + VERIFY_EQUAL(uri.path, U_("comp.infosystems.www.servers.unix")); + VERIFY_EQUAL(uri.query, U_("")); + VERIFY_EQUAL(uri.fragment, U_("")); + } + { + URI uri = ParseURI(U_("tel:+1-816-555-1212")); + VERIFY_EQUAL(uri.scheme, U_("tel")); + VERIFY_EQUAL(uri.username, U_("")); + VERIFY_EQUAL(uri.password, U_("")); + VERIFY_EQUAL(uri.host, U_("")); + VERIFY_EQUAL(uri.port, U_("")); + VERIFY_EQUAL(uri.path, U_("+1-816-555-1212")); + VERIFY_EQUAL(uri.query, U_("")); + VERIFY_EQUAL(uri.fragment, U_("")); + } + { + URI uri = ParseURI(U_("telnet://192.0.2.16:80/")); + VERIFY_EQUAL(uri.scheme, U_("telnet")); + VERIFY_EQUAL(uri.username, U_("")); + VERIFY_EQUAL(uri.password, U_("")); + VERIFY_EQUAL(uri.host, U_("192.0.2.16")); + VERIFY_EQUAL(uri.port, U_("80")); + VERIFY_EQUAL(uri.path, U_("/")); + VERIFY_EQUAL(uri.query, U_("")); + VERIFY_EQUAL(uri.fragment, U_("")); + } + { + URI uri = ParseURI(U_("urn:oasis:names:specification:docbook:dtd:xml:4.1.2")); + VERIFY_EQUAL(uri.scheme, U_("urn")); + VERIFY_EQUAL(uri.username, U_("")); + VERIFY_EQUAL(uri.password, U_("")); + VERIFY_EQUAL(uri.host, U_("")); + VERIFY_EQUAL(uri.port, U_("")); + VERIFY_EQUAL(uri.path, U_("oasis:names:specification:docbook:dtd:xml:4.1.2")); + VERIFY_EQUAL(uri.query, U_("")); + VERIFY_EQUAL(uri.fragment, U_("")); + } + + { + HTTP::Request req; + req.SetURI(ParseURI(U_("https://host/path?a1=a&a2=b"))); + VERIFY_EQUAL(req.protocol, HTTP::Protocol::HTTPS); + VERIFY_EQUAL(req.host, U_("host")); + VERIFY_EQUAL(req.path, U_("/path")); + VERIFY_EQUAL(req.query.size(), 2u); + if(req.query.size() == 2) + { + VERIFY_EQUAL(req.query[0], std::make_pair(U_("a1"), U_("a"))); + VERIFY_EQUAL(req.query[1], std::make_pair(U_("a2"), U_("b"))); + } + } + { + HTTP::Request req; + req.SetURI(ParseURI(U_("https://host/"))); + VERIFY_EQUAL(req.protocol, HTTP::Protocol::HTTPS); + VERIFY_EQUAL(req.host, U_("host")); + VERIFY_EQUAL(req.path, U_("/")); + } + +#endif // MODPLUG_TRACKER + + // https://github.com/kripken/emscripten/issues/4251 + #if MPT_OS_EMSCRIPTEN + volatile int transpose = 32; + volatile int finetune = 0; + float exp = (transpose * 128.0f + finetune) * (1.0f / (12.0f * 128.0f)); + float f = ::powf(2.0f, exp); + double d = ::pow (2.0 , (double)exp); + VERIFY_EQUAL_EPS(d, 6.349605, 0.00001); + VERIFY_EQUAL_EPS(f, 6.349605, 0.00001); + #endif + +} + + +static MPT_NOINLINE void TestRandom() +{ + + #ifdef FLAKY_TESTS + + mpt::default_prng& prng = *s_PRNG; + + { + std::vector<std::size_t> hist(256); + for(std::size_t i = 0; i < 256*256; ++i) + { + uint8 value = mpt::random<uint8>(prng); + hist[value] += 1; + } + for(std::size_t i = 0; i < 256; ++i) + { + VERIFY_EQUAL_QUIET_NONCONT(mpt::is_in_range(hist[i], 16u, 65520u), true); + } + } + { + std::vector<std::size_t> hist(256); + for(std::size_t i = 0; i < 256*256; ++i) + { + int8 value = mpt::random<int8>(prng); + hist[static_cast<int>(value) + 0x80] += 1; + } + for(std::size_t i = 0; i < 256; ++i) + { + VERIFY_EQUAL_QUIET_NONCONT(mpt::is_in_range(hist[i], 16u, 65520u), true); + } + } + { + std::vector<std::size_t> hist(256); + for(std::size_t i = 0; i < 256*256; ++i) + { + uint8 value = mpt::random<uint8>(prng, 1); + hist[value] += 1; + } + for(std::size_t i = 0; i < 256; ++i) + { + if(i < 2) + { + VERIFY_EQUAL_QUIET_NONCONT(mpt::is_in_range(hist[i], 16u, 65520u), true); + } else + { + VERIFY_EQUAL_QUIET_NONCONT(hist[i], 0u); + } + } + } + #endif +} + + +static MPT_NOINLINE void TestCharsets() +{ + + // Path splitting + +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS + + VERIFY_EQUAL(P_("").GetDrive(), P_("")); + VERIFY_EQUAL(P_("").GetDir(), P_("")); + VERIFY_EQUAL(P_("").GetPath(), P_("")); + VERIFY_EQUAL(P_("").GetFileName(), P_("")); + VERIFY_EQUAL(P_("").GetFileExt(), P_("")); + VERIFY_EQUAL(P_("").GetFullFileName(), P_("")); + + VERIFY_EQUAL(P_("C:\\").GetDrive(), P_("C:")); + VERIFY_EQUAL(P_("C:\\").GetDir(), P_("\\")); + VERIFY_EQUAL(P_("C:\\").GetPath(), P_("C:\\")); + VERIFY_EQUAL(P_("C:\\").GetFileName(), P_("")); + VERIFY_EQUAL(P_("C:\\").GetFileExt(), P_("")); + VERIFY_EQUAL(P_("C:\\").GetFullFileName(), P_("")); + + VERIFY_EQUAL(P_("\\directory\\").GetDrive(), P_("")); + VERIFY_EQUAL(P_("\\directory\\").GetDir(), P_("\\directory\\")); + VERIFY_EQUAL(P_("\\directory\\").GetPath(), P_("\\directory\\")); + VERIFY_EQUAL(P_("\\directory\\").GetFileName(), P_("")); + VERIFY_EQUAL(P_("\\directory\\").GetFileExt(), P_("")); + VERIFY_EQUAL(P_("\\directory\\").GetFullFileName(), P_("")); + + VERIFY_EQUAL(P_("\\directory\\file.txt").GetDrive(), P_("")); + VERIFY_EQUAL(P_("\\directory\\file.txt").GetDir(), P_("\\directory\\")); + VERIFY_EQUAL(P_("\\directory\\file.txt").GetPath(), P_("\\directory\\")); + VERIFY_EQUAL(P_("\\directory\\file.txt").GetFileName(), P_("file")); + VERIFY_EQUAL(P_("\\directory\\file.txt").GetFileExt(), P_(".txt")); + VERIFY_EQUAL(P_("\\directory\\file.txt").GetFullFileName(), P_("file.txt")); + + VERIFY_EQUAL(P_(".").GetDrive(), P_("")); + VERIFY_EQUAL(P_(".").GetDir(), P_("")); + VERIFY_EQUAL(P_(".").GetPath(), P_("")); + VERIFY_EQUAL(P_(".").GetFileName(), P_(".")); + VERIFY_EQUAL(P_(".").GetFileExt(), P_("")); + VERIFY_EQUAL(P_(".").GetFullFileName(), P_(".")); + + VERIFY_EQUAL(P_("..").GetDrive(), P_("")); + VERIFY_EQUAL(P_("..").GetDir(), P_("")); + VERIFY_EQUAL(P_("..").GetPath(), P_("")); + VERIFY_EQUAL(P_("..").GetFileName(), P_("..")); + VERIFY_EQUAL(P_("..").GetFileExt(), P_("")); + VERIFY_EQUAL(P_("..").GetFullFileName(), P_("..")); + + VERIFY_EQUAL(P_("dir\\.").GetDrive(), P_("")); + VERIFY_EQUAL(P_("dir\\.").GetDir(), P_("dir\\")); + VERIFY_EQUAL(P_("dir\\.").GetPath(), P_("dir\\")); + VERIFY_EQUAL(P_("dir\\.").GetFileName(), P_(".")); + VERIFY_EQUAL(P_("dir\\.").GetFileExt(), P_("")); + VERIFY_EQUAL(P_("dir\\.").GetFullFileName(), P_(".")); + + VERIFY_EQUAL(P_("dir\\..").GetDrive(), P_("")); + VERIFY_EQUAL(P_("dir\\..").GetDir(), P_("dir\\")); + VERIFY_EQUAL(P_("dir\\..").GetPath(), P_("dir\\")); + VERIFY_EQUAL(P_("dir\\..").GetFileName(), P_("..")); + VERIFY_EQUAL(P_("dir\\..").GetFileExt(), P_("")); + VERIFY_EQUAL(P_("dir\\..").GetFullFileName(), P_("..")); + + VERIFY_EQUAL(P_(".txt").GetDrive(), P_("")); + VERIFY_EQUAL(P_(".txt").GetDir(), P_("")); + VERIFY_EQUAL(P_(".txt").GetPath(), P_("")); + VERIFY_EQUAL(P_(".txt").GetFileName(), P_(".txt")); + VERIFY_EQUAL(P_(".txt").GetFileExt(), P_("")); + VERIFY_EQUAL(P_(".txt").GetFullFileName(), P_(".txt")); + + VERIFY_EQUAL(P_("C:tmp.txt").GetDrive(), P_("C:")); + VERIFY_EQUAL(P_("C:tmp.txt").GetDir(), P_("")); + VERIFY_EQUAL(P_("C:tmp.txt").GetPath(), P_("C:")); + VERIFY_EQUAL(P_("C:tmp.txt").GetFileName(), P_("tmp")); + VERIFY_EQUAL(P_("C:tmp.txt").GetFileExt(), P_(".txt")); + VERIFY_EQUAL(P_("C:tmp.txt").GetFullFileName(), P_("tmp.txt")); + + VERIFY_EQUAL(P_("C:tempdir\\tmp.txt").GetDrive(), P_("C:")); + VERIFY_EQUAL(P_("C:tempdir\\tmp.txt").GetDir(), P_("tempdir\\")); + VERIFY_EQUAL(P_("C:tempdir\\tmp.txt").GetPath(), P_("C:tempdir\\")); + VERIFY_EQUAL(P_("C:tempdir\\tmp.txt").GetFileName(), P_("tmp")); + VERIFY_EQUAL(P_("C:tempdir\\tmp.txt").GetFileExt(), P_(".txt")); + VERIFY_EQUAL(P_("C:tempdir\\tmp.txt").GetFullFileName(), P_("tmp.txt")); + + VERIFY_EQUAL(P_("C:\\tempdir\\tmp.txt").GetDrive(), P_("C:")); + VERIFY_EQUAL(P_("C:\\tempdir\\tmp.txt").GetDir(), P_("\\tempdir\\")); + VERIFY_EQUAL(P_("C:\\tempdir\\tmp.txt").GetPath(), P_("C:\\tempdir\\")); + VERIFY_EQUAL(P_("C:\\tempdir\\tmp.txt").GetFileName(), P_("tmp")); + VERIFY_EQUAL(P_("C:\\tempdir\\tmp.txt").GetFileExt(), P_(".txt")); + VERIFY_EQUAL(P_("C:\\tempdir\\tmp.txt").GetFullFileName(), P_("tmp.txt")); + + VERIFY_EQUAL(P_("C:\\tempdir\\tmp.foo.txt").GetFileName(), P_("tmp.foo")); + VERIFY_EQUAL(P_("C:\\tempdir\\tmp.foo.txt").GetFileExt(), P_(".txt")); + + VERIFY_EQUAL(P_("\\\\server").GetDrive(), P_("\\\\server")); + VERIFY_EQUAL(P_("\\\\server").GetDir(), P_("")); + VERIFY_EQUAL(P_("\\\\server").GetPath(), P_("\\\\server")); + VERIFY_EQUAL(P_("\\\\server").GetFileName(), P_("")); + VERIFY_EQUAL(P_("\\\\server").GetFileExt(), P_("")); + VERIFY_EQUAL(P_("\\\\server").GetFullFileName(), P_("")); + + VERIFY_EQUAL(P_("\\\\server\\").GetDrive(), P_("\\\\server\\")); + VERIFY_EQUAL(P_("\\\\server\\").GetDir(), P_("")); + VERIFY_EQUAL(P_("\\\\server\\").GetPath(), P_("\\\\server\\")); + VERIFY_EQUAL(P_("\\\\server\\").GetFileName(), P_("")); + VERIFY_EQUAL(P_("\\\\server\\").GetFileExt(), P_("")); + VERIFY_EQUAL(P_("\\\\server\\").GetFullFileName(), P_("")); + + VERIFY_EQUAL(P_("\\\\server\\share").GetDrive(), P_("\\\\server\\share")); + VERIFY_EQUAL(P_("\\\\server\\share").GetDir(), P_("")); + VERIFY_EQUAL(P_("\\\\server\\share").GetPath(), P_("\\\\server\\share")); + VERIFY_EQUAL(P_("\\\\server\\share").GetFileName(), P_("")); + VERIFY_EQUAL(P_("\\\\server\\share").GetFileExt(), P_("")); + VERIFY_EQUAL(P_("\\\\server\\share").GetFullFileName(), P_("")); + + VERIFY_EQUAL(P_("\\\\server\\share\\").GetDrive(), P_("\\\\server\\share")); + VERIFY_EQUAL(P_("\\\\server\\share\\").GetDir(), P_("\\")); + VERIFY_EQUAL(P_("\\\\server\\share\\").GetPath(), P_("\\\\server\\share\\")); + VERIFY_EQUAL(P_("\\\\server\\share\\").GetFileName(), P_("")); + VERIFY_EQUAL(P_("\\\\server\\share\\").GetFileExt(), P_("")); + VERIFY_EQUAL(P_("\\\\server\\share\\").GetFullFileName(), P_("")); + + VERIFY_EQUAL(P_("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetDrive(), P_("\\\\server\\share")); + VERIFY_EQUAL(P_("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetDir(), P_("\\dir1\\dir2\\")); + VERIFY_EQUAL(P_("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetPath(), P_("\\\\server\\share\\dir1\\dir2\\")); + VERIFY_EQUAL(P_("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetFileName(), P_("name.foo")); + VERIFY_EQUAL(P_("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetFileExt(), P_(".ext")); + VERIFY_EQUAL(P_("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetFullFileName(), P_("name.foo.ext")); + + VERIFY_EQUAL(P_("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetDrive(), P_("C:")); + VERIFY_EQUAL(P_("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetDir(), P_("\\tempdir\\dir.2\\")); + VERIFY_EQUAL(P_("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetPath(), P_("C:\\tempdir\\dir.2\\")); + VERIFY_EQUAL(P_("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetFileName(), P_("tmp.foo")); + VERIFY_EQUAL(P_("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetFileExt(), P_(".txt")); + VERIFY_EQUAL(P_("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetFullFileName(), P_("tmp.foo.txt")); + + VERIFY_EQUAL(P_("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetDrive(), P_("\\\\server\\share")); + VERIFY_EQUAL(P_("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetDir(), P_("\\dir1\\dir2\\")); + VERIFY_EQUAL(P_("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetPath(), P_("\\\\server\\share\\dir1\\dir2\\")); + VERIFY_EQUAL(P_("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetFileName(), P_("name.foo")); + VERIFY_EQUAL(P_("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetFileExt(), P_(".ext")); + VERIFY_EQUAL(P_("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetFullFileName(), P_("name.foo.ext")); +#endif + + + + // Path conversions +#ifdef MODPLUG_TRACKER + const mpt::PathString exePath = P_("C:\\OpenMPT\\"); + VERIFY_EQUAL(P_("C:\\OpenMPT\\").AbsolutePathToRelative(exePath), P_(".\\")); + VERIFY_EQUAL(P_("c:\\OpenMPT\\foo").AbsolutePathToRelative(exePath), P_(".\\foo")); + VERIFY_EQUAL(P_("C:\\foo").AbsolutePathToRelative(exePath), P_("\\foo")); + VERIFY_EQUAL(P_(".\\").RelativePathToAbsolute(exePath), P_("C:\\OpenMPT\\")); + VERIFY_EQUAL(P_(".\\foo").RelativePathToAbsolute(exePath), P_("C:\\OpenMPT\\foo")); + VERIFY_EQUAL(P_("\\foo").RelativePathToAbsolute(exePath), P_("C:\\foo")); + VERIFY_EQUAL(P_("\\\\server\\path\\file").AbsolutePathToRelative(exePath), P_("\\\\server\\path\\file")); + VERIFY_EQUAL(P_("\\\\server\\path\\file").RelativePathToAbsolute(exePath), P_("\\\\server\\path\\file")); + + VERIFY_EQUAL(P_("").Simplify(), P_("")); + VERIFY_EQUAL(P_(" ").Simplify(), P_(" ")); + VERIFY_EQUAL(P_("foo\\bar").Simplify(), P_("foo\\bar")); + VERIFY_EQUAL(P_(".\\foo\\bar").Simplify(), P_(".\\foo\\bar")); + VERIFY_EQUAL(P_(".\\\\foo\\bar").Simplify(), P_(".\\foo\\bar")); + VERIFY_EQUAL(P_("./\\foo\\bar").Simplify(), P_(".\\foo\\bar")); + VERIFY_EQUAL(P_("\\foo\\bar").Simplify(), P_("\\foo\\bar")); + VERIFY_EQUAL(P_("A:\\name_1\\.\\name_2\\..\\name_3\\").Simplify(), P_("A:\\name_1\\name_3")); + VERIFY_EQUAL(P_("A:\\name_1\\..\\name_2\\./name_3").Simplify(), P_("A:\\name_2\\name_3")); + VERIFY_EQUAL(P_("A:\\name_1\\.\\name_2\\.\\name_3\\..\\name_4\\..").Simplify(), P_("A:\\name_1\\name_2")); + VERIFY_EQUAL(P_("A:foo\\\\bar").Simplify(), P_("A:\\foo\\bar")); + VERIFY_EQUAL(P_("C:\\..").Simplify(), P_("C:\\")); + VERIFY_EQUAL(P_("C:\\.").Simplify(), P_("C:\\")); + VERIFY_EQUAL(P_("\\\\foo\\..\\.bar").Simplify(), P_("\\\\.bar")); + VERIFY_EQUAL(P_("\\\\foo\\..\\..\\bar").Simplify(), P_("\\\\bar")); +#endif + + + +#ifdef MODPLUG_TRACKER +#if MPT_COMPILER_MSVC + + // Tracker code currently relies on BROKEN MSVC behaviour with respect to %s + // handling in printf functions when the target is a wide string. + // Ensure that Microsoft KEEPS this BROKEN behavour (it conflicts with C99 and + // C++11 demanded semantics. + // Additionally, the tracker code makes use of %hs and %ls which are the only + // way to unconditionally expect narrow or wide string parameters + // respectively; %ls is standard conforming, %hs is a Microsoft extension. + // We check all possible combinations and expect Microsoft-style behaviour + // here. + + char src_char[256]; + wchar_t src_wchar_t[256]; + TCHAR src_TCHAR[256]; + + char dst_char[256]; + wchar_t dst_wchar_t[256]; + TCHAR dst_TCHAR[256]; + + MemsetZero(src_char); + MemsetZero(src_wchar_t); + MemsetZero(src_TCHAR); + + strcpy(src_char, "ab"); + wcscpy(src_wchar_t, L"ab"); + _tcscpy(src_TCHAR, _T("ab")); + +#define MPT_TEST_PRINTF(dst_type, function, format, src_type) \ + do { \ + MemsetZero(dst_ ## dst_type); \ + function(dst_ ## dst_type, format, src_ ## src_type); \ + VERIFY_EQUAL(std::char_traits< dst_type >::compare(dst_ ## dst_type, src_ ## dst_type, std::char_traits< dst_type >::length( src_ ## dst_type ) + 1), 0); \ + } while(0) \ +/**/ + +#define MPT_TEST_PRINTF_N(dst_type, function, format, src_type) \ + do { \ + MemsetZero(dst_ ## dst_type); \ + function(dst_ ## dst_type, 255, format, src_ ## src_type); \ + VERIFY_EQUAL(std::char_traits< dst_type >::compare(dst_ ## dst_type, src_ ## dst_type, std::char_traits< dst_type >::length( src_ ## dst_type ) + 1), 0); \ + } while(0) \ +/**/ + + // CRT narrow + MPT_TEST_PRINTF(char, sprintf, "%s", char); + MPT_TEST_PRINTF(char, sprintf, "%S", wchar_t); + MPT_TEST_PRINTF(char, sprintf, "%hs", char); + MPT_TEST_PRINTF(char, sprintf, "%hS", char); + MPT_TEST_PRINTF(char, sprintf, "%ls", wchar_t); + MPT_TEST_PRINTF(char, sprintf, "%lS", wchar_t); + MPT_TEST_PRINTF(char, sprintf, "%ws", wchar_t); + MPT_TEST_PRINTF(char, sprintf, "%wS", wchar_t); + + // CRT wide + MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%s", wchar_t); + MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%S", char); + MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%hs", char); + MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%hS", char); + MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%ls", wchar_t); + MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%lS", wchar_t); + MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%ws", wchar_t); + MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%wS", wchar_t); + + // WinAPI TCHAR + MPT_TEST_PRINTF(TCHAR, wsprintf, _T("%s"), TCHAR); + MPT_TEST_PRINTF(TCHAR, wsprintf, _T("%hs"), char); + MPT_TEST_PRINTF(TCHAR, wsprintf, _T("%hS"), char); + MPT_TEST_PRINTF(TCHAR, wsprintf, _T("%ls"), wchar_t); + MPT_TEST_PRINTF(TCHAR, wsprintf, _T("%lS"), wchar_t); + + // WinAPI CHAR + MPT_TEST_PRINTF(char, wsprintfA, "%s", char); + MPT_TEST_PRINTF(char, wsprintfA, "%S", wchar_t); + MPT_TEST_PRINTF(char, wsprintfA, "%hs", char); + MPT_TEST_PRINTF(char, wsprintfA, "%hS", char); + MPT_TEST_PRINTF(char, wsprintfA, "%ls", wchar_t); + MPT_TEST_PRINTF(char, wsprintfA, "%lS", wchar_t); + + // WinAPI WCHAR + MPT_TEST_PRINTF(wchar_t, wsprintfW, L"%s", wchar_t); + MPT_TEST_PRINTF(wchar_t, wsprintfW, L"%S", char); + MPT_TEST_PRINTF(wchar_t, wsprintfW, L"%hs", char); + MPT_TEST_PRINTF(wchar_t, wsprintfW, L"%hS", char); + MPT_TEST_PRINTF(wchar_t, wsprintfW, L"%ls", wchar_t); + MPT_TEST_PRINTF(wchar_t, wsprintfW, L"%lS", wchar_t); + +#undef MPT_TEST_PRINTF +#undef MPT_TEST_PRINTF_n + +#endif +#endif + + VERIFY_EQUAL(mpt::CompareNoCaseAscii("f", "f", 6) == 0, true); + VERIFY_EQUAL(mpt::CompareNoCaseAscii("f", "F", 6) == 0, true); + VERIFY_EQUAL(mpt::CompareNoCaseAscii("F", "f", 6) == 0, true); + VERIFY_EQUAL(mpt::CompareNoCaseAscii("f", "g", 6) < 0, true); + VERIFY_EQUAL(mpt::CompareNoCaseAscii("h", "g", 6) > 0, true); + VERIFY_EQUAL(mpt::CompareNoCaseAscii("fgh", "FgH", 6) == 0, true); + VERIFY_EQUAL(mpt::CompareNoCaseAscii("012345678", "012345678", 9) == 0, true); + VERIFY_EQUAL(mpt::CompareNoCaseAscii("", "012345678", 9) < 0, true); + VERIFY_EQUAL(mpt::CompareNoCaseAscii("FgH", "", 6) > 0, true); + VERIFY_EQUAL(mpt::CompareNoCaseAscii("FgH", "F", 6) > 0, true); + VERIFY_EQUAL(mpt::CompareNoCaseAscii("FgH", "Fg", 6) > 0, true); + VERIFY_EQUAL(mpt::CompareNoCaseAscii("FgH", "fg", 6) > 0, true); + VERIFY_EQUAL(mpt::CompareNoCaseAscii("0123456789", "FgH", 0) == 0, true); + VERIFY_EQUAL(mpt::CompareNoCaseAscii("FgH", "fgh", 1) == 0, true); + VERIFY_EQUAL(mpt::CompareNoCaseAscii("FgH", "fgh", 2) == 0, true); + VERIFY_EQUAL(mpt::CompareNoCaseAscii("FgH", "fgh", 3) == 0, true); + VERIFY_EQUAL(mpt::CompareNoCaseAscii("FgH", "fghi", 3) == 0, true); + VERIFY_EQUAL(mpt::CompareNoCaseAscii("FgH", "fghi", 4) < 0, true); + VERIFY_EQUAL(mpt::CompareNoCaseAscii("FIH", "fghi", 1) == 0, true); + VERIFY_EQUAL(mpt::CompareNoCaseAscii("FIH", "fghi", 2) > 0, true); + +} + + +#ifdef MODPLUG_TRACKER + +struct CustomSettingsTestType +{ + float x; + float y; + CustomSettingsTestType(float x_ = 0.0f, float y_ = 0.0f) : x(x_), y(y_) { } +}; + +} // namespace Test + +template <> +inline Test::CustomSettingsTestType FromSettingValue(const SettingValue &val) +{ + MPT_ASSERT(val.GetTypeTag() == "myType"); + mpt::ustring xy = val.as<mpt::ustring>(); + if(xy.empty()) + { + return Test::CustomSettingsTestType(0.0f, 0.0f); + } + std::size_t pos = xy.find(U_("|")); + mpt::ustring x = xy.substr(0, pos); + mpt::ustring y = xy.substr(pos + 1); + return Test::CustomSettingsTestType(ConvertStrTo<float>(x), ConvertStrTo<float>(y)); +} + +template <> +inline SettingValue ToSettingValue(const Test::CustomSettingsTestType &val) +{ + return SettingValue(mpt::ufmt::val(val.x) + U_("|") + mpt::ufmt::val(val.y), "myType"); +} + +namespace Test { + +#endif // MODPLUG_TRACKER + + +static MPT_NOINLINE void TestSettings() +{ + +#ifdef MODPLUG_TRACKER + + VERIFY_EQUAL(SettingPath(U_("a"),U_("b")) < SettingPath(U_("a"),U_("c")), true); + VERIFY_EQUAL(!(SettingPath(U_("c"),U_("b")) < SettingPath(U_("a"),U_("c"))), true); + + { + DefaultSettingsContainer conf; + + int32 foobar = conf.Read(U_("Test"), U_("bar"), 23); + conf.Write(U_("Test"), U_("bar"), 64); + conf.Write(U_("Test"), U_("bar"), 42); + conf.Read(U_("Test"), U_("baz"), 4711); + foobar = conf.Read(U_("Test"), U_("bar"), 28); + } + + { + DefaultSettingsContainer conf; + + int32 foobar = conf.Read(U_("Test"), U_("bar"), 28); + VERIFY_EQUAL(foobar, 42); + conf.Write(U_("Test"), U_("bar"), 43); + } + + { + DefaultSettingsContainer conf; + + int32 foobar = conf.Read(U_("Test"), U_("bar"), 123); + VERIFY_EQUAL(foobar, 43); + conf.Write(U_("Test"), U_("bar"), 88); + } + + { + DefaultSettingsContainer conf; + + Setting<int> foo(conf, U_("Test"), U_("bar"), 99); + + VERIFY_EQUAL(foo, 88); + + foo = 7; + + } + + { + DefaultSettingsContainer conf; + Setting<int> foo(conf, U_("Test"), U_("bar"), 99); + VERIFY_EQUAL(foo, 7); + } + + + { + DefaultSettingsContainer conf; + conf.Read(U_("Test"), U_("struct"), std::string("")); + conf.Write(U_("Test"), U_("struct"), std::string("")); + } + + { + DefaultSettingsContainer conf; + CustomSettingsTestType dummy = conf.Read(U_("Test"), U_("struct"), CustomSettingsTestType(1.0f, 1.0f)); + dummy = CustomSettingsTestType(0.125f, 32.0f); + conf.Write(U_("Test"), U_("struct"), dummy); + } + + { + DefaultSettingsContainer conf; + Setting<CustomSettingsTestType> dummyVar(conf, U_("Test"), U_("struct"), CustomSettingsTestType(1.0f, 1.0f)); + CustomSettingsTestType dummy = dummyVar; + VERIFY_EQUAL(dummy.x, 0.125f); + VERIFY_EQUAL(dummy.y, 32.0f); + } + +#endif // MODPLUG_TRACKER + +} + + +// Test MIDI Event generating / reading +static MPT_NOINLINE void TestMIDIEvents() +{ + uint32 midiEvent; + + midiEvent = MIDIEvents::CC(MIDIEvents::MIDICC_Balance_Coarse, 13, 40); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetTypeFromEvent(midiEvent), MIDIEvents::evControllerChange); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetChannelFromEvent(midiEvent), 13); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte1FromEvent(midiEvent), MIDIEvents::MIDICC_Balance_Coarse); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte2FromEvent(midiEvent), 40); + + midiEvent = MIDIEvents::NoteOn(10, 50, 120); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetTypeFromEvent(midiEvent), MIDIEvents::evNoteOn); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetChannelFromEvent(midiEvent), 10); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte1FromEvent(midiEvent), 50); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte2FromEvent(midiEvent), 120); + + midiEvent = MIDIEvents::NoteOff(15, 127, 42); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetTypeFromEvent(midiEvent), MIDIEvents::evNoteOff); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetChannelFromEvent(midiEvent), 15); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte1FromEvent(midiEvent), 127); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte2FromEvent(midiEvent), 42); + + midiEvent = MIDIEvents::ProgramChange(1, 127); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetTypeFromEvent(midiEvent), MIDIEvents::evProgramChange); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetChannelFromEvent(midiEvent), 1); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte1FromEvent(midiEvent), 127); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte2FromEvent(midiEvent), 0); + + midiEvent = MIDIEvents::PitchBend(2, MIDIEvents::pitchBendCentre); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetTypeFromEvent(midiEvent), MIDIEvents::evPitchBend); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetChannelFromEvent(midiEvent), 2); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte1FromEvent(midiEvent), 0x00); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte2FromEvent(midiEvent), 0x40); + + midiEvent = MIDIEvents::System(MIDIEvents::sysStart); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetTypeFromEvent(midiEvent), MIDIEvents::evSystem); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetChannelFromEvent(midiEvent), MIDIEvents::sysStart); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte1FromEvent(midiEvent), 0); + VERIFY_EQUAL_NONCONT(MIDIEvents::GetDataByte2FromEvent(midiEvent), 0); +} + + +// Check if our test file was loaded correctly. +static void TestLoadXMFile(const CSoundFile &sndFile) +{ +#ifdef MODPLUG_TRACKER + const CModDoc *pModDoc = sndFile.GetpModDoc(); + VERIFY_EQUAL_NONCONT(pModDoc->IsChannelUnused(0), true); + VERIFY_EQUAL_NONCONT(pModDoc->IsChannelUnused(1), false); +#endif // MODPLUG_TRACKER + + // Global Variables + VERIFY_EQUAL_NONCONT(sndFile.GetTitle(), "Test Module"); + VERIFY_EQUAL_NONCONT(sndFile.m_songMessage.substr(0, 32), "OpenMPT Module Loader Test Suite"); + VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultTempo, TEMPO(139, 0)); + VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultSpeed, 5); + VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultGlobalVolume, 128); + VERIFY_EQUAL_NONCONT(sndFile.m_nVSTiVolume, 42); + VERIFY_EQUAL_NONCONT(sndFile.m_nSamplePreAmp, 23); + VERIFY_EQUAL_NONCONT((sndFile.m_SongFlags & SONG_FILE_FLAGS), SONG_LINEARSLIDES | SONG_EXFILTERRANGE); + VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[MSF_COMPATIBLE_PLAY], true); + VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[kMIDICCBugEmulation], false); + VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[kMPTOldSwingBehaviour], false); + VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[kOldMIDIPitchBends], false); + VERIFY_EQUAL_NONCONT(sndFile.GetMixLevels(), MixLevels::Compatible); + VERIFY_EQUAL_NONCONT(sndFile.m_nTempoMode, TempoMode::Modern); + VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultRowsPerBeat, 6); + VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultRowsPerMeasure, 12); + VERIFY_EQUAL_NONCONT(sndFile.m_dwCreatedWithVersion, MPT_V("1.19.02.05")); + VERIFY_EQUAL_NONCONT(sndFile.Order().GetRestartPos(), 1); + + // Macros + VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetParameteredMacroType(0), kSFxReso); + VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetParameteredMacroType(1), kSFxDryWet); + VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetFixedMacroType(), kZxxResoFltMode); + + // Channels + VERIFY_EQUAL_NONCONT(sndFile.GetNumChannels(), 2); + VERIFY_EQUAL_NONCONT((sndFile.ChnSettings[0].szName == "First Channel"), true); +#ifndef NO_PLUGINS + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].nMixPlugin, 0); +#endif // NO_PLUGINS + + VERIFY_EQUAL_NONCONT((sndFile.ChnSettings[1].szName == "Second Channel"), true); +#ifndef NO_PLUGINS + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].nMixPlugin, 1); +#endif // NO_PLUGINS + + // Samples + VERIFY_EQUAL_NONCONT(sndFile.GetNumSamples(), 3); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[1] == "Pulse Sample"), true); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[2] == "Empty Sample"), true); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[3] == "Unassigned Sample"), true); +#ifdef MODPLUG_TRACKER + VERIFY_EQUAL_NONCONT(pModDoc->FindSampleParent(1), 1); + VERIFY_EQUAL_NONCONT(pModDoc->FindSampleParent(2), 1); + VERIFY_EQUAL_NONCONT(pModDoc->FindSampleParent(3), INSTRUMENTINDEX_INVALID); +#endif // MODPLUG_TRACKER + const ModSample &sample = sndFile.GetSample(1); + VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 1); + VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 1); + VERIFY_EQUAL_NONCONT(sample.GetElementarySampleSize(), 1); + VERIFY_EQUAL_NONCONT(sample.GetSampleSizeInBytes(), 16); + VERIFY_EQUAL_NONCONT(sample.nFineTune, 35); + VERIFY_EQUAL_NONCONT(sample.RelativeTone, 1); + VERIFY_EQUAL_NONCONT(sample.nVolume, 32 * 4); + VERIFY_EQUAL_NONCONT(sample.nGlobalVol, 64); + VERIFY_EQUAL_NONCONT(sample.nPan, 160); + VERIFY_EQUAL_NONCONT(sample.uFlags, CHN_PANNING | CHN_LOOP | CHN_PINGPONGLOOP); + + VERIFY_EQUAL_NONCONT(sample.nLoopStart, 1); + VERIFY_EQUAL_NONCONT(sample.nLoopEnd, 8); + + VERIFY_EQUAL_NONCONT(sample.nVibType, VIB_SQUARE); + VERIFY_EQUAL_NONCONT(sample.nVibSweep, 3); + VERIFY_EQUAL_NONCONT(sample.nVibRate, 4); + VERIFY_EQUAL_NONCONT(sample.nVibDepth, 5); + + // Sample Data + for(size_t i = 0; i < 6; i++) + { + VERIFY_EQUAL_NONCONT(sample.sample8()[i], 18); + } + for(size_t i = 6; i < 16; i++) + { + VERIFY_EQUAL_NONCONT(sample.sample8()[i], 0); + } + + // Instruments + VERIFY_EQUAL_NONCONT(sndFile.GetNumInstruments(), 1); + const ModInstrument *pIns = sndFile.Instruments[1]; + VERIFY_EQUAL_NONCONT(pIns->nFadeOut, 1024); + VERIFY_EQUAL_NONCONT(pIns->nPan, 128); + VERIFY_EQUAL_NONCONT(pIns->dwFlags, InstrumentFlags(0)); + + VERIFY_EQUAL_NONCONT(pIns->nPPS, 0); + VERIFY_EQUAL_NONCONT(pIns->nPPC, NOTE_MIDDLEC - 1); + + VERIFY_EQUAL_NONCONT(pIns->nVolRampUp, 1200); + VERIFY_EQUAL_NONCONT(pIns->resampling, SRCMODE_SINC8LP); + + VERIFY_EQUAL_NONCONT(pIns->IsCutoffEnabled(), false); + VERIFY_EQUAL_NONCONT(pIns->GetCutoff(), 0); + VERIFY_EQUAL_NONCONT(pIns->IsResonanceEnabled(), false); + VERIFY_EQUAL_NONCONT(pIns->GetResonance(), 0); + VERIFY_EQUAL_NONCONT(pIns->filterMode, FilterMode::Unchanged); + + VERIFY_EQUAL_NONCONT(pIns->nVolSwing, 0); + VERIFY_EQUAL_NONCONT(pIns->nPanSwing, 0); + VERIFY_EQUAL_NONCONT(pIns->nCutSwing, 0); + VERIFY_EQUAL_NONCONT(pIns->nResSwing, 0); + + VERIFY_EQUAL_NONCONT(pIns->nNNA, NewNoteAction::NoteCut); + VERIFY_EQUAL_NONCONT(pIns->nDCT, DuplicateCheckType::None); + + VERIFY_EQUAL_NONCONT(pIns->nMixPlug, 1); + VERIFY_EQUAL_NONCONT(pIns->nMidiChannel, 16); + VERIFY_EQUAL_NONCONT(pIns->nMidiProgram, 64); + VERIFY_EQUAL_NONCONT(pIns->wMidiBank, 2); + VERIFY_EQUAL_NONCONT(pIns->midiPWD, 8); + + VERIFY_EQUAL_NONCONT(pIns->pTuning, sndFile.GetDefaultTuning()); + + VERIFY_EQUAL_NONCONT(pIns->pitchToTempoLock, TEMPO(0, 0)); + + VERIFY_EQUAL_NONCONT(pIns->pluginVelocityHandling, PLUGIN_VELOCITYHANDLING_VOLUME); + VERIFY_EQUAL_NONCONT(pIns->pluginVolumeHandling, PLUGIN_VOLUMEHANDLING_MIDI); + + for(size_t i = sndFile.GetModSpecifications().noteMin; i < sndFile.GetModSpecifications().noteMax; i++) + { + VERIFY_EQUAL_NONCONT(pIns->Keyboard[i], (i == NOTE_MIDDLEC - 1) ? 2 : 1); + } + + VERIFY_EQUAL_NONCONT(pIns->VolEnv.dwFlags, ENV_ENABLED | ENV_SUSTAIN); + VERIFY_EQUAL_NONCONT(pIns->VolEnv.size(), 3); + VERIFY_EQUAL_NONCONT(pIns->VolEnv.nReleaseNode, ENV_RELEASE_NODE_UNSET); + VERIFY_EQUAL_NONCONT(pIns->VolEnv[2].tick, 96); + VERIFY_EQUAL_NONCONT(pIns->VolEnv[2].value, 0); + VERIFY_EQUAL_NONCONT(pIns->VolEnv.nSustainStart, 1); + VERIFY_EQUAL_NONCONT(pIns->VolEnv.nSustainEnd, 1); + + VERIFY_EQUAL_NONCONT(pIns->PanEnv.dwFlags, ENV_LOOP); + VERIFY_EQUAL_NONCONT(pIns->PanEnv.size(), 12); + VERIFY_EQUAL_NONCONT(pIns->PanEnv.nLoopStart, 9); + VERIFY_EQUAL_NONCONT(pIns->PanEnv.nLoopEnd, 11); + VERIFY_EQUAL_NONCONT(pIns->PanEnv.nReleaseNode, ENV_RELEASE_NODE_UNSET); + VERIFY_EQUAL_NONCONT(pIns->PanEnv[9].tick, 46); + VERIFY_EQUAL_NONCONT(pIns->PanEnv[9].value, 23); + + VERIFY_EQUAL_NONCONT(pIns->PitchEnv.dwFlags, EnvelopeFlags(0)); + VERIFY_EQUAL_NONCONT(pIns->PitchEnv.size(), 0); + + // Sequences + VERIFY_EQUAL_NONCONT(sndFile.Order.GetNumSequences(), 1); + VERIFY_EQUAL_NONCONT(sndFile.Order()[0], 0); + VERIFY_EQUAL_NONCONT(sndFile.Order()[1], 1); + + // Patterns + VERIFY_EQUAL_NONCONT(sndFile.Patterns.GetNumPatterns(), 2); + + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetName(), "First Pattern"); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetNumRows(), 64); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetNumChannels(), 2); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetOverrideSignature(), false); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetRowsPerBeat(), 0); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetRowsPerMeasure(), 0); + VERIFY_EQUAL_NONCONT(sndFile.Patterns.IsPatternEmpty(0), true); + + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetName(), "Second Pattern"); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetNumRows(), 32); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetNumChannels(), 2); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetOverrideSignature(), false); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetRowsPerBeat(), 0); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetRowsPerMeasure(), 0); + VERIFY_EQUAL_NONCONT(sndFile.Patterns.IsPatternEmpty(1), false); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->IsPcNote(), false); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->note, NOTE_NONE); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->instr, 0); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->volcmd, VOLCMD_VIBRATOSPEED); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->vol, 15); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 0)->IsEmpty(), true); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->IsEmpty(), false); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->IsPcNote(), false); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->note, NOTE_MIDDLEC + 12); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->instr, 45); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->volcmd, VOLCMD_VOLSLIDEDOWN); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->vol, 5); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->command, CMD_PANNING8); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->param, 0xFF); + + // Test 4-Bit Panning conversion + for(int i = 0; i < 16; i++) + { + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(10 + i, 0)->vol, i * 4); + } + + // Plugins +#ifndef NO_PLUGINS + const SNDMIXPLUGIN &plug = sndFile.m_MixPlugins[0]; + VERIFY_EQUAL_NONCONT(plug.GetName(), U_("First Plugin")); + VERIFY_EQUAL_NONCONT(plug.fDryRatio, 0.26f); + VERIFY_EQUAL_NONCONT(plug.IsMasterEffect(), true); + VERIFY_EQUAL_NONCONT(plug.GetGain(), 11); +#endif // NO_PLUGINS +} + + +// Check if our test file was loaded correctly. +static void TestLoadMPTMFile(const CSoundFile &sndFile) +{ + + // Global Variables + VERIFY_EQUAL_NONCONT(sndFile.GetTitle(), "Test Module_____________X"); + VERIFY_EQUAL_NONCONT(sndFile.m_songMessage.substr(0, 32), "OpenMPT Module Loader Test Suite"); + VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultTempo, TEMPO(139, 999)); + VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultSpeed, 5); + VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultGlobalVolume, 128); + VERIFY_EQUAL_NONCONT(sndFile.m_nVSTiVolume, 42); + VERIFY_EQUAL_NONCONT(sndFile.m_nSamplePreAmp, 23); + VERIFY_EQUAL_NONCONT((sndFile.m_SongFlags & SONG_FILE_FLAGS), SONG_LINEARSLIDES | SONG_EXFILTERRANGE | SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS); + VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[MSF_COMPATIBLE_PLAY], true); + VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[kMIDICCBugEmulation], false); + VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[kMPTOldSwingBehaviour], false); + VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[kOldMIDIPitchBends], false); + VERIFY_EQUAL_NONCONT(sndFile.GetMixLevels(), MixLevels::Compatible); + VERIFY_EQUAL_NONCONT(sndFile.m_nTempoMode, TempoMode::Modern); + VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultRowsPerBeat, 6); + VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultRowsPerMeasure, 12); + VERIFY_EQUAL_NONCONT(sndFile.m_dwCreatedWithVersion, MPT_V("1.19.02.05")); + VERIFY_EQUAL_NONCONT(sndFile.m_nResampling, SRCMODE_SINC8LP); + VERIFY_EQUAL_NONCONT(sndFile.m_songArtist, U_("Tester")); + VERIFY_EQUAL_NONCONT(sndFile.m_tempoSwing.size(), 6); + VERIFY_EQUAL_NONCONT(sndFile.m_tempoSwing[0], 29360125); + VERIFY_EQUAL_NONCONT(sndFile.m_tempoSwing[1], 4194305); + VERIFY_EQUAL_NONCONT(sndFile.m_tempoSwing[2], 29360128); + VERIFY_EQUAL_NONCONT(sndFile.m_tempoSwing[3], 4194305); + VERIFY_EQUAL_NONCONT(sndFile.m_tempoSwing[4], 29360128); + VERIFY_EQUAL_NONCONT(sndFile.m_tempoSwing[5], 4194305); + + // Edit history + VERIFY_EQUAL_NONCONT(sndFile.GetFileHistory().size() > 15, true); + const FileHistory &fh = sndFile.GetFileHistory().front(); + VERIFY_EQUAL_NONCONT(fh.loadDate.tm_year, 111); + VERIFY_EQUAL_NONCONT(fh.loadDate.tm_mon, 5); + VERIFY_EQUAL_NONCONT(fh.loadDate.tm_mday, 14); + VERIFY_EQUAL_NONCONT(fh.loadDate.tm_hour, 21); + VERIFY_EQUAL_NONCONT(fh.loadDate.tm_min, 8); + VERIFY_EQUAL_NONCONT(fh.loadDate.tm_sec, 32); + VERIFY_EQUAL_NONCONT((uint32)((double)fh.openTime / HISTORY_TIMER_PRECISION), 31); + + // Macros + VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetParameteredMacroType(0), kSFxReso); + VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetParameteredMacroType(1), kSFxDryWet); + VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetParameteredMacroType(2), kSFxPolyAT); + VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetFixedMacroType(), kZxxResoFltMode); + + // Channels + VERIFY_EQUAL_NONCONT(sndFile.GetNumChannels(), 70); + VERIFY_EQUAL_NONCONT((sndFile.ChnSettings[0].szName == "First Channel"), true); + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].nPan, 32); + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].nVolume, 32); + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].dwFlags, CHN_MUTE); +#ifndef NO_PLUGINS + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].nMixPlugin, 0); +#endif // NO_PLUGINS + + VERIFY_EQUAL_NONCONT((sndFile.ChnSettings[1].szName == "Second Channel"), true); + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].nPan, 128); + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].nVolume, 16); + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].dwFlags, CHN_SURROUND); +#ifndef NO_PLUGINS + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].nMixPlugin, 1); +#endif // NO_PLUGINS + + VERIFY_EQUAL_NONCONT((sndFile.ChnSettings[69].szName == "Last Channel______X"), true); + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[69].nPan, 256); + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[69].nVolume, 7); + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[69].dwFlags, ChannelFlags(0)); +#ifndef NO_PLUGINS + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[69].nMixPlugin, 1); +#endif // NO_PLUGINS + // Samples + VERIFY_EQUAL_NONCONT(sndFile.GetNumSamples(), 4); + { + const ModSample &sample = sndFile.GetSample(1); + VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 1); + VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 1); + VERIFY_EQUAL_NONCONT(sample.GetElementarySampleSize(), 1); + VERIFY_EQUAL_NONCONT(sample.GetSampleSizeInBytes(), 16); + VERIFY_EQUAL_NONCONT(sample.GetSampleRate(MOD_TYPE_MPT), 9001); + VERIFY_EQUAL_NONCONT(sample.nVolume, 32 * 4); + VERIFY_EQUAL_NONCONT(sample.nGlobalVol, 16); + VERIFY_EQUAL_NONCONT(sample.nPan, 160); + VERIFY_EQUAL_NONCONT(sample.uFlags, CHN_PANNING | CHN_LOOP | CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN); + + VERIFY_EQUAL_NONCONT(sample.nLoopStart, 1); + VERIFY_EQUAL_NONCONT(sample.nLoopEnd, 8); + VERIFY_EQUAL_NONCONT(sample.nSustainStart, 1); + VERIFY_EQUAL_NONCONT(sample.nSustainEnd, 7); + + VERIFY_EQUAL_NONCONT(sample.nVibType, VIB_SQUARE); + VERIFY_EQUAL_NONCONT(sample.nVibSweep, 3); + VERIFY_EQUAL_NONCONT(sample.nVibRate, 4); + VERIFY_EQUAL_NONCONT(sample.nVibDepth, 5); + + // Sample Data + for(size_t i = 0; i < 6; i++) + { + VERIFY_EQUAL_NONCONT(sample.sample8()[i], 18); + } + for(size_t i = 6; i < 16; i++) + { + VERIFY_EQUAL_NONCONT(sample.sample8()[i], 0); + } + + VERIFY_EQUAL_NONCONT(sample.cues[0], 2); + VERIFY_EQUAL_NONCONT(sample.cues[8], 9); + } + + { + const ModSample &sample = sndFile.GetSample(2); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[2] == "Stereo / 16-Bit"), true); + VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 4); + VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 2); + VERIFY_EQUAL_NONCONT(sample.GetElementarySampleSize(), 2); + VERIFY_EQUAL_NONCONT(sample.GetSampleSizeInBytes(), 16 * 4); + VERIFY_EQUAL_NONCONT(sample.GetSampleRate(MOD_TYPE_MPT), 16000); + VERIFY_EQUAL_NONCONT(sample.uFlags, CHN_16BIT | CHN_STEREO | CHN_LOOP); + + // Sample Data (Stereo Interleaved) + for(size_t i = 0; i < 7; i++) + { + VERIFY_EQUAL_NONCONT(sample.sample16()[4 + i], int16(-32768)); + } + + VERIFY_EQUAL_NONCONT(sample.cues[0], 3); + VERIFY_EQUAL_NONCONT(sample.cues[8], 14); + } + + // External sample + { + const ModSample &sample = sndFile.GetSample(4); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[4] == "Overridden Name"), true); + VERIFY_EQUAL_NONCONT((sample.filename == "External"), true); +#ifdef MPT_EXTERNAL_SAMPLES + VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 1); + VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 1); + VERIFY_EQUAL_NONCONT(sample.GetElementarySampleSize(), 1); + VERIFY_EQUAL_NONCONT(sample.GetSampleSizeInBytes(), 64); + + VERIFY_EQUAL_NONCONT(sample.nLoopStart, 42); + VERIFY_EQUAL_NONCONT(sample.nLoopEnd, 55); + VERIFY_EQUAL_NONCONT(sample.nSustainStart, 42); + VERIFY_EQUAL_NONCONT(sample.nSustainEnd, 55); + VERIFY_EQUAL_NONCONT(sample.uFlags, CHN_LOOP | CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN | SMP_KEEPONDISK); +#endif // MPT_EXTERNAL_SAMPLES + VERIFY_EQUAL_NONCONT(sample.GetSampleRate(MOD_TYPE_MPT), 10101); + VERIFY_EQUAL_NONCONT(sample.nVolume, 26 * 4); + VERIFY_EQUAL_NONCONT(sample.nGlobalVol, 26); + VERIFY_EQUAL_NONCONT(sample.nPan, 26 * 4); + + VERIFY_EQUAL_NONCONT(sample.nVibType, VIB_SINE); + VERIFY_EQUAL_NONCONT(sample.nVibSweep, 37); + VERIFY_EQUAL_NONCONT(sample.nVibRate, 42); + VERIFY_EQUAL_NONCONT(sample.nVibDepth, 23); + + // Sample Data +#ifdef MPT_EXTERNAL_SAMPLES + for(size_t i = 0; i < 16; i++) + { + VERIFY_EQUAL_NONCONT(sample.sample8()[i], int8(45)); + } +#endif // MPT_EXTERNAL_SAMPLES + + VERIFY_EQUAL_NONCONT(sample.cues[0], 10); + VERIFY_EQUAL_NONCONT(sample.cues[8], 50); + } + + // Instruments + VERIFY_EQUAL_NONCONT(sndFile.GetNumInstruments(), 2); + for(INSTRUMENTINDEX ins = 1; ins <= 2; ins++) + { + const ModInstrument *pIns = sndFile.Instruments[ins]; + VERIFY_EQUAL_NONCONT(pIns->nGlobalVol, 32); + VERIFY_EQUAL_NONCONT(pIns->nFadeOut, 1024); + VERIFY_EQUAL_NONCONT(pIns->nPan, 64); + VERIFY_EQUAL_NONCONT(pIns->dwFlags, INS_SETPANNING); + + VERIFY_EQUAL_NONCONT(pIns->nPPS, 8); + VERIFY_EQUAL_NONCONT(pIns->nPPC, (NOTE_MIDDLEC - NOTE_MIN) + 6); // F#5 + + VERIFY_EQUAL_NONCONT(pIns->nVolRampUp, 1200); + VERIFY_EQUAL_NONCONT(pIns->resampling, SRCMODE_SINC8LP); + + VERIFY_EQUAL_NONCONT(pIns->IsCutoffEnabled(), true); + VERIFY_EQUAL_NONCONT(pIns->GetCutoff(), 0x32); + VERIFY_EQUAL_NONCONT(pIns->IsResonanceEnabled(), true); + VERIFY_EQUAL_NONCONT(pIns->GetResonance(), 0x64); + VERIFY_EQUAL_NONCONT(pIns->filterMode, FilterMode::HighPass); + + VERIFY_EQUAL_NONCONT(pIns->nVolSwing, 0x30); + VERIFY_EQUAL_NONCONT(pIns->nPanSwing, 0x18); + VERIFY_EQUAL_NONCONT(pIns->nCutSwing, 0x0C); + VERIFY_EQUAL_NONCONT(pIns->nResSwing, 0x3C); + + VERIFY_EQUAL_NONCONT(pIns->nNNA, NewNoteAction::Continue); + VERIFY_EQUAL_NONCONT(pIns->nDCT, DuplicateCheckType::Note); + VERIFY_EQUAL_NONCONT(pIns->nDNA, DuplicateNoteAction::NoteFade); + + VERIFY_EQUAL_NONCONT(pIns->nMixPlug, 1); + VERIFY_EQUAL_NONCONT(pIns->nMidiChannel, 16); + VERIFY_EQUAL_NONCONT(pIns->nMidiProgram, 64); + VERIFY_EQUAL_NONCONT(pIns->wMidiBank, 2); + VERIFY_EQUAL_NONCONT(pIns->midiPWD, ins); + + VERIFY_EQUAL_NONCONT(pIns->pTuning, sndFile.GetDefaultTuning()); + + VERIFY_EQUAL_NONCONT(pIns->pitchToTempoLock, TEMPO(130, 2000)); + + VERIFY_EQUAL_NONCONT(pIns->pluginVelocityHandling, PLUGIN_VELOCITYHANDLING_VOLUME); + VERIFY_EQUAL_NONCONT(pIns->pluginVolumeHandling, PLUGIN_VOLUMEHANDLING_MIDI); + + for(size_t i = 0; i < NOTE_MAX; i++) + { + VERIFY_EQUAL_NONCONT(pIns->Keyboard[i], (i == NOTE_MIDDLEC - 1) ? 99 : 1); + VERIFY_EQUAL_NONCONT(pIns->NoteMap[i], (i == NOTE_MIDDLEC - 1) ? (i + 13) : (i + 1)); + } + + VERIFY_EQUAL_NONCONT(pIns->VolEnv.dwFlags, ENV_ENABLED | ENV_CARRY); + VERIFY_EQUAL_NONCONT(pIns->VolEnv.size(), 3); + VERIFY_EQUAL_NONCONT(pIns->VolEnv.nReleaseNode, 1); + VERIFY_EQUAL_NONCONT(pIns->VolEnv[2].tick, 96); + VERIFY_EQUAL_NONCONT(pIns->VolEnv[2].value, 0); + + VERIFY_EQUAL_NONCONT(pIns->PanEnv.dwFlags, ENV_LOOP); + VERIFY_EQUAL_NONCONT(pIns->PanEnv.size(), 76); + VERIFY_EQUAL_NONCONT(pIns->PanEnv.nLoopStart, 22); + VERIFY_EQUAL_NONCONT(pIns->PanEnv.nLoopEnd, 29); + VERIFY_EQUAL_NONCONT(pIns->PanEnv.nReleaseNode, ENV_RELEASE_NODE_UNSET); + VERIFY_EQUAL_NONCONT(pIns->PanEnv[75].tick, 427); + VERIFY_EQUAL_NONCONT(pIns->PanEnv[75].value, 27); + + VERIFY_EQUAL_NONCONT(pIns->PitchEnv.dwFlags, ENV_ENABLED | ENV_CARRY | ENV_SUSTAIN | ENV_FILTER); + VERIFY_EQUAL_NONCONT(pIns->PitchEnv.size(), 3); + VERIFY_EQUAL_NONCONT(pIns->PitchEnv.nSustainStart, 1); + VERIFY_EQUAL_NONCONT(pIns->PitchEnv.nSustainEnd, 2); + VERIFY_EQUAL_NONCONT(pIns->PitchEnv[1].tick, 96); + VERIFY_EQUAL_NONCONT(pIns->PitchEnv[1].value, 64); + } + // Sequences + VERIFY_EQUAL_NONCONT(sndFile.Order.GetNumSequences(), 2); + + VERIFY_EQUAL_NONCONT(sndFile.Order(0).GetLengthTailTrimmed(), 3); + VERIFY_EQUAL_NONCONT(sndFile.Order(0).GetName(), U_("First Sequence")); + VERIFY_EQUAL_NONCONT(sndFile.Order(0)[0], sndFile.Order.GetIgnoreIndex()); + VERIFY_EQUAL_NONCONT(sndFile.Order(0)[1], 0); + VERIFY_EQUAL_NONCONT(sndFile.Order(0)[2], sndFile.Order.GetIgnoreIndex()); + VERIFY_EQUAL_NONCONT(sndFile.Order(0).GetRestartPos(), 1); + + VERIFY_EQUAL_NONCONT(sndFile.Order(1).GetLengthTailTrimmed(), 3); + VERIFY_EQUAL_NONCONT(sndFile.Order(1).GetName(), U_("Second Sequence")); + VERIFY_EQUAL_NONCONT(sndFile.Order(1)[0], 1); + VERIFY_EQUAL_NONCONT(sndFile.Order(1)[1], 2); + VERIFY_EQUAL_NONCONT(sndFile.Order(1)[2], 3); + VERIFY_EQUAL_NONCONT(sndFile.Order(1).GetRestartPos(), 2); + + // Patterns + VERIFY_EQUAL_NONCONT(sndFile.Patterns.GetNumPatterns(), 2); + + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetName(), "First Pattern"); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetNumRows(), 70); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetNumChannels(), 70); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetOverrideSignature(), true); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetRowsPerBeat(), 5); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetRowsPerMeasure(), 10); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].HasTempoSwing(), true); + VERIFY_EQUAL_NONCONT(sndFile.Patterns.IsPatternEmpty(0), true); + + { + TempoSwing swing = sndFile.Patterns[0].GetTempoSwing(); + VERIFY_EQUAL_NONCONT(swing.size(), 5); + VERIFY_EQUAL_NONCONT(swing[0], 16770149); + VERIFY_EQUAL_NONCONT(swing[1], 16803696); + VERIFY_EQUAL_NONCONT(swing[2], 16770157); + VERIFY_EQUAL_NONCONT(swing[3], 29347774); + VERIFY_EQUAL_NONCONT(swing[4], 4194304); + } + + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetName(), "Second Pattern"); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetNumRows(), 32); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetNumChannels(), 70); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetOverrideSignature(), false); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetRowsPerBeat(), 0); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetRowsPerMeasure(), 0); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].HasTempoSwing(), false); + VERIFY_EQUAL_NONCONT(sndFile.Patterns.IsPatternEmpty(1), false); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->IsPcNote(), true); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->note, NOTE_PC); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->instr, 99); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->GetValueVolCol(), 1); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->GetValueEffectCol(), 200); + + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 0)->IsEmpty(), true); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->IsEmpty(), false); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->IsPcNote(), false); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->note, NOTE_MIDDLEC + 12); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->instr, 45); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->volcmd, VOLCMD_VOLSLIDEDOWN); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->vol, 5); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->command, CMD_PANNING8); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->param, 0xFF); + + // Plugins +#ifndef NO_PLUGINS + const SNDMIXPLUGIN &plug = sndFile.m_MixPlugins[0]; + VERIFY_EQUAL_NONCONT(plug.GetName(), U_("First Plugin")); + VERIFY_EQUAL_NONCONT(plug.fDryRatio, 0.26f); + VERIFY_EQUAL_NONCONT(plug.IsMasterEffect(), true); + VERIFY_EQUAL_NONCONT(plug.GetGain(), 11); + VERIFY_EQUAL_NONCONT(plug.pMixPlugin != nullptr, true); + if(plug.pMixPlugin) + { + VERIFY_EQUAL_NONCONT(plug.pMixPlugin->GetParameter(1), 0.5f); + VERIFY_EQUAL_NONCONT(plug.pMixPlugin->IsInstrument(), false); + } +#endif // NO_PLUGINS + +#ifdef MODPLUG_TRACKER + // MIDI Mapping + VERIFY_EQUAL_NONCONT(sndFile.GetMIDIMapper().GetCount(), 1); + const CMIDIMappingDirective &mapping = sndFile.GetMIDIMapper().GetDirective(0); + VERIFY_EQUAL_NONCONT(mapping.GetAllowPatternEdit(), true); + VERIFY_EQUAL_NONCONT(mapping.GetCaptureMIDI(), false); + VERIFY_EQUAL_NONCONT(mapping.IsActive(), true); + VERIFY_EQUAL_NONCONT(mapping.GetAnyChannel(), false); + VERIFY_EQUAL_NONCONT(mapping.GetChannel(), 5); + VERIFY_EQUAL_NONCONT(mapping.GetPlugIndex(), 1); + VERIFY_EQUAL_NONCONT(mapping.GetParamIndex(), 0); + VERIFY_EQUAL_NONCONT(mapping.GetEvent(), MIDIEvents::evControllerChange); + VERIFY_EQUAL_NONCONT(mapping.GetController(), MIDIEvents::MIDICC_ModulationWheel_Coarse); + + // Channel colors + const auto &chns = sndFile.ChnSettings; + VERIFY_EQUAL_NONCONT(chns[0].color, RGB(255, 0, 0)); + VERIFY_EQUAL_NONCONT(chns[1].color, RGB(0, 255, 0)); + VERIFY_EQUAL_NONCONT(chns[2].color, RGB(0, 0, 255)); + VERIFY_EQUAL_NONCONT(chns[3].color, ModChannelSettings::INVALID_COLOR); + VERIFY_EQUAL_NONCONT(chns[67].color, RGB(255, 0, 255)); + VERIFY_EQUAL_NONCONT(chns[68].color, RGB(255, 255, 0)); + VERIFY_EQUAL_NONCONT(chns[69].color, ModChannelSettings::INVALID_COLOR); +#endif + + VERIFY_EQUAL_NONCONT(sndFile.FrequencyToCutOff(sndFile.CutOffToFrequency(0)), 0); + VERIFY_EQUAL_NONCONT(sndFile.FrequencyToCutOff(sndFile.CutOffToFrequency(80)), 80); + VERIFY_EQUAL_NONCONT(sndFile.FrequencyToCutOff(sndFile.CutOffToFrequency(127)), 127); +} + + +// Check if our test file was loaded correctly. +static void TestLoadS3MFile(const CSoundFile &sndFile, bool resaved) +{ + + // Global Variables + VERIFY_EQUAL_NONCONT(sndFile.GetTitle(), "S3M_Test__________________X"); + VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultTempo, TEMPO(33, 0)); + VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultSpeed, 254); + VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultGlobalVolume, 32 * 4); + VERIFY_EQUAL_NONCONT(sndFile.m_nVSTiVolume, 36); + VERIFY_EQUAL_NONCONT(sndFile.m_nSamplePreAmp, 16); + VERIFY_EQUAL_NONCONT((sndFile.m_SongFlags & SONG_FILE_FLAGS), SONG_FASTVOLSLIDES); + VERIFY_EQUAL_NONCONT(sndFile.GetMixLevels(), MixLevels::Compatible); + VERIFY_EQUAL_NONCONT(sndFile.m_nTempoMode, TempoMode::Classic); + VERIFY_EQUAL_NONCONT(sndFile.m_dwLastSavedWithVersion, resaved ? Version::Current() : MPT_V("1.27.00.00")); + + // Channels + VERIFY_EQUAL_NONCONT(sndFile.GetNumChannels(), 4); + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].nPan, 0); + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].dwFlags, ChannelFlags(0)); + + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].nPan, 256); + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].dwFlags, CHN_MUTE); + + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[2].nPan, 85); + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[2].dwFlags, ChannelFlags(0)); + + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[3].nPan, 171); + VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[3].dwFlags, CHN_MUTE); + + // Samples + VERIFY_EQUAL_NONCONT(sndFile.GetNumSamples(), 4); + { + const ModSample &sample = sndFile.GetSample(1); + VERIFY_EQUAL_NONCONT(sndFile.m_szNames[1], "Sample_1__________________X"); + VERIFY_EQUAL_NONCONT(sample.filename, "Filename_1_X"); + VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 1); + VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 1); + VERIFY_EQUAL_NONCONT(sample.GetElementarySampleSize(), 1); + VERIFY_EQUAL_NONCONT(sample.GetSampleSizeInBytes(), 60); + VERIFY_EQUAL_NONCONT(sample.GetSampleRate(MOD_TYPE_S3M), 9001); + VERIFY_EQUAL_NONCONT(sample.nVolume, 32 * 4); + VERIFY_EQUAL_NONCONT(sample.nGlobalVol, 64); + VERIFY_EQUAL_NONCONT(sample.uFlags, CHN_LOOP); + + VERIFY_EQUAL_NONCONT(sample.nLoopStart, 16); + VERIFY_EQUAL_NONCONT(sample.nLoopEnd, 60); + + // Sample Data + for(size_t i = 0; i < 30; i++) + { + VERIFY_EQUAL_NONCONT(sample.sample8()[i], 127); + } + for(size_t i = 31; i < 60; i++) + { + VERIFY_EQUAL_NONCONT(sample.sample8()[i], -128); + } + } + + { + const ModSample &sample = sndFile.GetSample(2); + VERIFY_EQUAL_NONCONT(sndFile.m_szNames[2], "Empty"); + VERIFY_EQUAL_NONCONT(sample.GetSampleRate(MOD_TYPE_S3M), 16384); + VERIFY_EQUAL_NONCONT(sample.nVolume, 2 * 4); + } + + { + const ModSample &sample = sndFile.GetSample(3); + VERIFY_EQUAL_NONCONT(sndFile.m_szNames[3], "Stereo / 16-Bit"); + VERIFY_EQUAL_NONCONT(sample.filename, "Filename_3_X"); + VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 4); + VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 2); + VERIFY_EQUAL_NONCONT(sample.GetElementarySampleSize(), 2); + VERIFY_EQUAL_NONCONT(sample.GetSampleSizeInBytes(), 64); + VERIFY_EQUAL_NONCONT(sample.GetSampleRate(MOD_TYPE_S3M), 16000); + VERIFY_EQUAL_NONCONT(sample.nVolume, 0); + VERIFY_EQUAL_NONCONT(sample.uFlags, CHN_LOOP | CHN_16BIT | CHN_STEREO); + + VERIFY_EQUAL_NONCONT(sample.nLoopStart, 0); + VERIFY_EQUAL_NONCONT(sample.nLoopEnd, 16); + + // Sample Data (Stereo Interleaved) + for(size_t i = 0; i < 7; i++) + { + VERIFY_EQUAL_NONCONT(sample.sample16()[4 + i], int16(-32768)); + } + } + + { + const ModSample &sample = sndFile.GetSample(4); + VERIFY_EQUAL_NONCONT(sndFile.m_szNames[4], "adlib"); + VERIFY_EQUAL_NONCONT((sample.filename == ""), true); + VERIFY_EQUAL_NONCONT(sample.GetSampleRate(MOD_TYPE_S3M), 8363); + VERIFY_EQUAL_NONCONT(sample.nVolume, 58 * 4); + VERIFY_EQUAL_NONCONT(sample.uFlags, CHN_ADLIB); + VERIFY_EQUAL_NONCONT(sample.adlib, (OPLPatch{ { 0x00, 0x00, 0xC0, 0x00, 0xF0, 0xD2, 0x05, 0xB3, 0x01, 0x00, 0x00, 0x00 } })); + } + + // Orders + VERIFY_EQUAL_NONCONT(sndFile.Order().GetRestartPos(), 0); + VERIFY_EQUAL_NONCONT(sndFile.Order().GetLengthTailTrimmed(), 5); + VERIFY_EQUAL_NONCONT(sndFile.Order()[0], 0); + VERIFY_EQUAL_NONCONT(sndFile.Order()[1], sndFile.Order.GetIgnoreIndex()); + VERIFY_EQUAL_NONCONT(sndFile.Order()[2], sndFile.Order.GetInvalidPatIndex()); + VERIFY_EQUAL_NONCONT(sndFile.Order()[3], 1); + VERIFY_EQUAL_NONCONT(sndFile.Order()[4], 0); + + // Patterns + VERIFY_EQUAL_NONCONT(sndFile.Patterns.GetNumPatterns(), 2); + + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetNumRows(), 64); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetNumChannels(), 4); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetOverrideSignature(), false); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(0, 0)->note, NOTE_MIN + 12); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(1, 0)->note, NOTE_MIN + 107); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(0, 1)->volcmd, VOLCMD_VOLUME); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(0, 1)->vol, 0); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(1, 1)->volcmd, VOLCMD_VOLUME); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(1, 1)->vol, 64); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(2, 1)->volcmd, VOLCMD_PANNING); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(2, 1)->vol, 0); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(3, 1)->volcmd, VOLCMD_PANNING); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(3, 1)->vol, 64); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(0, 3)->command, CMD_SPEED); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[0].GetpModCommand(0, 3)->param, 0x11); + + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetNumRows(), 64); + VERIFY_EQUAL_NONCONT(sndFile.Patterns.IsPatternEmpty(1), false); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(63, 3)->param, 0x04); +} + + +// Check if our test file was loaded correctly. +static void TestLoadMODFile(CSoundFile &sndFile) +{ + // Global Variables + VERIFY_EQUAL_NONCONT(sndFile.GetTitle(), "MOD_Test___________X"); + VERIFY_EQUAL_NONCONT((sndFile.m_SongFlags & SONG_FILE_FLAGS), SONG_PT_MODE | SONG_AMIGALIMITS | SONG_ISAMIGA); + VERIFY_EQUAL_NONCONT(sndFile.GetMixLevels(), MixLevels::Compatible); + VERIFY_EQUAL_NONCONT(sndFile.m_nTempoMode, TempoMode::Classic); + VERIFY_EQUAL_NONCONT(sndFile.GetNumChannels(), 4); + VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[kMODOneShotLoops], true); + VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[kMODSampleSwap], true); + VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[kMODIgnorePanning], true); + VERIFY_EQUAL_NONCONT(sndFile.m_playBehaviour[kMODVBlankTiming], false); + + // Test GetLength code, in particular with subsongs + VERIFY_EQUAL_NONCONT(sndFile.GetLength(eNoAdjust, GetLengthTarget(0, 4)).back().targetReached, false); + + const auto allSubSongs = sndFile.GetLength(eNoAdjust, GetLengthTarget(true)); + VERIFY_EQUAL_NONCONT(allSubSongs.size(), 2); + VERIFY_EQUAL_EPS(allSubSongs[0].duration, 2.04, 0.1); + VERIFY_EQUAL_EPS(allSubSongs[1].duration, 118.84, 0.1); + VERIFY_EQUAL_NONCONT(allSubSongs[0].lastOrder, 0); + VERIFY_EQUAL_NONCONT(allSubSongs[0].lastRow, 1); + VERIFY_EQUAL_NONCONT(allSubSongs[1].lastOrder, 2); + VERIFY_EQUAL_NONCONT(allSubSongs[1].lastRow, 61); + VERIFY_EQUAL_NONCONT(allSubSongs[1].startOrder, 2); + VERIFY_EQUAL_NONCONT(allSubSongs[1].startRow, 0); + + // Samples + VERIFY_EQUAL_NONCONT(sndFile.GetNumSamples(), 31); + { + const ModSample &sample = sndFile.GetSample(1); + VERIFY_EQUAL_NONCONT(sndFile.m_szNames[1], "Sample_1_____________X"); + VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 1); + VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 1); + VERIFY_EQUAL_NONCONT(sample.GetElementarySampleSize(), 1); + VERIFY_EQUAL_NONCONT(sample.GetSampleSizeInBytes(), 1244); + VERIFY_EQUAL_NONCONT(sample.nFineTune, 0x70); + VERIFY_EQUAL_NONCONT(sample.RelativeTone, 0); + VERIFY_EQUAL_NONCONT(sample.nVolume, 256); + VERIFY_EQUAL_NONCONT(sample.nGlobalVol, 64); + VERIFY_EQUAL_NONCONT(sample.uFlags, CHN_LOOP); + + VERIFY_EQUAL_NONCONT(sample.nLoopStart, 0); + VERIFY_EQUAL_NONCONT(sample.nLoopEnd, 128); + + // Sample Data + VERIFY_EQUAL_NONCONT(sample.sample8()[0], 0); + VERIFY_EQUAL_NONCONT(sample.sample8()[1], 0); + VERIFY_EQUAL_NONCONT(sample.sample8()[2], -29); + } + { + const ModSample &sample = sndFile.GetSample(3); + VERIFY_EQUAL_NONCONT(sndFile.m_szNames[3], "OpenMPT Module Loader"); + VERIFY_EQUAL_NONCONT(sample.GetSampleSizeInBytes(), 0); + VERIFY_EQUAL_NONCONT(sample.nFineTune, -0x80); + VERIFY_EQUAL_NONCONT(sample.RelativeTone, 0); + VERIFY_EQUAL_NONCONT(sample.nVolume, 4); + VERIFY_EQUAL_NONCONT(sample.nGlobalVol, 64); + } + + // Orders + VERIFY_EQUAL_NONCONT(sndFile.Order().GetRestartPos(), 0); + VERIFY_EQUAL_NONCONT(sndFile.Order().GetLengthTailTrimmed(), 4); + VERIFY_EQUAL_NONCONT(sndFile.Order()[0], 0); + VERIFY_EQUAL_NONCONT(sndFile.Order()[1], 1); + VERIFY_EQUAL_NONCONT(sndFile.Order()[2], 2); + VERIFY_EQUAL_NONCONT(sndFile.Order()[3], 0); + + // Patterns + VERIFY_EQUAL_NONCONT(sndFile.Patterns.GetNumPatterns(), 3); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[2].GetNumRows(), 64); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[2].GetpModCommand(1, 0)->note, NOTE_MIDDLEC + 12); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[2].GetpModCommand(16, 3)->instr, 1); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[2].GetpModCommand(19, 3)->command, CMD_PANNING8); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[2].GetpModCommand(19, 3)->param, 0x28); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[2].GetpModCommand(20, 0)->command, CMD_TEMPO); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[2].GetpModCommand(20, 1)->command, CMD_SPEED); +} + + +#ifdef MODPLUG_TRACKER + +static bool ShouldRunTests() +{ + mpt::PathString theFile = theApp.GetInstallPath(); + if(theFile.IsDirectory() && (theFile + P_("test")).IsDirectory()) + { + if((theFile + P_("test\\test.mptm")).IsFile()) + { + return true; + } + } + return false; +} + +static mpt::PathString GetTestFilenameBase() +{ + mpt::PathString theFile = theApp.GetInstallPath(); + theFile += P_("test/test."); + return theFile; +} + +static mpt::PathString GetTempFilenameBase() +{ + return GetTestFilenameBase(); +} + +typedef CModDoc *TSoundFileContainer; + +static CSoundFile &GetSoundFile(TSoundFileContainer &sndFile) +{ + return sndFile->GetSoundFile(); +} + + +static TSoundFileContainer CreateSoundFileContainer(const mpt::PathString &filename) +{ + CModDoc *pModDoc = static_cast<CModDoc *>(theApp.OpenDocumentFile(filename.ToCString(), FALSE)); + return pModDoc; +} + +static void DestroySoundFileContainer(TSoundFileContainer &sndFile) +{ + sndFile->OnCloseDocument(); +} + +static void SaveTestFile(const TSoundFileContainer &sndFile, const mpt::PathString &filename) +{ + sndFile->DoSave(filename); + // Saving the file puts it in the MRU list... + theApp.RemoveMruItem(0); +} + +const auto SaveIT = SaveTestFile; +const auto SaveXM = SaveTestFile; +const auto SaveS3M = SaveTestFile; +const auto SaveMOD = SaveTestFile; + +#else // !MODPLUG_TRACKER + +static bool ShouldRunTests() +{ + #if MPT_TEST_HAS_FILESYSTEM + return true; + #else + return false; + #endif +} + +static mpt::PathString GetTestFilenameBase() +{ + return Test::GetPathPrefix() + P_("./test/test."); +} + +static mpt::PathString GetTempFilenameBase() +{ + return P_("./test."); +} + +typedef std::shared_ptr<CSoundFile> TSoundFileContainer; + +static CSoundFile &GetSoundFile(TSoundFileContainer &sndFile) +{ + return *sndFile.get(); +} + +static TSoundFileContainer CreateSoundFileContainer(const mpt::PathString &filename) +{ + mpt::ifstream stream(filename, std::ios::binary); + FileReader file = mpt::IO::make_FileCursor<mpt::PathString>(stream); + std::shared_ptr<CSoundFile> pSndFile = std::make_shared<CSoundFile>(); + pSndFile->Create(file, CSoundFile::loadCompleteModule); + return pSndFile; +} + +static void DestroySoundFileContainer(TSoundFileContainer & /* sndFile */ ) +{ + return; +} + +#ifndef MODPLUG_NO_FILESAVE + +static void SaveIT(const TSoundFileContainer &sndFile, const mpt::PathString &filename) +{ + mpt::ofstream f(filename, std::ios::binary); + sndFile->SaveIT(f, filename, false); +} + +static void SaveXM(const TSoundFileContainer &sndFile, const mpt::PathString &filename) +{ + mpt::ofstream f(filename, std::ios::binary); + sndFile->SaveXM(f, false); +} + +static void SaveS3M(const TSoundFileContainer &sndFile, const mpt::PathString &filename) +{ + mpt::ofstream f(filename, std::ios::binary); + sndFile->SaveS3M(f); +} + +static void SaveMOD(const TSoundFileContainer &sndFile, const mpt::PathString &filename) +{ + mpt::ofstream f(filename, std::ios::binary); + sndFile->SaveMod(f); +} + +#endif // !MODPLUG_NO_FILESAVE + +#endif // MODPLUG_TRACKER + + + +// Test file loading and saving +static MPT_NOINLINE void TestLoadSaveFile() +{ + if(!ShouldRunTests()) + { + return; + } + +#ifdef MODPLUG_TRACKER + bool saveMutedChannels = TrackerSettings::Instance().MiscSaveChannelMuteStatus; + TrackerSettings::Instance().MiscSaveChannelMuteStatus = true; +#endif + + mpt::PathString filenameBaseSrc = GetTestFilenameBase(); + mpt::PathString filenameBase = GetTempFilenameBase(); + + // Test MPTM file loading + { + TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBaseSrc + P_("mptm")); + + TestLoadMPTMFile(GetSoundFile(sndFileContainer)); + + #ifndef MODPLUG_NO_FILESAVE + // Test file saving + GetSoundFile(sndFileContainer).m_dwLastSavedWithVersion = Version::Current(); + SaveIT(sndFileContainer, filenameBase + P_("saved.mptm")); + #endif + + DestroySoundFileContainer(sndFileContainer); + } + + // Reload the saved file and test if everything is still working correctly. + #ifndef MODPLUG_NO_FILESAVE + { + TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBase + P_("saved.mptm")); + + TestLoadMPTMFile(GetSoundFile(sndFileContainer)); + + DestroySoundFileContainer(sndFileContainer); + + RemoveFile(filenameBase + P_("saved.mptm")); + } + #endif + + // Test XM file loading + { + TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBaseSrc + P_("xm")); + + TestLoadXMFile(GetSoundFile(sndFileContainer)); + + // In OpenMPT 1.20 (up to revision 1459), there was a bug in the XM saver + // that would create broken XMs if the sample map contained samples that + // were only referenced below C-1 or above B-8 (such samples should not + // be written). Let's insert a sample there and check if re-loading the + // file still works. + GetSoundFile(sndFileContainer).m_nSamples++; + GetSoundFile(sndFileContainer).Instruments[1]->Keyboard[110] = GetSoundFile(sndFileContainer).GetNumSamples(); + + #ifndef MODPLUG_NO_FILESAVE + // Test file saving + GetSoundFile(sndFileContainer).m_dwLastSavedWithVersion = Version::Current(); + SaveXM(sndFileContainer, filenameBase + P_("saved.xm")); + #endif + + DestroySoundFileContainer(sndFileContainer); + } + + // Reload the saved file and test if everything is still working correctly. + #ifndef MODPLUG_NO_FILESAVE + { + TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBase + P_("saved.xm")); + + TestLoadXMFile(GetSoundFile(sndFileContainer)); + + DestroySoundFileContainer(sndFileContainer); + + RemoveFile(filenameBase + P_("saved.xm")); + } + #endif + + // Test S3M file loading + { + TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBaseSrc + P_("s3m")); + auto &sndFile = GetSoundFile(sndFileContainer); + + TestLoadS3MFile(sndFile, false); + + // Test GetLength code, in particular with subsongs + sndFile.ChnSettings[1].dwFlags.reset(CHN_MUTE); + + VERIFY_EQUAL_EPS(sndFile.GetLength(eAdjustSamplePositions, GetLengthTarget(3, 1)).back().duration, 19.237, 0.01); + VERIFY_EQUAL_NONCONT(sndFile.GetLength(eAdjustSamplePositions, GetLengthTarget(2, 0).StartPos(0, 1, 0)).back().targetReached, false); + + auto allSubSongs = sndFile.GetLength(eNoAdjust, GetLengthTarget(true)); + VERIFY_EQUAL_NONCONT(allSubSongs.size(), 3); + double totalDuration = 0.0; + for(const auto &subSong : allSubSongs) + { + totalDuration += subSong.duration; + } + VERIFY_EQUAL_EPS(totalDuration, 3674.38, 1.0); + + #ifndef MODPLUG_NO_FILESAVE + // Test file saving + sndFile.ChnSettings[1].dwFlags.set(CHN_MUTE); + sndFile.m_dwLastSavedWithVersion = Version::Current(); + SaveS3M(sndFileContainer, filenameBase + P_("saved.s3m")); + #endif + + DestroySoundFileContainer(sndFileContainer); + } + + // Reload the saved file and test if everything is still working correctly. + #ifndef MODPLUG_NO_FILESAVE + { + TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBase + P_("saved.s3m")); + + TestLoadS3MFile(GetSoundFile(sndFileContainer), true); + + DestroySoundFileContainer(sndFileContainer); + + RemoveFile(filenameBase + P_("saved.s3m")); + } + #endif + + // Test MOD file loading + { + TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBaseSrc + P_("mod")); + auto &sndFile = GetSoundFile(sndFileContainer); + + TestLoadMODFile(sndFile); + +#ifndef MODPLUG_NO_FILESAVE + // Test file saving + SaveMOD(sndFileContainer, filenameBase + P_("saved.mod")); +#endif + + DestroySoundFileContainer(sndFileContainer); + } + + // Reload the saved file and test if everything is still working correctly. +#ifndef MODPLUG_NO_FILESAVE + { + TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBase + P_("saved.mod")); + TestLoadMODFile(GetSoundFile(sndFileContainer)); + DestroySoundFileContainer(sndFileContainer); + RemoveFile(filenameBase + P_("saved.mod")); + } +#endif + + // General file I/O tests + { + std::ostringstream f; + size_t bytesWritten; + mpt::IO::WriteVarInt(f, uint16(0), &bytesWritten); VERIFY_EQUAL_NONCONT(bytesWritten, 1); + mpt::IO::WriteVarInt(f, uint16(127), &bytesWritten); VERIFY_EQUAL_NONCONT(bytesWritten, 1); + mpt::IO::WriteVarInt(f, uint16(128), &bytesWritten); VERIFY_EQUAL_NONCONT(bytesWritten, 2); + mpt::IO::WriteVarInt(f, uint16(16383), &bytesWritten); VERIFY_EQUAL_NONCONT(bytesWritten, 2); + mpt::IO::WriteVarInt(f, uint16(16384), &bytesWritten); VERIFY_EQUAL_NONCONT(bytesWritten, 3); + mpt::IO::WriteVarInt(f, uint16(65535), &bytesWritten); VERIFY_EQUAL_NONCONT(bytesWritten, 3); + mpt::IO::WriteVarInt(f, uint64(0xFFFFFFFFFFFFFFFFull), &bytesWritten); VERIFY_EQUAL_NONCONT(bytesWritten, 10); + std::string data = f.str(); + FileReader file(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(data))); + uint64 v; + file.ReadVarInt(v); VERIFY_EQUAL_NONCONT(v, 0); + file.ReadVarInt(v); VERIFY_EQUAL_NONCONT(v, 127); + file.ReadVarInt(v); VERIFY_EQUAL_NONCONT(v, 128); + file.ReadVarInt(v); VERIFY_EQUAL_NONCONT(v, 16383); + file.ReadVarInt(v); VERIFY_EQUAL_NONCONT(v, 16384); + file.ReadVarInt(v); VERIFY_EQUAL_NONCONT(v, 65535); + file.ReadVarInt(v); VERIFY_EQUAL_NONCONT(v, 0xFFFFFFFFFFFFFFFFull); + } + +#ifdef MODPLUG_TRACKER + TrackerSettings::Instance().MiscSaveChannelMuteStatus = saveMutedChannels; +#endif +} + + +// Test various editing features +static MPT_NOINLINE void TestEditing() +{ +#ifdef MODPLUG_TRACKER + auto modDoc = static_cast<CModDoc *>(theApp.GetModDocTemplate()->CreateNewDocument()); + auto &sndFile = modDoc->GetSoundFile(); + sndFile.Create(FileReader(), CSoundFile::loadCompleteModule, modDoc); + sndFile.m_nChannels = 4; + sndFile.ChangeModTypeTo(MOD_TYPE_MPT); + + // Rearrange channels + sndFile.Patterns.ResizeArray(2); + sndFile.Patterns.Insert(0, 32); + sndFile.Patterns.Insert(1, 48); + sndFile.Patterns[1].SetName("Pattern"); + sndFile.Patterns[1].SetSignature(2, 4); + TempoSwing swing; + swing.resize(2); + sndFile.Patterns[1].SetTempoSwing(swing); + sndFile.Patterns[1].GetpModCommand(37, 0)->instr = 1; + sndFile.Patterns[1].GetpModCommand(37, 1)->instr = 2; + sndFile.Patterns[1].GetpModCommand(37, 2)->instr = 3; + sndFile.Patterns[1].GetpModCommand(37, 3)->instr = 4; + modDoc->ReArrangeChannels({ 3, 2, CHANNELINDEX_INVALID, 0 }); + modDoc->ReArrangeChannels({ 0, 1, 1, CHANNELINDEX_INVALID, 3 }); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetName(), "Pattern"); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetRowsPerBeat(), 2); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetRowsPerMeasure(), 4); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetTempoSwing(), swing); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 0)->instr, 4); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 1)->instr, 3); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 2)->instr, 3); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 3)->instr, 0); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 4)->instr, 1); + + // Rearrange samples + sndFile.m_nSamples = 2; + sndFile.GetSample(1).filename = "1"; + sndFile.m_szNames[1] = "1"; + sndFile.GetSample(2).filename = "2"; + sndFile.m_szNames[2] = "2"; + sndFile.GetSample(2).nLength = 16; + sndFile.GetSample(2).AllocateSample(); + modDoc->ReArrangeSamples({ 2, SAMPLEINDEX_INVALID, 1 }); + VERIFY_EQUAL_NONCONT(sndFile.GetSample(1).HasSampleData(), true); + VERIFY_EQUAL_NONCONT(sndFile.GetSample(1).filename, "2"); + VERIFY_EQUAL_NONCONT(sndFile.m_szNames[1], "2"); + VERIFY_EQUAL_NONCONT(sndFile.GetSample(2).filename, ""); + VERIFY_EQUAL_NONCONT(sndFile.m_szNames[2], ""); + VERIFY_EQUAL_NONCONT(sndFile.GetSample(3).filename, "1"); + VERIFY_EQUAL_NONCONT(sndFile.m_szNames[3], "1"); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 4)->instr, 3); + + // Convert / rearrange instruments + modDoc->ConvertSamplesToInstruments(); + modDoc->ReArrangeInstruments({ INSTRUMENTINDEX_INVALID, 2, 1, 3 }); + VERIFY_EQUAL_NONCONT(sndFile.Instruments[1]->name, ""); + VERIFY_EQUAL_NONCONT(sndFile.Instruments[2]->name, ""); + VERIFY_EQUAL_NONCONT(sndFile.Instruments[3]->name, "2"); + VERIFY_EQUAL_NONCONT(sndFile.Instruments[4]->name, "1"); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 4)->instr, 4); + modDoc->ConvertInstrumentsToSamples(); + VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 4)->instr, 3); + + modDoc->SetModified(); + VERIFY_EQUAL_NONCONT(modDoc->IsModified(), true); + VERIFY_EQUAL_NONCONT(modDoc->ModifiedSinceLastAutosave(), true); + VERIFY_EQUAL_NONCONT(modDoc->ModifiedSinceLastAutosave(), false); + + sndFile.Destroy(); + modDoc->OnCloseDocument(); +#endif +} + + +static void RunITCompressionTest(const std::vector<int8> &sampleData, FlagSet<ChannelFlags> smpFormat, bool it215) +{ + + ModSample smp; + smp.uFlags = smpFormat; + smp.pData.pSample = const_cast<int8 *>(sampleData.data()); + smp.nLength = mpt::saturate_cast<SmpLength>(sampleData.size() / smp.GetBytesPerSample()); + + std::string data; + + { + std::ostringstream f; + ITCompression compression(smp, it215, &f); + data = f.str(); + } + + { + FileReader file(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(data))); + + std::vector<int8> sampleDataNew(sampleData.size(), 0); + smp.pData.pSample = sampleDataNew.data(); + + ITDecompression decompression(file, smp, it215); + VERIFY_EQUAL_NONCONT(memcmp(sampleData.data(), sampleDataNew.data(), sampleData.size()), 0); + } +} + + +static MPT_NOINLINE void TestITCompression() +{ + // Test loading / saving of IT-compressed samples + const int sampleDataSize = 65536; + std::vector<int8> sampleData(sampleDataSize, 0); + std::srand(0); + for(int i = 0; i < sampleDataSize; i++) + { + sampleData[i] = mpt::random<int8>(*s_PRNG); + } + + // Run each compression test with IT215 compression and without. + for(int i = 0; i < 2; i++) + { + RunITCompressionTest(sampleData, ChannelFlags(0), i == 0); + RunITCompressionTest(sampleData, CHN_16BIT, i == 0); + RunITCompressionTest(sampleData, CHN_STEREO, i == 0); + RunITCompressionTest(sampleData, CHN_16BIT | CHN_STEREO, i == 0); + } +} + + + +#if 0 + +static bool RatioEqual(CTuningBase::RATIOTYPE a, CTuningBase::RATIOTYPE b) +{ + if(a == CTuningBase::RATIOTYPE(0) && b == CTuningBase::RATIOTYPE(0)) + { + return true; + } + if(a == CTuningBase::RATIOTYPE(0) || b == CTuningBase::RATIOTYPE(0)) + { + return false; + } + return (std::fabs(CTuningBase::RATIOTYPE(1) - (a/b)) < CTuningBase::RATIOTYPE(0.0001)); +} + + +static void CheckEqualTuningCollections(const CTuningCollection &a, const CTuningCollection &b) +{ + VERIFY_EQUAL(a.GetName(), b.GetName()); + VERIFY_EQUAL(a.GetNumTunings(), b.GetNumTunings()); + for(std::size_t tuning = 0; tuning < std::min(a.GetNumTunings(), b.GetNumTunings()); ++tuning) + { + VERIFY_EQUAL(a.GetTuning(tuning).GetName(), b.GetTuning(tuning).GetName()); + VERIFY_EQUAL(a.GetTuning(tuning).GetType(), b.GetTuning(tuning).GetType()); + VERIFY_EQUAL(a.GetTuning(tuning).GetGroupSize(), b.GetTuning(tuning).GetGroupSize()); + VERIFY_EQUAL(a.GetTuning(tuning).GetFineStepCount(), b.GetTuning(tuning).GetFineStepCount()); + VERIFY_EQUAL(RatioEqual(a.GetTuning(tuning).GetGroupRatio(), b.GetTuning(tuning).GetGroupRatio()), true); + VERIFY_EQUAL(a.GetTuning(tuning).GetValidityRange(), b.GetTuning(tuning).GetValidityRange()); + for(ModCommand::NOTE note = NOTE_MIN; note <= NOTE_MAX; ++note) + { + VERIFY_EQUAL(a.GetTuning(tuning).GetNoteName(note - NOTE_MIDDLEC), b.GetTuning(tuning).GetNoteName(note - NOTE_MIDDLEC)); + VERIFY_EQUAL(RatioEqual(a.GetTuning(tuning).GetRatio(note - NOTE_MIDDLEC), b.GetTuning(tuning).GetRatio(note - NOTE_MIDDLEC)), true); + } + } +} + +#endif + + + +static double Rand01() +{ + return mpt::random(*s_PRNG, 0.0, 1.0); +} + +template <class T> +T Rand(const T min, const T max) +{ + return mpt::saturate_round<T>(min + Rand01() * (max - min)); +} + +static void GenerateCommands(CPattern& pat, const double dProbPcs, const double dProbPc) +{ + const double dPcxProb = dProbPcs + dProbPc; + for(auto &m : pat) + { + const double rand = Rand01(); + if(rand < dPcxProb) + { + if(rand < dProbPcs) + m.note = NOTE_PCS; + else + m.note = NOTE_PC; + + m.instr = Rand<ModCommand::INSTR>(0, MAX_MIXPLUGINS); + m.SetValueVolCol(Rand<uint16>(0, ModCommand::maxColumnValue)); + m.SetValueEffectCol(Rand<uint16>(0, ModCommand::maxColumnValue)); + } + else + m.Clear(); + } +} + + +// Test PC note serialization +static MPT_NOINLINE void TestPCnoteSerialization() +{ + FileReader file; + std::unique_ptr<CSoundFile> pSndFile = std::make_unique<CSoundFile>(); + CSoundFile &sndFile = *pSndFile.get(); + sndFile.m_nType = MOD_TYPE_MPT; + sndFile.Patterns.DestroyPatterns(); + sndFile.m_nChannels = ModSpecs::mptm.channelsMax; + + sndFile.Patterns.Insert(0, ModSpecs::mptm.patternRowsMin); + sndFile.Patterns.Insert(1, 64); + GenerateCommands(sndFile.Patterns[1], 0.3, 0.3); + sndFile.Patterns.Insert(2, ModSpecs::mptm.patternRowsMax); + GenerateCommands(sndFile.Patterns[2], 0.5, 0.5); + + // Copy pattern data for comparison. + CPatternContainer patterns{ sndFile.Patterns }; + + std::stringstream mem; + WriteModPatterns(mem, sndFile.Patterns); + + VERIFY_EQUAL_NONCONT( mem.good(), true ); + + // Clear patterns. + sndFile.Patterns[0].ClearCommands(); + sndFile.Patterns[1].ClearCommands(); + sndFile.Patterns[2].ClearCommands(); + + // Read data back. + ReadModPatterns(mem, sndFile.Patterns); + + // Compare. + VERIFY_EQUAL_NONCONT( sndFile.Patterns[0].GetNumRows(), ModSpecs::mptm.patternRowsMin); + VERIFY_EQUAL_NONCONT( sndFile.Patterns[1].GetNumRows(), 64); + VERIFY_EQUAL_NONCONT( sndFile.Patterns[2].GetNumRows(), ModSpecs::mptm.patternRowsMax); + for(int i = 0; i < 3; i++) + { + VERIFY_EQUAL(sndFile.Patterns[i], patterns[i]); + } +} + + +static inline std::size_t strnlen(const char *str, std::size_t n) +{ +#if MPT_COMPILER_MSVC + return ::strnlen(str, n); +#else + if(n >= std::numeric_limits<std::size_t>::max()) + { + return std::strlen(str); + } + for(std::size_t i = 0; i < n; ++i) + { + if(str[i] == '\0') + { + return i; + } + } + return n; +#endif +} + + +// Test String I/O functionality +static MPT_NOINLINE void TestStringIO() +{ + char src0[4] = { '\0', 'X', ' ', 'X' }; // Weird empty buffer + char src1[4] = { 'X', ' ', '\0', 'X' }; // Weird buffer (hello Impulse Tracker) + char src2[4] = { 'X', 'Y', 'Z', ' ' }; // Full buffer, last character space + char src3[4] = { 'X', 'Y', 'Z', '!' }; // Full buffer, last character non-space + char src4[4] = { 'x', 'y', '\t', '\n' }; // Full buffer containing non-space whitespace + char dst1[6]; // Destination buffer, larger than source buffer + char dst2[3]; // Destination buffer, smaller than source buffer + +#define ReadTest(mode, dst, src, expectedResult) \ + std::memset(dst, 0x7f, sizeof(dst)); \ + mpt::String::WriteAutoBuf(dst) = mpt::String::ReadBuf(mpt::String:: mode , src); \ + VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, std::size(dst)), 0); /* Ensure that the strings are identical */ \ + for(size_t i = strlen(dst); i < std::size(dst); i++) \ + VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ + /**/ + +#define WriteTest(mode, dst, src, expectedResult) \ + std::memset(dst, 0x7f, sizeof(dst)); \ + mpt::String::WriteBuf(mpt::String:: mode , dst) = mpt::String::ReadAutoBuf(src); \ + VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, std::size(dst)), 0); /* Ensure that the strings are identical */ \ + for(size_t i = Test::strnlen(dst, std::size(dst)); i < std::size(dst); i++) \ + VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ + /**/ + + // Check reading of null-terminated string into larger buffer + ReadTest(nullTerminated, dst1, src0, ""); + ReadTest(nullTerminated, dst1, src1, "X "); + ReadTest(nullTerminated, dst1, src2, "XYZ"); + ReadTest(nullTerminated, dst1, src3, "XYZ"); + ReadTest(nullTerminated, dst1, src4, "xy\t"); + + // Check reading of string that should be null-terminated, but is maybe too long to still hold the null character. + ReadTest(maybeNullTerminated, dst1, src0, ""); + ReadTest(maybeNullTerminated, dst1, src1, "X "); + ReadTest(maybeNullTerminated, dst1, src2, "XYZ "); + ReadTest(maybeNullTerminated, dst1, src3, "XYZ!"); + ReadTest(maybeNullTerminated, dst1, src4, "xy\t\n"); + + // Check reading of space-padded strings with ignored last character + ReadTest(spacePaddedNull, dst1, src0, " X"); + ReadTest(spacePaddedNull, dst1, src1, "X"); + ReadTest(spacePaddedNull, dst1, src2, "XYZ"); + ReadTest(spacePaddedNull, dst1, src3, "XYZ"); + ReadTest(spacePaddedNull, dst1, src4, "xy\t"); + + // Check reading of space-padded strings + ReadTest(spacePadded, dst1, src0, " X X"); + ReadTest(spacePadded, dst1, src1, "X X"); + ReadTest(spacePadded, dst1, src2, "XYZ"); + ReadTest(spacePadded, dst1, src3, "XYZ!"); + ReadTest(spacePadded, dst1, src4, "xy\t\n"); + + /////////////////////////////// + + // Check reading of null-terminated string into smaller buffer + ReadTest(nullTerminated, dst2, src0, ""); + ReadTest(nullTerminated, dst2, src1, "X "); + ReadTest(nullTerminated, dst2, src2, "XY"); + ReadTest(nullTerminated, dst2, src3, "XY"); + ReadTest(nullTerminated, dst2, src4, "xy"); + + // Check reading of string that should be null-terminated, but is maybe too long to still hold the null character. + ReadTest(maybeNullTerminated, dst2, src0, ""); + ReadTest(maybeNullTerminated, dst2, src1, "X "); + ReadTest(maybeNullTerminated, dst2, src2, "XY"); + ReadTest(maybeNullTerminated, dst2, src3, "XY"); + ReadTest(maybeNullTerminated, dst2, src4, "xy"); + + // Check reading of space-padded strings with ignored last character + ReadTest(spacePaddedNull, dst2, src0, " X"); + ReadTest(spacePaddedNull, dst2, src1, "X"); + ReadTest(spacePaddedNull, dst2, src2, "XY"); + ReadTest(spacePaddedNull, dst2, src3, "XY"); + ReadTest(spacePaddedNull, dst2, src4, "xy"); + + // Check reading of space-padded strings + ReadTest(spacePadded, dst2, src0, " X"); + ReadTest(spacePadded, dst2, src1, "X "); + ReadTest(spacePadded, dst2, src2, "XY"); + ReadTest(spacePadded, dst2, src3, "XY"); + ReadTest(spacePadded, dst2, src4, "xy"); + + /////////////////////////////// + + // Check writing of null-terminated string into larger buffer + WriteTest(nullTerminated, dst1, src0, ""); + WriteTest(nullTerminated, dst1, src1, "X "); + WriteTest(nullTerminated, dst1, src2, "XYZ "); + WriteTest(nullTerminated, dst1, src3, "XYZ!"); + + // Check writing of string that should be null-terminated, but is maybe too long to still hold the null character. + WriteTest(maybeNullTerminated, dst1, src0, ""); + WriteTest(maybeNullTerminated, dst1, src1, "X "); + WriteTest(maybeNullTerminated, dst1, src2, "XYZ "); + WriteTest(maybeNullTerminated, dst1, src3, "XYZ!"); + + // Check writing of space-padded strings with last character set to null + WriteTest(spacePaddedNull, dst1, src0, " "); + WriteTest(spacePaddedNull, dst1, src1, "X "); + WriteTest(spacePaddedNull, dst1, src2, "XYZ "); + WriteTest(spacePaddedNull, dst1, src3, "XYZ! "); + + // Check writing of space-padded strings + WriteTest(spacePadded, dst1, src0, " "); + WriteTest(spacePadded, dst1, src1, "X "); + WriteTest(spacePadded, dst1, src2, "XYZ "); + WriteTest(spacePadded, dst1, src3, "XYZ! "); + + /////////////////////////////// + + // Check writing of null-terminated string into smaller buffer + WriteTest(nullTerminated, dst2, src0, ""); + WriteTest(nullTerminated, dst2, src1, "X "); + WriteTest(nullTerminated, dst2, src2, "XY"); + WriteTest(nullTerminated, dst2, src3, "XY"); + + // Check writing of string that should be null-terminated, but is maybe too long to still hold the null character. + WriteTest(maybeNullTerminated, dst2, src0, ""); + WriteTest(maybeNullTerminated, dst2, src1, "X "); + WriteTest(maybeNullTerminated, dst2, src2, "XYZ"); + WriteTest(maybeNullTerminated, dst2, src3, "XYZ"); + + // Check writing of space-padded strings with last character set to null + WriteTest(spacePaddedNull, dst2, src0, " "); + WriteTest(spacePaddedNull, dst2, src1, "X "); + WriteTest(spacePaddedNull, dst2, src2, "XY"); + WriteTest(spacePaddedNull, dst2, src3, "XY"); + + // Check writing of space-padded strings + WriteTest(spacePadded, dst2, src0, " "); + WriteTest(spacePadded, dst2, src1, "X "); + WriteTest(spacePadded, dst2, src2, "XYZ"); + WriteTest(spacePadded, dst2, src3, "XYZ"); + +#undef ReadTest +#undef WriteTest + + { + + std::string dststring; + std::string src0string = std::string(src0, std::size(src0)); + std::string src1string = std::string(src1, std::size(src1)); + std::string src2string = std::string(src2, std::size(src2)); + std::string src3string = std::string(src3, std::size(src3)); + +#define ReadTest(mode, dst, src, expectedResult) \ + dst = mpt::String::ReadBuf(mpt::String:: mode , src); \ + VERIFY_EQUAL_NONCONT(dst, expectedResult); /* Ensure that the strings are identical */ \ + /**/ + +#define WriteTest(mode, dst, src, expectedResult) \ + std::memset(dst, 0x7f, sizeof(dst)); \ + mpt::String::WriteBuf(mpt::String:: mode , dst) = src; \ + VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, std::size(dst)), 0); /* Ensure that the strings are identical */ \ + for(size_t i = Test::strnlen(dst, std::size(dst)); i < std::size(dst); i++) \ + VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ + /**/ + + // Check reading of null-terminated string into std::string + ReadTest(nullTerminated, dststring, src0, ""); + ReadTest(nullTerminated, dststring, src1, "X "); + ReadTest(nullTerminated, dststring, src2, "XYZ"); + ReadTest(nullTerminated, dststring, src3, "XYZ"); + ReadTest(nullTerminated, dststring, src4, "xy\t"); + + // Check reading of string that should be null-terminated, but is maybe too long to still hold the null character. + ReadTest(maybeNullTerminated, dststring, src0, ""); + ReadTest(maybeNullTerminated, dststring, src1, "X "); + ReadTest(maybeNullTerminated, dststring, src2, "XYZ "); + ReadTest(maybeNullTerminated, dststring, src3, "XYZ!"); + ReadTest(maybeNullTerminated, dststring, src4, "xy\t\n"); + + // Check reading of space-padded strings with ignored last character + ReadTest(spacePaddedNull, dststring, src0, " X"); + ReadTest(spacePaddedNull, dststring, src1, "X"); + ReadTest(spacePaddedNull, dststring, src2, "XYZ"); + ReadTest(spacePaddedNull, dststring, src3, "XYZ"); + ReadTest(spacePaddedNull, dststring, src4, "xy\t"); + + // Check reading of space-padded strings + ReadTest(spacePadded, dststring, src0, " X X"); + ReadTest(spacePadded, dststring, src1, "X X"); + ReadTest(spacePadded, dststring, src2, "XYZ"); + ReadTest(spacePadded, dststring, src3, "XYZ!"); + ReadTest(spacePadded, dststring, src4, "xy\t\n"); + + /////////////////////////////// + + // Check writing of null-terminated string into larger buffer + WriteTest(nullTerminated, dst1, src0string, ""); + WriteTest(nullTerminated, dst1, src1string, "X "); + WriteTest(nullTerminated, dst1, src2string, "XYZ "); + WriteTest(nullTerminated, dst1, src3string, "XYZ!"); + + // Check writing of string that should be null-terminated, but is maybe too long to still hold the null character. + WriteTest(maybeNullTerminated, dst1, src0string, ""); + WriteTest(maybeNullTerminated, dst1, src1string, "X "); + WriteTest(maybeNullTerminated, dst1, src2string, "XYZ "); + WriteTest(maybeNullTerminated, dst1, src3string, "XYZ!"); + + // Check writing of space-padded strings with last character set to null + WriteTest(spacePaddedNull, dst1, src0string, " "); + WriteTest(spacePaddedNull, dst1, src1string, "X "); + WriteTest(spacePaddedNull, dst1, src2string, "XYZ "); + WriteTest(spacePaddedNull, dst1, src3string, "XYZ! "); + + // Check writing of space-padded strings + WriteTest(spacePadded, dst1, src0string, " "); + WriteTest(spacePadded, dst1, src1string, "X "); + WriteTest(spacePadded, dst1, src2string, "XYZ "); + WriteTest(spacePadded, dst1, src3string, "XYZ! "); + + /////////////////////////////// + + // Check writing of null-terminated string into smaller buffer + WriteTest(nullTerminated, dst2, src0string, ""); + WriteTest(nullTerminated, dst2, src1string, "X "); + WriteTest(nullTerminated, dst2, src2string, "XY"); + WriteTest(nullTerminated, dst2, src3string, "XY"); + + // Check writing of string that should be null-terminated, but is maybe too long to still hold the null character. + WriteTest(maybeNullTerminated, dst2, src0string, ""); + WriteTest(maybeNullTerminated, dst2, src1string, "X "); + WriteTest(maybeNullTerminated, dst2, src2string, "XYZ"); + WriteTest(maybeNullTerminated, dst2, src3string, "XYZ"); + + // Check writing of space-padded strings with last character set to null + WriteTest(spacePaddedNull, dst2, src0string, " "); + WriteTest(spacePaddedNull, dst2, src1string, "X "); + WriteTest(spacePaddedNull, dst2, src2string, "XY"); + WriteTest(spacePaddedNull, dst2, src3string, "XY"); + + // Check writing of space-padded strings + WriteTest(spacePadded, dst2, src0string, " "); + WriteTest(spacePadded, dst2, src1string, "X "); + WriteTest(spacePadded, dst2, src2string, "XYZ"); + WriteTest(spacePadded, dst2, src3string, "XYZ"); + + /////////////////////////////// + +#undef ReadTest +#undef WriteTest + + } + + { + + char s0[4] = {'\0', 'X', ' ', 'X' }; + char s2[4] = { 'X', ' ','\0', 'X' }; + char s4[4] = { 'X', 'Y', 'Z', ' ' }; + + char d2[2] = {'\0','\0'}; + char d3[3] = {'\0','\0','\0'}; + char d4[4] = {'\0','\0','\0','\0'}; + char d5[5] = {'\0','\0','\0','\0','\0'}; + + #define CopyTest(dst, src, expectedResult) \ + std::memset(dst, 0x7f, sizeof(dst)); \ + mpt::String::WriteAutoBuf(dst) = mpt::String::ReadAutoBuf(src); \ + VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, std::size(dst)), 0); /* Ensure that the strings are identical */ \ + for(size_t i = strlen(dst); i < std::size(dst); i++) \ + VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ + /**/ + + CopyTest(d2, s0, ""); + CopyTest(d2, s2, "X"); + CopyTest(d2, s4, "X"); + CopyTest(d3, s0, ""); + CopyTest(d3, s2, "X "); + CopyTest(d3, s4, "XY"); + CopyTest(d4, s0, ""); + CopyTest(d4, s2, "X "); + CopyTest(d4, s4, "XYZ"); + CopyTest(d5, s0, ""); + CopyTest(d5, s2, "X "); + CopyTest(d5, s4, "XYZ "); + + #undef CopyTest + + #define CopyTestN(dst, src, len, expectedResult) \ + std::memset(dst, 0x7f, sizeof(dst)); \ + mpt::String::WriteAutoBuf(dst) = mpt::String::ReadAutoBuf(src, std::min(std::size(src), static_cast<std::size_t>(len))); \ + VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, std::size(dst)), 0); /* Ensure that the strings are identical */ \ + for(size_t i = strlen(dst); i < std::size(dst); i++) \ + VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ + /**/ + + CopyTestN(d2, s0, 1, ""); + CopyTestN(d2, s2, 1, "X"); + CopyTestN(d2, s4, 1, "X"); + CopyTestN(d3, s0, 1, ""); + CopyTestN(d3, s2, 1, "X"); + CopyTestN(d3, s4, 1, "X"); + CopyTestN(d4, s0, 1, ""); + CopyTestN(d4, s2, 1, "X"); + CopyTestN(d4, s4, 1, "X"); + CopyTestN(d5, s0, 1, ""); + CopyTestN(d5, s2, 1, "X"); + CopyTestN(d5, s4, 1, "X"); + + CopyTestN(d2, s0, 2, ""); + CopyTestN(d2, s2, 2, "X"); + CopyTestN(d2, s4, 2, "X"); + CopyTestN(d3, s0, 2, ""); + CopyTestN(d3, s2, 2, "X "); + CopyTestN(d3, s4, 2, "XY"); + CopyTestN(d4, s0, 2, ""); + CopyTestN(d4, s2, 2, "X "); + CopyTestN(d4, s4, 2, "XY"); + CopyTestN(d5, s0, 2, ""); + CopyTestN(d5, s2, 2, "X "); + CopyTestN(d5, s4, 2, "XY"); + + CopyTestN(d2, s0, 3, ""); + CopyTestN(d2, s2, 3, "X"); + CopyTestN(d2, s4, 3, "X"); + CopyTestN(d3, s0, 3, ""); + CopyTestN(d3, s2, 3, "X "); + CopyTestN(d3, s4, 3, "XY"); + CopyTestN(d4, s0, 3, ""); + CopyTestN(d4, s2, 3, "X "); + CopyTestN(d4, s4, 3, "XYZ"); + CopyTestN(d5, s0, 3, ""); + CopyTestN(d5, s2, 3, "X "); + CopyTestN(d5, s4, 3, "XYZ"); + + CopyTestN(d2, s0, 4, ""); + CopyTestN(d2, s2, 4, "X"); + CopyTestN(d2, s4, 4, "X"); + CopyTestN(d3, s0, 4, ""); + CopyTestN(d3, s2, 4, "X "); + CopyTestN(d3, s4, 4, "XY"); + CopyTestN(d4, s0, 4, ""); + CopyTestN(d4, s2, 4, "X "); + CopyTestN(d4, s4, 4, "XYZ"); + CopyTestN(d5, s0, 4, ""); + CopyTestN(d5, s2, 4, "X "); + CopyTestN(d5, s4, 4, "XYZ "); + + CopyTestN(d2, s0, 5, ""); + CopyTestN(d2, s2, 5, "X"); + CopyTestN(d2, s4, 5, "X"); + CopyTestN(d3, s0, 5, ""); + CopyTestN(d3, s2, 5, "X "); + CopyTestN(d3, s4, 5, "XY"); + CopyTestN(d4, s0, 5, ""); + CopyTestN(d4, s2, 5, "X "); + CopyTestN(d4, s4, 5, "XYZ"); + CopyTestN(d5, s0, 5, ""); + CopyTestN(d5, s2, 5, "X "); + CopyTestN(d5, s4, 5, "XYZ "); + + #undef CopyTest + + } + +} + + +static MPT_NOINLINE void TestSampleConversion() +{ + std::vector<uint8> sourceBufContainer(65536 * 4); + std::vector<uint8> targetBufContainer(65536 * 6); + + uint8 *sourceBuf = &(sourceBufContainer[0]); + void *targetBuf = &(targetBufContainer[0]); + + // Signed 8-Bit Integer PCM + // Unsigned 8-Bit Integer PCM + // Delta 8-Bit Integer PCM + { + uint8 *source8 = sourceBuf; + for(size_t i = 0; i < 256; i++) + { + source8[i] = static_cast<uint8>(i); + } + + int8 *signed8 = static_cast<int8 *>(targetBuf); + uint8 *unsigned8 = static_cast<uint8 *>(targetBuf) + 256; + int8 *delta8 = static_cast<int8 *>(targetBuf) + 512; + int8 delta = 0; + CopySample<SC::DecodeInt8>(signed8, 256, 1, mpt::byte_cast<const std::byte *>(source8), 256, 1); + CopySample<SC::DecodeUint8>(reinterpret_cast<int8 *>(unsigned8), 256, 1, mpt::byte_cast<const std::byte *>(source8), 256, 1); + CopySample<SC::DecodeInt8Delta>(delta8, 256, 1, mpt::byte_cast<const std::byte *>(source8), 256, 1); + + for(size_t i = 0; i < 256; i++) + { + delta += static_cast<int8>(i); + VERIFY_EQUAL_QUIET_NONCONT(signed8[i], static_cast<int8>(i)); + VERIFY_EQUAL_QUIET_NONCONT(unsigned8[i], static_cast<uint8>(i + 0x80u)); + VERIFY_EQUAL_QUIET_NONCONT(delta8[i], static_cast<int8>(delta)); + } + } + + // Signed 16-Bit Integer PCM + // Unsigned 16-Bit Integer PCM + // Delta 16-Bit Integer PCM + { + // Little Endian + + uint8 *source16 = sourceBuf; + for(size_t i = 0; i < 65536; i++) + { + source16[i * 2 + 0] = static_cast<uint8>(i & 0xFF); + source16[i * 2 + 1] = static_cast<uint8>(i >> 8); + } + + int16 *signed16 = static_cast<int16 *>(targetBuf); + uint16 *unsigned16 = static_cast<uint16 *>(targetBuf) + 65536; + int16 *delta16 = static_cast<int16 *>(targetBuf) + 65536 * 2; + int16 delta = 0; + CopySample<SC::DecodeInt16<0, littleEndian16> >(signed16, 65536, 1, mpt::byte_cast<const std::byte *>(source16), 65536 * 2, 1); + CopySample<SC::DecodeInt16<0x8000u, littleEndian16> >(reinterpret_cast<int16*>(unsigned16), 65536, 1, mpt::byte_cast<const std::byte *>(source16), 65536 * 2, 1); + CopySample<SC::DecodeInt16Delta<littleEndian16> >(delta16, 65536, 1, mpt::byte_cast<const std::byte *>(source16), 65536 * 2, 1); + + for(size_t i = 0; i < 65536; i++) + { + delta += static_cast<int16>(i); + VERIFY_EQUAL_QUIET_NONCONT(signed16[i], static_cast<int16>(i)); + VERIFY_EQUAL_QUIET_NONCONT(unsigned16[i], static_cast<uint16>(i + 0x8000u)); + VERIFY_EQUAL_QUIET_NONCONT(delta16[i], static_cast<int16>(delta)); + } + + // Big Endian + + for(size_t i = 0; i < 65536; i++) + { + source16[i * 2 + 0] = static_cast<uint8>(i >> 8); + source16[i * 2 + 1] = static_cast<uint8>(i & 0xFF); + } + + CopySample<SC::DecodeInt16<0, bigEndian16> >(signed16, 65536, 1, mpt::byte_cast<const std::byte *>(source16), 65536 * 2, 1); + CopySample<SC::DecodeInt16<0x8000u, bigEndian16> >(reinterpret_cast<int16*>(unsigned16), 65536, 1, mpt::byte_cast<const std::byte *>(source16), 65536 * 2, 1); + CopySample<SC::DecodeInt16Delta<bigEndian16> >(delta16, 65536, 1, mpt::byte_cast<const std::byte *>(source16), 65536 * 2, 1); + + delta = 0; + for(size_t i = 0; i < 65536; i++) + { + delta += static_cast<int16>(i); + VERIFY_EQUAL_QUIET_NONCONT(signed16[i], static_cast<int16>(i)); + VERIFY_EQUAL_QUIET_NONCONT(unsigned16[i], static_cast<uint16>(i + 0x8000u)); + VERIFY_EQUAL_QUIET_NONCONT(delta16[i], static_cast<int16>(delta)); + } + + } + + // Signed 24-Bit Integer PCM + { + uint8 *source24 = sourceBuf; + for(size_t i = 0; i < 65536; i++) + { + source24[i * 3 + 0] = 0; + source24[i * 3 + 1] = static_cast<uint8>(i & 0xFF); + source24[i * 3 + 2] = static_cast<uint8>(i >> 8); + } + + int16 *truncated16 = static_cast<int16 *>(targetBuf); + ModSample sample; + sample.Initialize(); + sample.nLength = 65536; + sample.uFlags.set(CHN_16BIT); + sample.pData.pSample = (static_cast<int16 *>(targetBuf) + 65536); + CopyAndNormalizeSample<SC::NormalizationChain<SC::Convert<int16, int32>, SC::DecodeInt24<0, littleEndian24> > >(sample, mpt::byte_cast<const std::byte *>(source24), 3*65536); + CopySample<SC::ConversionChain<SC::ConvertShift<int16, int32, 16>, SC::DecodeInt24<0, littleEndian24> > >(truncated16, 65536, 1, mpt::byte_cast<const std::byte *>(source24), 65536 * 3, 1); + + for(size_t i = 0; i < 65536; i++) + { + VERIFY_EQUAL_QUIET_NONCONT(sample.sample16()[i], static_cast<int16>(i)); + VERIFY_EQUAL_QUIET_NONCONT(truncated16[i], static_cast<int16>(i)); + } + } + + // Float 32-Bit + { + uint8 *source32 = sourceBuf; + for(size_t i = 0; i < 65536; i++) + { + IEEE754binary32BE floatbits = IEEE754binary32BE((static_cast<float>(i) / 65536.0f) - 0.5f); + source32[i * 4 + 0] = mpt::byte_cast<uint8>(floatbits.GetByte(0)); + source32[i * 4 + 1] = mpt::byte_cast<uint8>(floatbits.GetByte(1)); + source32[i * 4 + 2] = mpt::byte_cast<uint8>(floatbits.GetByte(2)); + source32[i * 4 + 3] = mpt::byte_cast<uint8>(floatbits.GetByte(3)); + } + + int16 *truncated16 = static_cast<int16 *>(targetBuf); + ModSample sample; + sample.Initialize(); + sample.nLength = 65536; + sample.uFlags.set(CHN_16BIT); + sample.pData.pSample = static_cast<int16 *>(targetBuf) + 65536; + CopyAndNormalizeSample<SC::NormalizationChain<SC::Convert<int16, float32>, SC::DecodeFloat32<bigEndian32> > >(sample, mpt::byte_cast<const std::byte *>(source32), 4*65536); + CopySample<SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeFloat32<bigEndian32> > >(truncated16, 65536, 1, mpt::byte_cast<const std::byte *>(source32), 65536 * 4, 1); + + for(size_t i = 0; i < 65536; i++) + { + VERIFY_EQUAL_QUIET_NONCONT(sample.sample16()[i], static_cast<int16>(i - 0x8000u)); + VERIFY_EQUAL_QUIET_NONCONT(std::abs(truncated16[i] - static_cast<int16>((i - 0x8000u) / 2)) <= 1, true); + } + } + + // ALaw + { + for(unsigned int i = 0; i < 256; ++i) + { + std::byte in = mpt::byte_cast<std::byte>(static_cast<uint8>(i)); + std::byte out = SC::EncodeALaw{}(SC::DecodeInt16ALaw{}(&in)); + VERIFY_EQUAL_NONCONT(in, out); + } + VERIFY_EQUAL_NONCONT(SC::EncodeALaw{}(-32768), SC::EncodeALaw{}(-32256)); + VERIFY_EQUAL_NONCONT(SC::EncodeALaw{}(-32767), SC::EncodeALaw{}(-32256)); + VERIFY_EQUAL_NONCONT(SC::EncodeALaw{}(-1), SC::EncodeALaw{}(-8)); + VERIFY_EQUAL_NONCONT(SC::EncodeALaw{}(0), SC::EncodeALaw{}(8)); + VERIFY_EQUAL_NONCONT(SC::EncodeALaw{}(1), SC::EncodeALaw{}(8)); + VERIFY_EQUAL_NONCONT(SC::EncodeALaw{}(32766), SC::EncodeALaw{}(32256)); + VERIFY_EQUAL_NONCONT(SC::EncodeALaw{}(32767), SC::EncodeALaw{}(32256)); +#if 0 + // compare with reference impl + for (int i = -32768; i <= 32767; ++i) + { + VERIFY_EQUAL_NONCONT(SC::EncodeALaw{}(i), mpt::byte_cast<std::byte>(alaw_encode(i))); + } +#endif + } + + // uLaw + { + for(unsigned int i = 0; i < 256; ++i) + { + std::byte in = mpt::byte_cast<std::byte>(static_cast<uint8>(i)); + std::byte out = SC::EncodeuLaw{}(SC::DecodeInt16uLaw{}(&in)); + VERIFY_EQUAL_NONCONT(in, out); + } +#if 0 + // compare with reference impl + /* + bool lastMatch = true; + */ + for(int i = -32768; i <= 32767; ++i) + { + /* + uint8 mine = mpt::byte_cast<uint8>(SC::EncodeuLaw{}(i)); + uint8 ref = ulaw_encode(i); + if(lastMatch) + { + if(mine == ref) + { + VERIFY_EQUAL_NONCONT(mine, ref); + lastMatch = true; + } else + { + VERIFY_EQUAL_NONCONT(std::abs(static_cast<int>(mine) - static_cast<int>(ref)) <= 1, true); + lastMatch = false; + } + } else + { + VERIFY_EQUAL_NONCONT(mine, ref); + lastMatch = true; + } + */ + //MPT_LOG_GLOBAL(LogNotification, "test", MPT_UFORMAT("{} {} {}")(i, ulaw_encode(i), mpt::byte_cast<uint8>(SC::EncodeuLaw{}(i)))); + VERIFY_EQUAL_NONCONT(SC::EncodeuLaw{}(i), mpt::byte_cast<std::byte>(ulaw_encode(i))); + } +#endif + } + + // Range checks + { + int8 oneSample = 1; + char *signed8 = reinterpret_cast<char *>(targetBuf); + memset(signed8, 0, 4); + CopySample<SC::DecodeInt8>(reinterpret_cast<int8*>(targetBuf), 4, 1, reinterpret_cast<const std::byte*>(&oneSample), sizeof(oneSample), 1); + VERIFY_EQUAL_NONCONT(signed8[0], 1); + VERIFY_EQUAL_NONCONT(signed8[1], 0); + VERIFY_EQUAL_NONCONT(signed8[2], 0); + VERIFY_EQUAL_NONCONT(signed8[3], 0); + } + + // Dither + { + std::vector<MixSampleInt> buffer(64); + DithersOpenMPT dithers(mpt::global_random_device(), 2 /* DitherModPlug */ , 2); + for(std::size_t i = 0; i < 64; ++i) + { + std::visit( + [&](auto &dither) + { + buffer[i] = dither.template process<16>(0, buffer[i]); + }, + dithers.Variant() + ); + } + std::vector<MixSampleInt> expected = { + 727, + -557, + -552, + -727, + 439, + 405, + 703, + -337, + 235, + -776, + -458, + 905, + -110, + 158, + 374, + -362, + 283, + 306, + 710, + 304, + -608, + 536, + -501, + -593, + -349, + 812, + 916, + 53, + -953, + 881, + -236, + -20, + -623, + -895, + -302, + -415, + 899, + -948, + -766, + -186, + -390, + -169, + 253, + -622, + -769, + -1001, + 1019, + 787, + -239, + 718, + -423, + 988, + -91, + 763, + -933, + -510, + 484, + 794, + -340, + 552, + 866, + -608, + 35, + 395}; + for(std::size_t i = 0; i < 64; ++i) + { + VERIFY_EQUAL_QUIET_NONCONT(buffer[i], expected[i]); + } + } +} + + +} // namespace Test + +OPENMPT_NAMESPACE_END + +#else //Case: ENABLE_TESTS is not defined. + +OPENMPT_NAMESPACE_BEGIN + +namespace Test { + +void DoTests() +{ + return; +} + +} // namespace Test + +OPENMPT_NAMESPACE_END + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/test/test.flac b/Src/external_dependencies/openmpt-trunk/test/test.flac Binary files differnew file mode 100644 index 00000000..d2e59570 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/test.flac diff --git a/Src/external_dependencies/openmpt-trunk/test/test.h b/Src/external_dependencies/openmpt-trunk/test/test.h new file mode 100644 index 00000000..4c87487f --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/test.h @@ -0,0 +1,24 @@ +/* + * test.h + * ------ + * Purpose: Unit tests for OpenMPT. + * Notes : We need FAAAAAAAR more unit tests! + * Authors: Olivier Lapicque + * OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "openmpt/all/BuildSettings.hpp" + +OPENMPT_NAMESPACE_BEGIN + +namespace Test { + +void DoTests(); + +} // namespace Test + +OPENMPT_NAMESPACE_END diff --git a/Src/external_dependencies/openmpt-trunk/test/test.mod b/Src/external_dependencies/openmpt-trunk/test/test.mod Binary files differnew file mode 100644 index 00000000..de9d0b2c --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/test.mod diff --git a/Src/external_dependencies/openmpt-trunk/test/test.mptm b/Src/external_dependencies/openmpt-trunk/test/test.mptm Binary files differnew file mode 100644 index 00000000..5b5a2784 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/test.mptm diff --git a/Src/external_dependencies/openmpt-trunk/test/test.s3m b/Src/external_dependencies/openmpt-trunk/test/test.s3m Binary files differnew file mode 100644 index 00000000..5013ee83 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/test.s3m diff --git a/Src/external_dependencies/openmpt-trunk/test/test.xm b/Src/external_dependencies/openmpt-trunk/test/test.xm Binary files differnew file mode 100644 index 00000000..3f2024d9 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/test/test.xm |