diff --git a/Builds/Linux/Makefile b/Builds/Linux/Makefile index f16b9d7fa4..55d6a5b12b 100644 --- a/Builds/Linux/Makefile +++ b/Builds/Linux/Makefile @@ -342,6 +342,7 @@ OBJECTS := \ $(OBJDIR)/juce_PropertiesFile_8faca026.o \ $(OBJDIR)/juce_RecentlyOpenedFilesList_f791257.o \ $(OBJDIR)/juce_UndoManager_33b197fc.o \ + $(OBJDIR)/juce_UnitTest_4a02c10d.o \ .PHONY: clean @@ -1873,4 +1874,9 @@ $(OBJDIR)/juce_UndoManager_33b197fc.o: ../../src/utilities/juce_UndoManager.cpp @echo "Compiling juce_UndoManager.cpp" @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" +$(OBJDIR)/juce_UnitTest_4a02c10d.o: ../../src/utilities/juce_UnitTest.cpp + -@mkdir -p $(OBJDIR) + @echo "Compiling juce_UnitTest.cpp" + @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" + -include $(OBJECTS:%.o=%.d) diff --git a/Builds/MacOSX/Juce.xcodeproj/project.pbxproj b/Builds/MacOSX/Juce.xcodeproj/project.pbxproj index 5c968ac4b6..83a14bbac3 100644 --- a/Builds/MacOSX/Juce.xcodeproj/project.pbxproj +++ b/Builds/MacOSX/Juce.xcodeproj/project.pbxproj @@ -337,6 +337,7 @@ 0DE731ED3EC994B9227C2741 = { isa = PBXBuildFile; fileRef = 4BE2727CCD2CD7603402C8FE; }; A124FE5709D9324B5BAFFE53 = { isa = PBXBuildFile; fileRef = A618FC3255ECE14EC9259E6B; }; A84A5CA4654AE87192A6A096 = { isa = PBXBuildFile; fileRef = A59A5DCFCCAAEA79D03C2B27; }; + 55EDB4D9B702B469DB4655C3 = { isa = PBXBuildFile; fileRef = ADE5F12AA5AD969E2C7002B3; }; 389351359BA78C682E1931A6 = { isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libjucedebug.a; sourceTree = BUILT_PRODUCTS_DIR; }; 144F56FCF3DF9EC922765901 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_Application.cpp; path = ../../src/application/juce_Application.cpp; sourceTree = SOURCE_ROOT; }; F6A490BA93AC3558E9A6FBB0 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_Application.h; path = ../../src/application/juce_Application.h; sourceTree = SOURCE_ROOT; }; @@ -1021,6 +1022,8 @@ EECE464606AE845BFC4B941B = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_UndoableAction.h; path = ../../src/utilities/juce_UndoableAction.h; sourceTree = SOURCE_ROOT; }; A59A5DCFCCAAEA79D03C2B27 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_UndoManager.cpp; path = ../../src/utilities/juce_UndoManager.cpp; sourceTree = SOURCE_ROOT; }; 3C9E6597968358B57374502C = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_UndoManager.h; path = ../../src/utilities/juce_UndoManager.h; sourceTree = SOURCE_ROOT; }; + ADE5F12AA5AD969E2C7002B3 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_UnitTest.cpp; path = ../../src/utilities/juce_UnitTest.cpp; sourceTree = SOURCE_ROOT; }; + 75700D13513346310CEAC30D = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_UnitTest.h; path = ../../src/utilities/juce_UnitTest.h; sourceTree = SOURCE_ROOT; }; 2FD5C998952BE08F8ED3F262 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce.h; path = ../../juce.h; sourceTree = SOURCE_ROOT; }; 01778F26212AECCBF2452804 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_Config.h; path = ../../juce_Config.h; sourceTree = SOURCE_ROOT; }; D443FD24B52106986FC8A531 = { isa = PBXGroup; children = ( @@ -1803,7 +1806,9 @@ FA833EFA9E93C7DBE6624676, EECE464606AE845BFC4B941B, A59A5DCFCCAAEA79D03C2B27, - 3C9E6597968358B57374502C ); name = utilities; sourceTree = ""; }; + 3C9E6597968358B57374502C, + ADE5F12AA5AD969E2C7002B3, + 75700D13513346310CEAC30D ); name = utilities; sourceTree = ""; }; 9AA3D660772E3D4C64EC7859 = { isa = PBXGroup; children = ( D443FD24B52106986FC8A531, F2B0E44A08E127FD31184241, @@ -2231,7 +2236,8 @@ D8B9941A1375579D6CE7C1C4, 0DE731ED3EC994B9227C2741, A124FE5709D9324B5BAFFE53, - A84A5CA4654AE87192A6A096 ); runOnlyForDeploymentPostprocessing = 0; }; + A84A5CA4654AE87192A6A096, + 55EDB4D9B702B469DB4655C3 ); runOnlyForDeploymentPostprocessing = 0; }; 01555BA382FAED280FF5F58A = { isa = PBXNativeTarget; buildConfigurationList = D14EC3F0DC5861E37D35E15A; buildPhases = ( 8914D147048B9EE245989ACB ); buildRules = ( ); dependencies = ( ); name = Juce; productName = Juce; productReference = 389351359BA78C682E1931A6; productType = "com.apple.product-type.library.static"; }; FCFFEDFC83101B196BFA2AE2 = { isa = PBXProject; buildConfigurationList = 3A1CB6B5ECF4A79CDE35CC66; compatibilityVersion = "Xcode 3.0"; hasScannedForEncodings = 0; mainGroup = 992526B83B6AA618A4FAECEB; projectDirPath = ""; projectRoot = ""; targets = ( 01555BA382FAED280FF5F58A ); }; diff --git a/Builds/VisualStudio2005/Juce.vcproj b/Builds/VisualStudio2005/Juce.vcproj index 8cec0e60f9..383ca444b4 100644 --- a/Builds/VisualStudio2005/Juce.vcproj +++ b/Builds/VisualStudio2005/Juce.vcproj @@ -964,6 +964,8 @@ + + diff --git a/Builds/VisualStudio2008/Juce.vcproj b/Builds/VisualStudio2008/Juce.vcproj index 29722a3839..49322d242b 100644 --- a/Builds/VisualStudio2008/Juce.vcproj +++ b/Builds/VisualStudio2008/Juce.vcproj @@ -964,6 +964,8 @@ + + diff --git a/Builds/VisualStudio2008_DLL/Juce.vcproj b/Builds/VisualStudio2008_DLL/Juce.vcproj index 9134e904cf..17a74e0e72 100644 --- a/Builds/VisualStudio2008_DLL/Juce.vcproj +++ b/Builds/VisualStudio2008_DLL/Juce.vcproj @@ -966,6 +966,8 @@ + + diff --git a/Builds/VisualStudio2010/Juce.vcxproj b/Builds/VisualStudio2010/Juce.vcxproj index fd63953d0d..2682d9d6d0 100644 --- a/Builds/VisualStudio2010/Juce.vcxproj +++ b/Builds/VisualStudio2010/Juce.vcxproj @@ -425,6 +425,7 @@ + @@ -768,6 +769,7 @@ + diff --git a/Builds/VisualStudio2010/Juce.vcxproj.filters b/Builds/VisualStudio2010/Juce.vcxproj.filters index 1d44545144..6cfafa2d97 100644 --- a/Builds/VisualStudio2010/Juce.vcxproj.filters +++ b/Builds/VisualStudio2010/Juce.vcxproj.filters @@ -1201,6 +1201,9 @@ Juce\Source\utilities + + Juce\Source\utilities + @@ -2226,6 +2229,9 @@ Juce\Source\utilities + + Juce\Source\utilities + Juce\Source diff --git a/Builds/iPhone/Juce.xcodeproj/project.pbxproj b/Builds/iPhone/Juce.xcodeproj/project.pbxproj index 752d134c36..08475319b3 100644 --- a/Builds/iPhone/Juce.xcodeproj/project.pbxproj +++ b/Builds/iPhone/Juce.xcodeproj/project.pbxproj @@ -337,6 +337,7 @@ 0DE731ED3EC994B9227C2741 = { isa = PBXBuildFile; fileRef = 4BE2727CCD2CD7603402C8FE; }; A124FE5709D9324B5BAFFE53 = { isa = PBXBuildFile; fileRef = A618FC3255ECE14EC9259E6B; }; A84A5CA4654AE87192A6A096 = { isa = PBXBuildFile; fileRef = A59A5DCFCCAAEA79D03C2B27; }; + 55EDB4D9B702B469DB4655C3 = { isa = PBXBuildFile; fileRef = ADE5F12AA5AD969E2C7002B3; }; 389351359BA78C682E1931A6 = { isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libjucedebug.a; sourceTree = BUILT_PRODUCTS_DIR; }; 144F56FCF3DF9EC922765901 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_Application.cpp; path = ../../src/application/juce_Application.cpp; sourceTree = SOURCE_ROOT; }; F6A490BA93AC3558E9A6FBB0 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_Application.h; path = ../../src/application/juce_Application.h; sourceTree = SOURCE_ROOT; }; @@ -1021,6 +1022,8 @@ EECE464606AE845BFC4B941B = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_UndoableAction.h; path = ../../src/utilities/juce_UndoableAction.h; sourceTree = SOURCE_ROOT; }; A59A5DCFCCAAEA79D03C2B27 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_UndoManager.cpp; path = ../../src/utilities/juce_UndoManager.cpp; sourceTree = SOURCE_ROOT; }; 3C9E6597968358B57374502C = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_UndoManager.h; path = ../../src/utilities/juce_UndoManager.h; sourceTree = SOURCE_ROOT; }; + ADE5F12AA5AD969E2C7002B3 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_UnitTest.cpp; path = ../../src/utilities/juce_UnitTest.cpp; sourceTree = SOURCE_ROOT; }; + 75700D13513346310CEAC30D = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_UnitTest.h; path = ../../src/utilities/juce_UnitTest.h; sourceTree = SOURCE_ROOT; }; 2FD5C998952BE08F8ED3F262 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce.h; path = ../../juce.h; sourceTree = SOURCE_ROOT; }; 01778F26212AECCBF2452804 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_Config.h; path = ../../juce_Config.h; sourceTree = SOURCE_ROOT; }; D443FD24B52106986FC8A531 = { isa = PBXGroup; children = ( @@ -1803,7 +1806,9 @@ FA833EFA9E93C7DBE6624676, EECE464606AE845BFC4B941B, A59A5DCFCCAAEA79D03C2B27, - 3C9E6597968358B57374502C ); name = utilities; sourceTree = ""; }; + 3C9E6597968358B57374502C, + ADE5F12AA5AD969E2C7002B3, + 75700D13513346310CEAC30D ); name = utilities; sourceTree = ""; }; 9AA3D660772E3D4C64EC7859 = { isa = PBXGroup; children = ( D443FD24B52106986FC8A531, F2B0E44A08E127FD31184241, @@ -2231,7 +2236,8 @@ D8B9941A1375579D6CE7C1C4, 0DE731ED3EC994B9227C2741, A124FE5709D9324B5BAFFE53, - A84A5CA4654AE87192A6A096 ); runOnlyForDeploymentPostprocessing = 0; }; + A84A5CA4654AE87192A6A096, + 55EDB4D9B702B469DB4655C3 ); runOnlyForDeploymentPostprocessing = 0; }; 01555BA382FAED280FF5F58A = { isa = PBXNativeTarget; buildConfigurationList = D14EC3F0DC5861E37D35E15A; buildPhases = ( 8914D147048B9EE245989ACB ); buildRules = ( ); dependencies = ( ); name = Juce; productName = Juce; productReference = 389351359BA78C682E1931A6; productType = "com.apple.product-type.library.static"; }; FCFFEDFC83101B196BFA2AE2 = { isa = PBXProject; buildConfigurationList = 3A1CB6B5ECF4A79CDE35CC66; compatibilityVersion = "Xcode 3.0"; hasScannedForEncodings = 0; mainGroup = 992526B83B6AA618A4FAECEB; projectDirPath = ""; projectRoot = ""; targets = ( 01555BA382FAED280FF5F58A ); }; diff --git a/Juce.jucer b/Juce.jucer index 2a20c74b72..847b302025 100644 --- a/Juce.jucer +++ b/Juce.jucer @@ -1494,6 +1494,10 @@ file="src/utilities/juce_UndoManager.cpp"/> + + setCurrentThreadAsMessageThread(); } #else diff --git a/extras/audio plugins/wrapper/juce_PluginHostType.h b/extras/audio plugins/wrapper/juce_PluginHostType.h index aa04520280..a5b586c7e6 100644 --- a/extras/audio plugins/wrapper/juce_PluginHostType.h +++ b/extras/audio plugins/wrapper/juce_PluginHostType.h @@ -51,6 +51,7 @@ public: MackieTracktionGeneric, SteinbergCubase4, SteinbergCubase5, + SteinbergCubase5Bridged, SteinbergCubaseGeneric, SteinbergWavelab5, SteinbergWavelab6, @@ -68,7 +69,12 @@ public: bool isCubase() const throw() { - return type == SteinbergCubase4 || type == SteinbergCubase5 || type == SteinbergCubaseGeneric; + return type == SteinbergCubase4 || type == SteinbergCubase5 || type == SteinbergCubase5Bridged || type == SteinbergCubaseGeneric; + } + + bool isCubaseBridged() const throw() + { + return type == SteinbergCubase5Bridged; } bool isTracktion() const throw() @@ -125,6 +131,7 @@ private: if (hostFilename.containsIgnoreCase ("Tracktion")) return MackieTracktionGeneric; if (hostFilename.containsIgnoreCase ("Cubase4")) return SteinbergCubase4; if (hostFilename.containsIgnoreCase ("Cubase5")) return SteinbergCubase5; + if (hostFilename.containsIgnoreCase ("VSTBridgeApp")) return SteinbergCubase5Bridged; if (hostFilename.containsIgnoreCase ("Cubase")) return SteinbergCubaseGeneric; if (hostFilename.containsIgnoreCase ("Wavelab 5")) return SteinbergWavelab5; if (hostFilename.containsIgnoreCase ("Wavelab 6" )) return SteinbergWavelab6; diff --git a/extras/juce demo/Builds/Linux/Makefile b/extras/juce demo/Builds/Linux/Makefile index a997472faf..eec41b6f8d 100644 --- a/extras/juce demo/Builds/Linux/Makefile +++ b/extras/juce demo/Builds/Linux/Makefile @@ -17,12 +17,12 @@ ifeq ($(CONFIG),Debug) LIBDIR := build OBJDIR := build/intermediate/Debug OUTDIR := build - CPPFLAGS := $(DEPFLAGS) -D "JUCER_LINUX_MAKE_7346DA2A=1" -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" + CPPFLAGS := $(DEPFLAGS) -D "JUCER_LINUX_MAKE_7346DA2A=1" -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -D "JUCE_UNIT_TESTS=1" -I "/usr/include" -I "/usr/include/freetype2" CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -g -ggdb -O0 CXXFLAGS += $(CFLAGS) LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound LDDEPS := - RESFLAGS := -D "JUCER_LINUX_MAKE_7346DA2A=1" -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" + RESFLAGS := -D "JUCER_LINUX_MAKE_7346DA2A=1" -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -D "JUCE_UNIT_TESTS=1" -I "/usr/include" -I "/usr/include/freetype2" TARGET := JuceDemo BLDCMD = $(CXX) -o $(OUTDIR)/$(TARGET) $(OBJECTS) $(LDFLAGS) $(RESOURCES) $(TARGET_ARCH) endif @@ -32,12 +32,12 @@ ifeq ($(CONFIG),Release) LIBDIR := build OBJDIR := build/intermediate/Release OUTDIR := build - CPPFLAGS := $(DEPFLAGS) -D "JUCER_LINUX_MAKE_7346DA2A=1" -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" + CPPFLAGS := $(DEPFLAGS) -D "JUCER_LINUX_MAKE_7346DA2A=1" -D "LINUX=1" -D "NDEBUG=1" -D "JUCE_UNIT_TESTS=1" -I "/usr/include" -I "/usr/include/freetype2" CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -Os CXXFLAGS += $(CFLAGS) LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound LDDEPS := - RESFLAGS := -D "JUCER_LINUX_MAKE_7346DA2A=1" -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" + RESFLAGS := -D "JUCER_LINUX_MAKE_7346DA2A=1" -D "LINUX=1" -D "NDEBUG=1" -D "JUCE_UNIT_TESTS=1" -I "/usr/include" -I "/usr/include/freetype2" TARGET := JuceDemo BLDCMD = $(CXX) -o $(OUTDIR)/$(TARGET) $(OBJECTS) $(LDFLAGS) $(RESOURCES) $(TARGET_ARCH) endif diff --git a/extras/juce demo/Builds/MacOSX/Juce Demo.xcodeproj/project.pbxproj b/extras/juce demo/Builds/MacOSX/Juce Demo.xcodeproj/project.pbxproj index 30d0ca4e39..8bbfcd9c5c 100644 --- a/extras/juce demo/Builds/MacOSX/Juce Demo.xcodeproj/project.pbxproj +++ b/extras/juce demo/Builds/MacOSX/Juce Demo.xcodeproj/project.pbxproj @@ -190,7 +190,8 @@ GCC_PREPROCESSOR_DEFINITIONS = ( "JUCER_XCODE_MAC_F6D2F4CF=1", "_DEBUG=1", - "DEBUG=1 "); }; name = Debug; }; + "DEBUG=1 ", + "JUCE_UNIT_TESTS=1"); }; name = Debug; }; 8EF7C7FFD55219581A5075F3 = { isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_BIT)"; PREBINDING = NO; @@ -204,7 +205,8 @@ GCC_PREPROCESSOR_DEFINITIONS = ( "JUCER_XCODE_MAC_F6D2F4CF=1", "_NDEBUG=1", - "NDEBUG=1 "); }; name = Release; }; + "NDEBUG=1 ", + "JUCE_UNIT_TESTS=1"); }; name = Release; }; 7FA986B99AFC795723E00AB0 = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; GCC_C_LANGUAGE_STANDARD = c99; diff --git a/extras/juce demo/Builds/VisualStudio2005/Juce Demo.vcproj b/extras/juce demo/Builds/VisualStudio2005/Juce Demo.vcproj index 189a5ceede..e5e68b54f4 100644 --- a/extras/juce demo/Builds/VisualStudio2005/Juce Demo.vcproj +++ b/extras/juce demo/Builds/VisualStudio2005/Juce Demo.vcproj @@ -33,7 +33,7 @@ BufferSecurityCheck="" DebugInformationFormat="4" AdditionalIncludeDirectories="" - PreprocessorDefinitions="JUCER_VS2005_78A5003;WIN32;_WINDOWS;_DEBUG" + PreprocessorDefinitions="JUCER_VS2005_78A5003;WIN32;_WINDOWS;_DEBUG;JUCE_UNIT_TESTS=1" RuntimeLibrary="1" RuntimeTypeInfo="true" UsePrecompiledHeader="0" @@ -89,7 +89,7 @@ InlineFunctionExpansion="1" StringPooling="true" AdditionalIncludeDirectories="" - PreprocessorDefinitions="JUCER_VS2005_78A5003;WIN32;_WINDOWS;NDEBUG" + PreprocessorDefinitions="JUCER_VS2005_78A5003;WIN32;_WINDOWS;NDEBUG;JUCE_UNIT_TESTS=1" RuntimeLibrary="0" RuntimeTypeInfo="true" UsePrecompiledHeader="0" diff --git a/extras/juce demo/Builds/VisualStudio2008/Juce Demo.vcproj b/extras/juce demo/Builds/VisualStudio2008/Juce Demo.vcproj index 89859b2f07..425c8b058d 100644 --- a/extras/juce demo/Builds/VisualStudio2008/Juce Demo.vcproj +++ b/extras/juce demo/Builds/VisualStudio2008/Juce Demo.vcproj @@ -33,7 +33,7 @@ BufferSecurityCheck="" DebugInformationFormat="4" AdditionalIncludeDirectories="" - PreprocessorDefinitions="JUCER_VS2008_78A5006;WIN32;_WINDOWS;_DEBUG" + PreprocessorDefinitions="JUCER_VS2008_78A5006;WIN32;_WINDOWS;_DEBUG;JUCE_UNIT_TESTS=1" RuntimeLibrary="1" RuntimeTypeInfo="true" UsePrecompiledHeader="0" @@ -89,7 +89,7 @@ InlineFunctionExpansion="1" StringPooling="true" AdditionalIncludeDirectories="" - PreprocessorDefinitions="JUCER_VS2008_78A5006;WIN32;_WINDOWS;NDEBUG" + PreprocessorDefinitions="JUCER_VS2008_78A5006;WIN32;_WINDOWS;NDEBUG;JUCE_UNIT_TESTS=1" RuntimeLibrary="0" RuntimeTypeInfo="true" UsePrecompiledHeader="0" diff --git a/extras/juce demo/Builds/VisualStudio2010/Juce Demo.vcxproj b/extras/juce demo/Builds/VisualStudio2010/Juce Demo.vcxproj index f3ec3fc9e4..25925328cf 100644 --- a/extras/juce demo/Builds/VisualStudio2010/Juce Demo.vcxproj +++ b/extras/juce demo/Builds/VisualStudio2010/Juce Demo.vcxproj @@ -54,7 +54,7 @@ Disabled EditAndContinue %(AdditionalIncludeDirectories) - JUCER_VS2010_78A501D;WIN32;_WINDOWS;_DEBUG;%(PreprocessorDefinitions) + JUCER_VS2010_78A501D;WIN32;_WINDOWS;_DEBUG;JUCE_UNIT_TESTS=1;%(PreprocessorDefinitions) MultiThreadedDebug true @@ -92,7 +92,7 @@ MaxSpeed %(AdditionalIncludeDirectories) - JUCER_VS2010_78A501D;WIN32;_WINDOWS;NDEBUG;%(PreprocessorDefinitions) + JUCER_VS2010_78A501D;WIN32;_WINDOWS;NDEBUG;JUCE_UNIT_TESTS=1;%(PreprocessorDefinitions) MultiThreaded true diff --git a/extras/juce demo/Builds/iPhone/Juce Demo.xcodeproj/project.pbxproj b/extras/juce demo/Builds/iPhone/Juce Demo.xcodeproj/project.pbxproj index 7974546204..2cba2e41db 100644 --- a/extras/juce demo/Builds/iPhone/Juce Demo.xcodeproj/project.pbxproj +++ b/extras/juce demo/Builds/iPhone/Juce Demo.xcodeproj/project.pbxproj @@ -175,7 +175,8 @@ GCC_PREPROCESSOR_DEFINITIONS = ( "JUCER_XCODE_IPHONE_25ADD7EF=1", "_DEBUG=1", - "DEBUG=1 "); }; name = Debug; }; + "DEBUG=1 ", + "JUCE_UNIT_TESTS=1"); }; name = Debug; }; 8EF7C7FFD55219581A5075F3 = { isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_BIT)"; PREBINDING = NO; @@ -189,7 +190,8 @@ GCC_PREPROCESSOR_DEFINITIONS = ( "JUCER_XCODE_IPHONE_25ADD7EF=1", "_NDEBUG=1", - "NDEBUG=1 "); }; name = Release; }; + "NDEBUG=1 ", + "JUCE_UNIT_TESTS=1"); }; name = Release; }; 7FA986B99AFC795723E00AB0 = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; GCC_C_LANGUAGE_STANDARD = c99; diff --git a/extras/juce demo/Juce Demo.jucer b/extras/juce demo/Juce Demo.jucer index be21b69ff1..1523bc52fc 100644 --- a/extras/juce demo/Juce Demo.jucer +++ b/extras/juce demo/Juce Demo.jucer @@ -24,9 +24,9 @@ + osxSDK="default" osxCompatibility="default" defines="JUCE_UNIT_TESTS=1"/> + osxSDK="default" osxCompatibility="default" defines="JUCE_UNIT_TESTS=1"/> diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index d18d6eb407..1bc39b284a 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -1195,9 +1195,6 @@ FileLogger::FileLogger (const File& logFile_, logFile_.create(); } - logStream = logFile_.createOutputStream (256); - jassert (logStream != 0); - String welcome; welcome << "\r\n**********************************************************\r\n" << welcomeMessage @@ -1213,14 +1210,12 @@ FileLogger::~FileLogger() void FileLogger::logMessage (const String& message) { - if (logStream != 0) - { - DBG (message); + DBG (message); - const ScopedLock sl (logLock); - (*logStream) << message << "\r\n"; - logStream->flush(); - } + const ScopedLock sl (logLock); + + FileOutputStream out (logFile, 256); + out << message << "\r\n"; } void FileLogger::trimFileSize (int maxFileSizeBytes) const @@ -2215,125 +2210,6 @@ BEGIN_JUCE_NAMESPACE extern void juce_CheckForDanglingStreams(); // (in juce_OutputStream.cpp) #endif -#if JUCE_DEBUG - -namespace SimpleUnitTests -{ - template - class AtomicTester - { - public: - AtomicTester() {} - - static void testInteger() - { - Atomic a, b; - a.set ((Type) 10); - a += (Type) 15; - a.memoryBarrier(); - a -= (Type) 5; - ++a; ++a; --a; - a.memoryBarrier(); - - testFloat(); - } - - static void testFloat() - { - Atomic a, b; - a = (Type) 21; - a.memoryBarrier(); - - /* These are some simple test cases to check the atomics - let me know - if any of these assertions fail on your system! - */ - jassert (a.get() == (Type) 21); - jassert (a.compareAndSetValue ((Type) 100, (Type) 50) == (Type) 21); - jassert (a.get() == (Type) 21); - jassert (a.compareAndSetValue ((Type) 101, a.get()) == (Type) 21); - jassert (a.get() == (Type) 101); - jassert (! a.compareAndSetBool ((Type) 300, (Type) 200)); - jassert (a.get() == (Type) 101); - jassert (a.compareAndSetBool ((Type) 200, a.get())); - jassert (a.get() == (Type) 200); - - jassert (a.exchange ((Type) 300) == (Type) 200); - jassert (a.get() == (Type) 300); - - b = a; - jassert (b.get() == a.get()); - } - }; - - static void runBasicTests() - { - // Some simple test code, to keep an eye on things and make sure these functions - // work ok on all platforms. Let me know if any of these assertions fail on your system! - - static_jassert (sizeof (pointer_sized_int) == sizeof (void*)); - static_jassert (sizeof (int8) == 1); - static_jassert (sizeof (uint8) == 1); - static_jassert (sizeof (int16) == 2); - static_jassert (sizeof (uint16) == 2); - static_jassert (sizeof (int32) == 4); - static_jassert (sizeof (uint32) == 4); - static_jassert (sizeof (int64) == 8); - static_jassert (sizeof (uint64) == 8); - - char a1[7]; - jassert (numElementsInArray(a1) == 7); - int a2[3]; - jassert (numElementsInArray(a2) == 3); - - jassert (ByteOrder::swap ((uint16) 0x1122) == 0x2211); - jassert (ByteOrder::swap ((uint32) 0x11223344) == 0x44332211); - jassert (ByteOrder::swap ((uint64) literal64bit (0x1122334455667788)) == literal64bit (0x8877665544332211)); - - // Some quick stream tests.. - int randomInt = Random::getSystemRandom().nextInt(); - int64 randomInt64 = Random::getSystemRandom().nextInt64(); - double randomDouble = Random::getSystemRandom().nextDouble(); - String randomString; - for (int i = 50; --i >= 0;) - randomString << (juce_wchar) (Random::getSystemRandom().nextInt() & 0xffff); - - MemoryOutputStream mo; - mo.writeInt (randomInt); - mo.writeIntBigEndian (randomInt); - mo.writeCompressedInt (randomInt); - mo.writeString (randomString); - mo.writeInt64 (randomInt64); - mo.writeInt64BigEndian (randomInt64); - mo.writeDouble (randomDouble); - mo.writeDoubleBigEndian (randomDouble); - - MemoryInputStream mi (mo.getData(), mo.getDataSize(), false); - jassert (mi.readInt() == randomInt); - jassert (mi.readIntBigEndian() == randomInt); - jassert (mi.readCompressedInt() == randomInt); - jassert (mi.readString() == randomString); - jassert (mi.readInt64() == randomInt64); - jassert (mi.readInt64BigEndian() == randomInt64); - jassert (mi.readDouble() == randomDouble); - jassert (mi.readDoubleBigEndian() == randomDouble); - - AtomicTester ::testInteger(); - AtomicTester ::testInteger(); - AtomicTester ::testInteger(); - AtomicTester ::testInteger(); - AtomicTester ::testInteger(); - AtomicTester ::testInteger(); - AtomicTester ::testInteger(); - AtomicTester ::testFloat(); - #if ! JUCE_64BIT_ATOMICS_UNAVAILABLE // 64-bit intrinsics aren't available on some old platforms - AtomicTester ::testInteger(); - AtomicTester ::testInteger(); - AtomicTester ::testFloat(); - #endif - } -} -#endif - static bool juceInitialisedNonGUI = false; void JUCE_PUBLIC_FUNCTION initialiseJuce_NonGUI() @@ -2344,14 +2220,22 @@ void JUCE_PUBLIC_FUNCTION initialiseJuce_NonGUI() JUCE_AUTORELEASEPOOL - #if JUCE_DEBUG - SimpleUnitTests::runBasicTests(); - #endif - DBG (SystemStats::getJUCEVersion()); SystemStats::initialiseStats(); Random::getSystemRandom().setSeedRandomly(); // (mustn't call this before initialiseStats() because it relies on the time being set up) } + + // Some basic tests, to keep an eye on things and make sure these types work ok + // on all platforms. Let me know if any of these assertions fail on your system! + static_jassert (sizeof (pointer_sized_int) == sizeof (void*)); + static_jassert (sizeof (int8) == 1); + static_jassert (sizeof (uint8) == 1); + static_jassert (sizeof (int16) == 2); + static_jassert (sizeof (uint16) == 2); + static_jassert (sizeof (int32) == 4); + static_jassert (sizeof (uint32) == 4); + static_jassert (sizeof (int64) == 8); + static_jassert (sizeof (uint64) == 8); } void JUCE_PUBLIC_FUNCTION shutdownJuce_NonGUI() @@ -2431,6 +2315,93 @@ void JUCE_PUBLIC_FUNCTION shutdownJuce_GUI() #endif +#if JUCE_UNIT_TESTS + +class AtomicTests : public UnitTest +{ +public: + AtomicTests() : UnitTest ("Atomics") {} + + void runTest() + { + beginTest ("Misc"); + + char a1[7]; + expect (numElementsInArray(a1) == 7); + int a2[3]; + expect (numElementsInArray(a2) == 3); + + expect (ByteOrder::swap ((uint16) 0x1122) == 0x2211); + expect (ByteOrder::swap ((uint32) 0x11223344) == 0x44332211); + expect (ByteOrder::swap ((uint64) literal64bit (0x1122334455667788)) == literal64bit (0x8877665544332211)); + + beginTest ("Atomic types"); + AtomicTester ::testInteger (*this); + AtomicTester ::testInteger (*this); + AtomicTester ::testInteger (*this); + AtomicTester ::testInteger (*this); + AtomicTester ::testInteger (*this); + AtomicTester ::testInteger (*this); + AtomicTester ::testInteger (*this); + AtomicTester ::testFloat (*this); + #if ! JUCE_64BIT_ATOMICS_UNAVAILABLE // 64-bit intrinsics aren't available on some old platforms + AtomicTester ::testInteger (*this); + AtomicTester ::testInteger (*this); + AtomicTester ::testFloat (*this); + #endif + } + + template + class AtomicTester + { + public: + AtomicTester() {} + + static void testInteger (UnitTest& test) + { + Atomic a, b; + a.set ((Type) 10); + a += (Type) 15; + a.memoryBarrier(); + a -= (Type) 5; + ++a; ++a; --a; + a.memoryBarrier(); + + testFloat (test); + } + + static void testFloat (UnitTest& test) + { + Atomic a, b; + a = (Type) 21; + a.memoryBarrier(); + + /* These are some simple test cases to check the atomics - let me know + if any of these assertions fail on your system! + */ + test.expect (a.get() == (Type) 21); + test.expect (a.compareAndSetValue ((Type) 100, (Type) 50) == (Type) 21); + test.expect (a.get() == (Type) 21); + test.expect (a.compareAndSetValue ((Type) 101, a.get()) == (Type) 21); + test.expect (a.get() == (Type) 101); + test.expect (! a.compareAndSetBool ((Type) 300, (Type) 200)); + test.expect (a.get() == (Type) 101); + test.expect (a.compareAndSetBool ((Type) 200, a.get())); + test.expect (a.get() == (Type) 200); + + test.expect (a.exchange ((Type) 300) == (Type) 200); + test.expect (a.get() == (Type) 300); + + b = a; + test.expect (b.get() == a.get()); + } + }; +}; + +static AtomicTests atomicUnitTests; + +#endif + END_JUCE_NAMESPACE /*** End of inlined file: juce_Initialisation.cpp ***/ @@ -7531,7 +7502,8 @@ int File::findChildFiles (Array& results, } if (searchRecursively && itemIsDirectory - && fileTypeMatches (whatToLookFor | findDirectories, true, itemIsHidden)) + && ((! itemIsHidden) || (whatToLookFor & File::ignoreHiddenFiles) == 0)) + { total += fileFound.findChildFiles (results, whatToLookFor, true, wildCardPattern); } @@ -9777,6 +9749,50 @@ int64 MemoryInputStream::getPosition() return position; } +#if JUCE_UNIT_TESTS + +class MemoryStreamTests : public UnitTest +{ +public: + MemoryStreamTests() : UnitTest ("MemoryInputStream & MemoryOutputStream") {} + + void runTest() + { + beginTest ("Basics"); + + int randomInt = Random::getSystemRandom().nextInt(); + int64 randomInt64 = Random::getSystemRandom().nextInt64(); + double randomDouble = Random::getSystemRandom().nextDouble(); + String randomString; + for (int i = 50; --i >= 0;) + randomString << (juce_wchar) (Random::getSystemRandom().nextInt() & 0xffff); + + MemoryOutputStream mo; + mo.writeInt (randomInt); + mo.writeIntBigEndian (randomInt); + mo.writeCompressedInt (randomInt); + mo.writeString (randomString); + mo.writeInt64 (randomInt64); + mo.writeInt64BigEndian (randomInt64); + mo.writeDouble (randomDouble); + mo.writeDoubleBigEndian (randomDouble); + + MemoryInputStream mi (mo.getData(), mo.getDataSize(), false); + expect (mi.readInt() == randomInt); + expect (mi.readIntBigEndian() == randomInt); + expect (mi.readCompressedInt() == randomInt); + expect (mi.readString() == randomString); + expect (mi.readInt64() == randomInt64); + expect (mi.readInt64BigEndian() == randomInt64); + expect (mi.readDouble() == randomDouble); + expect (mi.readDoubleBigEndian() == randomDouble); + } +}; + +static MemoryStreamTests memoryInputStreamUnitTests; + +#endif + END_JUCE_NAMESPACE /*** End of inlined file: juce_MemoryInputStream.cpp ***/ @@ -13555,6 +13571,270 @@ void String::Concatenator::append (const String& s) } } +#if JUCE_UNIT_TESTS + +class StringTests : public UnitTest +{ +public: + StringTests() : UnitTest ("String class") {} + + void runTest() + { + { + beginTest ("Basics"); + + expect (String().length() == 0); + expect (String() == String::empty); + String s1, s2 ("abcd"); + expect (s1.isEmpty() && ! s1.isNotEmpty()); + expect (s2.isNotEmpty() && ! s2.isEmpty()); + expect (s2.length() == 4); + s1 = "abcd"; + expect (s2 == s1 && s1 == s2); + expect (s1 == "abcd" && s1 == L"abcd"); + expect (String ("abcd") == String (L"abcd")); + expect (String ("abcdefg", 4) == L"abcd"); + expect (String ("abcdefg", 4) == String (L"abcdefg", 4)); + expect (String::charToString ('x') == "x"); + expect (String::charToString (0) == String::empty); + expect (s2 + "e" == "abcde" && s2 + 'e' == "abcde"); + expect (s2 + L'e' == "abcde" && s2 + L"e" == "abcde"); + expect (s1.equalsIgnoreCase ("abcD") && s1 < "abce" && s1 > "abbb"); + expect (s1.startsWith ("ab") && s1.startsWith ("abcd") && ! s1.startsWith ("abcde")); + expect (s1.startsWithIgnoreCase ("aB") && s1.endsWithIgnoreCase ("CD")); + expect (s1.endsWith ("bcd") && ! s1.endsWith ("aabcd")); + expect (s1.indexOf (String::empty) == 0); + expect (s1.startsWith (String::empty) && s1.endsWith (String::empty) && s1.contains (String::empty)); + expect (s1.contains ("cd") && s1.contains ("ab") && s1.contains ("abcd")); + expect (s1.containsChar ('a') && ! s1.containsChar (0)); + expect (String ("abc foo bar").containsWholeWord ("abc") && String ("abc foo bar").containsWholeWord ("abc")); + } + + { + beginTest ("Operations"); + + String s ("012345678"); + expect (s.hashCode() != 0); + expect (s.hashCode64() != 0); + expect (s.hashCode() != (s + s).hashCode()); + expect (s.hashCode64() != (s + s).hashCode64()); + expect (s.compare (String ("012345678")) == 0); + expect (s.compare (String ("012345679")) < 0); + expect (s.compare (String ("012345676")) > 0); + expect (s.substring (2, 3) == String::charToString (s[2])); + expect (s.substring (0, 1) == String::charToString (s[0])); + expect (s.getLastCharacter() == s [s.length() - 1]); + expect (String::charToString (s.getLastCharacter()) == s.getLastCharacters (1)); + expect (s.substring (0, 3) == L"012"); + expect (s.substring (0, 100) == s); + expect (s.substring (-1, 100) == s); + expect (s.substring (3) == "345678"); + expect (s.indexOf (L"45") == 4); + expect (String ("444445").indexOf ("45") == 4); + expect (String ("444445").lastIndexOfChar ('4') == 4); + expect (String ("45454545x").lastIndexOf (L"45") == 6); + expect (String ("45454545x").lastIndexOfAnyOf ("456") == 7); + expect (String ("45454545x").lastIndexOfAnyOf (L"456x") == 8); + expect (String ("abABaBaBa").lastIndexOfIgnoreCase ("Ab") == 6); + expect (s.indexOfChar (L'4') == 4); + expect (s + s == "012345678012345678"); + expect (s.startsWith (s)); + expect (s.startsWith (s.substring (0, 4))); + expect (s.startsWith (s.dropLastCharacters (4))); + expect (s.endsWith (s.substring (5))); + expect (s.endsWith (s)); + expect (s.contains (s.substring (3, 6))); + expect (s.contains (s.substring (3))); + expect (s.startsWithChar (s[0])); + expect (s.endsWithChar (s.getLastCharacter())); + expect (s [s.length()] == 0); + expect (String ("abcdEFGH").toLowerCase() == String ("abcdefgh")); + expect (String ("abcdEFGH").toUpperCase() == String ("ABCDEFGH")); + + String s2 ("123"); + s2 << ((int) 4) << ((short) 5) << "678" << L"9" << '0'; + s2 += "xyz"; + expect (s2 == "1234567890xyz"); + + beginTest ("Numeric conversions"); + expect (String::empty.getIntValue() == 0); + expect (String::empty.getDoubleValue() == 0.0); + expect (String::empty.getFloatValue() == 0.0f); + expect (s.getIntValue() == 12345678); + expect (s.getLargeIntValue() == (int64) 12345678); + expect (s.getDoubleValue() == 12345678.0); + expect (s.getFloatValue() == 12345678.0f); + expect (String (-1234).getIntValue() == -1234); + expect (String ((int64) -1234).getLargeIntValue() == -1234); + expect (String (-1234.56).getDoubleValue() == -1234.56); + expect (String (-1234.56f).getFloatValue() == -1234.56f); + expect (("xyz" + s).getTrailingIntValue() == s.getIntValue()); + expect (s.getHexValue32() == 0x12345678); + expect (s.getHexValue64() == (int64) 0x12345678); + expect (String::toHexString (0x1234abcd).equalsIgnoreCase ("1234abcd")); + expect (String::toHexString ((int64) 0x1234abcd).equalsIgnoreCase ("1234abcd")); + expect (String::toHexString ((short) 0x12ab).equalsIgnoreCase ("12ab")); + + unsigned char data[] = { 1, 2, 3, 4, 0xa, 0xb, 0xc, 0xd }; + expect (String::toHexString (data, 8, 0).equalsIgnoreCase ("010203040a0b0c0d")); + expect (String::toHexString (data, 8, 1).equalsIgnoreCase ("01 02 03 04 0a 0b 0c 0d")); + expect (String::toHexString (data, 8, 2).equalsIgnoreCase ("0102 0304 0a0b 0c0d")); + + beginTest ("Subsections"); + String s3; + s3 = "abcdeFGHIJ"; + expect (s3.equalsIgnoreCase ("ABCdeFGhiJ")); + expect (s3.compareIgnoreCase (L"ABCdeFGhiJ") == 0); + expect (s3.containsIgnoreCase (s3.substring (3))); + expect (s3.indexOfAnyOf ("xyzf", 2, true) == 5); + expect (s3.indexOfAnyOf (L"xyzf", 2, false) == -1); + expect (s3.indexOfAnyOf ("xyzF", 2, false) == 5); + expect (s3.containsAnyOf (L"zzzFs")); + expect (s3.startsWith ("abcd")); + expect (s3.startsWithIgnoreCase (L"abCD")); + expect (s3.startsWith (String::empty)); + expect (s3.startsWithChar ('a')); + expect (s3.endsWith (String ("HIJ"))); + expect (s3.endsWithIgnoreCase (L"Hij")); + expect (s3.endsWith (String::empty)); + expect (s3.endsWithChar (L'J')); + expect (s3.indexOf ("HIJ") == 7); + expect (s3.indexOf (L"HIJK") == -1); + expect (s3.indexOfIgnoreCase ("hij") == 7); + expect (s3.indexOfIgnoreCase (L"hijk") == -1); + + String s4 (s3); + s4.append (String ("xyz123"), 3); + expect (s4 == s3 + "xyz"); + + expect (String (1234) < String (1235)); + expect (String (1235) > String (1234)); + expect (String (1234) >= String (1234)); + expect (String (1234) <= String (1234)); + expect (String (1235) >= String (1234)); + expect (String (1234) <= String (1235)); + + String s5 ("word word2 word3"); + expect (s5.containsWholeWord (String ("word2"))); + expect (s5.indexOfWholeWord ("word2") == 5); + expect (s5.containsWholeWord (L"word")); + expect (s5.containsWholeWord ("word3")); + expect (s5.containsWholeWord (s5)); + expect (s5.containsWholeWordIgnoreCase (L"Word2")); + expect (s5.indexOfWholeWordIgnoreCase ("Word2") == 5); + expect (s5.containsWholeWordIgnoreCase (L"Word")); + expect (s5.containsWholeWordIgnoreCase ("Word3")); + expect (! s5.containsWholeWordIgnoreCase (L"Wordx")); + expect (!s5.containsWholeWordIgnoreCase ("xWord2")); + expect (s5.containsNonWhitespaceChars()); + expect (! String (" \n\r\t").containsNonWhitespaceChars()); + + expect (s5.matchesWildcard (L"wor*", false)); + expect (s5.matchesWildcard ("wOr*", true)); + expect (s5.matchesWildcard (L"*word3", true)); + expect (s5.matchesWildcard ("*word?", true)); + expect (s5.matchesWildcard (L"Word*3", true)); + + expect (s5.fromFirstOccurrenceOf (String::empty, true, false) == s5); + expect (s5.fromFirstOccurrenceOf ("xword2", true, false) == s5.substring (100)); + expect (s5.fromFirstOccurrenceOf (L"word2", true, false) == s5.substring (5)); + expect (s5.fromFirstOccurrenceOf ("Word2", true, true) == s5.substring (5)); + expect (s5.fromFirstOccurrenceOf ("word2", false, false) == s5.getLastCharacters (6)); + expect (s5.fromFirstOccurrenceOf (L"Word2", false, true) == s5.getLastCharacters (6)); + + expect (s5.fromLastOccurrenceOf (String::empty, true, false) == s5); + expect (s5.fromLastOccurrenceOf (L"wordx", true, false) == s5); + expect (s5.fromLastOccurrenceOf ("word", true, false) == s5.getLastCharacters (5)); + expect (s5.fromLastOccurrenceOf (L"worD", true, true) == s5.getLastCharacters (5)); + expect (s5.fromLastOccurrenceOf ("word", false, false) == s5.getLastCharacters (1)); + expect (s5.fromLastOccurrenceOf (L"worD", false, true) == s5.getLastCharacters (1)); + + expect (s5.upToFirstOccurrenceOf (String::empty, true, false).isEmpty()); + expect (s5.upToFirstOccurrenceOf ("word4", true, false) == s5); + expect (s5.upToFirstOccurrenceOf (L"word2", true, false) == s5.substring (0, 10)); + expect (s5.upToFirstOccurrenceOf ("Word2", true, true) == s5.substring (0, 10)); + expect (s5.upToFirstOccurrenceOf (L"word2", false, false) == s5.substring (0, 5)); + expect (s5.upToFirstOccurrenceOf ("Word2", false, true) == s5.substring (0, 5)); + + expect (s5.upToLastOccurrenceOf (String::empty, true, false) == s5); + expect (s5.upToLastOccurrenceOf ("zword", true, false) == s5); + expect (s5.upToLastOccurrenceOf ("word", true, false) == s5.dropLastCharacters (1)); + expect (s5.dropLastCharacters(1).upToLastOccurrenceOf ("word", true, false) == s5.dropLastCharacters (1)); + expect (s5.upToLastOccurrenceOf ("Word", true, true) == s5.dropLastCharacters (1)); + expect (s5.upToLastOccurrenceOf ("word", false, false) == s5.dropLastCharacters (5)); + expect (s5.upToLastOccurrenceOf ("Word", false, true) == s5.dropLastCharacters (5)); + + expect (s5.replace ("word", L"xyz", false) == String ("xyz xyz2 xyz3")); + expect (s5.replace (L"Word", "xyz", true) == "xyz xyz2 xyz3"); + expect (s5.dropLastCharacters (1).replace ("Word", String ("xyz"), true) == L"xyz xyz2 xyz"); + expect (s5.replace ("Word", "", true) == " 2 3"); + expect (s5.replace ("Word2", L"xyz", true) == String ("word xyz word3")); + expect (s5.replaceCharacter (L'w', 'x') != s5); + expect (s5.replaceCharacter ('w', L'x').replaceCharacter ('x', 'w') == s5); + expect (s5.replaceCharacters ("wo", "xy") != s5); + expect (s5.replaceCharacters ("wo", "xy").replaceCharacters ("xy", L"wo") == s5); + expect (s5.retainCharacters ("1wordxya") == String ("wordwordword")); + expect (s5.retainCharacters (String::empty).isEmpty()); + expect (s5.removeCharacters ("1wordxya") == " 2 3"); + expect (s5.removeCharacters (String::empty) == s5); + expect (s5.initialSectionContainingOnly ("word") == L"word"); + expect (s5.initialSectionNotContaining (String ("xyz ")) == String ("word")); + expect (! s5.isQuotedString()); + expect (s5.quoted().isQuotedString()); + expect (! s5.quoted().unquoted().isQuotedString()); + expect (! String ("x'").isQuotedString()); + expect (String ("'x").isQuotedString()); + + String s6 (" \t xyz \t\r\n"); + expect (s6.trim() == String ("xyz")); + expect (s6.trim().trim() == "xyz"); + expect (s5.trim() == s5); + expect (s6.trimStart().trimEnd() == s6.trim()); + expect (s6.trimStart().trimEnd() == s6.trimEnd().trimStart()); + expect (s6.trimStart().trimStart().trimEnd().trimEnd() == s6.trimEnd().trimStart()); + expect (s6.trimStart() != s6.trimEnd()); + expect (("\t\r\n " + s6 + "\t\n \r").trim() == s6.trim()); + expect (String::repeatedString ("xyz", 3) == L"xyzxyzxyz"); + } + + { + beginTest ("UTF8"); + + String s ("word word2 word3"); + + { + char buffer [100]; + memset (buffer, 0xff, sizeof (buffer)); + s.copyToUTF8 (buffer, 100); + expect (String::fromUTF8 (buffer, 100) == s); + + juce_wchar bufferUnicode [100]; + memset (bufferUnicode, 0xff, sizeof (bufferUnicode)); + s.copyToUnicode (bufferUnicode, 100); + expect (String (bufferUnicode, 100) == s); + } + + { + juce_wchar wideBuffer [50]; + zerostruct (wideBuffer); + + for (int i = 0; i < numElementsInArray (wideBuffer) - 1; ++i) + wideBuffer[i] = (juce_wchar) (1 + Random::getSystemRandom().nextInt (std::numeric_limits::max() - 1)); + + String wide (wideBuffer); + expect (wide == (const juce_wchar*) wideBuffer); + expect (wide.length() == numElementsInArray (wideBuffer) - 1); + expect (String::fromUTF8 (wide.toUTF8()) == wide); + expect (String::fromUTF8 (wide.toUTF8()) == wideBuffer); + } + } + } +}; + +static StringTests stringUnitTests; + +#endif + END_JUCE_NAMESPACE /*** End of inlined file: juce_String.cpp ***/ @@ -17150,6 +17430,184 @@ Array & DeletedAtShutdown::getObjects() END_JUCE_NAMESPACE /*** End of inlined file: juce_DeletedAtShutdown.cpp ***/ + +/*** Start of inlined file: juce_UnitTest.cpp ***/ +BEGIN_JUCE_NAMESPACE + +UnitTest::UnitTest (const String& name_) + : name (name_), runner (0) +{ + getAllTests().add (this); +} + +UnitTest::~UnitTest() +{ + getAllTests().removeValue (this); +} + +Array& UnitTest::getAllTests() +{ + static Array tests; + return tests; +} + +void UnitTest::performTest (UnitTestRunner* const runner_) +{ + jassert (runner_ != 0); + runner = runner_; + runTest(); +} + +void UnitTest::logMessage (const String& message) +{ + runner->logMessage (message); +} + +void UnitTest::beginTest (const String& testName) +{ + runner->beginNewTest (this, testName); +} + +void UnitTest::expect (const bool result, const String& failureMessage) +{ + if (result) + runner->addPass(); + else + runner->addFail (failureMessage); +} + +UnitTestRunner::UnitTestRunner() + : currentTest (0), assertOnFailure (false) +{ +} + +UnitTestRunner::~UnitTestRunner() +{ +} + +void UnitTestRunner::resultsUpdated() +{ +} + +void UnitTestRunner::runTests (const Array& tests, const bool assertOnFailure_) +{ + results.clear(); + assertOnFailure = assertOnFailure_; + resultsUpdated(); + + for (int i = 0; i < tests.size(); ++i) + { + try + { + tests.getUnchecked(i)->performTest (this); + } + catch (...) + { + addFail ("An unhandled exception was thrown!"); + } + } + + endTest(); +} + +void UnitTestRunner::runAllTests (const bool assertOnFailure_) +{ + runTests (UnitTest::getAllTests(), assertOnFailure_); +} + +void UnitTestRunner::logMessage (const String& message) +{ + Logger::writeToLog (message); +} + +void UnitTestRunner::beginNewTest (UnitTest* const test, const String& subCategory) +{ + endTest(); + currentTest = test; + + TestResult* const r = new TestResult(); + r->unitTestName = test->getName(); + r->subcategoryName = subCategory; + r->passes = 0; + r->failures = 0; + results.add (r); + + logMessage ("-----------------------------------------------------------------"); + logMessage ("Starting test: " + r->unitTestName + " / " + subCategory + "..."); + + resultsUpdated(); +} + +void UnitTestRunner::endTest() +{ + if (results.size() > 0) + { + TestResult* const r = results.getLast(); + + if (r->failures > 0) + { + String m ("FAILED!!"); + m << r->failures << (r->failures == 1 ? "test" : "tests") + << " failed, out of a total of " << (r->passes + r->failures); + + logMessage (String::empty); + logMessage (m); + logMessage (String::empty); + } + else + { + logMessage ("All tests completed successfully"); + } + } +} + +void UnitTestRunner::addPass() +{ + { + const ScopedLock sl (results.getLock()); + + TestResult* const r = results.getLast(); + jassert (r != 0); // You need to call UnitTest::beginTest() before performing any tests! + + r->passes++; + + String message ("Test "); + message << (r->failures + r->passes) << " passed"; + logMessage (message); + } + + resultsUpdated(); +} + +void UnitTestRunner::addFail (const String& failureMessage) +{ + { + const ScopedLock sl (results.getLock()); + + TestResult* const r = results.getLast(); + jassert (r != 0); // You need to call UnitTest::beginTest() before performing any tests! + + r->failures++; + + String message ("!!! Test "); + message << (r->failures + r->passes) << " failed"; + + if (failureMessage.isNotEmpty()) + message << ": " << failureMessage; + + r->messages.add (message); + + logMessage (message); + } + + resultsUpdated(); + + if (assertOnFailure) { jassertfalse } +} + +END_JUCE_NAMESPACE +/*** End of inlined file: juce_UnitTest.cpp ***/ + #endif #if JUCE_BUILD_MISC @@ -242035,7 +242493,7 @@ private: void createWindow() { - DWORD exstyle = WS_EX_ACCEPTFILES; + DWORD exstyle = WS_EX_ACCEPTFILES; // | WS_EX_NOACTIVATE; DWORD type = WS_CLIPSIBLINGS | WS_CLIPCHILDREN; if (hasTitleBar()) @@ -248359,15 +248817,6 @@ bool AudioCDBurner::addAudioTrack (AudioSource* audioSource, int numSamples) // compiled on its own). #if JUCE_INCLUDED_FILE -using ::free; - -namespace MidiConstants -{ - static const int midiBufferSize = 1024 * 10; - static const int numInHeaders = 32; - static const int inBufferSize = 256; -} - class MidiInThread : public Thread { public: @@ -248375,18 +248824,19 @@ public: MidiInThread (MidiInput* const input_, MidiInputCallback* const callback_) : Thread ("Juce Midi"), - hIn (0), + deviceHandle (0), input (input_), callback (callback_), isStarted (false), - startTime (0), - pendingLength(0) + startTime (0) { - for (int i = MidiConstants::numInHeaders; --i >= 0;) + pending.ensureSize ((int) defaultBufferSize); + + for (int i = (int) numInHeaders; --i >= 0;) { zeromem (&hdr[i], sizeof (MIDIHDR)); hdr[i].lpData = inData[i]; - hdr[i].dwBufferLength = MidiConstants::inBufferSize; + hdr[i].dwBufferLength = (int) inBufferSize; } }; @@ -248394,12 +248844,12 @@ public: { stop(); - if (hIn != 0) + if (deviceHandle != 0) { int count = 5; while (--count >= 0) { - if (midiInClose (hIn) == MMSYSERR_NOERROR) + if (midiInClose (deviceHandle) == MMSYSERR_NOERROR) break; Sleep (20); @@ -248413,24 +248863,11 @@ public: if (byte < 0x80) return; - const int numBytes = MidiMessage::getMessageLengthFromFirstByte ((uint8) byte); - const double time = timeStampToTime (timeStamp); + const int time = timeStampToMs (timeStamp); { const ScopedLock sl (lock); - - if (pendingLength < MidiConstants::midiBufferSize - 12) - { - char* const p = pending + pendingLength; - *(double*) p = time; - *(uint32*) (p + 8) = numBytes; - *(uint32*) (p + 12) = message; - pendingLength += 12 + numBytes; - } - else - { - jassertfalse; // midi buffer overflow! You might need to increase the size.. - } + pending.addEvent (&message, 3, time); } notify(); @@ -248438,27 +248875,14 @@ public: void handleSysEx (MIDIHDR* const hdr, const uint32 timeStamp) { + const int time = timeStampToMs (timeStamp); const int num = hdr->dwBytesRecorded; if (num > 0) { - const double time = timeStampToTime (timeStamp); - { const ScopedLock sl (lock); - - if (pendingLength < MidiConstants::midiBufferSize - (8 + num)) - { - char* const p = pending + pendingLength; - *(double*) p = time; - *(uint32*) (p + 8) = num; - memcpy (p + 12, hdr->lpData, num); - pendingLength += 12 + num; - } - else - { - jassertfalse; // midi buffer overflow! You might need to increase the size.. - } + pending.addEvent (hdr->lpData, num, time); } notify(); @@ -248468,65 +248892,52 @@ public: void writeBlock (const int i) { hdr[i].dwBytesRecorded = 0; - MMRESULT res = midiInPrepareHeader (hIn, &hdr[i], sizeof (MIDIHDR)); + MMRESULT res = midiInPrepareHeader (deviceHandle, &hdr[i], sizeof (MIDIHDR)); jassert (res == MMSYSERR_NOERROR); - res = midiInAddBuffer (hIn, &hdr[i], sizeof (MIDIHDR)); + res = midiInAddBuffer (deviceHandle, &hdr[i], sizeof (MIDIHDR)); jassert (res == MMSYSERR_NOERROR); } void run() { - MemoryBlock pendingCopy (64); + MidiBuffer newEvents; + newEvents.ensureSize ((int) defaultBufferSize); while (! threadShouldExit()) { - for (int i = 0; i < MidiConstants::numInHeaders; ++i) + for (int i = 0; i < (int) numInHeaders; ++i) { if ((hdr[i].dwFlags & WHDR_DONE) != 0) { - MMRESULT res = midiInUnprepareHeader (hIn, &hdr[i], sizeof (MIDIHDR)); + MMRESULT res = midiInUnprepareHeader (deviceHandle, &hdr[i], sizeof (MIDIHDR)); (void) res; jassert (res == MMSYSERR_NOERROR); writeBlock (i); } } - int len; + newEvents.clear(); // (resets it without freeing allocated storage) { const ScopedLock sl (lock); - - len = pendingLength; - - if (len > 0) - { - pendingCopy.ensureSize (len); - pendingCopy.copyFrom (pending, 0, len); - pendingLength = 0; - } + newEvents.swapWith (pending); } //xxx needs to figure out if blocks are broken up or not - if (len == 0) + if (newEvents.isEmpty()) { wait (500); } else { - const char* p = (const char*) pendingCopy.getData(); + MidiMessage message (0xf4, 0.0); + int time; - while (len > 0) + for (MidiBuffer::Iterator i (newEvents); i.getNextEvent (message, time);) { - const double time = *(const double*) p; - const int messageLen = *(const int*) (p + 8); - - const MidiMessage message ((const uint8*) (p + 12), messageLen, time); - + message.setTimeStamp (time * 0.001); callback->handleIncomingMidiMessage (input, message); - - p += 12 + messageLen; - len -= 12 + messageLen; } } } @@ -248534,26 +248945,25 @@ public: void start() { - jassert (hIn != 0); - if (hIn != 0 && ! isStarted) + jassert (deviceHandle != 0); + if (deviceHandle != 0 && ! isStarted) { stop(); activeMidiThreads.addIfNotAlreadyThere (this); int i; - for (i = 0; i < MidiConstants::numInHeaders; ++i) + for (i = 0; i < (int) numInHeaders; ++i) writeBlock (i); startTime = Time::getMillisecondCounter(); - MMRESULT res = midiInStart (hIn); - + MMRESULT res = midiInStart (deviceHandle); jassert (res == MMSYSERR_NOERROR); if (res == MMSYSERR_NOERROR) { isStarted = true; - pendingLength = 0; + pending.clear(); startThread (6); } } @@ -248565,19 +248975,19 @@ public: { stopThread (5000); - midiInReset (hIn); - midiInStop (hIn); + midiInReset (deviceHandle); + midiInStop (deviceHandle); activeMidiThreads.removeValue (this); { const ScopedLock sl (lock); } - for (int i = MidiConstants::numInHeaders; --i >= 0;) + for (int i = (int) numInHeaders; --i >= 0;) { if ((hdr[i].dwFlags & WHDR_DONE) != 0) { int c = 10; - while (--c >= 0 && midiInUnprepareHeader (hIn, &hdr[i], sizeof (MIDIHDR)) == MIDIERR_STILLPLAYING) + while (--c >= 0 && midiInUnprepareHeader (deviceHandle, &hdr[i], sizeof (MIDIHDR)) == MIDIERR_STILLPLAYING) Sleep (20); jassert (c >= 0); @@ -248585,7 +248995,7 @@ public: } isStarted = false; - pendingLength = 0; + pending.clear(); } } @@ -248604,7 +249014,7 @@ public: juce_UseDebuggingNewOperator - HMIDIIN hIn; + HMIDIIN deviceHandle; private: static Array activeMidiThreads; @@ -248615,13 +249025,16 @@ private: uint32 startTime; CriticalSection lock; - MIDIHDR hdr [MidiConstants::numInHeaders]; - char inData [MidiConstants::numInHeaders] [MidiConstants::inBufferSize]; + enum { defaultBufferSize = 8192, + numInHeaders = 32, + inBufferSize = 256 }; + + MIDIHDR hdr [(int) numInHeaders]; + char inData [(int) numInHeaders] [(int) inBufferSize]; - int pendingLength; - char pending [MidiConstants::midiBufferSize]; + MidiBuffer pending; - double timeStampToTime (uint32 timeStamp) + int timeStampToMs (uint32 timeStamp) { timeStamp += startTime; @@ -248634,7 +249047,7 @@ private: timeStamp = now; } - return 0.001 * timeStamp; + return (int) timeStamp; } MidiInThread (const MidiInThread&); @@ -248686,7 +249099,7 @@ MidiInput* MidiInput::openDevice (const int index, MidiInputCallback* const call if (index == n) { deviceId = i; - name = String (mc.szPname, sizeof (mc.szPname)); + name = String (mc.szPname, numElementsInArray (mc.szPname)); break; } @@ -248705,7 +249118,7 @@ MidiInput* MidiInput::openDevice (const int index, MidiInputCallback* const call if (err == MMSYSERR_NOERROR) { - thread->hIn = h; + thread->deviceHandle = h; in->internal = thread.release(); return in.release(); } diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 33e51f81cb..a4737b4a59 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -64,7 +64,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 52 -#define JUCE_BUILDNUMBER 59 +#define JUCE_BUILDNUMBER 60 /** Current Juce version number. @@ -1757,36 +1757,43 @@ public: int compareLexicographically (const String& other) const throw(); /** Tests whether the string begins with another string. + If the parameter is an empty string, this will always return true. Uses a case-sensitive comparison. */ bool startsWith (const String& text) const throw(); /** Tests whether the string begins with a particular character. + If the character is 0, this will always return false. Uses a case-sensitive comparison. */ bool startsWithChar (juce_wchar character) const throw(); /** Tests whether the string begins with another string. + If the parameter is an empty string, this will always return true. Uses a case-insensitive comparison. */ bool startsWithIgnoreCase (const String& text) const throw(); /** Tests whether the string ends with another string. + If the parameter is an empty string, this will always return true. Uses a case-sensitive comparison. */ bool endsWith (const String& text) const throw(); /** Tests whether the string ends with a particular character. + If the character is 0, this will always return false. Uses a case-sensitive comparison. */ bool endsWithChar (juce_wchar character) const throw(); /** Tests whether the string ends with another string. + If the parameter is an empty string, this will always return true. Uses a case-insensitive comparison. */ bool endsWithIgnoreCase (const String& text) const throw(); /** Tests whether the string contains another substring. + If the parameter is an empty string, this will always return true. Uses a case-sensitive comparison. */ bool contains (const String& text) const throw(); @@ -13870,7 +13877,6 @@ public: private: File logFile; CriticalSection logLock; - ScopedPointer logStream; void trimFileSize (int maxFileSizeBytes) const; @@ -61385,6 +61391,221 @@ public: #endif #ifndef __JUCE_UNDOMANAGER_JUCEHEADER__ +#endif +#ifndef __JUCE_UNITTEST_JUCEHEADER__ + +/*** Start of inlined file: juce_UnitTest.h ***/ +#ifndef __JUCE_UNITTEST_JUCEHEADER__ +#define __JUCE_UNITTEST_JUCEHEADER__ + +class UnitTestRunner; + +/** + This is a base class for classes that perform a unit test. + + To write a test using this class, your code should look something like this: + + @code + class MyTest : public UnitTest + { + public: + MyTest() : UnitTest ("Foobar testing") {} + + void runTest() + { + beginTest ("Part 1"); + + expect (myFoobar.doesSomething()); + expect (myFoobar.doesSomethingElse()); + + beginTest ("Part 2"); + + expect (myOtherFoobar.doesSomething()); + expect (myOtherFoobar.doesSomethingElse()); + + ...etc.. + } + }; + + // Creating a static instance will automatically add the instance to the array + // returned by UnitTest::getAllTests(), so the test will be included when you call + // UnitTestRunner::runAllTests() + static MyTest test; + @endcode + + To run a test, use the UnitTestRunner class. + + @see UnitTestRunner +*/ +class UnitTest +{ +public: + + /** Creates a test with the given name. */ + UnitTest (const String& name); + + /** Destructor. */ + virtual ~UnitTest(); + + /** Returns the name of the test. */ + const String getName() const throw() { return name; } + + /** Runs the test, using the specified UnitTestRunner. + You shouldn't need to call this method directly - use + UnitTestRunner::runTests() instead. + */ + void performTest (UnitTestRunner* runner); + + /** Returns the set of all UnitTest objects that currently exist. */ + static Array& getAllTests(); + + /** Implement this method in your subclass to actually run your tests. + + The content of your implementation should call beginTest() and expect() + to perform the tests. + */ + virtual void runTest() = 0; + + /** Tells the system that a new subsection of tests is beginning. + This should be called from your runTest() method, and may be called + as many times as you like, to demarcate different sets of tests. + */ + void beginTest (const String& testName); + + /** Checks that the result of a test is true, and logs this result. + + In your runTest() method, you should call this method for each condition that + you want to check, e.g. + + @code + void runTest() + { + beginTest ("basic tests"); + expect (x + y == 2); + expect (getThing() == someThing); + ...etc... + } + @endcode + + If testResult is true, a pass is logged; if it's false, a failure is logged. + If the failure message is specified, it will be written to the log if the test fails. + */ + void expect (bool testResult, const String& failureMessage = String::empty); + + /** Writes a message to the test log. + This can only be called from within your runTest() method. + */ + void logMessage (const String& message); + +private: + + const String name; + UnitTestRunner* runner; + + UnitTest (const UnitTest&); + UnitTest& operator= (const UnitTest&); +}; + +/** + Runs a set of unit tests. + + You can instantiate one of these objects and use it to invoke tests on a set of + UnitTest objects. + + By using a subclass of UnitTestRunner, you can intercept logging messages and + perform custom behaviour when each test completes. + + @see UnitTest +*/ +class UnitTestRunner +{ +public: + + /** */ + UnitTestRunner(); + + /** Destructor. */ + virtual ~UnitTestRunner(); + + /** Runs a set of tests. + + The tests are performed in order, and the results are logged. To run all the + registered UnitTest objects that exist, use runAllTests(). + */ + void runTests (const Array& tests, bool assertOnFailure); + + /** Runs all the UnitTest objects that currently exist. + This calls runTests() for all the objects listed in UnitTest::getAllTests(). + */ + void runAllTests (bool assertOnFailure); + + /** Contains the results of a test. + + One of these objects is instantiated each time UnitTest::beginTest() is called, and + it contains details of the number of subsequent UnitTest::expect() calls that are + made. + */ + struct TestResult + { + /** The main name of this test (i.e. the name of the UnitTest object being run). */ + String unitTestName; + /** The name of the current subcategory (i.e. the name that was set when UnitTest::beginTest() was called). */ + String subcategoryName; + + /** The number of UnitTest::expect() calls that succeeded. */ + int passes; + /** The number of UnitTest::expect() calls that failed. */ + int failures; + + /** A list of messages describing the failed tests. */ + StringArray messages; + }; + + /** Returns the number of TestResult objects that have been performed. + @see getResult + */ + int getNumResults() const throw(); + + /** Returns one of the TestResult objects that describes a test that has been run. + @see getNumResults + */ + const TestResult* getResult (int index) const throw(); + +protected: + /** Called when the list of results changes. + You can override this to perform some sort of behaviour when results are added. + */ + virtual void resultsUpdated(); + + /** Logs a message about the current test progress. + By default this just writes the message to the Logger class, but you could override + this to do something else with the data. + */ + virtual void logMessage (const String& message); + +private: + + friend class UnitTest; + + UnitTest* currentTest; + String currentSubCategory; + OwnedArray results; + bool assertOnFailure; + + void beginNewTest (UnitTest* test, const String& subCategory); + void endTest(); + + void addPass(); + void addFail (const String& failureMessage); + + UnitTestRunner (const UnitTestRunner&); + UnitTestRunner& operator= (const UnitTestRunner&); +}; + +#endif // __JUCE_UNITTEST_JUCEHEADER__ +/*** End of inlined file: juce_UnitTest.h ***/ + + #endif #endif diff --git a/src/core/juce_FileLogger.cpp b/src/core/juce_FileLogger.cpp index 4c7df6fe5e..d6dfd64eca 100644 --- a/src/core/juce_FileLogger.cpp +++ b/src/core/juce_FileLogger.cpp @@ -50,9 +50,6 @@ FileLogger::FileLogger (const File& logFile_, logFile_.create(); } - logStream = logFile_.createOutputStream (256); - jassert (logStream != 0); - String welcome; welcome << "\r\n**********************************************************\r\n" << welcomeMessage @@ -69,14 +66,12 @@ FileLogger::~FileLogger() //============================================================================== void FileLogger::logMessage (const String& message) { - if (logStream != 0) - { - DBG (message); + DBG (message); - const ScopedLock sl (logLock); - (*logStream) << message << "\r\n"; - logStream->flush(); - } + const ScopedLock sl (logLock); + + FileOutputStream out (logFile, 256); + out << message << "\r\n"; } diff --git a/src/core/juce_FileLogger.h b/src/core/juce_FileLogger.h index f1985a1776..f5836b6f74 100644 --- a/src/core/juce_FileLogger.h +++ b/src/core/juce_FileLogger.h @@ -100,7 +100,6 @@ public: private: File logFile; CriticalSection logLock; - ScopedPointer logStream; void trimFileSize (int maxFileSizeBytes) const; diff --git a/src/core/juce_Initialisation.cpp b/src/core/juce_Initialisation.cpp index de7d04979e..86e0248887 100644 --- a/src/core/juce_Initialisation.cpp +++ b/src/core/juce_Initialisation.cpp @@ -50,125 +50,6 @@ BEGIN_JUCE_NAMESPACE extern void juce_CheckForDanglingStreams(); // (in juce_OutputStream.cpp) #endif -//============================================================================== -#if JUCE_DEBUG - -namespace SimpleUnitTests -{ - template - class AtomicTester - { - public: - AtomicTester() {} - - static void testInteger() - { - Atomic a, b; - a.set ((Type) 10); - a += (Type) 15; - a.memoryBarrier(); - a -= (Type) 5; - ++a; ++a; --a; - a.memoryBarrier(); - - testFloat(); - } - - static void testFloat() - { - Atomic a, b; - a = (Type) 21; - a.memoryBarrier(); - - /* These are some simple test cases to check the atomics - let me know - if any of these assertions fail on your system! - */ - jassert (a.get() == (Type) 21); - jassert (a.compareAndSetValue ((Type) 100, (Type) 50) == (Type) 21); - jassert (a.get() == (Type) 21); - jassert (a.compareAndSetValue ((Type) 101, a.get()) == (Type) 21); - jassert (a.get() == (Type) 101); - jassert (! a.compareAndSetBool ((Type) 300, (Type) 200)); - jassert (a.get() == (Type) 101); - jassert (a.compareAndSetBool ((Type) 200, a.get())); - jassert (a.get() == (Type) 200); - - jassert (a.exchange ((Type) 300) == (Type) 200); - jassert (a.get() == (Type) 300); - - b = a; - jassert (b.get() == a.get()); - } - }; - - static void runBasicTests() - { - // Some simple test code, to keep an eye on things and make sure these functions - // work ok on all platforms. Let me know if any of these assertions fail on your system! - - static_jassert (sizeof (pointer_sized_int) == sizeof (void*)); - static_jassert (sizeof (int8) == 1); - static_jassert (sizeof (uint8) == 1); - static_jassert (sizeof (int16) == 2); - static_jassert (sizeof (uint16) == 2); - static_jassert (sizeof (int32) == 4); - static_jassert (sizeof (uint32) == 4); - static_jassert (sizeof (int64) == 8); - static_jassert (sizeof (uint64) == 8); - - char a1[7]; - jassert (numElementsInArray(a1) == 7); - int a2[3]; - jassert (numElementsInArray(a2) == 3); - - jassert (ByteOrder::swap ((uint16) 0x1122) == 0x2211); - jassert (ByteOrder::swap ((uint32) 0x11223344) == 0x44332211); - jassert (ByteOrder::swap ((uint64) literal64bit (0x1122334455667788)) == literal64bit (0x8877665544332211)); - - // Some quick stream tests.. - int randomInt = Random::getSystemRandom().nextInt(); - int64 randomInt64 = Random::getSystemRandom().nextInt64(); - double randomDouble = Random::getSystemRandom().nextDouble(); - String randomString; - for (int i = 50; --i >= 0;) - randomString << (juce_wchar) (Random::getSystemRandom().nextInt() & 0xffff); - - MemoryOutputStream mo; - mo.writeInt (randomInt); - mo.writeIntBigEndian (randomInt); - mo.writeCompressedInt (randomInt); - mo.writeString (randomString); - mo.writeInt64 (randomInt64); - mo.writeInt64BigEndian (randomInt64); - mo.writeDouble (randomDouble); - mo.writeDoubleBigEndian (randomDouble); - - MemoryInputStream mi (mo.getData(), mo.getDataSize(), false); - jassert (mi.readInt() == randomInt); - jassert (mi.readIntBigEndian() == randomInt); - jassert (mi.readCompressedInt() == randomInt); - jassert (mi.readString() == randomString); - jassert (mi.readInt64() == randomInt64); - jassert (mi.readInt64BigEndian() == randomInt64); - jassert (mi.readDouble() == randomDouble); - jassert (mi.readDoubleBigEndian() == randomDouble); - - AtomicTester ::testInteger(); - AtomicTester ::testInteger(); - AtomicTester ::testInteger(); - AtomicTester ::testInteger(); - AtomicTester ::testInteger(); - AtomicTester ::testInteger(); - AtomicTester ::testInteger(); - AtomicTester ::testFloat(); - #if ! JUCE_64BIT_ATOMICS_UNAVAILABLE // 64-bit intrinsics aren't available on some old platforms - AtomicTester ::testInteger(); - AtomicTester ::testInteger(); - AtomicTester ::testFloat(); - #endif - } -} -#endif //============================================================================== static bool juceInitialisedNonGUI = false; @@ -181,14 +62,22 @@ void JUCE_PUBLIC_FUNCTION initialiseJuce_NonGUI() JUCE_AUTORELEASEPOOL - #if JUCE_DEBUG - SimpleUnitTests::runBasicTests(); - #endif - DBG (SystemStats::getJUCEVersion()); SystemStats::initialiseStats(); Random::getSystemRandom().setSeedRandomly(); // (mustn't call this before initialiseStats() because it relies on the time being set up) } + + // Some basic tests, to keep an eye on things and make sure these types work ok + // on all platforms. Let me know if any of these assertions fail on your system! + static_jassert (sizeof (pointer_sized_int) == sizeof (void*)); + static_jassert (sizeof (int8) == 1); + static_jassert (sizeof (uint8) == 1); + static_jassert (sizeof (int16) == 2); + static_jassert (sizeof (uint16) == 2); + static_jassert (sizeof (int32) == 4); + static_jassert (sizeof (uint32) == 4); + static_jassert (sizeof (int64) == 8); + static_jassert (sizeof (uint64) == 8); } void JUCE_PUBLIC_FUNCTION shutdownJuce_NonGUI() @@ -269,4 +158,95 @@ void JUCE_PUBLIC_FUNCTION shutdownJuce_GUI() #endif + +//============================================================================== +#if JUCE_UNIT_TESTS + +#include "../utilities/juce_UnitTest.h" + +class AtomicTests : public UnitTest +{ +public: + AtomicTests() : UnitTest ("Atomics") {} + + void runTest() + { + beginTest ("Misc"); + + char a1[7]; + expect (numElementsInArray(a1) == 7); + int a2[3]; + expect (numElementsInArray(a2) == 3); + + expect (ByteOrder::swap ((uint16) 0x1122) == 0x2211); + expect (ByteOrder::swap ((uint32) 0x11223344) == 0x44332211); + expect (ByteOrder::swap ((uint64) literal64bit (0x1122334455667788)) == literal64bit (0x8877665544332211)); + + beginTest ("Atomic types"); + AtomicTester ::testInteger (*this); + AtomicTester ::testInteger (*this); + AtomicTester ::testInteger (*this); + AtomicTester ::testInteger (*this); + AtomicTester ::testInteger (*this); + AtomicTester ::testInteger (*this); + AtomicTester ::testInteger (*this); + AtomicTester ::testFloat (*this); + #if ! JUCE_64BIT_ATOMICS_UNAVAILABLE // 64-bit intrinsics aren't available on some old platforms + AtomicTester ::testInteger (*this); + AtomicTester ::testInteger (*this); + AtomicTester ::testFloat (*this); + #endif + } + + template + class AtomicTester + { + public: + AtomicTester() {} + + static void testInteger (UnitTest& test) + { + Atomic a, b; + a.set ((Type) 10); + a += (Type) 15; + a.memoryBarrier(); + a -= (Type) 5; + ++a; ++a; --a; + a.memoryBarrier(); + + testFloat (test); + } + + static void testFloat (UnitTest& test) + { + Atomic a, b; + a = (Type) 21; + a.memoryBarrier(); + + /* These are some simple test cases to check the atomics - let me know + if any of these assertions fail on your system! + */ + test.expect (a.get() == (Type) 21); + test.expect (a.compareAndSetValue ((Type) 100, (Type) 50) == (Type) 21); + test.expect (a.get() == (Type) 21); + test.expect (a.compareAndSetValue ((Type) 101, a.get()) == (Type) 21); + test.expect (a.get() == (Type) 101); + test.expect (! a.compareAndSetBool ((Type) 300, (Type) 200)); + test.expect (a.get() == (Type) 101); + test.expect (a.compareAndSetBool ((Type) 200, a.get())); + test.expect (a.get() == (Type) 200); + + test.expect (a.exchange ((Type) 300) == (Type) 200); + test.expect (a.get() == (Type) 300); + + b = a; + test.expect (b.get() == a.get()); + } + }; +}; + +static AtomicTests atomicUnitTests; + +#endif + END_JUCE_NAMESPACE diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h index 47d4d5eba8..0f73be32ad 100644 --- a/src/core/juce_StandardHeader.h +++ b/src/core/juce_StandardHeader.h @@ -33,7 +33,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 52 -#define JUCE_BUILDNUMBER 59 +#define JUCE_BUILDNUMBER 60 /** Current Juce version number. diff --git a/src/io/files/juce_File.cpp b/src/io/files/juce_File.cpp index 02279e1bbb..86ad8d2d14 100644 --- a/src/io/files/juce_File.cpp +++ b/src/io/files/juce_File.cpp @@ -570,7 +570,8 @@ int File::findChildFiles (Array& results, } if (searchRecursively && itemIsDirectory - && fileTypeMatches (whatToLookFor | findDirectories, true, itemIsHidden)) + && ((! itemIsHidden) || (whatToLookFor & File::ignoreHiddenFiles) == 0)) + { total += fileFound.findChildFiles (results, whatToLookFor, true, wildCardPattern); } diff --git a/src/io/streams/juce_MemoryInputStream.cpp b/src/io/streams/juce_MemoryInputStream.cpp index ce419e5316..7188fcbbe2 100644 --- a/src/io/streams/juce_MemoryInputStream.cpp +++ b/src/io/streams/juce_MemoryInputStream.cpp @@ -92,4 +92,53 @@ int64 MemoryInputStream::getPosition() return position; } + +#if JUCE_UNIT_TESTS + +#include "../../utilities/juce_UnitTest.h" +#include "../../core/juce_Random.h" +#include "juce_MemoryOutputStream.h" + +class MemoryStreamTests : public UnitTest +{ +public: + MemoryStreamTests() : UnitTest ("MemoryInputStream & MemoryOutputStream") {} + + void runTest() + { + beginTest ("Basics"); + + int randomInt = Random::getSystemRandom().nextInt(); + int64 randomInt64 = Random::getSystemRandom().nextInt64(); + double randomDouble = Random::getSystemRandom().nextDouble(); + String randomString; + for (int i = 50; --i >= 0;) + randomString << (juce_wchar) (Random::getSystemRandom().nextInt() & 0xffff); + + MemoryOutputStream mo; + mo.writeInt (randomInt); + mo.writeIntBigEndian (randomInt); + mo.writeCompressedInt (randomInt); + mo.writeString (randomString); + mo.writeInt64 (randomInt64); + mo.writeInt64BigEndian (randomInt64); + mo.writeDouble (randomDouble); + mo.writeDoubleBigEndian (randomDouble); + + MemoryInputStream mi (mo.getData(), mo.getDataSize(), false); + expect (mi.readInt() == randomInt); + expect (mi.readIntBigEndian() == randomInt); + expect (mi.readCompressedInt() == randomInt); + expect (mi.readString() == randomString); + expect (mi.readInt64() == randomInt64); + expect (mi.readInt64BigEndian() == randomInt64); + expect (mi.readDouble() == randomDouble); + expect (mi.readDoubleBigEndian() == randomDouble); + } +}; + +static MemoryStreamTests memoryInputStreamUnitTests; + +#endif + END_JUCE_NAMESPACE diff --git a/src/juce_app_includes.h b/src/juce_app_includes.h index 34c40c6a3a..506012918a 100644 --- a/src/juce_app_includes.h +++ b/src/juce_app_includes.h @@ -758,5 +758,8 @@ #ifndef __JUCE_UNDOMANAGER_JUCEHEADER__ #include "utilities/juce_UndoManager.h" #endif +#ifndef __JUCE_UNITTEST_JUCEHEADER__ + #include "utilities/juce_UnitTest.h" +#endif #endif diff --git a/src/native/windows/juce_win32_Midi.cpp b/src/native/windows/juce_win32_Midi.cpp index ad3c1f17aa..feee1c3a0c 100644 --- a/src/native/windows/juce_win32_Midi.cpp +++ b/src/native/windows/juce_win32_Midi.cpp @@ -28,15 +28,6 @@ #if JUCE_INCLUDED_FILE //============================================================================== -using ::free; - -namespace MidiConstants -{ - static const int midiBufferSize = 1024 * 10; - static const int numInHeaders = 32; - static const int inBufferSize = 256; -} - class MidiInThread : public Thread { public: @@ -44,18 +35,19 @@ public: MidiInThread (MidiInput* const input_, MidiInputCallback* const callback_) : Thread ("Juce Midi"), - hIn (0), + deviceHandle (0), input (input_), callback (callback_), isStarted (false), - startTime (0), - pendingLength(0) + startTime (0) { - for (int i = MidiConstants::numInHeaders; --i >= 0;) + pending.ensureSize ((int) defaultBufferSize); + + for (int i = (int) numInHeaders; --i >= 0;) { zeromem (&hdr[i], sizeof (MIDIHDR)); hdr[i].lpData = inData[i]; - hdr[i].dwBufferLength = MidiConstants::inBufferSize; + hdr[i].dwBufferLength = (int) inBufferSize; } }; @@ -63,12 +55,12 @@ public: { stop(); - if (hIn != 0) + if (deviceHandle != 0) { int count = 5; while (--count >= 0) { - if (midiInClose (hIn) == MMSYSERR_NOERROR) + if (midiInClose (deviceHandle) == MMSYSERR_NOERROR) break; Sleep (20); @@ -83,24 +75,11 @@ public: if (byte < 0x80) return; - const int numBytes = MidiMessage::getMessageLengthFromFirstByte ((uint8) byte); - const double time = timeStampToTime (timeStamp); + const int time = timeStampToMs (timeStamp); { const ScopedLock sl (lock); - - if (pendingLength < MidiConstants::midiBufferSize - 12) - { - char* const p = pending + pendingLength; - *(double*) p = time; - *(uint32*) (p + 8) = numBytes; - *(uint32*) (p + 12) = message; - pendingLength += 12 + numBytes; - } - else - { - jassertfalse; // midi buffer overflow! You might need to increase the size.. - } + pending.addEvent (&message, 3, time); } notify(); @@ -108,27 +87,14 @@ public: void handleSysEx (MIDIHDR* const hdr, const uint32 timeStamp) { + const int time = timeStampToMs (timeStamp); const int num = hdr->dwBytesRecorded; if (num > 0) { - const double time = timeStampToTime (timeStamp); - { const ScopedLock sl (lock); - - if (pendingLength < MidiConstants::midiBufferSize - (8 + num)) - { - char* const p = pending + pendingLength; - *(double*) p = time; - *(uint32*) (p + 8) = num; - memcpy (p + 12, hdr->lpData, num); - pendingLength += 12 + num; - } - else - { - jassertfalse; // midi buffer overflow! You might need to increase the size.. - } + pending.addEvent (hdr->lpData, num, time); } notify(); @@ -138,65 +104,52 @@ public: void writeBlock (const int i) { hdr[i].dwBytesRecorded = 0; - MMRESULT res = midiInPrepareHeader (hIn, &hdr[i], sizeof (MIDIHDR)); + MMRESULT res = midiInPrepareHeader (deviceHandle, &hdr[i], sizeof (MIDIHDR)); jassert (res == MMSYSERR_NOERROR); - res = midiInAddBuffer (hIn, &hdr[i], sizeof (MIDIHDR)); + res = midiInAddBuffer (deviceHandle, &hdr[i], sizeof (MIDIHDR)); jassert (res == MMSYSERR_NOERROR); } void run() { - MemoryBlock pendingCopy (64); + MidiBuffer newEvents; + newEvents.ensureSize ((int) defaultBufferSize); while (! threadShouldExit()) { - for (int i = 0; i < MidiConstants::numInHeaders; ++i) + for (int i = 0; i < (int) numInHeaders; ++i) { if ((hdr[i].dwFlags & WHDR_DONE) != 0) { - MMRESULT res = midiInUnprepareHeader (hIn, &hdr[i], sizeof (MIDIHDR)); + MMRESULT res = midiInUnprepareHeader (deviceHandle, &hdr[i], sizeof (MIDIHDR)); (void) res; jassert (res == MMSYSERR_NOERROR); writeBlock (i); } } - int len; + newEvents.clear(); // (resets it without freeing allocated storage) { const ScopedLock sl (lock); - - len = pendingLength; - - if (len > 0) - { - pendingCopy.ensureSize (len); - pendingCopy.copyFrom (pending, 0, len); - pendingLength = 0; - } + newEvents.swapWith (pending); } //xxx needs to figure out if blocks are broken up or not - if (len == 0) + if (newEvents.isEmpty()) { wait (500); } else { - const char* p = (const char*) pendingCopy.getData(); + MidiMessage message (0xf4, 0.0); + int time; - while (len > 0) + for (MidiBuffer::Iterator i (newEvents); i.getNextEvent (message, time);) { - const double time = *(const double*) p; - const int messageLen = *(const int*) (p + 8); - - const MidiMessage message ((const uint8*) (p + 12), messageLen, time); - + message.setTimeStamp (time * 0.001); callback->handleIncomingMidiMessage (input, message); - - p += 12 + messageLen; - len -= 12 + messageLen; } } } @@ -204,26 +157,25 @@ public: void start() { - jassert (hIn != 0); - if (hIn != 0 && ! isStarted) + jassert (deviceHandle != 0); + if (deviceHandle != 0 && ! isStarted) { stop(); activeMidiThreads.addIfNotAlreadyThere (this); int i; - for (i = 0; i < MidiConstants::numInHeaders; ++i) + for (i = 0; i < (int) numInHeaders; ++i) writeBlock (i); startTime = Time::getMillisecondCounter(); - MMRESULT res = midiInStart (hIn); - + MMRESULT res = midiInStart (deviceHandle); jassert (res == MMSYSERR_NOERROR); if (res == MMSYSERR_NOERROR) { isStarted = true; - pendingLength = 0; + pending.clear(); startThread (6); } } @@ -235,19 +187,19 @@ public: { stopThread (5000); - midiInReset (hIn); - midiInStop (hIn); + midiInReset (deviceHandle); + midiInStop (deviceHandle); activeMidiThreads.removeValue (this); { const ScopedLock sl (lock); } - for (int i = MidiConstants::numInHeaders; --i >= 0;) + for (int i = (int) numInHeaders; --i >= 0;) { if ((hdr[i].dwFlags & WHDR_DONE) != 0) { int c = 10; - while (--c >= 0 && midiInUnprepareHeader (hIn, &hdr[i], sizeof (MIDIHDR)) == MIDIERR_STILLPLAYING) + while (--c >= 0 && midiInUnprepareHeader (deviceHandle, &hdr[i], sizeof (MIDIHDR)) == MIDIERR_STILLPLAYING) Sleep (20); jassert (c >= 0); @@ -255,7 +207,7 @@ public: } isStarted = false; - pendingLength = 0; + pending.clear(); } } @@ -274,7 +226,7 @@ public: juce_UseDebuggingNewOperator - HMIDIIN hIn; + HMIDIIN deviceHandle; private: static Array activeMidiThreads; @@ -285,13 +237,16 @@ private: uint32 startTime; CriticalSection lock; - MIDIHDR hdr [MidiConstants::numInHeaders]; - char inData [MidiConstants::numInHeaders] [MidiConstants::inBufferSize]; + enum { defaultBufferSize = 8192, + numInHeaders = 32, + inBufferSize = 256 }; + + MIDIHDR hdr [(int) numInHeaders]; + char inData [(int) numInHeaders] [(int) inBufferSize]; - int pendingLength; - char pending [MidiConstants::midiBufferSize]; + MidiBuffer pending; - double timeStampToTime (uint32 timeStamp) + int timeStampToMs (uint32 timeStamp) { timeStamp += startTime; @@ -304,7 +259,7 @@ private: timeStamp = now; } - return 0.001 * timeStamp; + return (int) timeStamp; } MidiInThread (const MidiInThread&); @@ -358,7 +313,7 @@ MidiInput* MidiInput::openDevice (const int index, MidiInputCallback* const call if (index == n) { deviceId = i; - name = String (mc.szPname, sizeof (mc.szPname)); + name = String (mc.szPname, numElementsInArray (mc.szPname)); break; } @@ -377,7 +332,7 @@ MidiInput* MidiInput::openDevice (const int index, MidiInputCallback* const call if (err == MMSYSERR_NOERROR) { - thread->hIn = h; + thread->deviceHandle = h; in->internal = thread.release(); return in.release(); } diff --git a/src/native/windows/juce_win32_Windowing.cpp b/src/native/windows/juce_win32_Windowing.cpp index 6091250310..6daa56c9cf 100644 --- a/src/native/windows/juce_win32_Windowing.cpp +++ b/src/native/windows/juce_win32_Windowing.cpp @@ -967,7 +967,7 @@ private: void createWindow() { - DWORD exstyle = WS_EX_ACCEPTFILES; + DWORD exstyle = WS_EX_ACCEPTFILES; // | WS_EX_NOACTIVATE; DWORD type = WS_CLIPSIBLINGS | WS_CLIPCHILDREN; if (hasTitleBar()) diff --git a/src/text/juce_String.cpp b/src/text/juce_String.cpp index c3ad771cbe..2ff5ecf462 100644 --- a/src/text/juce_String.cpp +++ b/src/text/juce_String.cpp @@ -2184,5 +2184,272 @@ void String::Concatenator::append (const String& s) } } +#if JUCE_UNIT_TESTS + +#include "../utilities/juce_UnitTest.h" +#include "../core/juce_Random.h" + +class StringTests : public UnitTest +{ +public: + StringTests() : UnitTest ("String class") {} + + void runTest() + { + { + beginTest ("Basics"); + + expect (String().length() == 0); + expect (String() == String::empty); + String s1, s2 ("abcd"); + expect (s1.isEmpty() && ! s1.isNotEmpty()); + expect (s2.isNotEmpty() && ! s2.isEmpty()); + expect (s2.length() == 4); + s1 = "abcd"; + expect (s2 == s1 && s1 == s2); + expect (s1 == "abcd" && s1 == L"abcd"); + expect (String ("abcd") == String (L"abcd")); + expect (String ("abcdefg", 4) == L"abcd"); + expect (String ("abcdefg", 4) == String (L"abcdefg", 4)); + expect (String::charToString ('x') == "x"); + expect (String::charToString (0) == String::empty); + expect (s2 + "e" == "abcde" && s2 + 'e' == "abcde"); + expect (s2 + L'e' == "abcde" && s2 + L"e" == "abcde"); + expect (s1.equalsIgnoreCase ("abcD") && s1 < "abce" && s1 > "abbb"); + expect (s1.startsWith ("ab") && s1.startsWith ("abcd") && ! s1.startsWith ("abcde")); + expect (s1.startsWithIgnoreCase ("aB") && s1.endsWithIgnoreCase ("CD")); + expect (s1.endsWith ("bcd") && ! s1.endsWith ("aabcd")); + expect (s1.indexOf (String::empty) == 0); + expect (s1.startsWith (String::empty) && s1.endsWith (String::empty) && s1.contains (String::empty)); + expect (s1.contains ("cd") && s1.contains ("ab") && s1.contains ("abcd")); + expect (s1.containsChar ('a') && ! s1.containsChar (0)); + expect (String ("abc foo bar").containsWholeWord ("abc") && String ("abc foo bar").containsWholeWord ("abc")); + } + + { + beginTest ("Operations"); + + String s ("012345678"); + expect (s.hashCode() != 0); + expect (s.hashCode64() != 0); + expect (s.hashCode() != (s + s).hashCode()); + expect (s.hashCode64() != (s + s).hashCode64()); + expect (s.compare (String ("012345678")) == 0); + expect (s.compare (String ("012345679")) < 0); + expect (s.compare (String ("012345676")) > 0); + expect (s.substring (2, 3) == String::charToString (s[2])); + expect (s.substring (0, 1) == String::charToString (s[0])); + expect (s.getLastCharacter() == s [s.length() - 1]); + expect (String::charToString (s.getLastCharacter()) == s.getLastCharacters (1)); + expect (s.substring (0, 3) == L"012"); + expect (s.substring (0, 100) == s); + expect (s.substring (-1, 100) == s); + expect (s.substring (3) == "345678"); + expect (s.indexOf (L"45") == 4); + expect (String ("444445").indexOf ("45") == 4); + expect (String ("444445").lastIndexOfChar ('4') == 4); + expect (String ("45454545x").lastIndexOf (L"45") == 6); + expect (String ("45454545x").lastIndexOfAnyOf ("456") == 7); + expect (String ("45454545x").lastIndexOfAnyOf (L"456x") == 8); + expect (String ("abABaBaBa").lastIndexOfIgnoreCase ("Ab") == 6); + expect (s.indexOfChar (L'4') == 4); + expect (s + s == "012345678012345678"); + expect (s.startsWith (s)); + expect (s.startsWith (s.substring (0, 4))); + expect (s.startsWith (s.dropLastCharacters (4))); + expect (s.endsWith (s.substring (5))); + expect (s.endsWith (s)); + expect (s.contains (s.substring (3, 6))); + expect (s.contains (s.substring (3))); + expect (s.startsWithChar (s[0])); + expect (s.endsWithChar (s.getLastCharacter())); + expect (s [s.length()] == 0); + expect (String ("abcdEFGH").toLowerCase() == String ("abcdefgh")); + expect (String ("abcdEFGH").toUpperCase() == String ("ABCDEFGH")); + + String s2 ("123"); + s2 << ((int) 4) << ((short) 5) << "678" << L"9" << '0'; + s2 += "xyz"; + expect (s2 == "1234567890xyz"); + + beginTest ("Numeric conversions"); + expect (String::empty.getIntValue() == 0); + expect (String::empty.getDoubleValue() == 0.0); + expect (String::empty.getFloatValue() == 0.0f); + expect (s.getIntValue() == 12345678); + expect (s.getLargeIntValue() == (int64) 12345678); + expect (s.getDoubleValue() == 12345678.0); + expect (s.getFloatValue() == 12345678.0f); + expect (String (-1234).getIntValue() == -1234); + expect (String ((int64) -1234).getLargeIntValue() == -1234); + expect (String (-1234.56).getDoubleValue() == -1234.56); + expect (String (-1234.56f).getFloatValue() == -1234.56f); + expect (("xyz" + s).getTrailingIntValue() == s.getIntValue()); + expect (s.getHexValue32() == 0x12345678); + expect (s.getHexValue64() == (int64) 0x12345678); + expect (String::toHexString (0x1234abcd).equalsIgnoreCase ("1234abcd")); + expect (String::toHexString ((int64) 0x1234abcd).equalsIgnoreCase ("1234abcd")); + expect (String::toHexString ((short) 0x12ab).equalsIgnoreCase ("12ab")); + + unsigned char data[] = { 1, 2, 3, 4, 0xa, 0xb, 0xc, 0xd }; + expect (String::toHexString (data, 8, 0).equalsIgnoreCase ("010203040a0b0c0d")); + expect (String::toHexString (data, 8, 1).equalsIgnoreCase ("01 02 03 04 0a 0b 0c 0d")); + expect (String::toHexString (data, 8, 2).equalsIgnoreCase ("0102 0304 0a0b 0c0d")); + + beginTest ("Subsections"); + String s3; + s3 = "abcdeFGHIJ"; + expect (s3.equalsIgnoreCase ("ABCdeFGhiJ")); + expect (s3.compareIgnoreCase (L"ABCdeFGhiJ") == 0); + expect (s3.containsIgnoreCase (s3.substring (3))); + expect (s3.indexOfAnyOf ("xyzf", 2, true) == 5); + expect (s3.indexOfAnyOf (L"xyzf", 2, false) == -1); + expect (s3.indexOfAnyOf ("xyzF", 2, false) == 5); + expect (s3.containsAnyOf (L"zzzFs")); + expect (s3.startsWith ("abcd")); + expect (s3.startsWithIgnoreCase (L"abCD")); + expect (s3.startsWith (String::empty)); + expect (s3.startsWithChar ('a')); + expect (s3.endsWith (String ("HIJ"))); + expect (s3.endsWithIgnoreCase (L"Hij")); + expect (s3.endsWith (String::empty)); + expect (s3.endsWithChar (L'J')); + expect (s3.indexOf ("HIJ") == 7); + expect (s3.indexOf (L"HIJK") == -1); + expect (s3.indexOfIgnoreCase ("hij") == 7); + expect (s3.indexOfIgnoreCase (L"hijk") == -1); + + String s4 (s3); + s4.append (String ("xyz123"), 3); + expect (s4 == s3 + "xyz"); + + expect (String (1234) < String (1235)); + expect (String (1235) > String (1234)); + expect (String (1234) >= String (1234)); + expect (String (1234) <= String (1234)); + expect (String (1235) >= String (1234)); + expect (String (1234) <= String (1235)); + + String s5 ("word word2 word3"); + expect (s5.containsWholeWord (String ("word2"))); + expect (s5.indexOfWholeWord ("word2") == 5); + expect (s5.containsWholeWord (L"word")); + expect (s5.containsWholeWord ("word3")); + expect (s5.containsWholeWord (s5)); + expect (s5.containsWholeWordIgnoreCase (L"Word2")); + expect (s5.indexOfWholeWordIgnoreCase ("Word2") == 5); + expect (s5.containsWholeWordIgnoreCase (L"Word")); + expect (s5.containsWholeWordIgnoreCase ("Word3")); + expect (! s5.containsWholeWordIgnoreCase (L"Wordx")); + expect (!s5.containsWholeWordIgnoreCase ("xWord2")); + expect (s5.containsNonWhitespaceChars()); + expect (! String (" \n\r\t").containsNonWhitespaceChars()); + + expect (s5.matchesWildcard (L"wor*", false)); + expect (s5.matchesWildcard ("wOr*", true)); + expect (s5.matchesWildcard (L"*word3", true)); + expect (s5.matchesWildcard ("*word?", true)); + expect (s5.matchesWildcard (L"Word*3", true)); + + expect (s5.fromFirstOccurrenceOf (String::empty, true, false) == s5); + expect (s5.fromFirstOccurrenceOf ("xword2", true, false) == s5.substring (100)); + expect (s5.fromFirstOccurrenceOf (L"word2", true, false) == s5.substring (5)); + expect (s5.fromFirstOccurrenceOf ("Word2", true, true) == s5.substring (5)); + expect (s5.fromFirstOccurrenceOf ("word2", false, false) == s5.getLastCharacters (6)); + expect (s5.fromFirstOccurrenceOf (L"Word2", false, true) == s5.getLastCharacters (6)); + + expect (s5.fromLastOccurrenceOf (String::empty, true, false) == s5); + expect (s5.fromLastOccurrenceOf (L"wordx", true, false) == s5); + expect (s5.fromLastOccurrenceOf ("word", true, false) == s5.getLastCharacters (5)); + expect (s5.fromLastOccurrenceOf (L"worD", true, true) == s5.getLastCharacters (5)); + expect (s5.fromLastOccurrenceOf ("word", false, false) == s5.getLastCharacters (1)); + expect (s5.fromLastOccurrenceOf (L"worD", false, true) == s5.getLastCharacters (1)); + + expect (s5.upToFirstOccurrenceOf (String::empty, true, false).isEmpty()); + expect (s5.upToFirstOccurrenceOf ("word4", true, false) == s5); + expect (s5.upToFirstOccurrenceOf (L"word2", true, false) == s5.substring (0, 10)); + expect (s5.upToFirstOccurrenceOf ("Word2", true, true) == s5.substring (0, 10)); + expect (s5.upToFirstOccurrenceOf (L"word2", false, false) == s5.substring (0, 5)); + expect (s5.upToFirstOccurrenceOf ("Word2", false, true) == s5.substring (0, 5)); + + expect (s5.upToLastOccurrenceOf (String::empty, true, false) == s5); + expect (s5.upToLastOccurrenceOf ("zword", true, false) == s5); + expect (s5.upToLastOccurrenceOf ("word", true, false) == s5.dropLastCharacters (1)); + expect (s5.dropLastCharacters(1).upToLastOccurrenceOf ("word", true, false) == s5.dropLastCharacters (1)); + expect (s5.upToLastOccurrenceOf ("Word", true, true) == s5.dropLastCharacters (1)); + expect (s5.upToLastOccurrenceOf ("word", false, false) == s5.dropLastCharacters (5)); + expect (s5.upToLastOccurrenceOf ("Word", false, true) == s5.dropLastCharacters (5)); + + expect (s5.replace ("word", L"xyz", false) == String ("xyz xyz2 xyz3")); + expect (s5.replace (L"Word", "xyz", true) == "xyz xyz2 xyz3"); + expect (s5.dropLastCharacters (1).replace ("Word", String ("xyz"), true) == L"xyz xyz2 xyz"); + expect (s5.replace ("Word", "", true) == " 2 3"); + expect (s5.replace ("Word2", L"xyz", true) == String ("word xyz word3")); + expect (s5.replaceCharacter (L'w', 'x') != s5); + expect (s5.replaceCharacter ('w', L'x').replaceCharacter ('x', 'w') == s5); + expect (s5.replaceCharacters ("wo", "xy") != s5); + expect (s5.replaceCharacters ("wo", "xy").replaceCharacters ("xy", L"wo") == s5); + expect (s5.retainCharacters ("1wordxya") == String ("wordwordword")); + expect (s5.retainCharacters (String::empty).isEmpty()); + expect (s5.removeCharacters ("1wordxya") == " 2 3"); + expect (s5.removeCharacters (String::empty) == s5); + expect (s5.initialSectionContainingOnly ("word") == L"word"); + expect (s5.initialSectionNotContaining (String ("xyz ")) == String ("word")); + expect (! s5.isQuotedString()); + expect (s5.quoted().isQuotedString()); + expect (! s5.quoted().unquoted().isQuotedString()); + expect (! String ("x'").isQuotedString()); + expect (String ("'x").isQuotedString()); + + String s6 (" \t xyz \t\r\n"); + expect (s6.trim() == String ("xyz")); + expect (s6.trim().trim() == "xyz"); + expect (s5.trim() == s5); + expect (s6.trimStart().trimEnd() == s6.trim()); + expect (s6.trimStart().trimEnd() == s6.trimEnd().trimStart()); + expect (s6.trimStart().trimStart().trimEnd().trimEnd() == s6.trimEnd().trimStart()); + expect (s6.trimStart() != s6.trimEnd()); + expect (("\t\r\n " + s6 + "\t\n \r").trim() == s6.trim()); + expect (String::repeatedString ("xyz", 3) == L"xyzxyzxyz"); + } + + { + beginTest ("UTF8"); + + String s ("word word2 word3"); + + { + char buffer [100]; + memset (buffer, 0xff, sizeof (buffer)); + s.copyToUTF8 (buffer, 100); + expect (String::fromUTF8 (buffer, 100) == s); + + juce_wchar bufferUnicode [100]; + memset (bufferUnicode, 0xff, sizeof (bufferUnicode)); + s.copyToUnicode (bufferUnicode, 100); + expect (String (bufferUnicode, 100) == s); + } + + { + juce_wchar wideBuffer [50]; + zerostruct (wideBuffer); + + for (int i = 0; i < numElementsInArray (wideBuffer) - 1; ++i) + wideBuffer[i] = (juce_wchar) (1 + Random::getSystemRandom().nextInt (std::numeric_limits::max() - 1)); + + String wide (wideBuffer); + expect (wide == (const juce_wchar*) wideBuffer); + expect (wide.length() == numElementsInArray (wideBuffer) - 1); + expect (String::fromUTF8 (wide.toUTF8()) == wide); + expect (String::fromUTF8 (wide.toUTF8()) == wideBuffer); + } + } + } +}; + +static StringTests stringUnitTests; + +#endif + END_JUCE_NAMESPACE diff --git a/src/text/juce_String.h b/src/text/juce_String.h index 2dab7a93d2..3c3dbd85ed 100644 --- a/src/text/juce_String.h +++ b/src/text/juce_String.h @@ -193,36 +193,43 @@ public: int compareLexicographically (const String& other) const throw(); /** Tests whether the string begins with another string. + If the parameter is an empty string, this will always return true. Uses a case-sensitive comparison. */ bool startsWith (const String& text) const throw(); /** Tests whether the string begins with a particular character. + If the character is 0, this will always return false. Uses a case-sensitive comparison. */ bool startsWithChar (juce_wchar character) const throw(); /** Tests whether the string begins with another string. + If the parameter is an empty string, this will always return true. Uses a case-insensitive comparison. */ bool startsWithIgnoreCase (const String& text) const throw(); /** Tests whether the string ends with another string. + If the parameter is an empty string, this will always return true. Uses a case-sensitive comparison. */ bool endsWith (const String& text) const throw(); /** Tests whether the string ends with a particular character. + If the character is 0, this will always return false. Uses a case-sensitive comparison. */ bool endsWithChar (juce_wchar character) const throw(); /** Tests whether the string ends with another string. + If the parameter is an empty string, this will always return true. Uses a case-insensitive comparison. */ bool endsWithIgnoreCase (const String& text) const throw(); /** Tests whether the string contains another substring. + If the parameter is an empty string, this will always return true. Uses a case-sensitive comparison. */ bool contains (const String& text) const throw(); diff --git a/src/utilities/juce_UnitTest.cpp b/src/utilities/juce_UnitTest.cpp new file mode 100644 index 0000000000..23120668d7 --- /dev/null +++ b/src/utilities/juce_UnitTest.cpp @@ -0,0 +1,207 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-10 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online at www.gnu.org/licenses. + + JUCE 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. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +#include "../core/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_UnitTest.h" +#include "../threads/juce_ScopedLock.h" + + +//============================================================================== +UnitTest::UnitTest (const String& name_) + : name (name_), runner (0) +{ + getAllTests().add (this); +} + +UnitTest::~UnitTest() +{ + getAllTests().removeValue (this); +} + +Array& UnitTest::getAllTests() +{ + static Array tests; + return tests; +} + +void UnitTest::performTest (UnitTestRunner* const runner_) +{ + jassert (runner_ != 0); + runner = runner_; + runTest(); +} + +void UnitTest::logMessage (const String& message) +{ + runner->logMessage (message); +} + +void UnitTest::beginTest (const String& testName) +{ + runner->beginNewTest (this, testName); +} + +void UnitTest::expect (const bool result, const String& failureMessage) +{ + if (result) + runner->addPass(); + else + runner->addFail (failureMessage); +} + +//============================================================================== +UnitTestRunner::UnitTestRunner() + : currentTest (0), assertOnFailure (false) +{ +} + +UnitTestRunner::~UnitTestRunner() +{ +} + +void UnitTestRunner::resultsUpdated() +{ +} + +void UnitTestRunner::runTests (const Array& tests, const bool assertOnFailure_) +{ + results.clear(); + assertOnFailure = assertOnFailure_; + resultsUpdated(); + + for (int i = 0; i < tests.size(); ++i) + { + try + { + tests.getUnchecked(i)->performTest (this); + } + catch (...) + { + addFail ("An unhandled exception was thrown!"); + } + } + + endTest(); +} + +void UnitTestRunner::runAllTests (const bool assertOnFailure_) +{ + runTests (UnitTest::getAllTests(), assertOnFailure_); +} + +void UnitTestRunner::logMessage (const String& message) +{ + Logger::writeToLog (message); +} + +void UnitTestRunner::beginNewTest (UnitTest* const test, const String& subCategory) +{ + endTest(); + currentTest = test; + + TestResult* const r = new TestResult(); + r->unitTestName = test->getName(); + r->subcategoryName = subCategory; + r->passes = 0; + r->failures = 0; + results.add (r); + + logMessage ("-----------------------------------------------------------------"); + logMessage ("Starting test: " + r->unitTestName + " / " + subCategory + "..."); + + resultsUpdated(); +} + +void UnitTestRunner::endTest() +{ + if (results.size() > 0) + { + TestResult* const r = results.getLast(); + + if (r->failures > 0) + { + String m ("FAILED!!"); + m << r->failures << (r->failures == 1 ? "test" : "tests") + << " failed, out of a total of " << (r->passes + r->failures); + + logMessage (String::empty); + logMessage (m); + logMessage (String::empty); + } + else + { + logMessage ("All tests completed successfully"); + } + } +} + +void UnitTestRunner::addPass() +{ + { + const ScopedLock sl (results.getLock()); + + TestResult* const r = results.getLast(); + jassert (r != 0); // You need to call UnitTest::beginTest() before performing any tests! + + r->passes++; + + String message ("Test "); + message << (r->failures + r->passes) << " passed"; + logMessage (message); + } + + resultsUpdated(); +} + +void UnitTestRunner::addFail (const String& failureMessage) +{ + { + const ScopedLock sl (results.getLock()); + + TestResult* const r = results.getLast(); + jassert (r != 0); // You need to call UnitTest::beginTest() before performing any tests! + + r->failures++; + + String message ("!!! Test "); + message << (r->failures + r->passes) << " failed"; + + if (failureMessage.isNotEmpty()) + message << ": " << failureMessage; + + r->messages.add (message); + + logMessage (message); + } + + resultsUpdated(); + + if (assertOnFailure) { jassertfalse } +} + +END_JUCE_NAMESPACE diff --git a/src/utilities/juce_UnitTest.h b/src/utilities/juce_UnitTest.h new file mode 100644 index 0000000000..7f67d5bc0e --- /dev/null +++ b/src/utilities/juce_UnitTest.h @@ -0,0 +1,245 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-10 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online at www.gnu.org/licenses. + + JUCE 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. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +#ifndef __JUCE_UNITTEST_JUCEHEADER__ +#define __JUCE_UNITTEST_JUCEHEADER__ + +#include "../text/juce_StringArray.h" +#include "../containers/juce_OwnedArray.h" +class UnitTestRunner; + + +//============================================================================== +/** + This is a base class for classes that perform a unit test. + + To write a test using this class, your code should look something like this: + + @code + class MyTest : public UnitTest + { + public: + MyTest() : UnitTest ("Foobar testing") {} + + void runTest() + { + beginTest ("Part 1"); + + expect (myFoobar.doesSomething()); + expect (myFoobar.doesSomethingElse()); + + beginTest ("Part 2"); + + expect (myOtherFoobar.doesSomething()); + expect (myOtherFoobar.doesSomethingElse()); + + ...etc.. + } + }; + + // Creating a static instance will automatically add the instance to the array + // returned by UnitTest::getAllTests(), so the test will be included when you call + // UnitTestRunner::runAllTests() + static MyTest test; + @endcode + + To run a test, use the UnitTestRunner class. + + @see UnitTestRunner +*/ +class UnitTest +{ +public: + //============================================================================== + /** Creates a test with the given name. */ + UnitTest (const String& name); + + /** Destructor. */ + virtual ~UnitTest(); + + /** Returns the name of the test. */ + const String getName() const throw() { return name; } + + /** Runs the test, using the specified UnitTestRunner. + You shouldn't need to call this method directly - use + UnitTestRunner::runTests() instead. + */ + void performTest (UnitTestRunner* runner); + + /** Returns the set of all UnitTest objects that currently exist. */ + static Array& getAllTests(); + + //============================================================================== + /** Implement this method in your subclass to actually run your tests. + + The content of your implementation should call beginTest() and expect() + to perform the tests. + */ + virtual void runTest() = 0; + + //============================================================================== + /** Tells the system that a new subsection of tests is beginning. + This should be called from your runTest() method, and may be called + as many times as you like, to demarcate different sets of tests. + */ + void beginTest (const String& testName); + + //============================================================================== + /** Checks that the result of a test is true, and logs this result. + + In your runTest() method, you should call this method for each condition that + you want to check, e.g. + + @code + void runTest() + { + beginTest ("basic tests"); + expect (x + y == 2); + expect (getThing() == someThing); + ...etc... + } + @endcode + + If testResult is true, a pass is logged; if it's false, a failure is logged. + If the failure message is specified, it will be written to the log if the test fails. + */ + void expect (bool testResult, const String& failureMessage = String::empty); + + //============================================================================== + /** Writes a message to the test log. + This can only be called from within your runTest() method. + */ + void logMessage (const String& message); + +private: + //============================================================================== + const String name; + UnitTestRunner* runner; + + UnitTest (const UnitTest&); + UnitTest& operator= (const UnitTest&); +}; + + +//============================================================================== +/** + Runs a set of unit tests. + + You can instantiate one of these objects and use it to invoke tests on a set of + UnitTest objects. + + By using a subclass of UnitTestRunner, you can intercept logging messages and + perform custom behaviour when each test completes. + + @see UnitTest +*/ +class UnitTestRunner +{ +public: + //============================================================================== + /** */ + UnitTestRunner(); + + /** Destructor. */ + virtual ~UnitTestRunner(); + + /** Runs a set of tests. + + The tests are performed in order, and the results are logged. To run all the + registered UnitTest objects that exist, use runAllTests(). + */ + void runTests (const Array& tests, bool assertOnFailure); + + /** Runs all the UnitTest objects that currently exist. + This calls runTests() for all the objects listed in UnitTest::getAllTests(). + */ + void runAllTests (bool assertOnFailure); + + //============================================================================== + /** Contains the results of a test. + + One of these objects is instantiated each time UnitTest::beginTest() is called, and + it contains details of the number of subsequent UnitTest::expect() calls that are + made. + */ + struct TestResult + { + /** The main name of this test (i.e. the name of the UnitTest object being run). */ + String unitTestName; + /** The name of the current subcategory (i.e. the name that was set when UnitTest::beginTest() was called). */ + String subcategoryName; + + /** The number of UnitTest::expect() calls that succeeded. */ + int passes; + /** The number of UnitTest::expect() calls that failed. */ + int failures; + + /** A list of messages describing the failed tests. */ + StringArray messages; + }; + + /** Returns the number of TestResult objects that have been performed. + @see getResult + */ + int getNumResults() const throw(); + + /** Returns one of the TestResult objects that describes a test that has been run. + @see getNumResults + */ + const TestResult* getResult (int index) const throw(); + +protected: + /** Called when the list of results changes. + You can override this to perform some sort of behaviour when results are added. + */ + virtual void resultsUpdated(); + + /** Logs a message about the current test progress. + By default this just writes the message to the Logger class, but you could override + this to do something else with the data. + */ + virtual void logMessage (const String& message); + +private: + //============================================================================== + friend class UnitTest; + + UnitTest* currentTest; + String currentSubCategory; + OwnedArray results; + bool assertOnFailure; + + void beginNewTest (UnitTest* test, const String& subCategory); + void endTest(); + + void addPass(); + void addFail (const String& failureMessage); + + UnitTestRunner (const UnitTestRunner&); + UnitTestRunner& operator= (const UnitTestRunner&); +}; + + +#endif // __JUCE_UNITTEST_JUCEHEADER__