Merge branch 'master' into refac/memory
This commit is contained in:
@@ -1,33 +1,34 @@
|
||||
INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
INCLUDE_DIRECTORIES("${CMAKE_CURRENT_BINARY_DIR}")
|
||||
INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/include")
|
||||
INCLUDE_DIRECTORIES("${CMAKE_BINARY_DIR}")
|
||||
INCLUDE_DIRECTORIES("${CMAKE_BINARY_DIR}/src")
|
||||
include(CTest)
|
||||
|
||||
SET(CMAKE_CXX_STANDARD 14)
|
||||
|
||||
SET(CMAKE_AUTOMOC ON)
|
||||
|
||||
# FIXME: remove this once we export include directories for LMMS
|
||||
IF(LMMS_BUILD_APPLE)
|
||||
INCLUDE_DIRECTORIES("/usr/local/include")
|
||||
ENDIF()
|
||||
|
||||
ADD_EXECUTABLE(tests
|
||||
EXCLUDE_FROM_ALL
|
||||
main.cpp
|
||||
QTestSuite
|
||||
$<TARGET_OBJECTS:lmmsobjs>
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
set(LMMS_TESTS
|
||||
src/core/ArrayVectorTest.cpp
|
||||
src/core/AutomatableModelTest.cpp
|
||||
src/core/MathTest.cpp
|
||||
src/core/ProjectVersionTest.cpp
|
||||
src/core/RelativePathsTest.cpp
|
||||
src/core/MemoryPoolTest.cpp
|
||||
|
||||
src/tracks/AutomationTrackTest.cpp
|
||||
)
|
||||
TARGET_COMPILE_DEFINITIONS(tests
|
||||
PRIVATE $<TARGET_PROPERTY:lmmsobjs,INTERFACE_COMPILE_DEFINITIONS>
|
||||
)
|
||||
TARGET_LINK_LIBRARIES(tests ${QT_LIBRARIES} ${QT_QTTEST_LIBRARY})
|
||||
TARGET_LINK_LIBRARIES(tests ${LMMS_REQUIRED_LIBS})
|
||||
|
||||
foreach(LMMS_TEST_SRC IN LISTS LMMS_TESTS)
|
||||
# TODO CMake 3.20: Use cmake_path
|
||||
get_filename_component(LMMS_TEST_NAME ${LMMS_TEST_SRC} NAME_WE)
|
||||
|
||||
add_executable(${LMMS_TEST_NAME} ${LMMS_TEST_SRC})
|
||||
add_test(NAME ${LMMS_TEST_NAME} COMMAND ${LMMS_TEST_NAME})
|
||||
|
||||
# TODO CMake 3.12: Propagate usage requirements by linking to lmmsobjs
|
||||
target_include_directories(${LMMS_TEST_NAME} PRIVATE $<TARGET_PROPERTY:lmmsobjs,INCLUDE_DIRECTORIES>)
|
||||
|
||||
target_static_libraries("${LMMS_TEST_NAME}" PRIVATE lmmsobjs)
|
||||
target_link_libraries(${LMMS_TEST_NAME} PRIVATE
|
||||
${QT_LIBRARIES}
|
||||
${QT_QTTEST_LIBRARY}
|
||||
)
|
||||
|
||||
target_compile_features(${LMMS_TEST_NAME} PRIVATE cxx_std_17)
|
||||
endforeach()
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#include "QTestSuite.h"
|
||||
|
||||
QList<QTestSuite*> QTestSuite::m_suites;
|
||||
|
||||
QTestSuite::QTestSuite(QObject *parent) : QObject(parent)
|
||||
{
|
||||
m_suites << this;
|
||||
}
|
||||
|
||||
QTestSuite::~QTestSuite()
|
||||
{
|
||||
m_suites.removeAll(this);
|
||||
}
|
||||
|
||||
QList<QTestSuite*> QTestSuite::suites()
|
||||
{
|
||||
return m_suites;
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#ifndef QTESTSUITE_H
|
||||
#define QTESTSUITE_H
|
||||
|
||||
#include <QtTest/QTest>
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
|
||||
class QTestSuite : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QTestSuite(QObject *parent = 0);
|
||||
~QTestSuite();
|
||||
|
||||
static QList<QTestSuite*> suites();
|
||||
|
||||
private:
|
||||
static QList<QTestSuite*> m_suites;
|
||||
};
|
||||
|
||||
#endif // QTESTSUITE_H
|
||||
@@ -5,7 +5,7 @@
|
||||
<song>
|
||||
<trackcontainer width="600" x="5" y="5" maximized="0" height="300" visible="1" type="song" minimized="0" >
|
||||
<track muted="0" type="0" name="Standard-Preset" >
|
||||
<instrumenttrack pan="0" fxch="0" pitch="0" basenote="57" vol="100" >
|
||||
<instrumenttrack pan="0" mixch="0" pitch="0" basenote="57" vol="100" >
|
||||
<instrument name="tripleoscillator" >
|
||||
<tripleoscillator phoffset2="0" userwavefile0="" finer0="0" userwavefile1="" finer1="0" userwavefile2="" finer2="0" coarse0="0" coarse1="0" coarse2="0" finel0="0" finel1="0" modalgo1="2" modalgo2="2" finel2="0" pan0="0" modalgo3="2" pan1="0" stphdetun0="0" pan2="0" stphdetun1="0" wavetype0="0" stphdetun2="0" wavetype1="0" wavetype2="0" vol0="33" vol1="33" phoffset0="0" phoffset1="0" vol2="33" />
|
||||
</instrument>
|
||||
@@ -30,7 +30,7 @@
|
||||
<bbtrack>
|
||||
<trackcontainer width="504" x="610" y="5" maximized="0" height="300" visible="1" type="bbtrackcontainer" minimized="0" >
|
||||
<track muted="0" type="0" name="Standard-Preset" >
|
||||
<instrumenttrack pan="0" fxch="0" pitch="0" basenote="57" vol="100" >
|
||||
<instrumenttrack pan="0" mixch="0" pitch="0" basenote="57" vol="100" >
|
||||
<instrument name="tripleoscillator" >
|
||||
<tripleoscillator phoffset2="0" userwavefile0="" finer0="0" userwavefile1="" finer1="0" userwavefile2="" finer2="0" coarse0="0" coarse1="0" coarse2="0" finel0="0" finel1="0" modalgo1="2" modalgo2="2" finel2="0" pan0="0" modalgo3="2" pan1="0" stphdetun0="0" pan2="0" stphdetun1="0" wavetype0="0" stphdetun2="0" wavetype1="0" wavetype2="0" vol0="33" vol1="33" phoffset0="0" phoffset1="0" vol2="33" />
|
||||
</instrument>
|
||||
@@ -68,203 +68,203 @@
|
||||
<object id="948063" />
|
||||
</automationpattern>
|
||||
</track>
|
||||
<fxmixer width="865" x="5" y="310" maximized="0" height="278" visible="1" minimized="0" >
|
||||
<fxchannel num="0" muted="0" volume="1" name="Master" >
|
||||
<mixer width="865" x="5" y="310" maximized="0" height="278" visible="1" minimized="0" >
|
||||
<mixerchannel num="0" muted="0" volume="1" name="Master" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="1" muted="0" volume="1" name="FX 1" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="1" muted="0" volume="1" name="Channel 1" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="2" muted="0" volume="1" name="FX 2" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="2" muted="0" volume="1" name="Channel 2" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="3" muted="0" volume="1" name="FX 3" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="3" muted="0" volume="1" name="Channel 3" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="4" muted="0" volume="1" name="FX 4" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="4" muted="0" volume="1" name="Channel 4" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="5" muted="0" volume="1" name="FX 5" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="5" muted="0" volume="1" name="Channel 5" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="6" muted="0" volume="1" name="FX 6" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="6" muted="0" volume="1" name="Channel 6" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="7" muted="0" volume="1" name="FX 7" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="7" muted="0" volume="1" name="Channel 7" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="8" muted="0" volume="1" name="FX 8" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="8" muted="0" volume="1" name="Channel 8" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="9" muted="0" volume="1" name="FX 9" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="9" muted="0" volume="1" name="Channel 9" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="10" muted="0" volume="1" name="FX 10" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="10" muted="0" volume="1" name="Channel 10" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="11" muted="0" volume="1" name="FX 11" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="11" muted="0" volume="1" name="Channel 11" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="12" muted="0" volume="1" name="FX 12" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="12" muted="0" volume="1" name="Channel 12" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="13" muted="0" volume="1" name="FX 13" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="13" muted="0" volume="1" name="Channel 13" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="14" muted="0" volume="1" name="FX 14" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="14" muted="0" volume="1" name="Channel 14" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="15" muted="0" volume="1" name="FX 15" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="15" muted="0" volume="1" name="Channel 15" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="16" muted="0" volume="1" name="FX 16" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="16" muted="0" volume="1" name="Channel 16" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="17" muted="0" volume="1" name="FX 17" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="17" muted="0" volume="1" name="Channel 17" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="18" muted="0" volume="1" name="FX 18" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="18" muted="0" volume="1" name="Channel 18" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="19" muted="0" volume="1" name="FX 19" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="19" muted="0" volume="1" name="Channel 19" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="20" muted="0" volume="1" name="FX 20" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="20" muted="0" volume="1" name="Channel 20" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="21" muted="0" volume="1" name="FX 21" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="21" muted="0" volume="1" name="Channel 21" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="22" muted="0" volume="1" name="FX 22" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="22" muted="0" volume="1" name="Channel 22" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="23" muted="0" volume="1" name="FX 23" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="23" muted="0" volume="1" name="Channel 23" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="24" muted="0" volume="1" name="FX 24" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="24" muted="0" volume="1" name="Channel 24" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="25" muted="0" volume="1" name="FX 25" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="25" muted="0" volume="1" name="Channel 25" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="26" muted="0" volume="1" name="FX 26" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="26" muted="0" volume="1" name="Channel 26" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="27" muted="0" volume="1" name="FX 27" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="27" muted="0" volume="1" name="Channel 27" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="28" muted="0" volume="1" name="FX 28" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="28" muted="0" volume="1" name="Channel 28" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="29" muted="0" volume="1" name="FX 29" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="29" muted="0" volume="1" name="Channel 29" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="30" muted="0" volume="1" name="FX 30" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="30" muted="0" volume="1" name="Channel 30" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="31" muted="0" volume="1" name="FX 31" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="31" muted="0" volume="1" name="Channel 31" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="32" muted="0" volume="1" name="FX 32" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="32" muted="0" volume="1" name="Channel 32" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="33" muted="0" volume="1" name="FX 33" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="33" muted="0" volume="1" name="Channel 33" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="34" muted="0" volume="1" name="FX 34" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="34" muted="0" volume="1" name="Channel 34" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="35" muted="0" volume="1" name="FX 35" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="35" muted="0" volume="1" name="Channel 35" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="36" muted="0" volume="1" name="FX 36" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="36" muted="0" volume="1" name="Channel 36" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="37" muted="0" volume="1" name="FX 37" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="37" muted="0" volume="1" name="Channel 37" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="38" muted="0" volume="1" name="FX 38" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="38" muted="0" volume="1" name="Channel 38" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="39" muted="0" volume="1" name="FX 39" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="39" muted="0" volume="1" name="Channel 39" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="40" muted="0" volume="1" name="FX 40" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="40" muted="0" volume="1" name="Channel 40" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="41" muted="0" volume="1" name="FX 41" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="41" muted="0" volume="1" name="Channel 41" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="42" muted="0" volume="1" name="FX 42" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="42" muted="0" volume="1" name="Channel 42" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="43" muted="0" volume="1" name="FX 43" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="43" muted="0" volume="1" name="Channel 43" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="44" muted="0" volume="1" name="FX 44" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="44" muted="0" volume="1" name="Channel 44" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="45" muted="0" volume="1" name="FX 45" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="45" muted="0" volume="1" name="Channel 45" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="46" muted="0" volume="1" name="FX 46" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="46" muted="0" volume="1" name="Channel 46" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="47" muted="0" volume="1" name="FX 47" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="47" muted="0" volume="1" name="Channel 47" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="48" muted="0" volume="1" name="FX 48" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="48" muted="0" volume="1" name="Channel 48" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="49" muted="0" volume="1" name="FX 49" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="49" muted="0" volume="1" name="Channel 49" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="50" muted="0" volume="1" name="FX 50" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="50" muted="0" volume="1" name="Channel 50" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="51" muted="0" volume="1" name="FX 51" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="51" muted="0" volume="1" name="Channel 51" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="52" muted="0" volume="1" name="FX 52" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="52" muted="0" volume="1" name="Channel 52" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="53" muted="0" volume="1" name="FX 53" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="53" muted="0" volume="1" name="Channel 53" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="54" muted="0" volume="1" name="FX 54" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="54" muted="0" volume="1" name="Channel 54" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="55" muted="0" volume="1" name="FX 55" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="55" muted="0" volume="1" name="Channel 55" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="56" muted="0" volume="1" name="FX 56" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="56" muted="0" volume="1" name="Channel 56" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="57" muted="0" volume="1" name="FX 57" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="57" muted="0" volume="1" name="Channel 57" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="58" muted="0" volume="1" name="FX 58" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="58" muted="0" volume="1" name="Channel 58" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="59" muted="0" volume="1" name="FX 59" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="59" muted="0" volume="1" name="Channel 59" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="60" muted="0" volume="1" name="FX 60" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="60" muted="0" volume="1" name="Channel 60" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="61" muted="0" volume="1" name="FX 61" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="61" muted="0" volume="1" name="Channel 61" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="62" muted="0" volume="1" name="FX 62" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="62" muted="0" volume="1" name="Channel 62" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="63" muted="0" volume="1" name="FX 63" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="63" muted="0" volume="1" name="Channel 63" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
<fxchannel num="64" muted="0" volume="1" name="FX 64" >
|
||||
</mixerchannel>
|
||||
<mixerchannel num="64" muted="0" volume="1" name="Channel 64" >
|
||||
<fxchain numofeffects="0" enabled="0" />
|
||||
</fxchannel>
|
||||
</fxmixer>
|
||||
</mixerchannel>
|
||||
</mixer>
|
||||
<controllerrackview width="258" x="880" y="310" maximized="0" height="278" visible="1" minimized="0" />
|
||||
<pianoroll width="840" x="-11" y="0" maximized="0" height="480" visible="0" minimized="0" />
|
||||
<automationeditor width="740" x="0" y="0" maximized="0" height="480" visible="0" minimized="0" />
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
#include "QTestSuite.h"
|
||||
|
||||
#include <QtTest/QTest>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "Engine.h"
|
||||
|
||||
void printAvailableSuits()
|
||||
{
|
||||
QStringList suitNames;
|
||||
for (QTestSuite* suite : QTestSuite::suites()) {
|
||||
suitNames.push_back(suite->metaObject()->className());
|
||||
}
|
||||
qDebug().noquote() << "Available test suits are: " << suitNames.join(", ");
|
||||
}
|
||||
|
||||
int runSuit(const QString& name, const QStringList& args)
|
||||
{
|
||||
for (QTestSuite* suite : QTestSuite::suites()) {
|
||||
if (suite->metaObject()->className() == name) {
|
||||
return QTest::qExec(suite, args);
|
||||
}
|
||||
}
|
||||
throw std::invalid_argument("No such test suit " + name.toStdString());
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
auto app = new QCoreApplication(argc, argv);
|
||||
Engine::init(true);
|
||||
|
||||
int rc = 0;
|
||||
|
||||
if (app->arguments().size() > 1) {
|
||||
try {
|
||||
runSuit(app->arguments()[1], app->arguments().mid(1));
|
||||
} catch (const std::invalid_argument& e) {
|
||||
qDebug().noquote() << e.what();
|
||||
printAvailableSuits();
|
||||
rc = -1;
|
||||
}
|
||||
} else {
|
||||
int numsuites = QTestSuite::suites().size();
|
||||
qDebug() << ">> Will run" << numsuites << "test suites";
|
||||
for (QTestSuite* suite : QTestSuite::suites()) {
|
||||
rc += QTest::qExec(suite, argc, argv);
|
||||
}
|
||||
qDebug() << "<<" << rc << "out of"<<numsuites<<"test suites failed.";
|
||||
}
|
||||
|
||||
Engine::destroy();
|
||||
return rc;
|
||||
}
|
||||
24
tests/scripted/README.md
Normal file
24
tests/scripted/README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Scripted checks
|
||||
|
||||
The checks in this directory are not C++ unit tests, but simple scripts that
|
||||
check the code for issues.
|
||||
|
||||
## Verify
|
||||
|
||||
[verify](verify) is a verification script for the scripts in this folder.
|
||||
|
||||
## Check strings
|
||||
|
||||
[check-strings](check-strings) checks for invalid strings not necessarily in
|
||||
the code.
|
||||
|
||||
Background: When you move a lot of classes and files, the code may be OK, but
|
||||
many strings outside the code (translations, style.css, .gitmodules etc.) may
|
||||
still need to be updated. This script checks whether these strings are still
|
||||
matching the code.
|
||||
|
||||
## Check namespace
|
||||
|
||||
[check-namespace](check-namespace) checks namespaces and a few related things
|
||||
like `#ifdef`s.
|
||||
|
||||
199
tests/scripted/check-namespace
Executable file
199
tests/scripted/check-namespace
Executable file
@@ -0,0 +1,199 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# This script checks the code for namespace related issues
|
||||
# Note: This script is not perfect. It can not parse all LMMS files,
|
||||
# and does not contain a complete C++ parser. If you encounter
|
||||
# difficulties with this tests, it could be a fault of your
|
||||
# changes, but it could also be a fault of this script.
|
||||
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import NamedTuple
|
||||
|
||||
|
||||
# types
|
||||
|
||||
class BlockType:
|
||||
pass
|
||||
|
||||
|
||||
IF_MACRO = BlockType()
|
||||
HEADER_GUARD = BlockType()
|
||||
CODE_BLOCK = BlockType()
|
||||
EXTERN = BlockType()
|
||||
|
||||
|
||||
class Expectation(NamedTuple):
|
||||
type: BlockType
|
||||
statement: str # expected end statement
|
||||
name: str # expected name in comment
|
||||
|
||||
|
||||
# global variables
|
||||
|
||||
errors = 0
|
||||
|
||||
|
||||
# functions
|
||||
|
||||
def caption(my_str):
|
||||
print(f'\n# {my_str}\n')
|
||||
|
||||
|
||||
def error(where, match, my_str):
|
||||
global errors
|
||||
errors += 1
|
||||
if match:
|
||||
line = match.string[:match.start()].count('\n') + 1 # first line is 1
|
||||
where = f'{where}:{line}'
|
||||
print(f'Error: {where}: {my_str}')
|
||||
|
||||
|
||||
if not Path('.gitmodules').is_file():
|
||||
print('You need to call this script from the LMMS top directory')
|
||||
exit(1)
|
||||
|
||||
result = subprocess.run(['git', 'ls-files', '*.[ch]', '*.[ch]pp', ':!tests/*'],
|
||||
capture_output=True, text=True, check=True)
|
||||
|
||||
known_no_namespace_lmms = {
|
||||
# main.cpp
|
||||
'src/core/main.cpp',
|
||||
# nothing to set under a namespace
|
||||
'include/debug.h',
|
||||
'include/versioninfo.h',
|
||||
'plugins/CarlaBase/CarlaConfig/config.h',
|
||||
'plugins/CarlaBase/DummyCarla.cpp',
|
||||
# unclear why it has no namespace
|
||||
'plugins/ZynAddSubFx/RemoteZynAddSubFx.cpp',
|
||||
}
|
||||
|
||||
exclude_files = re.compile(
|
||||
# not ours:
|
||||
'include/(aeffectx|fenv|ladspa).h|'
|
||||
'plugins/LadspaEffect/(calf|caps|cmt|swh|tap)/|'
|
||||
'plugins/MidiExport/MidiFile.hpp|'
|
||||
'plugins/ReverbSC/[a-z]|'
|
||||
'plugins/Sf2Player/fluidsynthshims.h|'
|
||||
'/portsmf/|'
|
||||
# only forward to headers that are not ours:
|
||||
'plugins/ZynAddSubFx/ThreadShims.h'
|
||||
)
|
||||
|
||||
files = [Path(f) for f in result.stdout.splitlines() if not exclude_files.search(f)]
|
||||
|
||||
# Debug argument
|
||||
if len(sys.argv) > 1:
|
||||
files = [Path(arg) for arg in sys.argv[1:]]
|
||||
|
||||
statement_pattern = re.compile(
|
||||
# Capture comments first to prevent them from matching any other regex
|
||||
# Include next line if line ends with backslash
|
||||
r'/[*](.|\n)*?[*]/|//(.*\\\n)*.*|'
|
||||
|
||||
# Macro with <30 lines and no other #if inside needs no end comment
|
||||
# Match here to prevent from matching next regex
|
||||
# With a (?!negative lookahead) we can allow all # except the ones followed by "if"
|
||||
r'^ *# *(?P<short_macro>ifn?def)(?=(([^#\n]|#(?!if|endif))*\n){,30} *# *endif)|'
|
||||
# Macro followed by name or comment
|
||||
# With (?P<name>...) you can do a backreference to \name later
|
||||
r'^ *# *(?P<named_macro>ifn?def|endif)(( *// *| +)(?P<macro_name>\w+))?|'
|
||||
# Other macros where we don't want the argument
|
||||
r'^ *# *(?P<macro>include|if|el(se|if))|'
|
||||
|
||||
# Namespace that contains no other braces needs no end comment
|
||||
# With a (?=lookahead) we can let the "namespace" part be eaten by the parser
|
||||
# but save the braces and their content for later so they can be matched again
|
||||
r'^ *namespace *[\w:]*\s*(?={[^{}]*})|'
|
||||
# Start of named namespace, extern "C" or just a opening brace
|
||||
r'(^ *(namespace +(?P<namespace>[\w:]+)|(?P<extern>extern *"C"))\s*)?(?P<opening_brace>{)|'
|
||||
# End of namespace including comment, or just a closing brace
|
||||
r'(?P<closing_brace>})( *// *namespace +(?P<namespace_end>[\w:]+))?'
|
||||
|
||||
# In all the regexes above match both tab and space when a space is used
|
||||
r''.replace(' ', r'[\t ]'),
|
||||
# Make ^ match on every line, not just beginning of file
|
||||
re.MULTILINE)
|
||||
|
||||
# Comments and whitespace followed by header guard
|
||||
header_guard_pattern = re.compile(r'^(/[*](.|\n)*?[*]/|//.*|\s)*#\s*(ifndef|pragma\s+once)')
|
||||
|
||||
# Namespace lmms
|
||||
namespace_pattern = re.compile(r'^\s*namespace\s+lmms', re.MULTILINE)
|
||||
|
||||
#
|
||||
# the real code
|
||||
#
|
||||
|
||||
caption('namespace checks')
|
||||
|
||||
for cur_file in files:
|
||||
if cur_file.is_file():
|
||||
cur_text = cur_file.read_text(errors='replace')
|
||||
|
||||
if cur_file.as_posix() not in known_no_namespace_lmms:
|
||||
namespace_pattern.search(cur_text) or error(cur_file, None, f'File has no namespace lmms')
|
||||
|
||||
header_guard = str(cur_file).endswith('.h')
|
||||
expectations = [] # type: list[Expectation]
|
||||
|
||||
if header_guard:
|
||||
if not header_guard_pattern.match(cur_text):
|
||||
error(cur_file, None, 'First statement should be header guard')
|
||||
header_guard = False
|
||||
|
||||
for m in statement_pattern.finditer(cur_text):
|
||||
# Find the matched regex group
|
||||
statement = m.group('opening_brace') or m.group('closing_brace')
|
||||
if not statement:
|
||||
statement = '#' + (m.group('macro') or m.group('named_macro') or m.group('short_macro') or '')
|
||||
if not statement:
|
||||
continue
|
||||
|
||||
# Start statements
|
||||
if statement == '{':
|
||||
etype = EXTERN if m.group('extern') else CODE_BLOCK
|
||||
expectations.append(Expectation(etype, '}', m.group('namespace')))
|
||||
elif statement.startswith('#if'):
|
||||
etype = HEADER_GUARD if header_guard else IF_MACRO
|
||||
expectations.append(Expectation(etype, '#endif', m.group('macro_name')))
|
||||
header_guard = False
|
||||
|
||||
# End statements
|
||||
elif statement == '#endif' or statement.startswith('#el') or statement == '}':
|
||||
if not expectations:
|
||||
error(cur_file, m, f'Unexpected {statement}')
|
||||
break
|
||||
if statement.startswith('#el') and expectations[-1].statement == '#endif':
|
||||
# After an #else or #elif the comment is no longer mandatory, since it's hard to define
|
||||
expectations[-1] = Expectation(IF_MACRO, '#endif', '')
|
||||
# Don't pop the expectations, we are still waiting for the #endif
|
||||
continue
|
||||
exp = expectations.pop()
|
||||
name = m.group('macro_name') or m.group('namespace_end') or ''
|
||||
if statement != exp.statement:
|
||||
error(cur_file, m, f'Expected {exp.statement} before {statement}')
|
||||
break
|
||||
# Require no end comment for header guard
|
||||
elif exp.type is HEADER_GUARD and not name:
|
||||
continue
|
||||
elif exp.name and name != exp.name:
|
||||
comment = 'namespace ' if exp.type is CODE_BLOCK else ''
|
||||
error(cur_file, m, f'Missing comment // {comment}{exp.name}')
|
||||
|
||||
# Extra checks
|
||||
elif statement == '#include':
|
||||
if any(True for e in expectations if e.type is CODE_BLOCK):
|
||||
error(cur_file, m, '#include inside a code block')
|
||||
|
||||
else:
|
||||
# Leftover expected statements
|
||||
for exp in reversed(expectations):
|
||||
error(cur_file, None, f'Expected {exp.statement} before end of file')
|
||||
|
||||
caption('summary')
|
||||
|
||||
print(f'{str(errors)} errors.')
|
||||
exit(1 if errors > 0 else 0)
|
||||
151
tests/scripted/check-strings
Executable file
151
tests/scripted/check-strings
Executable file
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# This script checks for strings like paths or class names that are *not* in the source code, but e.g. in
|
||||
# translation files, stylesheets or git files.
|
||||
# Invalid strings *in the source code* are usually recognized when you compile them, but other strings may
|
||||
# be overseen, which is why this script checks strings *outside of the source code*.
|
||||
|
||||
|
||||
import re
|
||||
import subprocess
|
||||
import xml.etree.ElementTree as ElementTree
|
||||
from pathlib import Path
|
||||
import tinycss2
|
||||
|
||||
|
||||
# global variables
|
||||
|
||||
errors = 0
|
||||
|
||||
|
||||
# functions
|
||||
|
||||
def caption(my_str):
|
||||
print(f'\n# {my_str}\n')
|
||||
|
||||
def error(where, my_str):
|
||||
global errors
|
||||
errors += 1
|
||||
print(f'Error: {where}: {my_str}')
|
||||
|
||||
# if a string makes a classname, we need to check if that class is in the source
|
||||
# however, some of these strings name classes that are not from LMMS, so these can be ignored for such checks:
|
||||
def is_our_class(classname: str) -> int:
|
||||
return classname[0] != 'Q' # Qt classes
|
||||
|
||||
|
||||
# prepare some variables
|
||||
|
||||
if not Path('.gitmodules').is_file():
|
||||
print('You need to call this script from the LMMS top directory')
|
||||
exit(1)
|
||||
|
||||
result = subprocess.run(['git', 'ls-files', '*.[ch]', '*.[ch]pp', '*.ui', ':!tests/*'],
|
||||
capture_output=True, text=True, check=True)
|
||||
files = [Path(f) for f in result.stdout.splitlines()]
|
||||
carlabase = 'carlabase' if Path('plugins/carlabase').is_dir() else 'CarlaBase'
|
||||
carlapath = f'plugins/{carlabase}/carla/'
|
||||
result = subprocess.run(['git',
|
||||
'--git-dir', f'{carlapath}/.git',
|
||||
'--work-tree', f'{carlapath}',
|
||||
'ls-files', 'resources/ui', 'source/frontend'], capture_output=True, text=True, check=True)
|
||||
files.extend([Path(f'{carlapath}/{f}') for f in result.stdout.splitlines()])
|
||||
|
||||
classes = set()
|
||||
class_pat = re.compile(r'^\s*class(?:\s+LMMS_EXPORT)?\s+([a-zA-Z_][a-zA-Z0-9_]*)', re.MULTILINE)
|
||||
class_pat_ui = re.compile(r'<class>([a-zA-Z_][a-zA-Z0-9_]*)</class>')
|
||||
for cur_file in files:
|
||||
if cur_file.is_file():
|
||||
text = cur_file.read_text(errors='replace')
|
||||
classes.update(re.findall(class_pat_ui if cur_file.suffix == '.ui' else class_pat, text))
|
||||
|
||||
|
||||
# the real checks
|
||||
|
||||
caption('.gitmodules')
|
||||
|
||||
for p in re.findall(r'\[submodule "([^"]+)"\]\s*$', Path('.gitmodules').read_text(errors='replace'), re.MULTILINE):
|
||||
if not Path(p).is_dir():
|
||||
error('.gitmodules', f'Directory does not exist: {p}')
|
||||
|
||||
|
||||
caption('locale')
|
||||
|
||||
classes_found = set()
|
||||
for cur_file in Path('data/locale').glob('*.ts'):
|
||||
tree = ElementTree.parse(str(cur_file))
|
||||
root = tree.getroot()
|
||||
for location in root.findall('./context/name'):
|
||||
classes_found.add(location.text)
|
||||
for c in sorted(classes_found):
|
||||
if is_our_class(c) and '::' not in c and c not in classes:
|
||||
error('data/locale', f'Class does not exist in source code: {c}')
|
||||
|
||||
|
||||
caption('themes')
|
||||
|
||||
|
||||
GUI_NAMESPACE_PREFIX = "lmms--gui"
|
||||
|
||||
|
||||
def unscope_classname(stylesheet, cname):
|
||||
# Strip the namespace part from the given class name,
|
||||
# while expecting it to have one in the first place.
|
||||
SCOPE_TOKEN = "--"
|
||||
i = cname.rfind(SCOPE_TOKEN) + len(SCOPE_TOKEN)
|
||||
|
||||
assert i>=0
|
||||
|
||||
return cname[i:]
|
||||
|
||||
|
||||
for theme in sorted([d for d in Path('data/themes').iterdir() if d.is_dir()]):
|
||||
classes_in_sheet = set()
|
||||
stylesheet = theme / 'style.css'
|
||||
rules = tinycss2.parse_stylesheet(Path(stylesheet).read_text(errors='replace'))
|
||||
for rule in rules:
|
||||
if rule.type == 'qualified-rule':
|
||||
class_found = False
|
||||
for c in rule.prelude:
|
||||
if c.type == 'ident' and not class_found:
|
||||
if is_our_class(c.value):
|
||||
if str(c.value).startswith(GUI_NAMESPACE_PREFIX):
|
||||
classes_in_sheet.add(unscope_classname(stylesheet, c.value))
|
||||
else:
|
||||
error(stylesheet.as_posix(), f"Namespace prefix missing from class {c.value}")
|
||||
class_found = True
|
||||
# After whitespace or comma comes a new class
|
||||
elif c.type == 'whitespace' or (c.type == 'literal' and c.value == ','):
|
||||
class_found = False
|
||||
missing_classes = classes_in_sheet - classes
|
||||
for class_in_sheet in sorted(missing_classes):
|
||||
error(stylesheet.as_posix(), f'Class does not exist in source code: {class_in_sheet}')
|
||||
|
||||
|
||||
caption('patches (checks only plugins/)')
|
||||
|
||||
pat = re.compile(r'/(plugins/\S*)', re.MULTILINE)
|
||||
calf = re.compile(r'calf/.*/modules\.') # these are a bit complicated to fix...
|
||||
for cur_file in sorted(Path('.').glob('*/patches/*.patch')):
|
||||
if Path(cur_file).is_file():
|
||||
paths_in_patches = set()
|
||||
for line in pat.findall(cur_file.read_text(errors='replace')):
|
||||
if not calf.search(line):
|
||||
paths_in_patches.add(Path(line))
|
||||
for mpath in sorted(paths_in_patches):
|
||||
# in case of LADSPA SWH effects, check that the XML exists, not the C file
|
||||
# (because the C files are not generated until a build is done)
|
||||
if mpath.parent == Path('plugins/LadspaEffect/swh/ladspa/'):
|
||||
mpath = mpath.with_suffix('.xml')
|
||||
if not mpath.is_file():
|
||||
error(cur_file.as_posix(), f'Source file does not exist: {mpath.as_posix()}')
|
||||
|
||||
|
||||
|
||||
|
||||
# summary
|
||||
|
||||
caption('summary')
|
||||
|
||||
print(f'{str(errors)} errors.')
|
||||
exit(1 if errors > 0 else 0)
|
||||
231
tests/scripted/verify
Executable file
231
tests/scripted/verify
Executable file
@@ -0,0 +1,231 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def set_git_config():
|
||||
"""Set local git identity, but only in local repo (to not overwrite user settings)"""
|
||||
"""Also, fix git issue: fatal: transport 'file' not allowed"""
|
||||
subprocess.run(['git', 'config', 'user.name', 'James Bond'])
|
||||
subprocess.run(['git', 'config', 'user.email', '007@sis.gov.uk'])
|
||||
subprocess.run(['git', 'config', '--global', 'protocol.file.allow', 'always'])
|
||||
|
||||
|
||||
def create_file(filename: str, file_content: str):
|
||||
"""Create a file in the current directory and adds it to git"""
|
||||
Path(filename).parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(filename, "w", encoding='utf-8') as textfile:
|
||||
print(file_content, file=textfile)
|
||||
subprocess.run(['git', 'add', filename], check=True)
|
||||
|
||||
|
||||
class ScriptTest():
|
||||
def __init__(self, scriptpath: Path):
|
||||
self.scriptpath = Path(scriptpath)
|
||||
|
||||
def __enter__(self):
|
||||
"""Create temporary, minimal test environment, and change to it"""
|
||||
self.lmms_tmpdir = tempfile.TemporaryDirectory(dir='.')
|
||||
os.chdir(self.lmms_tmpdir.name)
|
||||
# prerequirements
|
||||
Path('data/themes').mkdir(parents=True)
|
||||
subprocess.run(['git', 'init', '-b', 'main'], check=True)
|
||||
set_git_config()
|
||||
subprocess.run(['git', 'submodule', 'add', '../../carla', 'plugins/CarlaBase/carla'], check=True)
|
||||
create_file('src/core/classes.cpp', 'namespace lmms {\nclass TestClass\n}')
|
||||
create_file('data/locale/de.ts',
|
||||
'<?xml version="1.0" ?><!DOCTYPE TS><TS language="de" version="2.1">\n'
|
||||
' <context>\n'
|
||||
' <name>TestClass</name>\n'
|
||||
' <message>\n'
|
||||
' <location filename="../../src/core/classes.cpp" line="20"/>\n'
|
||||
' <source>About LMMS</source>\n'
|
||||
' <translation>Über LMMS</translation>\n'
|
||||
' </message>\n'
|
||||
'</context>\n'
|
||||
'</TS>\n')
|
||||
subprocess.run(['git', 'commit', '-m', 'Initial commit'], check=True)
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
"""Leave and destroy temporary test environment"""
|
||||
os.chdir('..')
|
||||
self.lmms_tmpdir.cleanup()
|
||||
|
||||
def expect(self, expectation: str):
|
||||
"""Check if "expectation" is in the output"""
|
||||
if expectation not in self.result.stdout:
|
||||
raise RuntimeError(f'Expected "{expectation}" in script output')
|
||||
|
||||
def run(self, expected_returncode: int = 1): # default: something goes wrong ("to the safe side")
|
||||
"""Run the script, check the exit code and store the result"""
|
||||
command = [str(self.scriptpath)]
|
||||
if os.name == 'nt':
|
||||
command.insert(0, sys.executable)
|
||||
self.result = subprocess.run(command, capture_output=True, text=True)
|
||||
print('--->8--- Script output BEGIN --->8---')
|
||||
print(self.result.stdout)
|
||||
print('--->8--- Script output END --->8---')
|
||||
if self.result.stderr:
|
||||
print('--->8--- Script error output BEGIN --->8---')
|
||||
print(self.result.stderr)
|
||||
print('--->8--- Script error output END --->8---')
|
||||
# make sure script returned "error" (because we test for errors) and that the output is as expected
|
||||
if self.result.returncode != expected_returncode:
|
||||
raise RuntimeError(f"Script \"check-strings\" returned {self.result.returncode}, "
|
||||
f"but {expected_returncode} expected")
|
||||
|
||||
|
||||
lmms_main_path = Path(__file__).resolve().parent.parent.parent
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
os.chdir(tmpdir)
|
||||
check_strings = lmms_main_path / 'tests' / 'scripted' / 'check-strings'
|
||||
check_namespace = lmms_main_path / 'tests' / 'scripted' / 'check-namespace'
|
||||
|
||||
# create dummy carla repo
|
||||
Path('carla').mkdir()
|
||||
os.chdir('carla')
|
||||
subprocess.run(['git', 'init', '-b', 'main'], check=True)
|
||||
set_git_config()
|
||||
create_file('README.md', 'hello world')
|
||||
subprocess.run(['git', 'commit', '-m', 'Initial commit'], check=True)
|
||||
os.chdir('..')
|
||||
|
||||
Path('lmms').mkdir()
|
||||
os.chdir('lmms')
|
||||
|
||||
# minimal working example
|
||||
with ScriptTest(check_strings) as test:
|
||||
test.run(0) # exitcode 0 - no errors expected
|
||||
test.expect('0 errors')
|
||||
|
||||
with ScriptTest(check_strings) as test:
|
||||
create_file('data/locale/fr.ts',
|
||||
'<?xml version="1.0" ?><!DOCTYPE TS><TS language="de" version="2.1">\n'
|
||||
' <context>\n'
|
||||
' <name>NonExistentClass</name>\n'
|
||||
' <message>\n'
|
||||
' <location filename="../../src/core/classes.cpp" line="20"/>\n'
|
||||
' <source>About LMMS</source>\n'
|
||||
' <translation>À propos de LMMS</translation>\n'
|
||||
' </message>\n'
|
||||
'</context>\n'
|
||||
'</TS>\n')
|
||||
test.run()
|
||||
test.expect('Error: data/locale: Class does not exist in source code: NonExistentClass')
|
||||
test.expect('1 errors')
|
||||
|
||||
with ScriptTest(check_strings) as test:
|
||||
create_file('data/themes/classic/style.css',
|
||||
'lmms--gui--NonExistentClass {'
|
||||
'\tcolor: #d1d8e4;\n'
|
||||
'}')
|
||||
test.run()
|
||||
test.expect('Error: data/themes/classic/style.css: Class does not exist in source code: NonExistentClass')
|
||||
test.expect('1 errors')
|
||||
|
||||
with ScriptTest(check_namespace) as test:
|
||||
# minimal working example
|
||||
test.run(0) # exitcode 0 - no errors expected
|
||||
test.expect('0 errors')
|
||||
|
||||
create_file('01_OddBraceWithinMacro.cpp', '''
|
||||
#if HAS_EVEN_BRACES
|
||||
namespace lmms {}
|
||||
#endif
|
||||
namespace lmms {
|
||||
#if HAS_ODD_BRACE
|
||||
}
|
||||
#endif
|
||||
''')
|
||||
create_file('02_IncludeInCodeBlock.cpp', '''
|
||||
#include <good>
|
||||
extern "C" {
|
||||
#include "alright.c"
|
||||
}
|
||||
namespace lmms {
|
||||
#include <bad>
|
||||
}
|
||||
''')
|
||||
create_file('03_MacroComments.h', '''
|
||||
#ifndef HEADER_GUARD_NEEDS_NO_COMMENT
|
||||
#define HEADER_GUARD_NEEDS_NO_COMMENT
|
||||
#ifdef HAS_NESTED_NEEDS_COMMENT
|
||||
#ifdef SHORT_NO_NESTED_NEEDS_NO_COMMENT
|
||||
namespace lmms {}
|
||||
#endif
|
||||
#endif
|
||||
#ifdef HAS_COMMENT
|
||||
#ifdef SHORT_NO_NESTED_NEEDS_NO_COMMENT
|
||||
#else
|
||||
#endif
|
||||
#endif // HAS_COMMENT
|
||||
#endif
|
||||
''')
|
||||
create_file('04_NamespaceComments.cpp', '''
|
||||
namespace lmms {
|
||||
namespace ShortNamespace {
|
||||
class WithDeclarationsOnly;
|
||||
}
|
||||
namespace LongNamespace {
|
||||
class WithDefinition {
|
||||
int x;
|
||||
};
|
||||
}
|
||||
} // namespace lmms
|
||||
''')
|
||||
create_file('05_NoHeaderGuard.h', '''
|
||||
#include <cstdio>
|
||||
namespace lmms {}
|
||||
''')
|
||||
create_file('06_PragmaButNoLmms.h', '''
|
||||
// should not cause header guard warning
|
||||
#pragma once
|
||||
namespace not_lmms {}
|
||||
''')
|
||||
create_file('07_MismatchingEndifName.h', '''
|
||||
#ifndef ABC_H
|
||||
namespace lmms {
|
||||
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
|
||||
}
|
||||
#endif // XYZ_H
|
||||
''')
|
||||
create_file('08_MismatchingNamespaceEndName.h', '''
|
||||
#ifndef ABC_H
|
||||
namespace lmms {
|
||||
{}
|
||||
} // namespace smml
|
||||
#endif // ABC_H
|
||||
''')
|
||||
create_file('09_NoEndifAfterElseIsOk.cpp', '''
|
||||
#ifdef XYZ
|
||||
namespace lmms {
|
||||
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
|
||||
}
|
||||
#else
|
||||
namespace lmms {
|
||||
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
|
||||
}
|
||||
#endif
|
||||
''')
|
||||
test.run()
|
||||
test.expect('8 errors')
|
||||
test.expect('''
|
||||
Error: 01_OddBraceWithinMacro.cpp:7: Expected #endif before }
|
||||
Error: 02_IncludeInCodeBlock.cpp:7: #include inside a code block
|
||||
Error: 03_MacroComments.h:8: Missing comment // HAS_NESTED_NEEDS_COMMENT
|
||||
Error: 04_NamespaceComments.cpp:10: Missing comment // namespace LongNamespace
|
||||
Error: 05_NoHeaderGuard.h: First statement should be header guard
|
||||
Error: 06_PragmaButNoLmms.h: File has no namespace lmms
|
||||
Error: 07_MismatchingEndifName.h:36: Missing comment // ABC_H
|
||||
Error: 08_MismatchingNamespaceEndName.h:5: Missing comment // namespace lmms
|
||||
''')
|
||||
|
||||
|
||||
# if we made it until here without an exception, all tests have been passed
|
||||
print("SUCCESS")
|
||||
841
tests/src/core/ArrayVectorTest.cpp
Normal file
841
tests/src/core/ArrayVectorTest.cpp
Normal file
@@ -0,0 +1,841 @@
|
||||
/*
|
||||
* ArrayVectorTest.cpp
|
||||
*
|
||||
* Copyright (c) 2023 Dominic Clark
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ArrayVector.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QtTest/QtTest>
|
||||
#include <array>
|
||||
#include <iterator>
|
||||
|
||||
using lmms::ArrayVector;
|
||||
|
||||
struct ShouldNotConstruct
|
||||
{
|
||||
ShouldNotConstruct() { QFAIL("should not construct"); }
|
||||
};
|
||||
|
||||
struct ShouldNotDestruct
|
||||
{
|
||||
~ShouldNotDestruct() { QFAIL("should not destruct"); }
|
||||
};
|
||||
|
||||
enum class Construction { Default, Copy, Move, CopyAssign, MoveAssign };
|
||||
|
||||
struct Constructible
|
||||
{
|
||||
Constructible() : construction{Construction::Default} {}
|
||||
Constructible(const Constructible&) : construction{Construction::Copy} {}
|
||||
Constructible(Constructible&&) : construction{Construction::Move} {}
|
||||
Constructible& operator=(const Constructible&) { construction = Construction::CopyAssign; return *this; }
|
||||
Constructible& operator=(Constructible&&) { construction = Construction::MoveAssign; return *this; }
|
||||
Construction construction;
|
||||
};
|
||||
|
||||
struct DestructorCheck
|
||||
{
|
||||
~DestructorCheck() { *destructed = true; }
|
||||
bool* destructed;
|
||||
};
|
||||
|
||||
class ArrayVectorTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void defaultConstructorTest()
|
||||
{
|
||||
// Ensure no elements are constructed
|
||||
const auto v = ArrayVector<ShouldNotConstruct, 1>();
|
||||
// Ensure the container is empty
|
||||
QVERIFY(v.empty());
|
||||
}
|
||||
|
||||
void copyConstructorTest()
|
||||
{
|
||||
{
|
||||
// Ensure all elements are copy constructed
|
||||
const auto v = ArrayVector<Constructible, 1>{{}};
|
||||
const auto copy = v;
|
||||
for (const auto& element : copy) {
|
||||
QCOMPARE(element.construction, Construction::Copy);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Ensure corresponding elements are used
|
||||
const auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
const auto copy = v;
|
||||
const auto expected = std::array{1, 2, 3};
|
||||
QVERIFY(std::equal(copy.begin(), copy.end(), expected.begin(), expected.end()));
|
||||
}
|
||||
}
|
||||
|
||||
void moveConstructorTest()
|
||||
{
|
||||
{
|
||||
// Ensure all elements are move constructed
|
||||
auto v = ArrayVector<Constructible, 1>{{}};
|
||||
const auto moved = std::move(v);
|
||||
for (const auto& element : moved) {
|
||||
QCOMPARE(element.construction, Construction::Move);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Ensure corresponding elements are used
|
||||
auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
const auto moved = std::move(v);
|
||||
const auto expected = std::array{1, 2, 3};
|
||||
QVERIFY(std::equal(moved.begin(), moved.end(), expected.begin(), expected.end()));
|
||||
// Move construction should leave the source empty
|
||||
QVERIFY(v.empty());
|
||||
}
|
||||
}
|
||||
|
||||
void fillValueConstructorTest()
|
||||
{
|
||||
// Ensure all elements are copy constructed
|
||||
const auto v = ArrayVector<Constructible, 2>(1, {});
|
||||
for (const auto& element : v) {
|
||||
QCOMPARE(element.construction, Construction::Copy);
|
||||
}
|
||||
// Ensure the container has the correct size
|
||||
QCOMPARE(v.size(), std::size_t{1});
|
||||
}
|
||||
|
||||
void fillDefaultConstructorTest()
|
||||
{
|
||||
// Ensure all elements are copy constructed
|
||||
const auto v = ArrayVector<Constructible, 2>(1);
|
||||
for (const auto& element : v) {
|
||||
QCOMPARE(element.construction, Construction::Default);
|
||||
}
|
||||
// Ensure the container has the correct size
|
||||
QCOMPARE(v.size(), std::size_t{1});
|
||||
}
|
||||
|
||||
void rangeConstructorTest()
|
||||
{
|
||||
{
|
||||
// Ensure the elements are copy constructed from normal iterators
|
||||
const auto data = std::array{Constructible{}};
|
||||
const auto v = ArrayVector<Constructible, 1>(data.begin(), data.end());
|
||||
for (const auto& element : v) {
|
||||
QCOMPARE(element.construction, Construction::Copy);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Ensure the elements are move constructed from move iterators
|
||||
auto data = std::array{Constructible{}};
|
||||
const auto v = ArrayVector<Constructible, 1>(
|
||||
std::move_iterator{data.begin()}, std::move_iterator{data.end()});
|
||||
for (const auto& element : v) {
|
||||
QCOMPARE(element.construction, Construction::Move);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Ensure corresponding elements are used
|
||||
const auto data = std::array{1, 2, 3};
|
||||
const auto v = ArrayVector<int, 5>(data.begin(), data.end());
|
||||
QVERIFY(std::equal(v.begin(), v.end(), data.begin(), data.end()));
|
||||
}
|
||||
}
|
||||
|
||||
void initializerListConstructorTest()
|
||||
{
|
||||
// Ensure the container is constructed with the correct data
|
||||
const auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
const auto expected = std::array{1, 2, 3};
|
||||
QVERIFY(std::equal(v.begin(), v.end(), expected.begin(), expected.end()));
|
||||
}
|
||||
|
||||
void destructorTest()
|
||||
{
|
||||
{
|
||||
// Should not call destructors for space without elements
|
||||
const auto v = ArrayVector<ShouldNotDestruct, 1>{};
|
||||
}
|
||||
{
|
||||
// Should call destructors for all elements
|
||||
auto destructed = false;
|
||||
{
|
||||
const auto v = ArrayVector<DestructorCheck, 1>{{&destructed}};
|
||||
}
|
||||
QVERIFY(destructed);
|
||||
}
|
||||
}
|
||||
|
||||
void copyAssignmentTest()
|
||||
{
|
||||
{
|
||||
// Self-assignment should not change the contents
|
||||
auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
const auto oldValue = v;
|
||||
v = v;
|
||||
QCOMPARE(v, oldValue);
|
||||
}
|
||||
{
|
||||
// Assignment to a larger container should copy assign
|
||||
const auto src = ArrayVector<Constructible, 5>(3);
|
||||
auto dst = ArrayVector<Constructible, 5>(5);
|
||||
dst = src;
|
||||
QCOMPARE(dst.size(), std::size_t{3});
|
||||
for (const auto& element : dst) {
|
||||
QCOMPARE(element.construction, Construction::CopyAssign);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Assignment to a smaller container should copy construct
|
||||
const auto src = ArrayVector<Constructible, 5>(3);
|
||||
auto dst = ArrayVector<Constructible, 5>{};
|
||||
dst = src;
|
||||
QCOMPARE(dst.size(), std::size_t{3});
|
||||
for (const auto& element : dst) {
|
||||
QCOMPARE(element.construction, Construction::Copy);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Ensure corresponding elements are used
|
||||
const auto src = ArrayVector<int, 5>{1, 2, 3};
|
||||
auto dst = ArrayVector<int, 5>{};
|
||||
dst = src;
|
||||
QCOMPARE(dst, (ArrayVector<int, 5>{1, 2, 3}));
|
||||
}
|
||||
}
|
||||
|
||||
void moveAssignmentTest()
|
||||
{
|
||||
{
|
||||
// Self-assignment should not change the contents
|
||||
//// Please note the following:
|
||||
//// https://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2468
|
||||
//// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81159
|
||||
auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
const auto oldValue = v;
|
||||
#if __GNUC__ >= 13
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wself-move"
|
||||
#endif
|
||||
v = std::move(v);
|
||||
#if __GNUC__ >= 13
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
QCOMPARE(v, oldValue);
|
||||
}
|
||||
{
|
||||
// Assignment to a larger container should move assign
|
||||
auto src = ArrayVector<Constructible, 5>(3);
|
||||
auto dst = ArrayVector<Constructible, 5>(5);
|
||||
dst = std::move(src);
|
||||
QCOMPARE(dst.size(), std::size_t{3});
|
||||
for (const auto& element : dst) {
|
||||
QCOMPARE(element.construction, Construction::MoveAssign);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Assignment to a smaller container should move construct
|
||||
auto src = ArrayVector<Constructible, 5>(3);
|
||||
auto dst = ArrayVector<Constructible, 5>{};
|
||||
dst = std::move(src);
|
||||
QCOMPARE(dst.size(), std::size_t{3});
|
||||
for (const auto& element : dst) {
|
||||
QCOMPARE(element.construction, Construction::Move);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Ensure corresponding elements are used
|
||||
auto src = ArrayVector<int, 5>{1, 2, 3};
|
||||
auto dst = ArrayVector<int, 5>{};
|
||||
dst = std::move(src);
|
||||
QCOMPARE(dst, (ArrayVector<int, 5>{1, 2, 3}));
|
||||
}
|
||||
}
|
||||
|
||||
void initializerListAssignmentTest()
|
||||
{
|
||||
{
|
||||
// Assignment to a larger container should copy assign
|
||||
auto v = ArrayVector<Constructible, 2>(2);
|
||||
v = {Constructible{}};
|
||||
QCOMPARE(v.size(), std::size_t{1});
|
||||
for (const auto& element : v) {
|
||||
QCOMPARE(element.construction, Construction::CopyAssign);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Assignment to a smaller container should copy construct
|
||||
auto v = ArrayVector<Constructible, 2>{};
|
||||
v = {Constructible{}};
|
||||
QCOMPARE(v.size(), std::size_t{1});
|
||||
for (const auto& element : v) {
|
||||
QCOMPARE(element.construction, Construction::Copy);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Ensure corresponding elements are used
|
||||
auto v = ArrayVector<int, 5>{};
|
||||
v = {1, 2, 3};
|
||||
QCOMPARE(v, (ArrayVector<int, 5>{1, 2, 3}));
|
||||
}
|
||||
}
|
||||
|
||||
void fillValueAssignTest()
|
||||
{
|
||||
{
|
||||
// Assignment to a larger container should copy assign
|
||||
auto v = ArrayVector<Constructible, 5>(5);
|
||||
v.assign(3, {});
|
||||
QCOMPARE(v.size(), std::size_t{3});
|
||||
for (const auto& element : v) {
|
||||
QCOMPARE(element.construction, Construction::CopyAssign);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Assignment to a smaller container should copy construct
|
||||
auto v = ArrayVector<Constructible, 5>{};
|
||||
v.assign(3, {});
|
||||
QCOMPARE(v.size(), std::size_t{3});
|
||||
for (const auto& element : v) {
|
||||
QCOMPARE(element.construction, Construction::Copy);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Ensure correct value is filled
|
||||
auto v = ArrayVector<int, 5>{};
|
||||
v.assign(3, 1);
|
||||
QCOMPARE(v, (ArrayVector<int, 5>{1, 1, 1}));
|
||||
}
|
||||
}
|
||||
|
||||
void rangeAssignTest()
|
||||
{
|
||||
{
|
||||
// Assignment to a larger container should copy assign
|
||||
const auto data = std::array{Constructible{}};
|
||||
auto v = ArrayVector<Constructible, 2>(2);
|
||||
v.assign(data.begin(), data.end());
|
||||
QCOMPARE(v.size(), std::size_t{1});
|
||||
for (const auto& element : v) {
|
||||
QCOMPARE(element.construction, Construction::CopyAssign);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Assignment to a smaller container should copy construct
|
||||
const auto data = std::array{Constructible{}};
|
||||
auto v = ArrayVector<Constructible, 2>{};
|
||||
v.assign(data.begin(), data.end());
|
||||
QCOMPARE(v.size(), std::size_t{1});
|
||||
for (const auto& element : v) {
|
||||
QCOMPARE(element.construction, Construction::Copy);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Ensure correct value is filled
|
||||
const auto data = std::array{1, 2, 3};
|
||||
auto v = ArrayVector<int, 5>{};
|
||||
v.assign(data.begin(), data.end());
|
||||
QCOMPARE(v, (ArrayVector<int, 5>{1, 2, 3}));
|
||||
}
|
||||
}
|
||||
|
||||
void atTest()
|
||||
{
|
||||
{
|
||||
// Non-const version
|
||||
auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
QCOMPARE(v.at(1), 2);
|
||||
QVERIFY_EXCEPTION_THROWN(v.at(3), std::out_of_range);
|
||||
}
|
||||
{
|
||||
// Const version
|
||||
const auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
QCOMPARE(v.at(1), 2);
|
||||
QVERIFY_EXCEPTION_THROWN(v.at(3), std::out_of_range);
|
||||
}
|
||||
}
|
||||
|
||||
void subscriptTest()
|
||||
{
|
||||
{
|
||||
// Non-const version
|
||||
auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
QCOMPARE(v[1], 2);
|
||||
}
|
||||
{
|
||||
// Const version
|
||||
const auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
QCOMPARE(v[1], 2);
|
||||
}
|
||||
}
|
||||
|
||||
void frontTest()
|
||||
{
|
||||
{
|
||||
// Non-const version
|
||||
auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
QCOMPARE(v.front(), 1);
|
||||
}
|
||||
{
|
||||
// Const version
|
||||
const auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
QCOMPARE(v.front(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
void backTest()
|
||||
{
|
||||
{
|
||||
// Non-const version
|
||||
auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
QCOMPARE(v.back(), 3);
|
||||
}
|
||||
{
|
||||
// Const version
|
||||
const auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
QCOMPARE(v.back(), 3);
|
||||
}
|
||||
}
|
||||
|
||||
void dataTest()
|
||||
{
|
||||
{
|
||||
// Non-const version
|
||||
auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
QCOMPARE(v.data(), &v.front());
|
||||
}
|
||||
{
|
||||
// Const version
|
||||
const auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
QCOMPARE(v.data(), &v.front());
|
||||
}
|
||||
}
|
||||
|
||||
void beginEndTest()
|
||||
{
|
||||
const auto expected = std::array{1, 2, 3};
|
||||
{
|
||||
// Non-const version
|
||||
auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
QVERIFY(std::equal(v.begin(), v.end(), expected.begin(), expected.end()));
|
||||
QVERIFY(std::equal(v.cbegin(), v.cend(), expected.begin(), expected.end()));
|
||||
}
|
||||
{
|
||||
// Const version
|
||||
const auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
QVERIFY(std::equal(v.begin(), v.end(), expected.begin(), expected.end()));
|
||||
}
|
||||
}
|
||||
|
||||
void rbeginRendTest()
|
||||
{
|
||||
const auto expected = std::array{3, 2, 1};
|
||||
{
|
||||
// Non-const version
|
||||
auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
QVERIFY(std::equal(v.rbegin(), v.rend(), expected.begin(), expected.end()));
|
||||
QVERIFY(std::equal(v.crbegin(), v.crend(), expected.begin(), expected.end()));
|
||||
}
|
||||
{
|
||||
// Const version
|
||||
const auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
QVERIFY(std::equal(v.rbegin(), v.rend(), expected.begin(), expected.end()));
|
||||
}
|
||||
}
|
||||
|
||||
void emptyFullSizeMaxCapacityTest()
|
||||
{
|
||||
auto v = ArrayVector<int, 2>{};
|
||||
QVERIFY(v.empty());
|
||||
QVERIFY(!v.full());
|
||||
QCOMPARE(v.size(), std::size_t{0});
|
||||
QCOMPARE(v.max_size(), std::size_t{2});
|
||||
QCOMPARE(v.capacity(), std::size_t{2});
|
||||
|
||||
v.push_back(1);
|
||||
QVERIFY(!v.empty());
|
||||
QVERIFY(!v.full());
|
||||
QCOMPARE(v.size(), std::size_t{1});
|
||||
QCOMPARE(v.max_size(), std::size_t{2});
|
||||
QCOMPARE(v.capacity(), std::size_t{2});
|
||||
|
||||
v.push_back(2);
|
||||
QVERIFY(!v.empty());
|
||||
QVERIFY(v.full());
|
||||
QCOMPARE(v.size(), std::size_t{2});
|
||||
QCOMPARE(v.max_size(), std::size_t{2});
|
||||
QCOMPARE(v.capacity(), std::size_t{2});
|
||||
|
||||
auto empty = ArrayVector<int, 0>{};
|
||||
QVERIFY(empty.empty());
|
||||
QVERIFY(empty.full());
|
||||
QCOMPARE(empty.size(), std::size_t{0});
|
||||
QCOMPARE(empty.max_size(), std::size_t{0});
|
||||
QCOMPARE(empty.capacity(), std::size_t{0});
|
||||
}
|
||||
|
||||
void insertValueTest()
|
||||
{
|
||||
{
|
||||
// Copy
|
||||
const auto data = Constructible{};
|
||||
auto v = ArrayVector<Constructible, 1>{};
|
||||
v.insert(v.cbegin(), data);
|
||||
QCOMPARE(v.size(), std::size_t{1});
|
||||
QCOMPARE(v[0].construction, Construction::Copy);
|
||||
}
|
||||
{
|
||||
// Move
|
||||
auto v = ArrayVector<Constructible, 1>{};
|
||||
v.insert(v.cbegin(), Constructible{});
|
||||
QCOMPARE(v.size(), std::size_t{1});
|
||||
QCOMPARE(v[0].construction, Construction::Move);
|
||||
}
|
||||
{
|
||||
// Ensure the correct value is used (copy)
|
||||
const auto data = 1;
|
||||
auto v = ArrayVector<int, 5>{2, 3};
|
||||
v.insert(v.cbegin(), data);
|
||||
QCOMPARE(v, (ArrayVector<int, 5>{1, 2, 3}));
|
||||
}
|
||||
{
|
||||
// Ensure the correct value is used (move)
|
||||
auto v = ArrayVector<int, 5>{2, 3};
|
||||
v.insert(v.cbegin(), 1);
|
||||
QCOMPARE(v, (ArrayVector<int, 5>{1, 2, 3}));
|
||||
}
|
||||
}
|
||||
|
||||
void insertFillValueTest()
|
||||
{
|
||||
{
|
||||
// Insertion should copy construct
|
||||
auto v = ArrayVector<Constructible, 5>{};
|
||||
v.insert(v.cbegin(), 3, {});
|
||||
QCOMPARE(v.size(), std::size_t{3});
|
||||
for (const auto& element : v) {
|
||||
QCOMPARE(element.construction, Construction::Copy);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Ensure correct value is filled
|
||||
auto v = ArrayVector<int, 5>{1, 3};
|
||||
v.insert(v.cbegin() + 1, 3, 2);
|
||||
QCOMPARE(v, (ArrayVector<int, 5>{1, 2, 2, 2, 3}));
|
||||
}
|
||||
}
|
||||
|
||||
void insertRangeTest()
|
||||
{
|
||||
{
|
||||
// Insertion should copy construct
|
||||
const auto data = std::array{Constructible{}};
|
||||
auto v = ArrayVector<Constructible, 2>{};
|
||||
v.insert(v.cbegin(), data.begin(), data.end());
|
||||
QCOMPARE(v.size(), std::size_t{1});
|
||||
for (const auto& element : v) {
|
||||
QCOMPARE(element.construction, Construction::Copy);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Ensure correct value is filled
|
||||
const auto data = std::array{2, 3};
|
||||
auto v = ArrayVector<int, 5>{1, 4};
|
||||
v.insert(v.cbegin() + 1, data.begin(), data.end());
|
||||
QCOMPARE(v, (ArrayVector<int, 5>{1, 2, 3, 4}));
|
||||
}
|
||||
}
|
||||
|
||||
void insertInitializerListTest()
|
||||
{
|
||||
{
|
||||
// Insertion should copy construct
|
||||
auto v = ArrayVector<Constructible, 2>{};
|
||||
v.insert(v.cbegin(), {Constructible{}});
|
||||
QCOMPARE(v.size(), std::size_t{1});
|
||||
for (const auto& element : v) {
|
||||
QCOMPARE(element.construction, Construction::Copy);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Ensure corresponding elements are used
|
||||
auto v = ArrayVector<int, 5>{1, 4};
|
||||
v.insert(v.cbegin() + 1, {2, 3});
|
||||
QCOMPARE(v, (ArrayVector<int, 5>{1, 2, 3, 4}));
|
||||
}
|
||||
}
|
||||
|
||||
void emplaceTest()
|
||||
{
|
||||
{
|
||||
// Ensure the value is constructed in-place
|
||||
auto v = ArrayVector<Constructible, 1>{};
|
||||
v.emplace(v.cbegin());
|
||||
QCOMPARE(v.size(), std::size_t{1});
|
||||
QCOMPARE(v[0].construction, Construction::Default);
|
||||
}
|
||||
{
|
||||
// Ensure the correct value is used (move)
|
||||
auto v = ArrayVector<int, 5>{2, 3};
|
||||
v.emplace(v.cbegin(), 1);
|
||||
QCOMPARE(v, (ArrayVector<int, 5>{1, 2, 3}));
|
||||
}
|
||||
}
|
||||
|
||||
void eraseTest()
|
||||
{
|
||||
{
|
||||
// Ensure destructors are run
|
||||
auto destructed = false;
|
||||
auto v = ArrayVector<DestructorCheck, 1>{{&destructed}};
|
||||
v.erase(v.cbegin());
|
||||
QVERIFY(destructed);
|
||||
}
|
||||
{
|
||||
// Ensure the result is correct
|
||||
auto v = ArrayVector<int, 5>{10, 1, 2, 3};
|
||||
v.erase(v.cbegin());
|
||||
QCOMPARE(v, (ArrayVector<int, 5>{1, 2, 3}));
|
||||
}
|
||||
}
|
||||
|
||||
void eraseRangeTest()
|
||||
{
|
||||
{
|
||||
// Ensure destructors are run
|
||||
auto destructed = false;
|
||||
auto v = ArrayVector<DestructorCheck, 1>{{&destructed}};
|
||||
v.erase(v.cbegin(), v.cend());
|
||||
QVERIFY(destructed);
|
||||
}
|
||||
{
|
||||
// Ensure the result is correct
|
||||
auto v = ArrayVector<int, 5>{1, 20, 21, 2, 3};
|
||||
v.erase(v.cbegin() + 1, v.cbegin() + 3);
|
||||
QCOMPARE(v, (ArrayVector<int, 5>{1, 2, 3}));
|
||||
}
|
||||
}
|
||||
|
||||
void pushBackTest()
|
||||
{
|
||||
{
|
||||
// Copy
|
||||
const auto data = Constructible{};
|
||||
auto v = ArrayVector<Constructible, 1>{};
|
||||
v.push_back(data);
|
||||
QCOMPARE(v.size(), std::size_t{1});
|
||||
QCOMPARE(v[0].construction, Construction::Copy);
|
||||
}
|
||||
{
|
||||
// Move
|
||||
auto v = ArrayVector<Constructible, 1>{};
|
||||
v.push_back({});
|
||||
QCOMPARE(v.size(), std::size_t{1});
|
||||
QCOMPARE(v[0].construction, Construction::Move);
|
||||
}
|
||||
{
|
||||
// Ensure the correct value is used (copy)
|
||||
const auto data = 3;
|
||||
auto v = ArrayVector<int, 5>{1, 2};
|
||||
v.push_back(data);
|
||||
QCOMPARE(v, (ArrayVector<int, 5>{1, 2, 3}));
|
||||
}
|
||||
{
|
||||
// Ensure the correct value is used (move)
|
||||
auto v = ArrayVector<int, 5>{1, 2};
|
||||
v.push_back(3);
|
||||
QCOMPARE(v, (ArrayVector<int, 5>{1, 2, 3}));
|
||||
}
|
||||
}
|
||||
|
||||
void emplaceBackTest()
|
||||
{
|
||||
{
|
||||
// Ensure the value is constructed in-place
|
||||
auto v = ArrayVector<Constructible, 1>{};
|
||||
v.emplace_back();
|
||||
QCOMPARE(v.size(), std::size_t{1});
|
||||
QCOMPARE(v[0].construction, Construction::Default);
|
||||
}
|
||||
{
|
||||
// Ensure the correct value is used (move)
|
||||
auto v = ArrayVector<int, 5>{1, 2};
|
||||
v.emplace_back(3);
|
||||
QCOMPARE(v, (ArrayVector<int, 5>{1, 2, 3}));
|
||||
}
|
||||
}
|
||||
|
||||
void popBackTest()
|
||||
{
|
||||
{
|
||||
// Ensure destructors are run
|
||||
auto destructed = false;
|
||||
auto v = ArrayVector<DestructorCheck, 1>{{&destructed}};
|
||||
v.pop_back();
|
||||
QVERIFY(destructed);
|
||||
}
|
||||
{
|
||||
// Ensure the result is correct
|
||||
auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
v.pop_back();
|
||||
QCOMPARE(v, (ArrayVector<int, 5>{1, 2}));
|
||||
}
|
||||
}
|
||||
|
||||
void resizeDefaultTest()
|
||||
{
|
||||
{
|
||||
// Smaller
|
||||
auto destructed = false;
|
||||
auto v = ArrayVector<DestructorCheck, 1>{{&destructed}};
|
||||
QCOMPARE(v.size(), std::size_t{1});
|
||||
v.resize(0);
|
||||
QCOMPARE(v.size(), std::size_t{0});
|
||||
QVERIFY(destructed);
|
||||
}
|
||||
{
|
||||
// Bigger
|
||||
auto v = ArrayVector<Constructible, 1>{};
|
||||
QCOMPARE(v.size(), std::size_t{0});
|
||||
v.resize(1);
|
||||
QCOMPARE(v.size(), std::size_t{1});
|
||||
QCOMPARE(v[0].construction, Construction::Default);
|
||||
}
|
||||
{
|
||||
// Too big
|
||||
auto v = ArrayVector<int, 1>{};
|
||||
QVERIFY_EXCEPTION_THROWN(v.resize(2), std::length_error);
|
||||
}
|
||||
}
|
||||
|
||||
void resizeValueTest()
|
||||
{
|
||||
{
|
||||
// Smaller
|
||||
auto dummy = false;
|
||||
auto destructed = false;
|
||||
auto v = ArrayVector<DestructorCheck, 1>{{&destructed}};
|
||||
QCOMPARE(v.size(), std::size_t{1});
|
||||
v.resize(0, {&dummy});
|
||||
QCOMPARE(v.size(), std::size_t{0});
|
||||
QVERIFY(destructed);
|
||||
}
|
||||
{
|
||||
// Bigger
|
||||
auto v = ArrayVector<Constructible, 1>{};
|
||||
QCOMPARE(v.size(), std::size_t{0});
|
||||
v.resize(1, {});
|
||||
QCOMPARE(v.size(), std::size_t{1});
|
||||
QCOMPARE(v[0].construction, Construction::Copy);
|
||||
}
|
||||
{
|
||||
// Too big
|
||||
auto v = ArrayVector<int, 1>{};
|
||||
QVERIFY_EXCEPTION_THROWN(v.resize(2), std::length_error);
|
||||
}
|
||||
{
|
||||
// Ensure the correct value is used
|
||||
auto v = ArrayVector<int, 1>{};
|
||||
v.resize(1, 1);
|
||||
QCOMPARE(v, (ArrayVector<int, 1>{1}));
|
||||
}
|
||||
}
|
||||
|
||||
void clearTest()
|
||||
{
|
||||
{
|
||||
// Ensure destructors are run
|
||||
auto destructed = false;
|
||||
auto v = ArrayVector<DestructorCheck, 1>{{&destructed}};
|
||||
v.clear();
|
||||
QVERIFY(destructed);
|
||||
}
|
||||
{
|
||||
// Ensure the result is correct
|
||||
auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
v.clear();
|
||||
QCOMPARE(v, (ArrayVector<int, 5>{}));
|
||||
}
|
||||
}
|
||||
|
||||
void memberSwapTest()
|
||||
{
|
||||
auto a = ArrayVector<int, 5>{1, 2, 3, 4};
|
||||
auto b = ArrayVector<int, 5>{2, 4, 6};
|
||||
|
||||
const auto aOriginal = a;
|
||||
const auto bOriginal = b;
|
||||
|
||||
a.swap(b);
|
||||
|
||||
QCOMPARE(a, bOriginal);
|
||||
QCOMPARE(b, aOriginal);
|
||||
}
|
||||
|
||||
void freeSwapTest()
|
||||
{
|
||||
auto a = ArrayVector<int, 5>{1, 2, 3, 4};
|
||||
auto b = ArrayVector<int, 5>{2, 4, 6};
|
||||
|
||||
const auto aOriginal = a;
|
||||
const auto bOriginal = b;
|
||||
|
||||
swap(a, b);
|
||||
|
||||
QCOMPARE(a, bOriginal);
|
||||
QCOMPARE(b, aOriginal);
|
||||
}
|
||||
|
||||
void comparisonTest()
|
||||
{
|
||||
const auto v = ArrayVector<int, 5>{1, 2, 3};
|
||||
const auto l = ArrayVector<int, 5>{1, 2, 2};
|
||||
const auto e = ArrayVector<int, 5>{1, 2, 3};
|
||||
const auto g = ArrayVector<int, 5>{1, 3, 3};
|
||||
|
||||
QVERIFY(l < v);
|
||||
QVERIFY(!(e < v));
|
||||
QVERIFY(!(g < v));
|
||||
|
||||
QVERIFY(l <= v);
|
||||
QVERIFY(e <= v);
|
||||
QVERIFY(!(g <= v));
|
||||
|
||||
QVERIFY(!(l > v));
|
||||
QVERIFY(!(e > v));
|
||||
QVERIFY(g > v);
|
||||
|
||||
QVERIFY(!(l >= v));
|
||||
QVERIFY(e >= v);
|
||||
QVERIFY(g >= v);
|
||||
|
||||
QVERIFY(!(l == v));
|
||||
QVERIFY(e == v);
|
||||
QVERIFY(!(g == v));
|
||||
|
||||
QVERIFY(l != v);
|
||||
QVERIFY(!(e != v));
|
||||
QVERIFY(g != v);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(ArrayVectorTest)
|
||||
#include "ArrayVectorTest.moc"
|
||||
@@ -22,15 +22,16 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "QTestSuite.h"
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
#include "AutomatableModel.h"
|
||||
#include "ComboBoxModel.h"
|
||||
#include "Engine.h"
|
||||
|
||||
class AutomatableModelTest : QTestSuite
|
||||
class AutomatableModelTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
bool m1Changed, m2Changed;
|
||||
void resetChanged() { m1Changed = m2Changed = false; }
|
||||
|
||||
@@ -41,8 +42,23 @@ private slots: // helper slots
|
||||
private slots: // tests
|
||||
//! Test that upcast and exact casts work,
|
||||
//! but no downcast or any other casts
|
||||
|
||||
void initTestCase()
|
||||
{
|
||||
using namespace lmms;
|
||||
Engine::init(true);
|
||||
}
|
||||
|
||||
void cleanupTestCase()
|
||||
{
|
||||
using namespace lmms;
|
||||
Engine::destroy();
|
||||
}
|
||||
|
||||
void CastTests()
|
||||
{
|
||||
using namespace lmms;
|
||||
|
||||
ComboBoxModel comboModel;
|
||||
AutomatableModel* amPtr = &comboModel;
|
||||
QVERIFY(nullptr == amPtr->dynamicCast<FloatModel>()); // not a parent class
|
||||
@@ -60,6 +76,8 @@ private slots: // tests
|
||||
|
||||
void LinkTests()
|
||||
{
|
||||
using namespace lmms;
|
||||
|
||||
BoolModel m1(false), m2(false);
|
||||
|
||||
QObject::connect(&m1, SIGNAL(dataChanged()),
|
||||
@@ -96,6 +114,7 @@ private slots: // tests
|
||||
QVERIFY(m2.value());
|
||||
QVERIFY(!m3.value());
|
||||
}
|
||||
} AutomatableModelTests;
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(AutomatableModelTest)
|
||||
#include "AutomatableModelTest.moc"
|
||||
|
||||
54
tests/src/core/MathTest.cpp
Normal file
54
tests/src/core/MathTest.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* MathTest.cpp
|
||||
*
|
||||
* Copyright (c) 2023 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <QDir>
|
||||
#include <QObject>
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
#include "lmms_math.h"
|
||||
|
||||
class MathTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void NumDigitsTest()
|
||||
{
|
||||
using namespace lmms;
|
||||
QCOMPARE(numDigitsAsInt(1.f), 1);
|
||||
QCOMPARE(numDigitsAsInt(9.9f), 2);
|
||||
QCOMPARE(numDigitsAsInt(10.f), 2);
|
||||
QCOMPARE(numDigitsAsInt(0.f), 1);
|
||||
QCOMPARE(numDigitsAsInt(-100.f), 4);
|
||||
QCOMPARE(numDigitsAsInt(-99.f), 3);
|
||||
QCOMPARE(numDigitsAsInt(-0.4f), 1); // there is no "-0" for LED spinbox
|
||||
QCOMPARE(numDigitsAsInt(-0.99f), 2);
|
||||
QCOMPARE(numDigitsAsInt(1000000000), 10);
|
||||
QCOMPARE(numDigitsAsInt(-1000000000), 11);
|
||||
QCOMPARE(numDigitsAsInt(900000000), 9);
|
||||
QCOMPARE(numDigitsAsInt(-900000000), 10);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(MathTest)
|
||||
#include "MathTest.moc"
|
||||
@@ -22,14 +22,15 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "QTestSuite.h"
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
#include "MemoryPool.h"
|
||||
|
||||
#include <array>
|
||||
#include <stack>
|
||||
|
||||
class MemoryPoolTest : QTestSuite
|
||||
using namespace lmms;
|
||||
|
||||
class MemoryPoolTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
@@ -54,6 +55,8 @@ private slots:
|
||||
ptrs.pop();
|
||||
}
|
||||
}
|
||||
} MemoryPoolTests;
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(MemoryPoolTest)
|
||||
|
||||
#include "MemoryPoolTest.moc"
|
||||
|
||||
@@ -22,23 +22,25 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "QTestSuite.h"
|
||||
|
||||
#include "ProjectVersion.h"
|
||||
|
||||
class ProjectVersionTest : QTestSuite
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
class ProjectVersionTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void ProjectVersionComparisonTests()
|
||||
{
|
||||
QVERIFY(ProjectVersion("1.1.0", ProjectVersion::Minor) > "1.0.3");
|
||||
QVERIFY(ProjectVersion("1.1.0", ProjectVersion::Major) < "2.1.0");
|
||||
QVERIFY(ProjectVersion("1.1.0", ProjectVersion::Release) > "0.2.1");
|
||||
QVERIFY(ProjectVersion("1.1.4", ProjectVersion::Release) < "1.1.10");
|
||||
QVERIFY(ProjectVersion("1.1.0", ProjectVersion::Minor) == "1.1.5");
|
||||
QVERIFY( ! ( ProjectVersion("3.1.0", ProjectVersion::Minor) < "2.2.5" ) );
|
||||
QVERIFY( ! ( ProjectVersion("2.5.0", ProjectVersion::Release) < "2.2.5" ) );
|
||||
using namespace lmms;
|
||||
|
||||
QVERIFY(ProjectVersion("1.1.0", ProjectVersion::CompareType::Minor) > "1.0.3");
|
||||
QVERIFY(ProjectVersion("1.1.0", ProjectVersion::CompareType::Major) < "2.1.0");
|
||||
QVERIFY(ProjectVersion("1.1.0", ProjectVersion::CompareType::Release) > "0.2.1");
|
||||
QVERIFY(ProjectVersion("1.1.4", ProjectVersion::CompareType::Release) < "1.1.10");
|
||||
QVERIFY(ProjectVersion("1.1.0", ProjectVersion::CompareType::Minor) == "1.1.5");
|
||||
QVERIFY( ! ( ProjectVersion("3.1.0", ProjectVersion::CompareType::Minor) < "2.2.5" ) );
|
||||
QVERIFY( ! ( ProjectVersion("2.5.0", ProjectVersion::CompareType::Release) < "2.2.5" ) );
|
||||
//A pre-release version has lower precedence than a normal version
|
||||
QVERIFY(ProjectVersion("1.1.0") > "1.1.0-alpha");
|
||||
//But higher precedence than the previous version
|
||||
@@ -60,7 +62,7 @@ private slots:
|
||||
QVERIFY(ProjectVersion("1.2.3.42") == "1.2.3");
|
||||
//CompareVersion "All" should compare every identifier
|
||||
QVERIFY(
|
||||
ProjectVersion("1.0.0-a.b.c.d.e.f.g.h.i.j.k.l", ProjectVersion::All)
|
||||
ProjectVersion("1.0.0-a.b.c.d.e.f.g.h.i.j.k.l", ProjectVersion::CompareType::All)
|
||||
< "1.0.0-a.b.c.d.e.f.g.h.i.j.k.m"
|
||||
);
|
||||
//Prerelease identifiers may contain hyphens
|
||||
@@ -73,6 +75,7 @@ private slots:
|
||||
//An identifier of the form "-x" is non-numeric, not negative
|
||||
QVERIFY(ProjectVersion("1.0.0-alpha.-1") > "1.0.0-alpha.1");
|
||||
}
|
||||
} ProjectVersionTests;
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(ProjectVersionTest)
|
||||
#include "ProjectVersionTest.moc"
|
||||
|
||||
@@ -22,20 +22,22 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "QTestSuite.h"
|
||||
#include <QDir>
|
||||
#include <QObject>
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
#include "ConfigManager.h"
|
||||
#include "SampleBuffer.h"
|
||||
#include "PathUtil.h"
|
||||
#include "SampleBuffer.h"
|
||||
|
||||
#include <QDir>
|
||||
|
||||
class RelativePathsTest : QTestSuite
|
||||
class RelativePathsTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void PathUtilComparisonTests()
|
||||
{
|
||||
using namespace lmms;
|
||||
|
||||
QFileInfo fi(ConfigManager::inst()->factorySamplesDir() + "/drums/kick01.ogg");
|
||||
QVERIFY(fi.exists());
|
||||
|
||||
@@ -64,6 +66,7 @@ private slots:
|
||||
QCOMPARE(PathUtil::toAbsolute(""), empty);
|
||||
QCOMPARE(PathUtil::toShortestRelative(""), empty);
|
||||
}
|
||||
} RelativePathTests;
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(RelativePathsTest)
|
||||
#include "RelativePathsTest.moc"
|
||||
|
||||
@@ -22,83 +22,98 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "QTestSuite.h"
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
#include "QCoreApplication"
|
||||
|
||||
#include "AutomationPattern.h"
|
||||
#include "AutomationClip.h"
|
||||
#include "AutomationTrack.h"
|
||||
#include "BBTrack.h"
|
||||
#include "BBTrackContainer.h"
|
||||
#include "DetuningHelper.h"
|
||||
#include "InstrumentTrack.h"
|
||||
#include "Pattern.h"
|
||||
#include "MidiClip.h"
|
||||
#include "PatternClip.h"
|
||||
#include "PatternTrack.h"
|
||||
#include "PatternStore.h"
|
||||
#include "TrackContainer.h"
|
||||
|
||||
#include "Engine.h"
|
||||
#include "Song.h"
|
||||
|
||||
class AutomationTrackTest : QTestSuite
|
||||
class AutomationTrackTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
using namespace lmms;
|
||||
Engine::init(true);
|
||||
}
|
||||
|
||||
void testPatternLinear()
|
||||
void cleanupTestCase()
|
||||
{
|
||||
AutomationPattern p(nullptr);
|
||||
p.setProgressionType(AutomationPattern::LinearProgression);
|
||||
p.putValue(0, 0.0, false);
|
||||
p.putValue(100, 1.0, false);
|
||||
|
||||
QCOMPARE(p.valueAt(0), 0.0f);
|
||||
QCOMPARE(p.valueAt(25), 0.25f);
|
||||
QCOMPARE(p.valueAt(50), 0.5f);
|
||||
QCOMPARE(p.valueAt(75), 0.75f);
|
||||
QCOMPARE(p.valueAt(100), 1.0f);
|
||||
QCOMPARE(p.valueAt(150), 1.0f);
|
||||
using namespace lmms;
|
||||
Engine::destroy();
|
||||
}
|
||||
|
||||
void testPatternDiscrete()
|
||||
void testClipLinear()
|
||||
{
|
||||
AutomationPattern p(nullptr);
|
||||
p.setProgressionType(AutomationPattern::DiscreteProgression);
|
||||
p.putValue(0, 0.0, false);
|
||||
p.putValue(100, 1.0, false);
|
||||
using namespace lmms;
|
||||
|
||||
QCOMPARE(p.valueAt(0), 0.0f);
|
||||
QCOMPARE(p.valueAt(50), 0.0f);
|
||||
QCOMPARE(p.valueAt(100), 1.0f);
|
||||
QCOMPARE(p.valueAt(150), 1.0f);
|
||||
AutomationClip c(nullptr);
|
||||
c.setProgressionType(AutomationClip::ProgressionType::Linear);
|
||||
c.putValue(0, 0.0, false);
|
||||
c.putValue(100, 1.0, false);
|
||||
|
||||
QCOMPARE(c.valueAt(0), 0.0f);
|
||||
QCOMPARE(c.valueAt(25), 0.25f);
|
||||
QCOMPARE(c.valueAt(50), 0.5f);
|
||||
QCOMPARE(c.valueAt(75), 0.75f);
|
||||
QCOMPARE(c.valueAt(100), 1.0f);
|
||||
QCOMPARE(c.valueAt(150), 1.0f);
|
||||
}
|
||||
|
||||
void testPatterns()
|
||||
void testClipDiscrete()
|
||||
{
|
||||
using namespace lmms;
|
||||
|
||||
AutomationClip c(nullptr);
|
||||
c.setProgressionType(AutomationClip::ProgressionType::Discrete);
|
||||
c.putValue(0, 0.0, false);
|
||||
c.putValue(100, 1.0, false);
|
||||
|
||||
QCOMPARE(c.valueAt(0), 0.0f);
|
||||
QCOMPARE(c.valueAt(50), 0.0f);
|
||||
QCOMPARE(c.valueAt(100), 1.0f);
|
||||
QCOMPARE(c.valueAt(150), 1.0f);
|
||||
}
|
||||
|
||||
void testClips()
|
||||
{
|
||||
using namespace lmms;
|
||||
|
||||
FloatModel model;
|
||||
|
||||
auto song = Engine::getSong();
|
||||
AutomationTrack track(song);
|
||||
|
||||
AutomationPattern p1(&track);
|
||||
p1.setProgressionType(AutomationPattern::LinearProgression);
|
||||
p1.putValue(0, 0.0, false);
|
||||
p1.putValue(10, 1.0, false);
|
||||
p1.movePosition(0);
|
||||
p1.addObject(&model);
|
||||
AutomationClip c1(&track);
|
||||
c1.setProgressionType(AutomationClip::ProgressionType::Linear);
|
||||
c1.putValue(0, 0.0, false);
|
||||
c1.putValue(10, 1.0, false);
|
||||
c1.movePosition(0);
|
||||
c1.addObject(&model);
|
||||
|
||||
AutomationPattern p2(&track);
|
||||
p2.setProgressionType(AutomationPattern::LinearProgression);
|
||||
p2.putValue(0, 0.0, false);
|
||||
p2.putValue(100, 1.0, false);
|
||||
p2.movePosition(100);
|
||||
p2.addObject(&model);
|
||||
AutomationClip c2(&track);
|
||||
c2.setProgressionType(AutomationClip::ProgressionType::Linear);
|
||||
c2.putValue(0, 0.0, false);
|
||||
c2.putValue(100, 1.0, false);
|
||||
c2.movePosition(100);
|
||||
c2.addObject(&model);
|
||||
|
||||
AutomationPattern p3(&track);
|
||||
p3.addObject(&model);
|
||||
AutomationClip c3(&track);
|
||||
c3.addObject(&model);
|
||||
//XXX: Why is this even necessary?
|
||||
p3.clear();
|
||||
c3.clear();
|
||||
|
||||
QCOMPARE(song->automatedValuesAt( 0)[&model], 0.0f);
|
||||
QCOMPARE(song->automatedValuesAt( 5)[&model], 0.5f);
|
||||
@@ -110,24 +125,26 @@ private slots:
|
||||
|
||||
void testLengthRespected()
|
||||
{
|
||||
using namespace lmms;
|
||||
|
||||
FloatModel model;
|
||||
|
||||
auto song = Engine::getSong();
|
||||
AutomationTrack track(song);
|
||||
|
||||
AutomationPattern p(&track);
|
||||
p.setProgressionType(AutomationPattern::LinearProgression);
|
||||
p.addObject(&model);
|
||||
AutomationClip c(&track);
|
||||
c.setProgressionType(AutomationClip::ProgressionType::Linear);
|
||||
c.addObject(&model);
|
||||
|
||||
p.putValue(0, 0.0, false);
|
||||
p.putValue(100, 1.0, false);
|
||||
c.putValue(0, 0.0, false);
|
||||
c.putValue(100, 1.0, false);
|
||||
|
||||
p.changeLength(100);
|
||||
c.changeLength(100);
|
||||
QCOMPARE(song->automatedValuesAt( 0)[&model], 0.0f);
|
||||
QCOMPARE(song->automatedValuesAt( 50)[&model], 0.5f);
|
||||
QCOMPARE(song->automatedValuesAt(100)[&model], 1.0f);
|
||||
|
||||
p.changeLength(50);
|
||||
c.changeLength(50);
|
||||
QCOMPARE(song->automatedValuesAt( 0)[&model], 0.0f);
|
||||
QCOMPARE(song->automatedValuesAt( 50)[&model], 0.5f);
|
||||
QCOMPARE(song->automatedValuesAt(100)[&model], 0.5f);
|
||||
@@ -135,59 +152,63 @@ private slots:
|
||||
|
||||
void testInlineAutomation()
|
||||
{
|
||||
using namespace lmms;
|
||||
|
||||
auto song = Engine::getSong();
|
||||
|
||||
InstrumentTrack* instrumentTrack =
|
||||
dynamic_cast<InstrumentTrack*>(Track::create(Track::InstrumentTrack, song));
|
||||
InstrumentTrack instrumentTrack(song);
|
||||
|
||||
Pattern* notePattern = dynamic_cast<Pattern*>(instrumentTrack->createTCO(0));
|
||||
notePattern->changeLength(TimePos(4, 0));
|
||||
Note* note = notePattern->addNote(Note(TimePos(4, 0)), false);
|
||||
MidiClip midiClip(&instrumentTrack);
|
||||
midiClip.changeLength(TimePos(4, 0));
|
||||
Note* note = midiClip.addNote(Note(TimePos(4, 0)), false);
|
||||
note->createDetuning();
|
||||
|
||||
DetuningHelper* dh = note->detuning();
|
||||
auto pattern = dh->automationPattern();
|
||||
pattern->setProgressionType( AutomationPattern::LinearProgression );
|
||||
pattern->putValue(TimePos(0, 0), 0.0);
|
||||
pattern->putValue(TimePos(4, 0), 1.0);
|
||||
auto clip = dh->automationClip();
|
||||
clip->setProgressionType( AutomationClip::ProgressionType::Linear );
|
||||
clip->putValue(TimePos(0, 0), 0.0);
|
||||
clip->putValue(TimePos(4, 0), 1.0);
|
||||
|
||||
QCOMPARE(pattern->valueAt(TimePos(0, 0)), 0.0f);
|
||||
QCOMPARE(pattern->valueAt(TimePos(1, 0)), 0.25f);
|
||||
QCOMPARE(pattern->valueAt(TimePos(2, 0)), 0.5f);
|
||||
QCOMPARE(pattern->valueAt(TimePos(4, 0)), 1.0f);
|
||||
QCOMPARE(clip->valueAt(TimePos(0, 0)), 0.0f);
|
||||
QCOMPARE(clip->valueAt(TimePos(1, 0)), 0.25f);
|
||||
QCOMPARE(clip->valueAt(TimePos(2, 0)), 0.5f);
|
||||
QCOMPARE(clip->valueAt(TimePos(4, 0)), 1.0f);
|
||||
}
|
||||
|
||||
void testBBTrack()
|
||||
void testPatternTrack()
|
||||
{
|
||||
auto song = Engine::getSong();
|
||||
auto bbContainer = Engine::getBBTrackContainer();
|
||||
BBTrack bbTrack(song);
|
||||
Track* automationTrack = Track::create(Track::AutomationTrack, bbContainer);
|
||||
using namespace lmms;
|
||||
|
||||
QVERIFY(automationTrack->numOfTCOs());
|
||||
AutomationPattern* p1 = dynamic_cast<AutomationPattern*>(automationTrack->getTCO(0));
|
||||
QVERIFY(p1);
|
||||
auto song = Engine::getSong();
|
||||
auto patternStore = Engine::patternStore();
|
||||
PatternTrack patternTrack(song);
|
||||
AutomationTrack automationTrack(patternStore);
|
||||
automationTrack.createClipsForPattern(patternStore->numOfPatterns() - 1);
|
||||
|
||||
QVERIFY(automationTrack.numOfClips());
|
||||
auto c1 = dynamic_cast<AutomationClip*>(automationTrack.getClip(0));
|
||||
QVERIFY(c1);
|
||||
|
||||
FloatModel model;
|
||||
|
||||
p1->setProgressionType(AutomationPattern::LinearProgression);
|
||||
p1->putValue(0, 0.0, false);
|
||||
p1->putValue(10, 1.0, false);
|
||||
p1->addObject(&model);
|
||||
c1->setProgressionType(AutomationClip::ProgressionType::Linear);
|
||||
c1->putValue(0, 0.0, false);
|
||||
c1->putValue(10, 1.0, false);
|
||||
c1->addObject(&model);
|
||||
|
||||
QCOMPARE(bbContainer->automatedValuesAt( 0, bbTrack.index())[&model], 0.0f);
|
||||
QCOMPARE(bbContainer->automatedValuesAt( 5, bbTrack.index())[&model], 0.5f);
|
||||
QCOMPARE(bbContainer->automatedValuesAt(10, bbTrack.index())[&model], 1.0f);
|
||||
QCOMPARE(bbContainer->automatedValuesAt(50, bbTrack.index())[&model], 1.0f);
|
||||
QCOMPARE(patternStore->automatedValuesAt( 0, patternTrack.patternIndex())[&model], 0.0f);
|
||||
QCOMPARE(patternStore->automatedValuesAt( 5, patternTrack.patternIndex())[&model], 0.5f);
|
||||
QCOMPARE(patternStore->automatedValuesAt(10, patternTrack.patternIndex())[&model], 1.0f);
|
||||
QCOMPARE(patternStore->automatedValuesAt(50, patternTrack.patternIndex())[&model], 1.0f);
|
||||
|
||||
BBTrack bbTrack2(song);
|
||||
PatternTrack patternTrack2(song);
|
||||
|
||||
QCOMPARE(bbContainer->automatedValuesAt(5, bbTrack.index())[&model], 0.5f);
|
||||
QVERIFY(! bbContainer->automatedValuesAt(5, bbTrack2.index()).size());
|
||||
QCOMPARE(patternStore->automatedValuesAt(5, patternTrack.patternIndex())[&model], 0.5f);
|
||||
QVERIFY(! patternStore->automatedValuesAt(5, patternTrack2.patternIndex()).size());
|
||||
|
||||
BBTCO tco(&bbTrack);
|
||||
tco.changeLength(TimePos::ticksPerBar() * 2);
|
||||
tco.movePosition(0);
|
||||
PatternClip clip(&patternTrack);
|
||||
clip.changeLength(TimePos::ticksPerBar() * 2);
|
||||
clip.movePosition(0);
|
||||
|
||||
QCOMPARE(song->automatedValuesAt(0)[&model], 0.0f);
|
||||
QCOMPARE(song->automatedValuesAt(5)[&model], 0.5f);
|
||||
@@ -196,27 +217,30 @@ private slots:
|
||||
|
||||
void testGlobalAutomation()
|
||||
{
|
||||
using namespace lmms;
|
||||
|
||||
// Global automation should not have priority, see https://github.com/LMMS/lmms/issues/4268
|
||||
// Tests regression caused by 75077f6200a5aee3a5821aae48a3b8466ed8714a
|
||||
auto song = Engine::getSong();
|
||||
|
||||
auto globalTrack = song->globalAutomationTrack();
|
||||
AutomationPattern globalPattern(globalTrack);
|
||||
AutomationClip globalClip(globalTrack);
|
||||
|
||||
AutomationTrack localTrack(song);
|
||||
AutomationPattern localPattern(&localTrack);
|
||||
AutomationClip localClip(&localTrack);
|
||||
|
||||
FloatModel model;
|
||||
globalPattern.setProgressionType(AutomationPattern::DiscreteProgression);
|
||||
localPattern.setProgressionType(AutomationPattern::DiscreteProgression);
|
||||
globalPattern.addObject(&model);
|
||||
localPattern.addObject(&model);
|
||||
globalPattern.putValue(0, 100.0f, false);
|
||||
localPattern.putValue(0, 50.0f, false);
|
||||
globalClip.setProgressionType(AutomationClip::ProgressionType::Discrete);
|
||||
localClip.setProgressionType(AutomationClip::ProgressionType::Discrete);
|
||||
globalClip.addObject(&model);
|
||||
localClip.addObject(&model);
|
||||
globalClip.putValue(0, 100.0f, false);
|
||||
localClip.putValue(0, 50.0f, false);
|
||||
|
||||
QCOMPARE(song->automatedValuesAt(0)[&model], 50.0f);
|
||||
}
|
||||
|
||||
} AutomationTrackTest;
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(AutomationTrackTest)
|
||||
#include "AutomationTrackTest.moc"
|
||||
|
||||
Reference in New Issue
Block a user