//------------------------------------------------------------------------ // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Examples // Filename : public.sdk/source/vst/utility/testing.h // Created by : Steinberg, 04/2021 // Description : Utility classes for custom testing in the vst validator // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/test/itest.h" #include #include #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** How to use the validator to run your own tests? \ingroup TestClass It is possible to run your own tests when the validator checks your plug-in. First you have to register a test factory in your plugin factory: \code{.cpp} #include "public.sdk/source/vst/utility/testing.h" BEGIN_FACTORY_DEF(... DEF_CLASS2 (Your Plugin Processor) DEF_CLASS2 (Your Plugin Controller) DEF_CLASS2 (INLINE_UID_FROM_FUID (getTestFactoryUID ()), PClassInfo::kManyInstances, kTestClass, "Test Factory", 0, "", "", "", createTestFactoryInstance) END_FACTORY \endcode Second: write your tests: \code{.cpp} #include "public.sdk/source/main/moduleinit.h" #include "public.sdk/source/vst/utility/testing.h" static ModuleInitializer InitMyTests ([] () { registerTest ("MyTests", STR ("two plus two is four"), [] (ITestResult* testResult) { auto result = 2 + 2; if (result == 4) return true; testResult->addErrorMessage (STR ("Unexpected universe change where 2+2 != 4.")); return false; }); }); \endcode If you need access to your audio effect or edit controller you can write your tests as done in the adelay example: \code{.cpp} #include "public.sdk/source/main/moduleinit.h" #include "public.sdk/source/vst/testsuite/vsttestsuite.h" #include "public.sdk/source/vst/utility/testing.h" static ModuleInitializer InitMyTests ([] () { registerTest ("MyTests", STR ("check one two three"), [] (FUnknown* context, ITestResult* testResult) { if (auto plugProvider = U::cast (context)) { auto controller = plugProvider->getController (); auto testController = U::cast (controller); if (!controller) { testResult->addErrorMessage (String ("Unknown IEditController")); return false; } bool result = testController->doTest (); plugProvider->releasePlugIn (nullptr, controller); return (result); } return false; }); }); \endcode After that recompile and if the validator does not run automatically after every build, start the validator manually and let it check your plug-in. */ //------------------------------------------------------------------------ /** create a Test Factory instance */ FUnknown* createTestFactoryInstance (void*); /** the test factory class ID */ static const DECLARE_UID (TestFactoryUID, 0x70AA33A3, 0x1AE74B24, 0xB726F784, 0xB706C080); /** get the test factory class ID */ const FUID& getTestFactoryUID (); /** simple test function */ using TestFunc = std::function; /** register a simple test function */ void registerTest (FIDString name, const tchar* desc, const TestFunc& func); /** register a simple test function */ void registerTest (FIDString name, const tchar* desc, TestFunc&& func); /** test function with context pointer */ using TestFuncWithContext = std::function; /** register a test function with context pointer */ void registerTest (FIDString name, const tchar* desc, const TestFuncWithContext& func); /** register a test function with context pointer */ void registerTest (FIDString name, const tchar* desc, TestFuncWithContext&& func); /** register a custom test, the test object will be owned by the implementation */ void registerTest (FIDString name, ITest* test); //------------------------------------------------------------------------ namespace Test { //------------------------------------------------------------------------ template ::value>::type* = nullptr> inline constexpr bool equal (const T& lhs, const T& rhs) noexcept { return std::abs (lhs - rhs) <= std::numeric_limits::epsilon (); } //------------------------------------------------------------------------ template ::value>::type* = nullptr> inline constexpr bool equal (const T& lhs, const T& rhs) noexcept { return lhs == rhs; } //------------------------------------------------------------------------ template inline constexpr bool notEqual (const T& lhs, const T& rhs) noexcept { return equal (lhs, rhs) == false; } //------------------------------------------------------------------------ template inline constexpr bool maxDiff (const T& lhs, const T& rhs, const T& maxDiff) noexcept { return std::abs (lhs - rhs) <= maxDiff; } #ifndef SMTG_DISABLE_VST_TEST_MACROS #ifndef SMTG_MAKE_STRING_PRIVATE_DONT_USE #define SMTG_MAKE_STRING_PRIVATE_DONT_USE(x) #x #define SMTG_MAKE_STRING(x) SMTG_MAKE_STRING_PRIVATE_DONT_USE (x) #endif // SMTG_MAKE_STRING_PRIVATE_DONT_USE #define EXPECT(condition) \ { \ if (!(condition)) \ { \ testResult->addErrorMessage (STR (__FILE__ ":" SMTG_MAKE_STRING ( \ __LINE__) ": error: " SMTG_MAKE_STRING (condition))); \ return false; \ } \ } #define EXPECT_TRUE(condition) EXPECT (condition) #define EXPECT_FALSE(condition) EXPECT (!condition) #define EXPECT_EQ(var1, var2) EXPECT ((var1 == var2)) #define EXPECT_NE(var1, var2) EXPECT ((var1 != var2)) #endif // SMTG_DISABLE_VST_TEST_MACROS //------------------------------------------------------------------------ } // Test } // Vst } // Steinberg