Initial release

This commit is contained in:
civ
2026-08-16 18:24:52 +07:00
commit 876886a39a
13244 changed files with 2353959 additions and 0 deletions
@@ -0,0 +1,159 @@
set(validator_sources
${SDK_ROOT}/public.sdk/source/common/memorystream.cpp
${SDK_ROOT}/public.sdk/source/main/moduleinit.cpp
${SDK_ROOT}/public.sdk/source/main/moduleinit.h
${SDK_ROOT}/public.sdk/source/vst/moduleinfo/moduleinfoparser.cpp
${SDK_ROOT}/public.sdk/source/vst/moduleinfo/moduleinfoparser.h
${SDK_ROOT}/public.sdk/source/vst/hosting/test/connectionproxytest.cpp
${SDK_ROOT}/public.sdk/source/vst/hosting/test/eventlisttest.cpp
${SDK_ROOT}/public.sdk/source/vst/hosting/test/hostclassestest.cpp
${SDK_ROOT}/public.sdk/source/vst/hosting/test/parameterchangestest.cpp
${SDK_ROOT}/public.sdk/source/vst/hosting/test/pluginterfacesupporttest.cpp
${SDK_ROOT}/public.sdk/source/vst/hosting/test/processdatatest.cpp
${SDK_ROOT}/public.sdk/source/vst/hosting/plugprovider.cpp
${SDK_ROOT}/public.sdk/source/vst/hosting/plugprovider.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/bus/busactivation.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/bus/busactivation.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/bus/busconsistency.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/bus/busconsistency.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/bus/businvalidindex.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/bus/businvalidindex.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/bus/checkaudiobusarrangement.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/bus/checkaudiobusarrangement.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/bus/scanbusses.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/bus/scanbusses.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/bus/sidechainarrangement.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/bus/sidechainarrangement.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/general/editorclasses.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/general/editorclasses.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/general/midilearn.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/general/midilearn.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/general/midimapping.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/general/midimapping.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/general/plugcompat.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/general/plugcompat.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/general/scanparameters.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/general/scanparameters.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/general/suspendresume.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/general/suspendresume.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/general/terminit.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/general/terminit.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/noteexpression/keyswitch.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/noteexpression/keyswitch.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/noteexpression/noteexpression.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/noteexpression/noteexpression.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/automation.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/automation.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/process.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/process.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/processcontextrequirements.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/processcontextrequirements.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/processformat.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/processformat.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/processinputoverwriting.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/processinputoverwriting.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/processtail.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/processtail.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/processthreaded.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/processthreaded.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/silenceflags.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/silenceflags.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/silenceprocessing.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/silenceprocessing.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/speakerarrangement.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/speakerarrangement.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/variableblocksize.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/processing/variableblocksize.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/state/bypasspersistence.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/state/bypasspersistence.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/state/invalidstatetransition.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/state/invalidstatetransition.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/state/repeatidenticalstatetransition.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/state/repeatidenticalstatetransition.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/state/validstatetransition.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/state/validstatetransition.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/testbase.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/testbase.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/unit/checkunitstructure.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/unit/checkunitstructure.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/unit/scanprograms.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/unit/scanprograms.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/unit/scanunits.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/unit/scanunits.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/vststructsizecheck.h
${SDK_ROOT}/public.sdk/source/vst/testsuite/vsttestsuite.cpp
${SDK_ROOT}/public.sdk/source/vst/testsuite/vsttestsuite.h
${SDK_ROOT}/public.sdk/source/vst/utility/testing.cpp
${SDK_ROOT}/public.sdk/source/vst/utility/testing.h
source/main.cpp
source/usediids.cpp
source/validator.cpp
source/validator.h
source/testsuite.h
)
if(SMTG_MAC)
set(validator_sources
${validator_sources}
${SDK_ROOT}/public.sdk/source/vst/hosting/module_mac.mm
)
set_source_files_properties(
"${SDK_ROOT}/public.sdk/source/vst/hosting/module_mac.mm" PROPERTIES
COMPILE_FLAGS "-fobjc-arc"
)
elseif(SMTG_LINUX)
set(validator_sources
${validator_sources}
${SDK_ROOT}/public.sdk/source/vst/hosting/module_linux.cpp
)
elseif(SMTG_WIN)
set(validator_sources
${validator_sources}
${SDK_ROOT}/public.sdk/source/vst/hosting/module_win32.cpp
)
endif(SMTG_MAC)
set(target validator)
add_executable(${target} ${validator_sources})
if(SDK_IDE_HOSTING_EXAMPLES_FOLDER)
set_target_properties(${target}
PROPERTIES
${SDK_IDE_HOSTING_EXAMPLES_FOLDER}
)
endif(SDK_IDE_HOSTING_EXAMPLES_FOLDER)
target_compile_features(${target}
PUBLIC
cxx_std_17
)
target_link_libraries(${target}
PRIVATE
sdk_hosting
)
smtg_target_codesign(${target})
smtg_target_setup_universal_binary(${target})
if(APPLE AND NOT XCODE)
find_library(COCOA_FRAMEWORK Cocoa)
target_link_libraries(${target}
PRIVATE
${COCOA_FRAMEWORK}
)
endif(APPLE AND NOT XCODE)
#####################################################
if(SMTG_RUN_VST_VALIDATOR)
####################
# Workaround for Xcode 15 not allowing to add adhoc_codesign linker setting
####################
if(SMTG_MAC AND XCODE_VERSION VERSION_GREATER_EQUAL 15)
add_custom_command(
TARGET ${target} POST_BUILD
COMMENT "[SMTG] Adhoc codesign workaround for Xcode 15"
COMMAND codesign --force --verbose --sign -
"$<TARGET_FILE:${target}>"
)
endif()
add_custom_command(TARGET ${target} POST_BUILD
COMMAND $<TARGET_FILE:${target}> "-selftest"
)
endif(SMTG_RUN_VST_VALIDATOR)
@@ -0,0 +1,18 @@
# Validator
## Introduction
Validator is a small command line host application which can be used to check your plug-in for **VST 3** conformity.
> See also: [Online Documentation](https://steinbergmedia.github.io/vst3_dev_portal/pages/What+is+the+VST+3+SDK/Validator.html).
## Getting Started
This application is part of the VST 3 SDK package. It is created with the VST 3 SDK root project.
> See the top-level README of the VST 3 SDK: https://github.com/steinbergmedia/vst3sdk.git
## Getting Help
* Read through the SDK documentation on the **[VST 3 Developer Portal](https://steinbergmedia.github.io/vst3_dev_portal/pages/index.html)**
* Ask some real people in the official **[VST 3 Developer Forum](https://forums.steinberg.net/c/developer/103)**
@@ -0,0 +1,86 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Validator
// Filename : public.sdk/samples/vst-hosting/validator/source/main.cpp
// Created by : Steinberg, 04/2005
// Description : main entry point
//
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#include "validator.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
void* moduleHandle = nullptr;
extern bool InitModule ();
extern bool DeinitModule ();
//------------------------------------------------------------------------
int run (int argc, char* argv[])
{
InitModule ();
auto result = Steinberg::Vst::Validator (argc, argv).run ();
DeinitModule ();
return result;
}
//------------------------------------------------------------------------
#if SMTG_OS_WINDOWS
//------------------------------------------------------------------------
using Utf8String = std::string;
//------------------------------------------------------------------------
using Utf8Args = std::vector<Utf8String>;
Utf8Args toUtf8Args (int argc, wchar_t* wargv[])
{
Utf8Args utf8Args;
for (int i = 0; i < argc; i++)
{
auto str = reinterpret_cast<const Steinberg::Vst::TChar*> (wargv[i]);
utf8Args.push_back (Steinberg::Vst::StringConvert::convert (str));
}
return utf8Args;
}
//------------------------------------------------------------------------
using Utf8ArgPtrs = std::vector<char*>;
Utf8ArgPtrs toUtf8ArgPtrs (Utf8Args& utf8Args)
{
Utf8ArgPtrs utf8ArgPtrs;
for (auto& el : utf8Args)
{
utf8ArgPtrs.push_back (el.data ());
}
return utf8ArgPtrs;
}
//------------------------------------------------------------------------
int wmain (int argc, wchar_t* wargv[])
{
Utf8Args utf8Args = toUtf8Args (argc, wargv);
Utf8ArgPtrs utf8ArgPtrs = toUtf8ArgPtrs (utf8Args);
char** argv = &(utf8ArgPtrs.at (0));
return run (argc, argv);
}
#else
//------------------------------------------------------------------------
int main (int argc, char* argv[])
{
return run (argc, argv);
}
#endif // SMTG_OS_WINDOWS
@@ -0,0 +1,109 @@
//-----------------------------------------------------------------------------
// Flags : clang-format SMTGSequencer
// Project : VST SDK
//
// Category : Validator
// Filename : public.sdk/samples/vst-hosting/validator/source/testsuite.h
// Created by : Steinberg, 05/2019
// Description : VST 3 Plug-in Validator test suite class
//
//-----------------------------------------------------------------------------
// 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 "base/source/fobject.h"
#include "pluginterfaces/test/itest.h"
#include <vector>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
// TestSuite
//------------------------------------------------------------------------
class TestSuite : public ITestSuite, public FObject
{
public:
TestSuite (FIDString _name) : name (_name) {}
tresult PLUGIN_API addTest (FIDString _name, ITest* test) SMTG_OVERRIDE
{
tests.push_back (IPtr<Test> (NEW Test (_name, test), false));
return kResultTrue;
}
tresult PLUGIN_API addTestSuite (FIDString _name, ITestSuite* testSuite) SMTG_OVERRIDE
{
testSuites.push_back (std::make_pair (_name, testSuite));
return kResultTrue;
}
tresult PLUGIN_API setEnvironment (ITest* /*environment*/) SMTG_OVERRIDE
{
return kNotImplemented;
}
int32 getTestCount () const { return static_cast<int32> (tests.size ()); }
tresult getTest (int32 index, ITest*& _test, std::string& _name) const
{
if (auto test = tests.at (index))
{
_test = test->test;
_name = test->name;
return kResultTrue;
}
return kResultFalse;
}
tresult getTestSuite (int32 index, ITestSuite*& testSuite, std::string& _name) const
{
if (index < 0 || index >= int32 (testSuites.size ()))
return kInvalidArgument;
const auto& ts = testSuites[index];
_name = ts.first;
testSuite = ts.second;
return kResultTrue;
}
ITestSuite* getTestSuite (FIDString _name) const
{
for (const auto& testSuite : testSuites)
{
if (testSuite.first == _name)
return testSuite.second;
}
return nullptr;
}
const std::string& getName () const { return name; }
OBJ_METHODS (TestSuite, FObject)
REFCOUNT_METHODS (FObject)
DEF_INTERFACES_1 (ITestSuite, FObject)
protected:
class Test : public FObject
{
public:
Test (FIDString _name, ITest* _test) : name (_name), test (_test) {}
std::string name;
IPtr<ITest> test;
};
std::string name;
std::vector<IPtr<Test>> tests;
using TestSuitePair = std::pair<std::string, IPtr<ITestSuite>>;
using TestSuiteVector = std::vector<TestSuitePair>;
TestSuiteVector testSuites;
};
//------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,29 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Validator
// Filename : public.sdk/samples/vst-hosting/validator/source/usediids.cpp
// Created by : Steinberg 09.2008
// Description : Interface symbols file
//
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
//#define INIT_CLASS_IID
// This macro definition modifies the behavior of DECLARE_CLASS_IID (funknown.h)
// and produces the actual symbols for all interface identifiers.
// It must be defined before including the interface headers and
// in only one source file!
//------------------------------------------------------------------------
//#define INIT_CLASS_IID
#include "pluginterfaces/base/ipluginbase.h"
#include "pluginterfaces/test/itest.h"
namespace Steinberg {
DEF_CLASS_IID (ITestResult)
}
@@ -0,0 +1,837 @@
//-----------------------------------------------------------------------------
// Flags : clang-format SMTGSequencer
// Project : VST SDK
//
// Category : Validator
// Filename : public.sdk/samples/vst-hosting/validator/source/validator.cpp
// Created by : Steinberg, 04/2005
// Description : VST 3 Plug-in Validator class
//
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#include "validator.h"
#include "testsuite.h"
#include "public.sdk/source/vst/hosting/plugprovider.h"
#include "public.sdk/source/vst/testsuite/general/plugcompat.h"
#include "public.sdk/source/vst/testsuite/vststructsizecheck.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include "public.sdk/source/vst/utility/testing.h"
#include "base/source/fcommandline.h"
#include "pluginterfaces/base/iplugincompatibility.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivstcomponent.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivsthostapplication.h"
#include "pluginterfaces/vst/ivstunits.h"
#include "pluginterfaces/base/funknownimpl.h"
#include "pluginterfaces/gui/iplugview.h"
#if SMTG_OS_WINDOWS
#include <conio.h>
#include <windows.h>
#endif
#include <array>
#include <cstdio>
#include <iostream>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
namespace {
constexpr auto SEPARATOR = "-------------------------------------------------------------\n";
constexpr auto VALIDATOR_INFO = kVstVersionString
" Plug-in Validator\n"
"Program by Steinberg (Built on " __DATE__ ")\n";
//------------------------------------------------------------------------
bool filterClassCategory (FIDString category, FIDString classCategory)
{
return strcmp (category, classCategory) == 0;
}
//------------------------------------------------------------------------
void printAllInstalledPlugins (std::ostream* os)
{
if (!os)
return;
*os << "Searching installed Plug-ins...\n";
os->flush ();
auto paths = VST3::Hosting::Module::getModulePaths ();
if (paths.empty ())
{
*os << "No Plug-ins found.\n";
return;
}
for (const auto& path : paths)
{
*os << path << "\n";
}
}
//------------------------------------------------------------------------
void printAllSnapshots (std::ostream* os)
{
if (!os)
return;
*os << "Searching installed Plug-ins...\n";
auto paths = VST3::Hosting::Module::getModulePaths ();
if (paths.empty ())
{
*os << "No Plug-ins found.\n";
return;
}
for (const auto& path : paths)
{
auto snapshots = VST3::Hosting::Module::getSnapshots (path);
if (snapshots.empty ())
{
*os << "No snapshots in " << path << "\n";
continue;
}
for (auto& snapshot : snapshots)
{
for (auto& desc : snapshot.images)
{
*os << "Snapshot : " << desc.path << "[" << desc.scaleFactor << "x]\n";
}
}
}
}
//------------------------------------------------------------------------
void printFactoryInfo (const VST3::Hosting::PluginFactory& factory, std::ostream* os)
{
if (os)
{
*os << "* Scanning classes...\n\n";
auto factoryInfo = factory.info ();
*os << " Factory Info:\n\tvendor = " << factoryInfo.vendor ()
<< "\n\turl = " << factoryInfo.url () << "\n\temail = " << factoryInfo.email ()
<< "\n\n";
//---print all included plug-ins---------------
uint32 i = 0;
for (auto& classInfo : factory.classInfos ())
{
*os << " Class Info " << i << ":\n\tname = " << classInfo.name ()
<< "\n\tcategory = " << classInfo.category ()
<< "\n\tsubCategories = " << classInfo.subCategoriesString ()
<< "\n\tversion = " << classInfo.version ()
<< "\n\tsdkVersion = " << classInfo.sdkVersion ()
<< "\n\tcid = " << classInfo.ID ().toString () << "\n\n";
++i;
}
}
}
//------------------------------------------------------------------------
void checkModuleSnapshots (const VST3::Hosting::Module::Ptr& module, std::ostream* infoStream)
{
if (infoStream)
*infoStream << "* Checking snapshots...\n\n";
auto snapshots = VST3::Hosting::Module::getSnapshots (module->getPath ());
if (snapshots.empty ())
{
if (infoStream)
*infoStream << "Info: No snapshots in Bundle.\n\n";
}
else
{
for (auto& classInfo : module->getFactory ().classInfos ())
{
if (!filterClassCategory (kVstAudioEffectClass, classInfo.category ().data ()))
continue;
bool found = false;
for (auto& snapshot : snapshots)
{
if (snapshot.uid == classInfo.ID ())
{
found = true;
if (infoStream)
{
*infoStream << "Found snapshots for '" << classInfo.name () << "'\n";
for (auto& image : snapshot.images)
*infoStream << " - " << image.path << " [" << image.scaleFactor
<< "x]\n";
*infoStream << "\n";
}
break;
}
}
if (!found)
{
if (infoStream)
{
*infoStream << "Info: No snapshot for '" << classInfo.name ()
<< "' in Bundle.\n\n";
}
}
}
}
}
#if SMTG_OS_LINUX
//------------------------------------------------------------------------
struct DummyRunLoop : U::ImplementsNonDestroyable<U::Directly<Steinberg::Linux::IRunLoop>>
{
using IEventHandler = Steinberg::Linux::IEventHandler;
using ITimerHandler = Steinberg::Linux::ITimerHandler;
using FileDescriptor = Steinberg::Linux::FileDescriptor;
using TimerInterval = Steinberg::Linux::TimerInterval;
static Steinberg::Linux::IRunLoop& instance ()
{
static DummyRunLoop drl;
return drl;
}
tresult PLUGIN_API registerEventHandler (IEventHandler* handler, FileDescriptor fd) override
{
return kNotImplemented;
}
tresult PLUGIN_API unregisterEventHandler (IEventHandler* handler) override
{
return kNotImplemented;
}
tresult PLUGIN_API registerTimer (ITimerHandler* handler, TimerInterval milliseconds) override
{
return kNotImplemented;
}
tresult PLUGIN_API unregisterTimer (ITimerHandler* handler) override { return kNotImplemented; }
};
#endif
//------------------------------------------------------------------------
//-- Options
constexpr auto optHelp = "help";
constexpr auto optVersion = "version";
constexpr auto optLocalInstance = "l";
constexpr auto optSuiteName = "suite";
constexpr auto optExtensiveTests = "e";
constexpr auto optQuiet = "q";
constexpr auto optTestComponentPath = "test-component";
constexpr auto optListInstalledPlugIns = "list";
constexpr auto optListPlugInSnapshots = "snapshots";
constexpr auto optCID = "cid";
constexpr auto optSelftest = "selftest";
//------------------------------------------------------------------------
} // anonymous
//------------------------------------------------------------------------
// Validator
//------------------------------------------------------------------------
Validator::Validator (int argc, char* argv[]) : argc (argc), argv (argv)
{
infoStream = &std::cout;
errorStream = &std::cout;
errorStream->setf (std::ios::unitbuf);
mPlugInterfaceSupport = owned (NEW PlugInterfaceSupport);
PluginContextFactory::instance ().setPluginContext (this->unknownCast ());
TestingPluginContext::set (this->unknownCast ());
}
//------------------------------------------------------------------------
Validator::~Validator ()
{
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API Validator::queryInterface (const char* _iid, void** obj)
{
QUERY_INTERFACE (_iid, obj, IHostApplication::iid, IHostApplication)
QUERY_INTERFACE (_iid, obj, ITestResult::iid, ITestResult)
if (mPlugInterfaceSupport && mPlugInterfaceSupport->queryInterface (_iid, obj) == kResultTrue)
return ::Steinberg::kResultOk;
return FObject::queryInterface (_iid, obj);
}
//------------------------------------------------------------------------
void PLUGIN_API Validator::addErrorMessage (const tchar* msg)
{
if (errorStream)
{
auto str = StringConvert::convert (msg);
if (addErrorWarningTextToOutput)
*errorStream << "ERROR: " << str << "\n";
else
*errorStream << str << "\n";
}
}
//------------------------------------------------------------------------
void PLUGIN_API Validator::addMessage (const tchar* msg)
{
if (infoStream)
{
auto str = StringConvert::convert (msg);
if (addErrorWarningTextToOutput)
*infoStream << "Info: " << str << "\n";
else
*infoStream << str << "\n";
}
}
//------------------------------------------------------------------------
tresult PLUGIN_API Validator::getName (String128 name)
{
StringConvert::convert ("vstvalidator", name, 127);
return kResultTrue;
}
//------------------------------------------------------------------------
tresult PLUGIN_API Validator::createInstance (TUID cid, TUID iid, void** obj)
{
FUID classID = FUID::fromTUID (cid);
FUID interfaceID = FUID::fromTUID (iid);
if (classID == IMessage::iid && interfaceID == IMessage::iid)
{
*obj = new HostMessage;
return kResultTrue;
}
else if (classID == IAttributeList::iid && interfaceID == IAttributeList::iid)
{
if (auto al = HostAttributeList::make ())
{
*obj = al.take ();
return kResultTrue;
}
return kOutOfMemory;
}
*obj = nullptr;
return kResultFalse;
}
//------------------------------------------------------------------------
int Validator::run ()
{
// defaults
bool useGlobalInstance = true;
bool useExtensiveTests = false;
std::string testSuiteName;
// parse command line
CommandLine::Descriptions desc;
CommandLine::VariablesMap valueMap;
CommandLine::FilesVector files;
using Description = CommandLine::Description;
desc.addOptions (
kVstVersionString " Plug-in Validator",
{{optHelp, "Print help", Description::kBool},
{optVersion, "Print version", Description::kBool},
{optLocalInstance, "Use local instance per test", Description::kBool},
{optSuiteName, "[name] Only run a special test suite", Description::kString},
{optExtensiveTests, "Run extensive tests [may take a long time]", Description::kBool},
{optQuiet, "Only print errors", Description::kBool},
{optCID, "Only test processor with specified class ID", Description::kString},
{optTestComponentPath,
"[path] Path to an additional component which includes custom tests",
Description::kString},
{optListInstalledPlugIns, "Show all installed Plug-Ins", Description::kBool},
{optSelftest, "Run a selftest", Description::kBool},
{optListPlugInSnapshots, "List snapshots from all installed Plug-Ins",
Description::kBool}});
CommandLine::parse (argc, argv, desc, valueMap, &files);
if (valueMap.count (optVersion))
{
std::cout << VALIDATOR_INFO;
return 0;
}
else if (valueMap.count (optListInstalledPlugIns))
{
printAllInstalledPlugins (infoStream);
return 0;
}
else if (valueMap.count (optListPlugInSnapshots))
{
printAllSnapshots (infoStream);
return 0;
}
else if (valueMap.count (optSelftest))
{
addErrorWarningTextToOutput = false;
auto testFactoryInstance = owned (createTestFactoryInstance (nullptr));
if (auto testFactory = U::cast<ITestFactory> (testFactoryInstance))
{
std::cout << "Running validator selftest:\n\n";
IPtr<TestSuite> testSuite = owned (new TestSuite (""));
if (testFactory->createTests (nullptr, testSuite) == kResultTrue)
runTestSuite (testSuite, nullptr);
std::cout << "Executed " << (numTestsFailed + numTestsPassed) << " Tests.\n";
std::cout << numTestsFailed << " failed test(s).\n";
return numTestsFailed == 0 ? 0 : 1;
}
return 1;
}
else if (valueMap.hasError () || valueMap.count (optHelp) || files.empty ())
{
std::cout << "\n" << desc << "\n";
std::cout << "Usage: vstvalidator [options] vst3module\n\n";
return 1;
}
if (valueMap.count (optLocalInstance))
useGlobalInstance = false;
if (valueMap.count (optExtensiveTests))
useExtensiveTests = true;
if (valueMap.count (optQuiet))
infoStream = nullptr;
if (valueMap.count (optSuiteName))
testSuiteName = valueMap[optSuiteName];
VST3::Optional<VST3::UID> testProcessor;
if (valueMap.count (optCID))
testProcessor = VST3::UID::fromString (valueMap[optCID]);
std::string customTestComponentPath;
if (valueMap.count (optTestComponentPath))
customTestComponentPath = valueMap[optTestComponentPath];
auto globalFailure = false;
for (auto& path : files)
{
#if SMTG_OS_WINDOWS
// TODO: Impl
#else
// if path is not absolute, create one
if (path[0] != '/')
{
auto realPath = realpath (path.data (), NULL);
if (realPath)
{
path.assign (realPath);
free (realPath);
}
}
if (customTestComponentPath.empty () == false && customTestComponentPath[0] != '/')
{
auto realPath = realpath (customTestComponentPath.data (), NULL);
if (realPath)
{
customTestComponentPath.assign (realPath);
free (realPath);
}
}
#endif
//---load VST module-----------------
if (infoStream)
*infoStream << "* Loading module...\n\n\t" << path << "\n\n";
std::string error;
auto module = Module::create (path, error);
if (!module)
{
*errorStream << "Invalid Module!\n";
if (!error.empty ())
*errorStream << error << "\n";
return -1;
}
#if SMTG_OS_LINUX
module->getFactory ().setHostContext (&DummyRunLoop::instance ());
#endif
testModule (module, {useGlobalInstance, useExtensiveTests, customTestComponentPath,
testSuiteName, std::move (testProcessor)});
if (numTestsFailed > 0)
globalFailure = true;
}
return globalFailure ? -1 : 0;
}
//------------------------------------------------------------------------
void Validator::testModule (Module::Ptr module, const ModuleTestConfig& config)
{
using TestFactoryMap = std::map<std::string, IPtr<ITestFactory>>;
using PlugProviderVector = std::vector<IPtr<PlugProvider>>;
numTestsFailed = numTestsPassed = 0;
//---check bundle structure--------------
std::string errorStr;
if (infoStream)
*infoStream << "* Check valid bundle structure...\n";
if (Module::validateBundleStructure (module->getPath (), errorStr) == false)
{
++numTestsFailed;
if (errorStream)
*errorStream << "Invalid bundle structure: " << errorStr << "\n";
}
if (infoStream)
*infoStream << "\n";
auto factory = module->getFactory ();
printFactoryInfo (module->getFactory (), infoStream);
//---check for snapshots-----------------
checkModuleSnapshots (module, infoStream);
Module::Ptr testModule;
PlugProviderVector plugProviders;
TestFactoryMap testFactories;
IPtr<IPluginCompatibility> plugCompatibility;
auto testSuite = owned (new TestSuite ("Tests"));
//---create tests---------------
if (infoStream)
*infoStream << "* Creating tests...\n\n";
for (auto& classInfo : factory.classInfos ())
{
if (filterClassCategory (kVstAudioEffectClass, classInfo.category ().data ()))
{
if (!config.testProcessor || *config.testProcessor == classInfo.ID ())
{
auto plugProvider =
owned (new PlugProvider (factory, classInfo, config.useGlobalInstance));
if (plugProvider)
{
auto tests = createTests (plugProvider, classInfo.name ().data (),
config.useExtensiveTests);
testSuite->addTestSuite (classInfo.name ().data (), tests);
plugProviders.emplace_back (plugProvider);
}
}
}
else if (filterClassCategory (kTestClass, classInfo.category ().data ()))
{ // gather test factories supplied by the plug-in
if (auto testFactory = factory.createInstance<ITestFactory> (classInfo.ID ()))
{
testFactories.insert (std::make_pair (classInfo.name ().data (), testFactory));
}
}
else if (filterClassCategory (kPluginCompatibilityClass, classInfo.category ().data ()))
{
if (plugCompatibility)
{
if (errorStream)
{
*errorStream
<< "Error: Factory contains multiple Plugin Compatibility classes.\n";
}
++numTestsFailed;
}
plugCompatibility = factory.createInstance<IPluginCompatibility> (classInfo.ID ());
if (!plugCompatibility)
{
if (errorStream)
{
*errorStream << "Error: Failed creating IPluginCompatibility instance.\n";
}
}
}
}
// now check testModule if supplied
if (config.customTestComponentPath.empty () == false)
{
std::string error;
testModule = Module::create (config.customTestComponentPath, error);
if (testModule)
{
const auto& _factory = testModule->getFactory ();
for (const auto& classInfo : _factory.classInfos ())
{
if (filterClassCategory (kTestClass, classInfo.category ().data ()))
{ // gather test factories supplied by the plug-in
if (auto testFactory = _factory.createInstance<ITestFactory> (classInfo.ID ()))
{
testFactories.insert (
std::make_pair (classInfo.name ().data (), testFactory));
}
}
}
}
else if (errorStream)
{
*errorStream << "Could not create custom test component ["
<< config.customTestComponentPath << "]\n";
}
}
if (infoStream && !testFactories.empty ())
*infoStream << "* Creating Plug-in supplied tests...\n\n";
// create plug-in supplied tests
for (const auto& item : testFactories)
{
for (const auto& plugProvider : plugProviders)
{
IPtr<TestSuite> plugTestSuite = owned (new TestSuite (item.first.data ()));
if (item.second->createTests (plugProvider->unknownCast (), plugTestSuite) ==
kResultTrue)
{
testSuite->addTestSuite (plugTestSuite->getName ().data (), plugTestSuite);
}
}
}
testFactories.clear ();
//---run tests---------------------------
if (infoStream)
*infoStream << "* Running tests...\n\n";
runTestSuite (testSuite,
config.testSuiteName.empty () ? nullptr : config.testSuiteName.data ());
if (plugCompatibility)
{
if (!checkPluginCompatibility (module, plugCompatibility, errorStream))
++numTestsFailed;
}
if (infoStream)
{
*infoStream << SEPARATOR;
*infoStream << "Result: " << numTestsPassed << " tests passed, " << numTestsFailed
<< " tests failed\n";
*infoStream << SEPARATOR;
}
}
//------------------------------------------------------------------------
namespace { // anonymous
//------------------------------------------------------------------------
template <typename T, typename... Args>
void createTest (ITestSuite* parent, ITestPlugProvider* plugProvider, Args&&... arguments)
{
auto test = owned (new T (plugProvider, std::forward<Args> (arguments)...));
parent->addTest (test->getName (), test);
}
//------------------------------------------------------------------------
void createSpeakerArrangementTest (ITestSuite* parent, ITestPlugProvider* plugProvider,
SymbolicSampleSizes sampleSize, SpeakerArrangement inSpArr,
SpeakerArrangement outSpArr)
{
createTest<SpeakerArrangementTest> (parent, plugProvider, sampleSize, inSpArr, outSpArr);
}
//------------------------------------------------------------------------
void createPrecisionTests (ITestSuite* parent, ITestPlugProvider* plugProvider,
SymbolicSampleSizes sampleSize, bool extensive)
{
createTest<ProcessTest> (parent, plugProvider, sampleSize);
createTest<ProcessThreadTest> (parent, plugProvider, sampleSize);
createTest<SilenceFlagsTest> (parent, plugProvider, sampleSize);
createTest<SilenceProcessingTest> (parent, plugProvider, sampleSize);
createTest<FlushParamTest> (parent, plugProvider, sampleSize);
createTest<FlushParamTest2> (parent, plugProvider, sampleSize);
createTest<FlushParamTest3> (parent, plugProvider, sampleSize);
createTest<VariableBlockSizeTest> (parent, plugProvider, sampleSize);
createTest<ProcessFormatTest> (parent, plugProvider, sampleSize);
createTest<BypassPersistenceTest> (parent, plugProvider, sampleSize);
if (extensive)
{
constexpr std::array<SpeakerArrangement, 15> saArray = {
{SpeakerArr::kMono, SpeakerArr::kStereo, SpeakerArr::kStereoSurround,
SpeakerArr::kStereoCenter, SpeakerArr::kStereoSide, SpeakerArr::kStereoCLfe,
SpeakerArr::k30Cine, SpeakerArr::k30Music, SpeakerArr::k31Cine, SpeakerArr::k31Music,
SpeakerArr::k40Cine, SpeakerArr::k40Music, SpeakerArr::k41Cine, SpeakerArr::k41Music,
SpeakerArr::k50}};
for (auto inArr : saArray)
{
for (auto outArr : saArray)
{
createSpeakerArrangementTest (parent, plugProvider, sampleSize, inArr, outArr);
}
}
constexpr std::array<int32, 3> autoRates = {{100, 50, 1}};
constexpr std::array<int32, 4> numParams = {{1, 2, 10, -1}};
for (auto inp : numParams)
{
for (auto iar : autoRates)
{
createTest<AutomationTest> (parent, plugProvider, sampleSize, iar, inp, false);
createTest<AutomationTest> (parent, plugProvider, sampleSize, iar, inp, true);
}
}
}
else
{
createSpeakerArrangementTest (parent, plugProvider, sampleSize, SpeakerArr::kStereo,
SpeakerArr::kStereo);
createSpeakerArrangementTest (parent, plugProvider, sampleSize, SpeakerArr::kMono,
SpeakerArr::kMono);
createTest<AutomationTest> (parent, plugProvider, sampleSize, 100, 1, false);
createTest<AutomationTest> (parent, plugProvider, sampleSize, 100, 1, true);
}
}
//------------------------------------------------------------------------
} // anonymous
//------------------------------------------------------------------------
IPtr<TestSuite> Validator::createTests (ITestPlugProvider* plugProvider,
const ConstString& plugName, bool extensive)
{
IPtr<TestSuite> plugTestSuite = owned (new TestSuite (plugName));
IPtr<TestSuite> generalTests = owned (new TestSuite ("General Tests"));
// todo: add tests here!
createTest<EditorClassesTest> (generalTests, plugProvider);
createTest<ScanBussesTest> (generalTests, plugProvider);
createTest<ScanParametersTest> (generalTests, plugProvider);
createTest<MidiMappingTest> (generalTests, plugProvider);
createTest<MidiLearnTest> (generalTests, plugProvider);
createTest<UnitInfoTest> (generalTests, plugProvider);
createTest<ProgramInfoTest> (generalTests, plugProvider);
createTest<TerminateInitializeTest> (generalTests, plugProvider);
createTest<UnitStructureTest> (generalTests, plugProvider);
createTest<ValidStateTransitionTest> (generalTests, plugProvider, kSample32);
createTest<ValidStateTransitionTest> (generalTests, plugProvider, kSample64);
// createTest<InvalidStateTransitionTest> (generalTests, plugProvider);
// createTest<RepeatIdenticalStateTransitionTest> (generalTests, plugProvider);
createTest<BusConsistencyTest> (generalTests, plugProvider);
// createTest<BusInvalidIndexTest> (generalTests, plugProvider);
createTest<BusActivationTest> (generalTests, plugProvider);
createTest<CheckAudioBusArrangementTest> (generalTests, plugProvider);
createTest<SideChainArrangementTest> (generalTests, plugProvider);
createTest<SuspendResumeTest> (generalTests, plugProvider, kSample32);
createTest<NoteExpressionTest> (generalTests, plugProvider);
createTest<KeyswitchTest> (generalTests, plugProvider);
createTest<ProcessContextRequirementsTest> (generalTests, plugProvider);
plugTestSuite->addTestSuite (generalTests->getName ().data (), generalTests);
IPtr<TestSuite> singlePrecisionTests =
owned (new TestSuite ("Single Precision (32 bit) Tests"));
createPrecisionTests (singlePrecisionTests, plugProvider, kSample32, extensive);
plugTestSuite->addTestSuite (singlePrecisionTests->getName ().data (), singlePrecisionTests);
IPtr<TestSuite> doublePrecisionTests =
owned (new TestSuite ("Double Precision (64 bit) Tests"));
createPrecisionTests (doublePrecisionTests, plugProvider, kSample64, extensive);
plugTestSuite->addTestSuite (doublePrecisionTests->getName ().data (), doublePrecisionTests);
return plugTestSuite;
}
//------------------------------------------------------------------------
void Validator::addTest (ITestSuite* _testSuite, TestBase* testItem)
{
_testSuite->addTest (testItem->getName (), testItem);
testItem->release ();
}
//------------------------------------------------------------------------
void Validator::runTestSuite (TestSuite* suite, FIDString nameFilter)
{
std::string name;
if (nameFilter == nullptr || suite->getName () == nameFilter)
{
nameFilter = nullptr; // make sure if suiteName is the namefilter that sub suite will run
ITest* testItem = nullptr;
// first run all tests in the suite
for (int32 i = 0; i < suite->getTestCount (); i++)
{
if (suite->getTest (i, testItem, name) == kResultTrue)
{
if (infoStream)
{
*infoStream << "[" << name;
if (auto desc = testItem->getDescription ())
{
auto descStr = StringConvert::convert (desc);
if (!descStr.empty ())
*infoStream << ": " << descStr;
}
*infoStream << "]\n";
}
if (testItem->setup ())
{
bool success = testItem->run (this);
if (success)
{
if (infoStream)
*infoStream << "[Succeeded]\n";
numTestsPassed++;
}
else
{
if (infoStream)
*infoStream << "[XXXXXXX Failed]\n";
if (errorStream && errorStream != infoStream)
{
*errorStream << "Test [" << name << "] ";
*errorStream << "Failed\n";
}
numTestsFailed++;
}
if (!testItem->teardown ())
{
if (infoStream)
*infoStream << "Failed to teardown test!\n";
if (errorStream && errorStream != infoStream)
{
*errorStream << "[" << name << "] ";
*errorStream << "Failed to teardown test!\n";
}
}
}
else
{
testItem->teardown ();
if (infoStream)
*infoStream << "Failed to setup test!\n";
if (errorStream && errorStream != infoStream)
{
*errorStream << "[" << name << "] ";
*errorStream << "Failed to setup test!\n";
}
}
if (infoStream)
*infoStream << "\n";
}
}
}
// next run sub suites
int32 subTestSuiteIndex = 0;
ITestSuite* subSuite = nullptr;
while (suite->getTestSuite (subTestSuiteIndex++, subSuite, name) == kResultTrue)
{
auto* ts = FCast<TestSuite> (subSuite);
if (ts)
{
if (infoStream)
{
*infoStream << SEPARATOR;
*infoStream << "TestSuite : " << ts->getName ().data () << "\n";
*infoStream << SEPARATOR << "\n";
}
runTestSuite (ts, nameFilter);
}
}
}
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,111 @@
//-----------------------------------------------------------------------------
// Flags : clang-format SMTGSequencer
// Project : VST SDK
//
// Category : Validator
// Filename : public.sdk/samples/vst-hosting/validator/source/validator.h
// Created by : Steinberg, 04/2005
// Description : VST 3 Plug-in Validator class
//
//-----------------------------------------------------------------------------
// 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 "public.sdk/source/vst/hosting/hostclasses.h"
#include "public.sdk/source/vst/hosting/module.h"
#include "public.sdk/source/vst/hosting/pluginterfacesupport.h"
#include "public.sdk/source/vst/testsuite/vsttestsuite.h"
#include "base/source/fstring.h"
#include "pluginterfaces/base/ipluginbase.h"
#include "pluginterfaces/test/itest.h"
#include <iosfwd>
namespace Steinberg {
namespace Vst {
class IComponent;
class IEditController;
class VstModule;
class TestBase;
class TestSuite;
//------------------------------------------------------------------------
/** Main Class of Validator.
* \defgroup Validator VST3 Validator
*/
class Validator : public FObject, public ITestResult, public IHostApplication
{
public:
//------------------------------------------------------------------------
Validator (int argc, char* argv[]);
~Validator () override;
int run ();
//------------------------------------------------------------------------
OBJ_METHODS (Validator, FObject)
REFCOUNT_METHODS (FObject)
tresult PLUGIN_API queryInterface (const char* _iid, void** obj) override;
//------------------------------------------------------------------------
protected:
using Module = VST3::Hosting::Module;
// ITestResult
void PLUGIN_API addErrorMessage (const tchar* msg) override;
void PLUGIN_API addMessage (const tchar* msg) override;
// IHostApplication
tresult PLUGIN_API getName (String128 name) override;
tresult PLUGIN_API createInstance (TUID cid, TUID iid, void** obj) override;
IPtr<TestSuite> createTests (ITestPlugProvider* plugProvider, const ConstString& plugName,
bool extensive);
void addTest (ITestSuite* testSuite, TestBase* test);
void runTestSuite (TestSuite* suite, FIDString nameFilter = nullptr);
struct ModuleTestConfig
{
ModuleTestConfig (bool useGlobalInstance, bool useExtensiveTests,
std::string& customTestComponentPath, std::string& testSuiteName,
VST3::Optional<VST3::UID>&& testProcessor)
: useGlobalInstance (useGlobalInstance)
, useExtensiveTests (useExtensiveTests)
, customTestComponentPath (customTestComponentPath)
, testSuiteName (testSuiteName)
, testProcessor (std::move (testProcessor))
{
}
bool useGlobalInstance {true};
bool useExtensiveTests {false};
std::string customTestComponentPath;
std::string testSuiteName;
VST3::Optional<VST3::UID> testProcessor;
};
void testModule (Module::Ptr module, const ModuleTestConfig& config);
int argc;
char** argv;
IPtr<PlugInterfaceSupport> mPlugInterfaceSupport;
int32 numTestsFailed {0};
int32 numTestsPassed {0};
bool addErrorWarningTextToOutput {true};
std::ostream* infoStream {nullptr};
std::ostream* errorStream {nullptr};
};
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg