diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bfecef1..644a1c89 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -610,7 +610,7 @@ if (APPLE) endif () -# CxxTest ---------------------------------------------------------------------- +# Test ---------------------------------------------------------------------- if (UNIX) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/test/") @@ -621,71 +621,28 @@ else () endforeach( OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES ) endif () -if (WIN32) - file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/test_main.cpp "") -endif () - -set(CXXTEST_INCLUDE_DIR $ENV{CXX_TEST_DIR}) -set(CXXTEST_PYTHON_TESTGEN_EXECUTABLE ${CXXTEST_INCLUDE_DIR}/bin/cxxtestgen) -set(TESTGEN_FILE ${CMAKE_CURRENT_BINARY_DIR}/test_main.cpp) - -find_package(PythonInterp REQUIRED) - - -add_executable (${TEST_PROJECT_NAME} ${TESTGEN_FILE} ${TEST_FILES}) +add_executable (${TEST_PROJECT_NAME} ${TEST_FILES}) create_source_groups(${TEST_FILES}) target_link_libraries(${TEST_PROJECT_NAME} ${LIB_CXX_PROJECT_NAME} ${LIB_JAVA_PROJECT_NAME} ${LIB_PROJECT_NAME} ${LIB_GUI_PROJECT_NAME}) -find_package(CxxTest) -if (CXXTEST_FOUND) - set_property( - TARGET ${TEST_PROJECT_NAME} - PROPERTY INCLUDE_DIRECTORIES - "${CXXTEST_INCLUDE_DIR}" - "${TEST_INCLUDE_PATHS}" - "${LIB_INCLUDE_PATHS}" - "${LIB_UTILITY_INCLUDE_PATHS}" - "${LIB_GUI_INCLUDE_PATHS}" - "${LIB_CXX_INCLUDE_PATHS}" - "${LIB_JAVA_INCLUDE_PATHS}" - "${EXTERNAL_INCLUDE_PATHS}" - "${EXTERNAL_C_INCLUDE_PATHS}" - "${Boost_INCLUDE_DIRS}" - ) -endif (CXXTEST_FOUND) +set_property( + TARGET ${TEST_PROJECT_NAME} + PROPERTY INCLUDE_DIRECTORIES + "${TEST_INCLUDE_PATHS}" + "${LIB_INCLUDE_PATHS}" + "${LIB_UTILITY_INCLUDE_PATHS}" + "${LIB_GUI_INCLUDE_PATHS}" + "${LIB_CXX_INCLUDE_PATHS}" + "${LIB_JAVA_INCLUDE_PATHS}" + "${EXTERNAL_INCLUDE_PATHS}" + "${EXTERNAL_C_INCLUDE_PATHS}" + "${Boost_INCLUDE_DIRS}" +) if (WIN32) - - add_custom_command( - TARGET ${TEST_PROJECT_NAME} - PRE_BUILD - COMMAND cd ${PROJECT_SOURCE_DIR} && ${PYTHON_EXECUTABLE} ${CXXTEST_PYTHON_TESTGEN_EXECUTABLE} --runner=ParenPrinter -o ${TESTGEN_FILE} ${TEST_FILES} - COMMENT "Generating unittest code with cxxtestgen" - ) - - add_custom_command( - TARGET ${TEST_PROJECT_NAME} - POST_BUILD - COMMAND cd $(ProjectDir)../../bin/test/\n$(OutDir)$(TargetName)$(TargetExt) - COMMENT "Running unittest code" - ) - set_target_properties(${TEST_PROJECT_NAME} PROPERTIES COMPILE_FLAGS "/bigobj") - -elseif (UNIX) - - add_custom_command( - OUTPUT ${TESTGEN_FILE} - COMMAND cd ${PROJECT_SOURCE_DIR} && ${PYTHON_EXECUTABLE} ${CXXTEST_PYTHON_TESTGEN_EXECUTABLE} --runner=ParenPrinter -o ${TESTGEN_FILE} ${TEST_FILES} - #BYPRODUCTS ${TESTGEN_FILE} - DEPENDS ${TEST_FILES} - COMMENT "Generating unittest code with cxxtestgen" - ) - - add_custom_target(gen DEPENDS ${TESTGEN_FILE}) - add_dependencies(${TEST_PROJECT_NAME} gen) endif () diff --git a/README.md b/README.md index cd11e2d7..9bb1edc8 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ * JDK 1.8 * QT 5.10.1 -* CxxTest 4.3 * Clang & LLVM 7.0 (doesn't quite work for windows, use unix setup below and skip all the ninja stuff)(installation guide http://clang.llvm.org/docs/LibASTMatchersTutorial.html) * Boost 1.68 * Image Magick 7.0.6 @@ -17,7 +16,6 @@ #### Environment Variables -* CXX_TEST_DIR - .../cxxtest-4.3 * CLANG_DIR - .../clang-llvm * BOOST_DIR - .../boost_1_68_0 diff --git a/bin/app/data/license/3rd_party_licenses/license_catch.txt b/bin/app/data/license/3rd_party_licenses/license_catch.txt new file mode 100644 index 00000000..127a5bc3 --- /dev/null +++ b/bin/app/data/license/3rd_party_licenses/license_catch.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/cmake/licenses.cmake b/cmake/licenses.cmake index 45575b78..353f6aac 100644 --- a/cmake/licenses.cmake +++ b/cmake/licenses.cmake @@ -22,6 +22,7 @@ endfunction(AddLicense) AddLicense("Boost" "1.64" "http://www.boost.org" "${LICENSEFOLDER}/license_boost.txt") +AddLicense("catch" "2.5.0" "https://github.com/catchorg/Catch2" "${LICENSEFOLDER}/license_catch.txt") AddLicense("Clang" "7.0.0" "http://clang.llvm.org/" "${LICENSEFOLDER}/license_clang.txt") AddLicense("CppSQLite" "3.2" "http://www.codeproject.com/Articles/6343/CppSQLite-C-Wrapper-for-SQLite" "${LICENSEFOLDER}/license_cpp_sqlite.txt") AddLicense("Eclipse" "" "https://github.com/eclipse/eclipse.jdt.core" "${LICENSEFOLDER}/license_eclipse.txt") diff --git a/src/external/CMakeLists.txt b/src/external/CMakeLists.txt index 07f7eb8e..f55c292d 100644 --- a/src/external/CMakeLists.txt +++ b/src/external/CMakeLists.txt @@ -1,6 +1,8 @@ add_files( EXTERNAL + catch/catch.hpp + sqlite/CppSQLite3.cpp sqlite/CppSQLite3.h diff --git a/src/external/catch/catch.hpp b/src/external/catch/catch.hpp new file mode 100644 index 00000000..192340ce --- /dev/null +++ b/src/external/catch/catch.hpp @@ -0,0 +1,14361 @@ +/* + * Catch v2.5.0 + * Generated: 2018-11-26 20:46:12.165372 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2018 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp + + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 5 +#define CATCH_VERSION_PATCH 0 + +#ifdef __clang__ +# pragma clang system_header +#elif defined __GNUC__ +# pragma GCC system_header +#endif + +// start catch_suppress_warnings.h + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(push) +# pragma warning(disable: 161 1682) +# else // __ICC +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# endif +#elif defined __GNUC__ + // GCC likes to warn on REQUIREs, and we cannot suppress them + // locally because g++'s support for _Pragma is lacking in older, + // still supported, versions +# pragma GCC diagnostic ignored "-Wparentheses" +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wpadded" +#endif +// end catch_suppress_warnings.h +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +# define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +#ifdef __APPLE__ +# include +# if TARGET_OS_OSX == 1 +# define CATCH_PLATFORM_MAC +# elif TARGET_OS_IPHONE == 1 +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h + +#ifdef CATCH_IMPL +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// start catch_user_interfaces.h + +namespace Catch { + unsigned int rngSeed(); +} + +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h + +// start catch_common.h + +// start catch_compiler_capabilities.h + +// Detect a number of compiler features - by compiler +// The following features are defined: +// +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +#ifdef __cplusplus + +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define CATCH_CPP14_OR_GREATER +# endif + +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER +# endif + +#endif + +#if defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#ifdef __clang__ + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING + +# endif +#endif // __CYGWIN__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#ifdef _MSC_VER + +# if _MSC_VER >= 1900 // Visual Studio 2015 or newer +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +# endif + +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif + +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif + +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Check if string_view is available and usable +// The check is split apart to work around v140 (VS2015) preprocessor issue... +#if defined(__has_include) +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW +#endif +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Check if variant is available and usable +#if defined(__has_include) +# if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 +# include +# if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +# define CATCH_CONFIG_NO_CPP17_VARIANT +# else +# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +# endif // defined(__clang__) && (__clang_major__ < 8) +# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // __has_include + +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) +# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +# define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif + +// end catch_compiler_capabilities.h +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#include +#include +#include + +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); + +namespace Catch { + + struct CaseSensitive { enum Choice { + Yes, + No + }; }; + + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; + + protected: + NonCopyable(); + virtual ~NonCopyable(); + }; + + struct SourceLineInfo { + + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo( SourceLineInfo && ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo& operator = ( SourceLineInfo && ) = default; + + bool empty() const noexcept; + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // Bring in operator<< from global namespace into Catch namespace + // This is necessary because the overload of operator<< above makes + // lookup stop at namespace Catch + using ::operator<<; + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() const; + }; + template + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + +// end catch_common.h +namespace Catch { + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h + +// start catch_interfaces_testcase.h + +#include +#include + +namespace Catch { + + class TestSpec; + + struct ITestInvoker { + virtual void invoke () const = 0; + virtual ~ITestInvoker(); + }; + + using ITestCasePtr = std::shared_ptr; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include + +namespace Catch { + + class StringData; + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. c_str() must return a null terminated + /// string, however, and so the StringRef will internally take ownership + /// (taking a copy), if necessary. In theory this ownership is not externally + /// visible - but it does mean (substring) StringRefs should not be shared between + /// threads. + class StringRef { + public: + using size_type = std::size_t; + + private: + friend struct StringRefTestAccess; + + char const* m_start; + size_type m_size; + + char* m_data = nullptr; + + void takeOwnership(); + + static constexpr char const* const s_empty = ""; + + public: // construction/ assignment + StringRef() noexcept + : StringRef( s_empty, 0 ) + {} + + StringRef( StringRef const& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ) + {} + + StringRef( StringRef&& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ), + m_data( other.m_data ) + { + other.m_data = nullptr; + } + + StringRef( char const* rawChars ) noexcept; + + StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + ~StringRef() noexcept { + delete[] m_data; + } + + auto operator = ( StringRef const &other ) noexcept -> StringRef& { + delete[] m_data; + m_data = nullptr; + m_start = other.m_start; + m_size = other.m_size; + return *this; + } + + operator std::string() const; + + void swap( StringRef& other ) noexcept; + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != ( StringRef const& other ) const noexcept -> bool; + + auto operator[] ( size_type index ) const noexcept -> char; + + public: // named queries + auto empty() const noexcept -> bool { + return m_size == 0; + } + auto size() const noexcept -> size_type { + return m_size; + } + + auto numberOfCharacters() const noexcept -> size_type; + auto c_str() const -> char const*; + + public: // substrings and searches + auto substr( size_type start, size_type size ) const noexcept -> StringRef; + + // Returns the current start pointer. + // Note that the pointer can change when if the StringRef is a substring + auto currentData() const noexcept -> char const*; + + private: // ownership queries - may not be consistent between calls + auto isOwned() const noexcept -> bool; + auto isSubstring() const noexcept -> bool; + }; + + auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; + auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; + auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; + + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + + inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } + +} // namespace Catch + +inline auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +// end catch_stringref.h +// start catch_type_traits.hpp + + +namespace Catch{ + +#ifdef CATCH_CPP17_OR_GREATER + template + inline constexpr auto is_unique = std::true_type{}; + + template + inline constexpr auto is_unique = std::bool_constant< + (!std::is_same_v && ...) && is_unique + >{}; +#else + +template +struct is_unique : std::true_type{}; + +template +struct is_unique : std::integral_constant +::value + && is_unique::value + && is_unique::value +>{}; + +#endif +} + +// end catch_type_traits.hpp +// start catch_preprocessor.hpp + + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF + +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) + +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name, __VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " - " #__VA_ARGS__ +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name,...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +// MSVC is adding extra space and needs more calls to properly remove () +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " -" #__VA_ARGS__ +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, __VA_ARGS__) +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +// end catch_preprocessor.hpp +namespace Catch { + +template +class TestInvokerAsMethod : public ITestInvoker { + void (C::*m_testAsMethod)(); +public: + TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {} + + void invoke() const override { + C obj; + (obj.*m_testAsMethod)(); + } +}; + +auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*; + +template +auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsMethod( testAsMethod ); +} + +struct NameAndTags { + NameAndTags( StringRef const& name_ = StringRef(), StringRef const& tags_ = StringRef() ) noexcept; + StringRef name; + StringRef tags; +}; + +struct AutoReg : NonCopyable { + AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept; + ~AutoReg(); +}; + +} // end namespace Catch + +#if defined(CATCH_CONFIG_DISABLE) + #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \ + static void TestName() + #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ + namespace{ \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + } \ + void TestName::test() + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION( TestName, ... ) \ + template \ + static void TestName() + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ + namespace{ \ + template \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \ + void test(); \ + }; \ + } \ + template \ + void TestName::test() +#endif + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ + static void TestName(); \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + static void TestName() + #define INTERNAL_CATCH_TESTCASE( ... ) \ + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + void TestName::test() + #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ + INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, ... )\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + template \ + static void TestFunc();\ + namespace {\ + template \ + struct TestName{\ + template \ + TestName(Ts...names){\ + CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \ + using expander = int[];\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \ + }\ + };\ + INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestName, Name, __VA_ARGS__) \ + }\ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + template \ + static void TestFunc() + +#if defined(CATCH_CPP17_OR_GREATER) +#define CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>,"Duplicate type detected in declaration of template test case"); +#else +#define CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>::value,"Duplicate type detected in declaration of template test case"); +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ ) ) +#endif + + #define INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestName, Name, ...)\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ + TestName(CATCH_REC_LIST_UD(INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME,Name, __VA_ARGS__));\ + return 0;\ + }(); + + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, ... ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ \ + template \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \ + void test();\ + };\ + template \ + struct TestNameClass{\ + template \ + TestNameClass(Ts...names){\ + CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \ + using expander = int[];\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \ + }\ + };\ + INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestNameClass, Name, __VA_ARGS__)\ + }\ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS\ + template \ + void TestName::test() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, __VA_ARGS__ ) ) +#endif + +// end catch_test_registry.h +// start catch_capture.hpp + +// start catch_assertionhandler.h + +// start catch_assertioninfo.h + +// start catch_result_type.h + +namespace Catch { + + // ResultWas::OfType enum + struct ResultWas { enum OfType { + Unknown = -1, + Ok = 0, + Info = 1, + Warning = 2, + + FailureBit = 0x10, + + ExpressionFailed = FailureBit | 1, + ExplicitFailure = FailureBit | 2, + + Exception = 0x100 | FailureBit, + + ThrewException = Exception | 1, + DidntThrowException = Exception | 2, + + FatalErrorCondition = 0x200 | FailureBit + + }; }; + + bool isOk( ResultWas::OfType resultType ); + bool isJustInfo( int flags ); + + // ResultDisposition::Flags enum + struct ResultDisposition { enum Flags { + Normal = 0x01, + + ContinueOnFailure = 0x02, // Failures fail test, but execution continues + FalseTest = 0x04, // Prefix expression with ! + SuppressFail = 0x08 // Failures are reported but do not fail the test + }; }; + + ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ); + + bool shouldContinueOnFailure( int flags ); + inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } + bool shouldSuppressFailure( int flags ); + +} // end namespace Catch + +// end catch_result_type.h +namespace Catch { + + struct AssertionInfo + { + StringRef macroName; + SourceLineInfo lineInfo; + StringRef capturedExpression; + ResultDisposition::Flags resultDisposition; + + // We want to delete this constructor but a compiler bug in 4.8 means + // the struct is then treated as non-aggregate + //AssertionInfo() = delete; + }; + +} // end namespace Catch + +// end catch_assertioninfo.h +// start catch_decomposer.h + +// start catch_tostring.h + +#include +#include +#include +#include +// start catch_stream.h + +#include +#include +#include + +namespace Catch { + + std::ostream& cout(); + std::ostream& cerr(); + std::ostream& clog(); + + class StringRef; + + struct IStream { + virtual ~IStream(); + virtual std::ostream& stream() const = 0; + }; + + auto makeStream( StringRef const &filename ) -> IStream const*; + + class ReusableStringStream { + std::size_t m_index; + std::ostream* m_oss; + public: + ReusableStringStream(); + ~ReusableStringStream(); + + auto str() const -> std::string; + + template + auto operator << ( T const& value ) -> ReusableStringStream& { + *m_oss << value; + return *this; + } + auto get() -> std::ostream& { return *m_oss; } + }; +} + +// end catch_stream.h + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +#include +#endif + +#ifdef __OBJC__ +// start catch_objc_arc.hpp + +#import + +#ifdef __has_feature +#define CATCH_ARC_ENABLED __has_feature(objc_arc) +#else +#define CATCH_ARC_ENABLED 0 +#endif + +void arcSafeRelease( NSObject* obj ); +id performOptionalSelector( id obj, SEL sel ); + +#if !CATCH_ARC_ENABLED +inline void arcSafeRelease( NSObject* obj ) { + [obj release]; +} +inline id performOptionalSelector( id obj, SEL sel ) { + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; + return nil; +} +#define CATCH_UNSAFE_UNRETAINED +#define CATCH_ARC_STRONG +#else +inline void arcSafeRelease( NSObject* ){} +inline id performOptionalSelector( id obj, SEL sel ) { +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" +#endif + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + return nil; +} +#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained +#define CATCH_ARC_STRONG __strong +#endif + +// end catch_objc_arc.hpp +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless +#endif + +namespace Catch { + namespace Detail { + + extern const std::string unprintableString; + + std::string rawMemoryToString( const void *object, std::size_t size ); + + template + std::string rawMemoryToString( const T& object ) { + return rawMemoryToString( &object, sizeof(object) ); + } + + template + class IsStreamInsertable { + template + static auto test(int) + -> decltype(std::declval() << std::declval(), std::true_type()); + + template + static auto test(...)->std::false_type; + + public: + static const bool value = decltype(test(0))::value; + }; + + template + std::string convertUnknownEnumToString( E e ); + + template + typename std::enable_if< + !std::is_enum::value && !std::is_base_of::value, + std::string>::type convertUnstreamable( T const& ) { + return Detail::unprintableString; + } + template + typename std::enable_if< + !std::is_enum::value && std::is_base_of::value, + std::string>::type convertUnstreamable(T const& ex) { + return ex.what(); + } + + template + typename std::enable_if< + std::is_enum::value + , std::string>::type convertUnstreamable( T const& value ) { + return convertUnknownEnumToString( value ); + } + +#if defined(_MANAGED) + //! Convert a CLR string to a utf8 std::string + template + std::string clrReferenceToString( T^ ref ) { + if (ref == nullptr) + return std::string("null"); + auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); + cli::pin_ptr p = &bytes[0]; + return std::string(reinterpret_cast(p), bytes->Length); + } +#endif + + } // namespace Detail + + // If we decide for C++14, change these to enable_if_ts + template + struct StringMaker { + template + static + typename std::enable_if<::Catch::Detail::IsStreamInsertable::value, std::string>::type + convert(const Fake& value) { + ReusableStringStream rss; + // NB: call using the function-like syntax to avoid ambiguity with + // user-defined templated operator<< under clang. + rss.operator<<(value); + return rss.str(); + } + + template + static + typename std::enable_if::value, std::string>::type + convert( const Fake& value ) { +#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER) + return Detail::convertUnstreamable(value); +#else + return CATCH_CONFIG_FALLBACK_STRINGIFIER(value); +#endif + } + }; + + namespace Detail { + + // This function dispatches all stringification requests inside of Catch. + // Should be preferably called fully qualified, like ::Catch::Detail::stringify + template + std::string stringify(const T& e) { + return ::Catch::StringMaker::type>::type>::convert(e); + } + + template + std::string convertUnknownEnumToString( E e ) { + return ::Catch::Detail::stringify(static_cast::type>(e)); + } + +#if defined(_MANAGED) + template + std::string stringify( T^ e ) { + return ::Catch::StringMaker::convert(e); + } +#endif + + } // namespace Detail + + // Some predefined specializations + + template<> + struct StringMaker { + static std::string convert(const std::string& str); + }; + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW + template<> + struct StringMaker { + static std::string convert(std::string_view str); + }; +#endif + + template<> + struct StringMaker { + static std::string convert(char const * str); + }; + template<> + struct StringMaker { + static std::string convert(char * str); + }; + +#ifdef CATCH_CONFIG_WCHAR + template<> + struct StringMaker { + static std::string convert(const std::wstring& wstr); + }; + +# ifdef CATCH_CONFIG_CPP17_STRING_VIEW + template<> + struct StringMaker { + static std::string convert(std::wstring_view str); + }; +# endif + + template<> + struct StringMaker { + static std::string convert(wchar_t const * str); + }; + template<> + struct StringMaker { + static std::string convert(wchar_t * str); + }; +#endif + + // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer, + // while keeping string semantics? + template + struct StringMaker { + static std::string convert(char const* str) { + return ::Catch::Detail::stringify(std::string{ str }); + } + }; + template + struct StringMaker { + static std::string convert(signed char const* str) { + return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) }); + } + }; + template + struct StringMaker { + static std::string convert(unsigned char const* str) { + return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) }); + } + }; + + template<> + struct StringMaker { + static std::string convert(int value); + }; + template<> + struct StringMaker { + static std::string convert(long value); + }; + template<> + struct StringMaker { + static std::string convert(long long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned int value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long long value); + }; + + template<> + struct StringMaker { + static std::string convert(bool b); + }; + + template<> + struct StringMaker { + static std::string convert(char c); + }; + template<> + struct StringMaker { + static std::string convert(signed char c); + }; + template<> + struct StringMaker { + static std::string convert(unsigned char c); + }; + + template<> + struct StringMaker { + static std::string convert(std::nullptr_t); + }; + + template<> + struct StringMaker { + static std::string convert(float value); + }; + template<> + struct StringMaker { + static std::string convert(double value); + }; + + template + struct StringMaker { + template + static std::string convert(U* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + + template + struct StringMaker { + static std::string convert(R C::* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + +#if defined(_MANAGED) + template + struct StringMaker { + static std::string convert( T^ ref ) { + return ::Catch::Detail::clrReferenceToString(ref); + } + }; +#endif + + namespace Detail { + template + std::string rangeToString(InputIterator first, InputIterator last) { + ReusableStringStream rss; + rss << "{ "; + if (first != last) { + rss << ::Catch::Detail::stringify(*first); + for (++first; first != last; ++first) + rss << ", " << ::Catch::Detail::stringify(*first); + } + rss << " }"; + return rss.str(); + } + } + +#ifdef __OBJC__ + template<> + struct StringMaker { + static std::string convert(NSString * nsstring) { + if (!nsstring) + return "nil"; + return std::string("@") + [nsstring UTF8String]; + } + }; + template<> + struct StringMaker { + static std::string convert(NSObject* nsObject) { + return ::Catch::Detail::stringify([nsObject description]); + } + + }; + namespace Detail { + inline std::string stringify( NSString* nsstring ) { + return StringMaker::convert( nsstring ); + } + + } // namespace Detail +#endif // __OBJC__ + +} // namespace Catch + +////////////////////////////////////////////////////// +// Separate std-lib types stringification, so it can be selectively enabled +// This means that we do not bring in + +#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS) +# define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER +# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER +# define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#endif + +// Separate std::pair specialization +#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) +#include +namespace Catch { + template + struct StringMaker > { + static std::string convert(const std::pair& pair) { + ReusableStringStream rss; + rss << "{ " + << ::Catch::Detail::stringify(pair.first) + << ", " + << ::Catch::Detail::stringify(pair.second) + << " }"; + return rss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER + +// Separate std::tuple specialization +#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) +#include +namespace Catch { + namespace Detail { + template< + typename Tuple, + std::size_t N = 0, + bool = (N < std::tuple_size::value) + > + struct TupleElementPrinter { + static void print(const Tuple& tuple, std::ostream& os) { + os << (N ? ", " : " ") + << ::Catch::Detail::stringify(std::get(tuple)); + TupleElementPrinter::print(tuple, os); + } + }; + + template< + typename Tuple, + std::size_t N + > + struct TupleElementPrinter { + static void print(const Tuple&, std::ostream&) {} + }; + + } + + template + struct StringMaker> { + static std::string convert(const std::tuple& tuple) { + ReusableStringStream rss; + rss << '{'; + Detail::TupleElementPrinter>::print(tuple, rss.get()); + rss << " }"; + return rss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER + +#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT) +#include +namespace Catch { + template<> + struct StringMaker { + static std::string convert(const std::monostate&) { + return "{ }"; + } + }; + + template + struct StringMaker> { + static std::string convert(const std::variant& variant) { + if (variant.valueless_by_exception()) { + return "{valueless variant}"; + } else { + return std::visit( + [](const auto& value) { + return ::Catch::Detail::stringify(value); + }, + variant + ); + } + } + }; +} +#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER + +namespace Catch { + struct not_this_one {}; // Tag type for detecting which begin/ end are being selected + + // Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace + using std::begin; + using std::end; + + not_this_one begin( ... ); + not_this_one end( ... ); + + template + struct is_range { + static const bool value = + !std::is_same())), not_this_one>::value && + !std::is_same())), not_this_one>::value; + }; + +#if defined(_MANAGED) // Managed types are never ranges + template + struct is_range { + static const bool value = false; + }; +#endif + + template + std::string rangeToString( Range const& range ) { + return ::Catch::Detail::rangeToString( begin( range ), end( range ) ); + } + + // Handle vector specially + template + std::string rangeToString( std::vector const& v ) { + ReusableStringStream rss; + rss << "{ "; + bool first = true; + for( bool b : v ) { + if( first ) + first = false; + else + rss << ", "; + rss << ::Catch::Detail::stringify( b ); + } + rss << " }"; + return rss.str(); + } + + template + struct StringMaker::value && !::Catch::Detail::IsStreamInsertable::value>::type> { + static std::string convert( R const& range ) { + return rangeToString( range ); + } + }; + + template + struct StringMaker { + static std::string convert(T const(&arr)[SZ]) { + return rangeToString(arr); + } + }; + +} // namespace Catch + +// Separate std::chrono::duration specialization +#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#include +#include +#include + +namespace Catch { + +template +struct ratio_string { + static std::string symbol(); +}; + +template +std::string ratio_string::symbol() { + Catch::ReusableStringStream rss; + rss << '[' << Ratio::num << '/' + << Ratio::den << ']'; + return rss.str(); +} +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; + + //////////// + // std::chrono::duration specializations + template + struct StringMaker> { + static std::string convert(std::chrono::duration const& duration) { + ReusableStringStream rss; + rss << duration.count() << ' ' << ratio_string::symbol() << 's'; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " s"; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " m"; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " h"; + return rss.str(); + } + }; + + //////////// + // std::chrono::time_point specialization + // Generic time_point cannot be specialized, only std::chrono::time_point + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const& time_point) { + return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; + } + }; + // std::chrono::time_point specialization + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const& time_point) { + auto converted = std::chrono::system_clock::to_time_t(time_point); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &converted); +#else + std::tm* timeInfo = std::gmtime(&converted); +#endif + + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// end catch_tostring.h +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4389) // '==' : signed/unsigned mismatch +#pragma warning(disable:4018) // more "signed/unsigned mismatch" +#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) +#pragma warning(disable:4180) // qualifier applied to function type has no meaning +#endif + +namespace Catch { + + struct ITransientExpression { + auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } + auto getResult() const -> bool { return m_result; } + virtual void streamReconstructedExpression( std::ostream &os ) const = 0; + + ITransientExpression( bool isBinaryExpression, bool result ) + : m_isBinaryExpression( isBinaryExpression ), + m_result( result ) + {} + + // We don't actually need a virtual destructor, but many static analysers + // complain if it's not here :-( + virtual ~ITransientExpression(); + + bool m_isBinaryExpression; + bool m_result; + + }; + + void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); + + template + class BinaryExpr : public ITransientExpression { + LhsT m_lhs; + StringRef m_op; + RhsT m_rhs; + + void streamReconstructedExpression( std::ostream &os ) const override { + formatReconstructedExpression + ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); + } + + public: + BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) + : ITransientExpression{ true, comparisonResult }, + m_lhs( lhs ), + m_op( op ), + m_rhs( rhs ) + {} + }; + + template + class UnaryExpr : public ITransientExpression { + LhsT m_lhs; + + void streamReconstructedExpression( std::ostream &os ) const override { + os << Catch::Detail::stringify( m_lhs ); + } + + public: + explicit UnaryExpr( LhsT lhs ) + : ITransientExpression{ false, lhs ? true : false }, + m_lhs( lhs ) + {} + }; + + // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) + template + auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast(lhs == rhs); } + template + auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } + template + auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } + template + auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } + template + auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } + + template + auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast(lhs != rhs); } + template + auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } + template + auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } + template + auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } + template + auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } + + template + class ExprLhs { + LhsT m_lhs; + public: + explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} + + template + auto operator == ( RhsT const& rhs ) -> BinaryExpr const { + return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs }; + } + auto operator == ( bool rhs ) -> BinaryExpr const { + return { m_lhs == rhs, m_lhs, "==", rhs }; + } + + template + auto operator != ( RhsT const& rhs ) -> BinaryExpr const { + return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs }; + } + auto operator != ( bool rhs ) -> BinaryExpr const { + return { m_lhs != rhs, m_lhs, "!=", rhs }; + } + + template + auto operator > ( RhsT const& rhs ) -> BinaryExpr const { + return { static_cast(m_lhs > rhs), m_lhs, ">", rhs }; + } + template + auto operator < ( RhsT const& rhs ) -> BinaryExpr const { + return { static_cast(m_lhs < rhs), m_lhs, "<", rhs }; + } + template + auto operator >= ( RhsT const& rhs ) -> BinaryExpr const { + return { static_cast(m_lhs >= rhs), m_lhs, ">=", rhs }; + } + template + auto operator <= ( RhsT const& rhs ) -> BinaryExpr const { + return { static_cast(m_lhs <= rhs), m_lhs, "<=", rhs }; + } + + auto makeUnaryExpr() const -> UnaryExpr { + return UnaryExpr{ m_lhs }; + } + }; + + void handleExpression( ITransientExpression const& expr ); + + template + void handleExpression( ExprLhs const& expr ) { + handleExpression( expr.makeUnaryExpr() ); + } + + struct Decomposer { + template + auto operator <= ( T const& lhs ) -> ExprLhs { + return ExprLhs{ lhs }; + } + + auto operator <=( bool value ) -> ExprLhs { + return ExprLhs{ value }; + } + }; + +} // end namespace Catch + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// end catch_decomposer.h +// start catch_interfaces_capture.h + +#include + +namespace Catch { + + class AssertionResult; + struct AssertionInfo; + struct SectionInfo; + struct SectionEndInfo; + struct MessageInfo; + struct Counts; + struct BenchmarkInfo; + struct BenchmarkStats; + struct AssertionReaction; + struct SourceLineInfo; + + struct ITransientExpression; + struct IGeneratorTracker; + + struct IResultCapture { + + virtual ~IResultCapture(); + + virtual bool sectionStarted( SectionInfo const& sectionInfo, + Counts& assertions ) = 0; + virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; + virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; + + virtual auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0; + + virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0; + virtual void benchmarkEnded( BenchmarkStats const& stats ) = 0; + + virtual void pushScopedMessage( MessageInfo const& message ) = 0; + virtual void popScopedMessage( MessageInfo const& message ) = 0; + + virtual void handleFatalErrorCondition( StringRef message ) = 0; + + virtual void handleExpr + ( AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction ) = 0; + virtual void handleMessage + ( AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction ) = 0; + virtual void handleUnexpectedExceptionNotThrown + ( AssertionInfo const& info, + AssertionReaction& reaction ) = 0; + virtual void handleUnexpectedInflightException + ( AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction ) = 0; + virtual void handleIncomplete + ( AssertionInfo const& info ) = 0; + virtual void handleNonExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction ) = 0; + + virtual bool lastAssertionPassed() = 0; + virtual void assertionPassed() = 0; + + // Deprecated, do not use: + virtual std::string getCurrentTestName() const = 0; + virtual const AssertionResult* getLastResult() const = 0; + virtual void exceptionEarlyReported() = 0; + }; + + IResultCapture& getResultCapture(); +} + +// end catch_interfaces_capture.h +namespace Catch { + + struct TestFailureException{}; + struct AssertionResultData; + struct IResultCapture; + class RunContext; + + class LazyExpression { + friend class AssertionHandler; + friend struct AssertionStats; + friend class RunContext; + + ITransientExpression const* m_transientExpression = nullptr; + bool m_isNegated; + public: + LazyExpression( bool isNegated ); + LazyExpression( LazyExpression const& other ); + LazyExpression& operator = ( LazyExpression const& ) = delete; + + explicit operator bool() const; + + friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; + }; + + struct AssertionReaction { + bool shouldDebugBreak = false; + bool shouldThrow = false; + }; + + class AssertionHandler { + AssertionInfo m_assertionInfo; + AssertionReaction m_reaction; + bool m_completed = false; + IResultCapture& m_resultCapture; + + public: + AssertionHandler + ( StringRef const& macroName, + SourceLineInfo const& lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition ); + ~AssertionHandler() { + if ( !m_completed ) { + m_resultCapture.handleIncomplete( m_assertionInfo ); + } + } + + template + void handleExpr( ExprLhs const& expr ) { + handleExpr( expr.makeUnaryExpr() ); + } + void handleExpr( ITransientExpression const& expr ); + + void handleMessage(ResultWas::OfType resultType, StringRef const& message); + + void handleExceptionThrownAsExpected(); + void handleUnexpectedExceptionNotThrown(); + void handleExceptionNotThrownAsExpected(); + void handleThrowingCallSkipped(); + void handleUnexpectedInflightException(); + + void complete(); + void setCompleted(); + + // query + auto allowThrows() const -> bool; + }; + + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString ); + +} // namespace Catch + +// end catch_assertionhandler.h +// start catch_message.h + +#include +#include + +namespace Catch { + + struct MessageInfo { + MessageInfo( StringRef const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ); + + StringRef macroName; + std::string message; + SourceLineInfo lineInfo; + ResultWas::OfType type; + unsigned int sequence; + + bool operator == ( MessageInfo const& other ) const; + bool operator < ( MessageInfo const& other ) const; + private: + static unsigned int globalCount; + }; + + struct MessageStream { + + template + MessageStream& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + ReusableStringStream m_stream; + }; + + struct MessageBuilder : MessageStream { + MessageBuilder( StringRef const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ); + + template + MessageBuilder& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + MessageInfo m_info; + }; + + class ScopedMessage { + public: + explicit ScopedMessage( MessageBuilder const& builder ); + ~ScopedMessage(); + + MessageInfo m_info; + }; + + class Capturer { + std::vector m_messages; + IResultCapture& m_resultCapture = getResultCapture(); + size_t m_captured = 0; + public: + Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ); + ~Capturer(); + + void captureValue( size_t index, std::string const& value ); + + template + void captureValues( size_t index, T const& value ) { + captureValue( index, Catch::Detail::stringify( value ) ); + } + + template + void captureValues( size_t index, T const& value, Ts const&... values ) { + captureValue( index, Catch::Detail::stringify(value) ); + captureValues( index+1, values... ); + } + }; + +} // end namespace Catch + +// end catch_message.h +#if !defined(CATCH_CONFIG_DISABLE) + +#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) + #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__ +#else + #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" +#endif + +#if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + +/////////////////////////////////////////////////////////////////////////////// +// Another way to speed-up compilation is to omit local try-catch for REQUIRE* +// macros. +#define INTERNAL_CATCH_TRY +#define INTERNAL_CATCH_CATCH( capturer ) + +#else // CATCH_CONFIG_FAST_COMPILE + +#define INTERNAL_CATCH_TRY try +#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); } + +#endif + +#define INTERNAL_CATCH_REACT( handler ) handler.complete(); + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ + INTERNAL_CATCH_TRY { \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \ + CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( (void)0, false && static_cast( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look + // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ + if( Catch::getResultCapture().lastAssertionPassed() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ + if( !Catch::getResultCapture().lastAssertionPassed() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handleExceptionNotThrownAsExpected(); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(expr); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( exceptionType const& ) { \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition ); \ + catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \ + auto varName = Catch::Capturer( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info, #__VA_ARGS__ ); \ + varName.captureValues( 0, __VA_ARGS__ ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_INFO( macroName, log ) \ + Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ); + +/////////////////////////////////////////////////////////////////////////////// +// Although this is matcher-based, it can be used with just a string +#define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( ... ) { \ + Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher##_catch_sr ); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +#endif // CATCH_CONFIG_DISABLE + +// end catch_capture.hpp +// start catch_section.h + +// start catch_section_info.h + +// start catch_totals.h + +#include + +namespace Catch { + + struct Counts { + Counts operator - ( Counts const& other ) const; + Counts& operator += ( Counts const& other ); + + std::size_t total() const; + bool allPassed() const; + bool allOk() const; + + std::size_t passed = 0; + std::size_t failed = 0; + std::size_t failedButOk = 0; + }; + + struct Totals { + + Totals operator - ( Totals const& other ) const; + Totals& operator += ( Totals const& other ); + + Totals delta( Totals const& prevTotals ) const; + + int error = 0; + Counts assertions; + Counts testCases; + }; +} + +// end catch_totals.h +#include + +namespace Catch { + + struct SectionInfo { + SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name ); + + // Deprecated + SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name, + std::string const& ) : SectionInfo( _lineInfo, _name ) {} + + std::string name; + std::string description; // !Deprecated: this will always be empty + SourceLineInfo lineInfo; + }; + + struct SectionEndInfo { + SectionInfo sectionInfo; + Counts prevAssertions; + double durationInSeconds; + }; + +} // end namespace Catch + +// end catch_section_info.h +// start catch_timer.h + +#include + +namespace Catch { + + auto getCurrentNanosecondsSinceEpoch() -> uint64_t; + auto getEstimatedClockResolution() -> uint64_t; + + class Timer { + uint64_t m_nanoseconds = 0; + public: + void start(); + auto getElapsedNanoseconds() const -> uint64_t; + auto getElapsedMicroseconds() const -> uint64_t; + auto getElapsedMilliseconds() const -> unsigned int; + auto getElapsedSeconds() const -> double; + }; + +} // namespace Catch + +// end catch_timer.h +#include + +namespace Catch { + + class Section : NonCopyable { + public: + Section( SectionInfo const& info ); + ~Section(); + + // This indicates whether the section should be executed or not + explicit operator bool() const; + + private: + SectionInfo m_info; + + std::string m_name; + Counts m_assertions; + bool m_sectionIncluded; + Timer m_timer; + }; + +} // end namespace Catch + +#define INTERNAL_CATCH_SECTION( ... ) \ + CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ + CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS + +#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ + CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \ + CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS + +// end catch_section.h +// start catch_benchmark.h + +#include +#include + +namespace Catch { + + class BenchmarkLooper { + + std::string m_name; + std::size_t m_count = 0; + std::size_t m_iterationsToRun = 1; + uint64_t m_resolution; + Timer m_timer; + + static auto getResolution() -> uint64_t; + public: + // Keep most of this inline as it's on the code path that is being timed + BenchmarkLooper( StringRef name ) + : m_name( name ), + m_resolution( getResolution() ) + { + reportStart(); + m_timer.start(); + } + + explicit operator bool() { + if( m_count < m_iterationsToRun ) + return true; + return needsMoreIterations(); + } + + void increment() { + ++m_count; + } + + void reportStart(); + auto needsMoreIterations() -> bool; + }; + +} // end namespace Catch + +#define BENCHMARK( name ) \ + for( Catch::BenchmarkLooper looper( name ); looper; looper.increment() ) + +// end catch_benchmark.h +// start catch_interfaces_exception.h + +// start catch_interfaces_registry_hub.h + +#include +#include + +namespace Catch { + + class TestCase; + struct ITestCaseRegistry; + struct IExceptionTranslatorRegistry; + struct IExceptionTranslator; + struct IReporterRegistry; + struct IReporterFactory; + struct ITagAliasRegistry; + class StartupExceptionRegistry; + + using IReporterFactoryPtr = std::shared_ptr; + + struct IRegistryHub { + virtual ~IRegistryHub(); + + virtual IReporterRegistry const& getReporterRegistry() const = 0; + virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; + + virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0; + + virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; + }; + + struct IMutableRegistryHub { + virtual ~IMutableRegistryHub(); + virtual void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) = 0; + virtual void registerListener( IReporterFactoryPtr const& factory ) = 0; + virtual void registerTest( TestCase const& testInfo ) = 0; + virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; + virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; + virtual void registerStartupException() noexcept = 0; + }; + + IRegistryHub const& getRegistryHub(); + IMutableRegistryHub& getMutableRegistryHub(); + void cleanUp(); + std::string translateActiveException(); + +} + +// end catch_interfaces_registry_hub.h +#if defined(CATCH_CONFIG_DISABLE) + #define INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( translatorName, signature) \ + static std::string translatorName( signature ) +#endif + +#include +#include +#include + +namespace Catch { + using exceptionTranslateFunction = std::string(*)(); + + struct IExceptionTranslator; + using ExceptionTranslators = std::vector>; + + struct IExceptionTranslator { + virtual ~IExceptionTranslator(); + virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0; + }; + + struct IExceptionTranslatorRegistry { + virtual ~IExceptionTranslatorRegistry(); + + virtual std::string translateActiveException() const = 0; + }; + + class ExceptionTranslatorRegistrar { + template + class ExceptionTranslator : public IExceptionTranslator { + public: + + ExceptionTranslator( std::string(*translateFunction)( T& ) ) + : m_translateFunction( translateFunction ) + {} + + std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override { + try { + if( it == itEnd ) + std::rethrow_exception(std::current_exception()); + else + return (*it)->translate( it+1, itEnd ); + } + catch( T& ex ) { + return m_translateFunction( ex ); + } + } + + protected: + std::string(*m_translateFunction)( T& ); + }; + + public: + template + ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { + getMutableRegistryHub().registerTranslator + ( new ExceptionTranslator( translateFunction ) ); + } + }; +} + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ + static std::string translatorName( signature ); \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + static std::string translatorName( signature ) + +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) + +// end catch_interfaces_exception.h +// start catch_approx.h + +#include + +namespace Catch { +namespace Detail { + + class Approx { + private: + bool equalityComparisonImpl(double other) const; + // Validates the new margin (margin >= 0) + // out-of-line to avoid including stdexcept in the header + void setMargin(double margin); + // Validates the new epsilon (0 < epsilon < 1) + // out-of-line to avoid including stdexcept in the header + void setEpsilon(double epsilon); + + public: + explicit Approx ( double value ); + + static Approx custom(); + + Approx operator-() const; + + template ::value>::type> + Approx operator()( T const& value ) { + Approx approx( static_cast(value) ); + approx.m_epsilon = m_epsilon; + approx.m_margin = m_margin; + approx.m_scale = m_scale; + return approx; + } + + template ::value>::type> + explicit Approx( T const& value ): Approx(static_cast(value)) + {} + + template ::value>::type> + friend bool operator == ( const T& lhs, Approx const& rhs ) { + auto lhs_v = static_cast(lhs); + return rhs.equalityComparisonImpl(lhs_v); + } + + template ::value>::type> + friend bool operator == ( Approx const& lhs, const T& rhs ) { + return operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator != ( T const& lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } + + template ::value>::type> + friend bool operator != ( Approx const& lhs, T const& rhs ) { + return !operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator <= ( T const& lhs, Approx const& rhs ) { + return static_cast(lhs) < rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator <= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value < static_cast(rhs) || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( T const& lhs, Approx const& rhs ) { + return static_cast(lhs) > rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value > static_cast(rhs) || lhs == rhs; + } + + template ::value>::type> + Approx& epsilon( T const& newEpsilon ) { + double epsilonAsDouble = static_cast(newEpsilon); + setEpsilon(epsilonAsDouble); + return *this; + } + + template ::value>::type> + Approx& margin( T const& newMargin ) { + double marginAsDouble = static_cast(newMargin); + setMargin(marginAsDouble); + return *this; + } + + template ::value>::type> + Approx& scale( T const& newScale ) { + m_scale = static_cast(newScale); + return *this; + } + + std::string toString() const; + + private: + double m_epsilon; + double m_margin; + double m_scale; + double m_value; + }; +} // end namespace Detail + +namespace literals { + Detail::Approx operator "" _a(long double val); + Detail::Approx operator "" _a(unsigned long long val); +} // end namespace literals + +template<> +struct StringMaker { + static std::string convert(Catch::Detail::Approx const& value); +}; + +} // end namespace Catch + +// end catch_approx.h +// start catch_string_manip.h + +#include +#include + +namespace Catch { + + bool startsWith( std::string const& s, std::string const& prefix ); + bool startsWith( std::string const& s, char prefix ); + bool endsWith( std::string const& s, std::string const& suffix ); + bool endsWith( std::string const& s, char suffix ); + bool contains( std::string const& s, std::string const& infix ); + void toLowerInPlace( std::string& s ); + std::string toLower( std::string const& s ); + std::string trim( std::string const& str ); + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); + + struct pluralise { + pluralise( std::size_t count, std::string const& label ); + + friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); + + std::size_t m_count; + std::string m_label; + }; +} + +// end catch_string_manip.h +#ifndef CATCH_CONFIG_DISABLE_MATCHERS +// start catch_capture_matchers.h + +// start catch_matchers.h + +#include +#include + +namespace Catch { +namespace Matchers { + namespace Impl { + + template struct MatchAllOf; + template struct MatchAnyOf; + template struct MatchNotOf; + + class MatcherUntypedBase { + public: + MatcherUntypedBase() = default; + MatcherUntypedBase ( MatcherUntypedBase const& ) = default; + MatcherUntypedBase& operator = ( MatcherUntypedBase const& ) = delete; + std::string toString() const; + + protected: + virtual ~MatcherUntypedBase(); + virtual std::string describe() const = 0; + mutable std::string m_cachedToString; + }; + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#endif + + template + struct MatcherMethod { + virtual bool match( ObjectT const& arg ) const = 0; + }; + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + + template + struct MatcherBase : MatcherUntypedBase, MatcherMethod { + + MatchAllOf operator && ( MatcherBase const& other ) const; + MatchAnyOf operator || ( MatcherBase const& other ) const; + MatchNotOf operator ! () const; + }; + + template + struct MatchAllOf : MatcherBase { + bool match( ArgT const& arg ) const override { + for( auto matcher : m_matchers ) { + if (!matcher->match(arg)) + return false; + } + return true; + } + std::string describe() const override { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; + bool first = true; + for( auto matcher : m_matchers ) { + if( first ) + first = false; + else + description += " and "; + description += matcher->toString(); + } + description += " )"; + return description; + } + + MatchAllOf& operator && ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; + } + + std::vector const*> m_matchers; + }; + template + struct MatchAnyOf : MatcherBase { + + bool match( ArgT const& arg ) const override { + for( auto matcher : m_matchers ) { + if (matcher->match(arg)) + return true; + } + return false; + } + std::string describe() const override { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; + bool first = true; + for( auto matcher : m_matchers ) { + if( first ) + first = false; + else + description += " or "; + description += matcher->toString(); + } + description += " )"; + return description; + } + + MatchAnyOf& operator || ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; + } + + std::vector const*> m_matchers; + }; + + template + struct MatchNotOf : MatcherBase { + + MatchNotOf( MatcherBase const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} + + bool match( ArgT const& arg ) const override { + return !m_underlyingMatcher.match( arg ); + } + + std::string describe() const override { + return "not " + m_underlyingMatcher.toString(); + } + MatcherBase const& m_underlyingMatcher; + }; + + template + MatchAllOf MatcherBase::operator && ( MatcherBase const& other ) const { + return MatchAllOf() && *this && other; + } + template + MatchAnyOf MatcherBase::operator || ( MatcherBase const& other ) const { + return MatchAnyOf() || *this || other; + } + template + MatchNotOf MatcherBase::operator ! () const { + return MatchNotOf( *this ); + } + + } // namespace Impl + +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch + +// end catch_matchers.h +// start catch_matchers_floating.h + +#include +#include + +namespace Catch { +namespace Matchers { + + namespace Floating { + + enum class FloatingPointKind : uint8_t; + + struct WithinAbsMatcher : MatcherBase { + WithinAbsMatcher(double target, double margin); + bool match(double const& matchee) const override; + std::string describe() const override; + private: + double m_target; + double m_margin; + }; + + struct WithinUlpsMatcher : MatcherBase { + WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType); + bool match(double const& matchee) const override; + std::string describe() const override; + private: + double m_target; + int m_ulps; + FloatingPointKind m_type; + }; + + } // namespace Floating + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff); + Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff); + Floating::WithinAbsMatcher WithinAbs(double target, double margin); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_floating.h +// start catch_matchers_generic.hpp + +#include +#include + +namespace Catch { +namespace Matchers { +namespace Generic { + +namespace Detail { + std::string finalizeDescription(const std::string& desc); +} + +template +class PredicateMatcher : public MatcherBase { + std::function m_predicate; + std::string m_description; +public: + + PredicateMatcher(std::function const& elem, std::string const& descr) + :m_predicate(std::move(elem)), + m_description(Detail::finalizeDescription(descr)) + {} + + bool match( T const& item ) const override { + return m_predicate(item); + } + + std::string describe() const override { + return m_description; + } +}; + +} // namespace Generic + + // The following functions create the actual matcher objects. + // The user has to explicitly specify type to the function, because + // infering std::function is hard (but possible) and + // requires a lot of TMP. + template + Generic::PredicateMatcher Predicate(std::function const& predicate, std::string const& description = "") { + return Generic::PredicateMatcher(predicate, description); + } + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_generic.hpp +// start catch_matchers_string.h + +#include + +namespace Catch { +namespace Matchers { + + namespace StdString { + + struct CasedString + { + CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ); + std::string adjustString( std::string const& str ) const; + std::string caseSensitivitySuffix() const; + + CaseSensitive::Choice m_caseSensitivity; + std::string m_str; + }; + + struct StringMatcherBase : MatcherBase { + StringMatcherBase( std::string const& operation, CasedString const& comparator ); + std::string describe() const override; + + CasedString m_comparator; + std::string m_operation; + }; + + struct EqualsMatcher : StringMatcherBase { + EqualsMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct ContainsMatcher : StringMatcherBase { + ContainsMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct StartsWithMatcher : StringMatcherBase { + StartsWithMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct EndsWithMatcher : StringMatcherBase { + EndsWithMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + + struct RegexMatcher : MatcherBase { + RegexMatcher( std::string regex, CaseSensitive::Choice caseSensitivity ); + bool match( std::string const& matchee ) const override; + std::string describe() const override; + + private: + std::string m_regex; + CaseSensitive::Choice m_caseSensitivity; + }; + + } // namespace StdString + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::RegexMatcher Matches( std::string const& regex, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_string.h +// start catch_matchers_vector.h + +#include + +namespace Catch { +namespace Matchers { + + namespace Vector { + namespace Detail { + template + size_t count(InputIterator first, InputIterator last, T const& item) { + size_t cnt = 0; + for (; first != last; ++first) { + if (*first == item) { + ++cnt; + } + } + return cnt; + } + template + bool contains(InputIterator first, InputIterator last, T const& item) { + for (; first != last; ++first) { + if (*first == item) { + return true; + } + } + return false; + } + } + + template + struct ContainsElementMatcher : MatcherBase> { + + ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} + + bool match(std::vector const &v) const override { + for (auto const& el : v) { + if (el == m_comparator) { + return true; + } + } + return false; + } + + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify( m_comparator ); + } + + T const& m_comparator; + }; + + template + struct ContainsMatcher : MatcherBase> { + + ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector const &v) const override { + // !TBD: see note in EqualsMatcher + if (m_comparator.size() > v.size()) + return false; + for (auto const& comparator : m_comparator) { + auto present = false; + for (const auto& el : v) { + if (el == comparator) { + present = true; + break; + } + } + if (!present) { + return false; + } + } + return true; + } + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify( m_comparator ); + } + + std::vector const& m_comparator; + }; + + template + struct EqualsMatcher : MatcherBase> { + + EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector const &v) const override { + // !TBD: This currently works if all elements can be compared using != + // - a more general approach would be via a compare template that defaults + // to using !=. but could be specialised for, e.g. std::vector etc + // - then just call that directly + if (m_comparator.size() != v.size()) + return false; + for (std::size_t i = 0; i < v.size(); ++i) + if (m_comparator[i] != v[i]) + return false; + return true; + } + std::string describe() const override { + return "Equals: " + ::Catch::Detail::stringify( m_comparator ); + } + std::vector const& m_comparator; + }; + + template + struct UnorderedEqualsMatcher : MatcherBase> { + UnorderedEqualsMatcher(std::vector const& target) : m_target(target) {} + bool match(std::vector const& vec) const override { + // Note: This is a reimplementation of std::is_permutation, + // because I don't want to include inside the common path + if (m_target.size() != vec.size()) { + return false; + } + auto lfirst = m_target.begin(), llast = m_target.end(); + auto rfirst = vec.begin(), rlast = vec.end(); + // Cut common prefix to optimize checking of permuted parts + while (lfirst != llast && *lfirst == *rfirst) { + ++lfirst; ++rfirst; + } + if (lfirst == llast) { + return true; + } + + for (auto mid = lfirst; mid != llast; ++mid) { + // Skip already counted items + if (Detail::contains(lfirst, mid, *mid)) { + continue; + } + size_t num_vec = Detail::count(rfirst, rlast, *mid); + if (num_vec == 0 || Detail::count(lfirst, llast, *mid) != num_vec) { + return false; + } + } + + return true; + } + + std::string describe() const override { + return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target); + } + private: + std::vector const& m_target; + }; + + } // namespace Vector + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + template + Vector::ContainsMatcher Contains( std::vector const& comparator ) { + return Vector::ContainsMatcher( comparator ); + } + + template + Vector::ContainsElementMatcher VectorContains( T const& comparator ) { + return Vector::ContainsElementMatcher( comparator ); + } + + template + Vector::EqualsMatcher Equals( std::vector const& comparator ) { + return Vector::EqualsMatcher( comparator ); + } + + template + Vector::UnorderedEqualsMatcher UnorderedEquals(std::vector const& target) { + return Vector::UnorderedEqualsMatcher(target); + } + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_vector.h +namespace Catch { + + template + class MatchExpr : public ITransientExpression { + ArgT const& m_arg; + MatcherT m_matcher; + StringRef m_matcherString; + public: + MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) + : ITransientExpression{ true, matcher.match( arg ) }, + m_arg( arg ), + m_matcher( matcher ), + m_matcherString( matcherString ) + {} + + void streamReconstructedExpression( std::ostream &os ) const override { + auto matcherAsString = m_matcher.toString(); + os << Catch::Detail::stringify( m_arg ) << ' '; + if( matcherAsString == Detail::unprintableString ) + os << m_matcherString; + else + os << matcherAsString; + } + }; + + using StringMatcher = Matchers::Impl::MatcherBase; + + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ); + + template + auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) -> MatchExpr { + return MatchExpr( arg, matcher, matcherString ); + } + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + INTERNAL_CATCH_TRY { \ + catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher, #matcher##_catch_sr ) ); \ + } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_MATCHES( macroName, exceptionType, resultDisposition, matcher, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(__VA_ARGS__ ); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( exceptionType const& ex ) { \ + catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher, #matcher##_catch_sr ) ); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +// end catch_capture_matchers.h +#endif +// start catch_generators.hpp + +// start catch_interfaces_generatortracker.h + + +#include + +namespace Catch { + + namespace Generators { + class GeneratorBase { + protected: + size_t m_size = 0; + + public: + GeneratorBase( size_t size ) : m_size( size ) {} + virtual ~GeneratorBase(); + auto size() const -> size_t { return m_size; } + }; + using GeneratorBasePtr = std::unique_ptr; + + } // namespace Generators + + struct IGeneratorTracker { + virtual ~IGeneratorTracker(); + virtual auto hasGenerator() const -> bool = 0; + virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0; + virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0; + virtual auto getIndex() const -> std::size_t = 0; + }; + +} // namespace Catch + +// end catch_interfaces_generatortracker.h +// start catch_enforce.h + +#include + +namespace Catch { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + template + [[noreturn]] + void throw_exception(Ex const& e) { + throw e; + } +#else // ^^ Exceptions are enabled // Exceptions are disabled vv + [[noreturn]] + void throw_exception(std::exception const& e); +#endif +} // namespace Catch; + +#define CATCH_PREPARE_EXCEPTION( type, msg ) \ + type( ( Catch::ReusableStringStream() << msg ).str() ) +#define CATCH_INTERNAL_ERROR( msg ) \ + Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg)) +#define CATCH_ERROR( msg ) \ + Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::domain_error, msg )) +#define CATCH_RUNTIME_ERROR( msg ) \ + Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::runtime_error, msg )) +#define CATCH_ENFORCE( condition, msg ) \ + do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false) + +// end catch_enforce.h +#include +#include +#include + +#include + +namespace Catch { +namespace Generators { + + // !TBD move this into its own location? + namespace pf{ + template + std::unique_ptr make_unique( Args&&... args ) { + return std::unique_ptr(new T(std::forward(args)...)); + } + } + + template + struct IGenerator { + virtual ~IGenerator() {} + virtual auto get( size_t index ) const -> T = 0; + }; + + template + class SingleValueGenerator : public IGenerator { + T m_value; + public: + SingleValueGenerator( T const& value ) : m_value( value ) {} + + auto get( size_t ) const -> T override { + return m_value; + } + }; + + template + class FixedValuesGenerator : public IGenerator { + std::vector m_values; + + public: + FixedValuesGenerator( std::initializer_list values ) : m_values( values ) {} + + auto get( size_t index ) const -> T override { + return m_values[index]; + } + }; + + template + class RangeGenerator : public IGenerator { + T const m_first; + T const m_last; + + public: + RangeGenerator( T const& first, T const& last ) : m_first( first ), m_last( last ) { + assert( m_last > m_first ); + } + + auto get( size_t index ) const -> T override { + // ToDo:: introduce a safe cast to catch potential overflows + return static_cast(m_first+index); + } + }; + + template + struct NullGenerator : IGenerator { + auto get( size_t ) const -> T override { + CATCH_INTERNAL_ERROR("A Null Generator is always empty"); + } + }; + + template + class Generator { + std::unique_ptr> m_generator; + size_t m_size; + + public: + Generator( size_t size, std::unique_ptr> generator ) + : m_generator( std::move( generator ) ), + m_size( size ) + {} + + auto size() const -> size_t { return m_size; } + auto operator[]( size_t index ) const -> T { + assert( index < m_size ); + return m_generator->get( index ); + } + }; + + std::vector randomiseIndices( size_t selectionSize, size_t sourceSize ); + + template + class GeneratorRandomiser : public IGenerator { + Generator m_baseGenerator; + + std::vector m_indices; + public: + GeneratorRandomiser( Generator&& baseGenerator, size_t numberOfItems ) + : m_baseGenerator( std::move( baseGenerator ) ), + m_indices( randomiseIndices( numberOfItems, m_baseGenerator.size() ) ) + {} + + auto get( size_t index ) const -> T override { + return m_baseGenerator[m_indices[index]]; + } + }; + + template + struct RequiresASpecialisationFor; + + template + auto all() -> Generator { return RequiresASpecialisationFor(); } + + template<> + auto all() -> Generator; + + template + auto range( T const& first, T const& last ) -> Generator { + return Generator( (last-first), pf::make_unique>( first, last ) ); + } + + template + auto random( T const& first, T const& last ) -> Generator { + auto gen = range( first, last ); + auto size = gen.size(); + + return Generator( size, pf::make_unique>( std::move( gen ), size ) ); + } + template + auto random( size_t size ) -> Generator { + return Generator( size, pf::make_unique>( all(), size ) ); + } + + template + auto values( std::initializer_list values ) -> Generator { + return Generator( values.size(), pf::make_unique>( values ) ); + } + template + auto value( T const& val ) -> Generator { + return Generator( 1, pf::make_unique>( val ) ); + } + + template + auto as() -> Generator { + return Generator( 0, pf::make_unique>() ); + } + + template + auto table( std::initializer_list>&& tuples ) -> Generator> { + return values>( std::forward>>( tuples ) ); + } + + template + struct Generators : GeneratorBase { + std::vector> m_generators; + + using type = T; + + Generators() : GeneratorBase( 0 ) {} + + void populate( T&& val ) { + m_size += 1; + m_generators.emplace_back( value( std::move( val ) ) ); + } + template + void populate( U&& val ) { + populate( T( std::move( val ) ) ); + } + void populate( Generator&& generator ) { + m_size += generator.size(); + m_generators.emplace_back( std::move( generator ) ); + } + + template + void populate( U&& valueOrGenerator, Gs... moreGenerators ) { + populate( std::forward( valueOrGenerator ) ); + populate( std::forward( moreGenerators )... ); + } + + auto operator[]( size_t index ) const -> T { + size_t sizes = 0; + for( auto const& gen : m_generators ) { + auto localIndex = index-sizes; + sizes += gen.size(); + if( index < sizes ) + return gen[localIndex]; + } + CATCH_INTERNAL_ERROR("Index '" << index << "' is out of range (" << sizes << ')'); + } + }; + + template + auto makeGenerators( Generator&& generator, Gs... moreGenerators ) -> Generators { + Generators generators; + generators.m_generators.reserve( 1+sizeof...(Gs) ); + generators.populate( std::move( generator ), std::forward( moreGenerators )... ); + return generators; + } + template + auto makeGenerators( Generator&& generator ) -> Generators { + Generators generators; + generators.populate( std::move( generator ) ); + return generators; + } + template + auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators { + return makeGenerators( value( std::forward( val ) ), std::forward( moreGenerators )... ); + } + template + auto makeGenerators( U&& val, Gs... moreGenerators ) -> Generators { + return makeGenerators( value( T( std::forward( val ) ) ), std::forward( moreGenerators )... ); + } + + auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; + + template + // Note: The type after -> is weird, because VS2015 cannot parse + // the expression used in the typedef inside, when it is in + // return type. Yeah, ¯\_(ツ)_/¯ + auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval()[0]) { + using UnderlyingType = typename decltype(generatorExpression())::type; + + IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo ); + if( !tracker.hasGenerator() ) + tracker.setGenerator( pf::make_unique>( generatorExpression() ) ); + + auto const& generator = static_cast const&>( *tracker.getGenerator() ); + return generator[tracker.getIndex()]; + } + +} // namespace Generators +} // namespace Catch + +#define GENERATE( ... ) \ + Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, []{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) + +// end catch_generators.hpp + +// These files are included here so the single_include script doesn't put them +// in the conditionally compiled sections +// start catch_test_case_info.h + +#include +#include +#include + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + + struct ITestInvoker; + + struct TestCaseInfo { + enum SpecialProperties{ + None = 0, + IsHidden = 1 << 1, + ShouldFail = 1 << 2, + MayFail = 1 << 3, + Throws = 1 << 4, + NonPortable = 1 << 5, + Benchmark = 1 << 6 + }; + + TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::vector const& _tags, + SourceLineInfo const& _lineInfo ); + + friend void setTags( TestCaseInfo& testCaseInfo, std::vector tags ); + + bool isHidden() const; + bool throws() const; + bool okToFail() const; + bool expectedToFail() const; + + std::string tagsAsString() const; + + std::string name; + std::string className; + std::string description; + std::vector tags; + std::vector lcaseTags; + SourceLineInfo lineInfo; + SpecialProperties properties; + }; + + class TestCase : public TestCaseInfo { + public: + + TestCase( ITestInvoker* testCase, TestCaseInfo&& info ); + + TestCase withName( std::string const& _newName ) const; + + void invoke() const; + + TestCaseInfo const& getTestCaseInfo() const; + + bool operator == ( TestCase const& other ) const; + bool operator < ( TestCase const& other ) const; + + private: + std::shared_ptr test; + }; + + TestCase makeTestCase( ITestInvoker* testCase, + std::string const& className, + NameAndTags const& nameAndTags, + SourceLineInfo const& lineInfo ); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_test_case_info.h +// start catch_interfaces_runner.h + +namespace Catch { + + struct IRunner { + virtual ~IRunner(); + virtual bool aborting() const = 0; + }; +} + +// end catch_interfaces_runner.h + +#ifdef __OBJC__ +// start catch_objc.hpp + +#import + +#include + +// NB. Any general catch headers included here must be included +// in catch.hpp first to make sure they are included by the single +// header for non obj-usage + +/////////////////////////////////////////////////////////////////////////////// +// This protocol is really only here for (self) documenting purposes, since +// all its methods are optional. +@protocol OcFixture + +@optional + +-(void) setUp; +-(void) tearDown; + +@end + +namespace Catch { + + class OcMethod : public ITestInvoker { + + public: + OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} + + virtual void invoke() const { + id obj = [[m_cls alloc] init]; + + performOptionalSelector( obj, @selector(setUp) ); + performOptionalSelector( obj, m_sel ); + performOptionalSelector( obj, @selector(tearDown) ); + + arcSafeRelease( obj ); + } + private: + virtual ~OcMethod() {} + + Class m_cls; + SEL m_sel; + }; + + namespace Detail{ + + inline std::string getAnnotation( Class cls, + std::string const& annotationName, + std::string const& testCaseName ) { + NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; + SEL sel = NSSelectorFromString( selStr ); + arcSafeRelease( selStr ); + id value = performOptionalSelector( cls, sel ); + if( value ) + return [(NSString*)value UTF8String]; + return ""; + } + } + + inline std::size_t registerTestMethods() { + std::size_t noTestMethods = 0; + int noClasses = objc_getClassList( nullptr, 0 ); + + Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); + objc_getClassList( classes, noClasses ); + + for( int c = 0; c < noClasses; c++ ) { + Class cls = classes[c]; + { + u_int count; + Method* methods = class_copyMethodList( cls, &count ); + for( u_int m = 0; m < count ; m++ ) { + SEL selector = method_getName(methods[m]); + std::string methodName = sel_getName(selector); + if( startsWith( methodName, "Catch_TestCase_" ) ) { + std::string testCaseName = methodName.substr( 15 ); + std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); + std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); + const char* className = class_getName( cls ); + + getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, NameAndTags( name.c_str(), desc.c_str() ), SourceLineInfo("",0) ) ); + noTestMethods++; + } + } + free(methods); + } + } + return noTestMethods; + } + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) + + namespace Matchers { + namespace Impl { + namespace NSStringMatchers { + + struct StringHolder : MatcherBase{ + StringHolder( NSString* substr ) : m_substr( [substr copy] ){} + StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} + StringHolder() { + arcSafeRelease( m_substr ); + } + + bool match( NSString* arg ) const override { + return false; + } + + NSString* CATCH_ARC_STRONG m_substr; + }; + + struct Equals : StringHolder { + Equals( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const override { + return (str != nil || m_substr == nil ) && + [str isEqualToString:m_substr]; + } + + std::string describe() const override { + return "equals string: " + Catch::Detail::stringify( m_substr ); + } + }; + + struct Contains : StringHolder { + Contains( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location != NSNotFound; + } + + std::string describe() const override { + return "contains string: " + Catch::Detail::stringify( m_substr ); + } + }; + + struct StartsWith : StringHolder { + StartsWith( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const override { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == 0; + } + + std::string describe() const override { + return "starts with: " + Catch::Detail::stringify( m_substr ); + } + }; + struct EndsWith : StringHolder { + EndsWith( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const override { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == [str length] - [m_substr length]; + } + + std::string describe() const override { + return "ends with: " + Catch::Detail::stringify( m_substr ); + } + }; + + } // namespace NSStringMatchers + } // namespace Impl + + inline Impl::NSStringMatchers::Equals + Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } + + inline Impl::NSStringMatchers::Contains + Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } + + inline Impl::NSStringMatchers::StartsWith + StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } + + inline Impl::NSStringMatchers::EndsWith + EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } + + } // namespace Matchers + + using namespace Matchers; + +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define OC_MAKE_UNIQUE_NAME( root, uniqueSuffix ) root##uniqueSuffix +#define OC_TEST_CASE2( name, desc, uniqueSuffix ) \ ++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Name_test_, uniqueSuffix ) \ +{ \ +return @ name; \ +} \ ++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Description_test_, uniqueSuffix ) \ +{ \ +return @ desc; \ +} \ +-(void) OC_MAKE_UNIQUE_NAME( Catch_TestCase_test_, uniqueSuffix ) + +#define OC_TEST_CASE( name, desc ) OC_TEST_CASE2( name, desc, __LINE__ ) + +// end catch_objc.hpp +#endif + +#ifdef CATCH_CONFIG_EXTERNAL_INTERFACES +// start catch_external_interfaces.h + +// start catch_reporter_bases.hpp + +// start catch_interfaces_reporter.h + +// start catch_config.hpp + +// start catch_test_spec_parser.h + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// start catch_test_spec.h + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// start catch_wildcard_pattern.h + +namespace Catch +{ + class WildcardPattern { + enum WildcardPosition { + NoWildcard = 0, + WildcardAtStart = 1, + WildcardAtEnd = 2, + WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd + }; + + public: + + WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ); + virtual ~WildcardPattern() = default; + virtual bool matches( std::string const& str ) const; + + private: + std::string adjustCase( std::string const& str ) const; + CaseSensitive::Choice m_caseSensitivity; + WildcardPosition m_wildcard = NoWildcard; + std::string m_pattern; + }; +} + +// end catch_wildcard_pattern.h +#include +#include +#include + +namespace Catch { + + class TestSpec { + struct Pattern { + virtual ~Pattern(); + virtual bool matches( TestCaseInfo const& testCase ) const = 0; + }; + using PatternPtr = std::shared_ptr; + + class NamePattern : public Pattern { + public: + NamePattern( std::string const& name ); + virtual ~NamePattern(); + virtual bool matches( TestCaseInfo const& testCase ) const override; + private: + WildcardPattern m_wildcardPattern; + }; + + class TagPattern : public Pattern { + public: + TagPattern( std::string const& tag ); + virtual ~TagPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const override; + private: + std::string m_tag; + }; + + class ExcludedPattern : public Pattern { + public: + ExcludedPattern( PatternPtr const& underlyingPattern ); + virtual ~ExcludedPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const override; + private: + PatternPtr m_underlyingPattern; + }; + + struct Filter { + std::vector m_patterns; + + bool matches( TestCaseInfo const& testCase ) const; + }; + + public: + bool hasFilters() const; + bool matches( TestCaseInfo const& testCase ) const; + + private: + std::vector m_filters; + + friend class TestSpecParser; + }; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_test_spec.h +// start catch_interfaces_tag_alias_registry.h + +#include + +namespace Catch { + + struct TagAlias; + + struct ITagAliasRegistry { + virtual ~ITagAliasRegistry(); + // Nullptr if not present + virtual TagAlias const* find( std::string const& alias ) const = 0; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; + + static ITagAliasRegistry const& get(); + }; + +} // end namespace Catch + +// end catch_interfaces_tag_alias_registry.h +namespace Catch { + + class TestSpecParser { + enum Mode{ None, Name, QuotedName, Tag, EscapedName }; + Mode m_mode = None; + bool m_exclusion = false; + std::size_t m_start = std::string::npos, m_pos = 0; + std::string m_arg; + std::vector m_escapeChars; + TestSpec::Filter m_currentFilter; + TestSpec m_testSpec; + ITagAliasRegistry const* m_tagAliases = nullptr; + + public: + TestSpecParser( ITagAliasRegistry const& tagAliases ); + + TestSpecParser& parse( std::string const& arg ); + TestSpec testSpec(); + + private: + void visitChar( char c ); + void startNewMode( Mode mode, std::size_t start ); + void escape(); + std::string subString() const; + + template + void addPattern() { + std::string token = subString(); + for( std::size_t i = 0; i < m_escapeChars.size(); ++i ) + token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 ); + m_escapeChars.clear(); + if( startsWith( token, "exclude:" ) ) { + m_exclusion = true; + token = token.substr( 8 ); + } + if( !token.empty() ) { + TestSpec::PatternPtr pattern = std::make_shared( token ); + if( m_exclusion ) + pattern = std::make_shared( pattern ); + m_currentFilter.m_patterns.push_back( pattern ); + } + m_exclusion = false; + m_mode = None; + } + + void addFilter(); + }; + TestSpec parseTestSpec( std::string const& arg ); + +} // namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_test_spec_parser.h +// start catch_interfaces_config.h + +#include +#include +#include +#include + +namespace Catch { + + enum class Verbosity { + Quiet = 0, + Normal, + High + }; + + struct WarnAbout { enum What { + Nothing = 0x00, + NoAssertions = 0x01, + NoTests = 0x02 + }; }; + + struct ShowDurations { enum OrNot { + DefaultForReporter, + Always, + Never + }; }; + struct RunTests { enum InWhatOrder { + InDeclarationOrder, + InLexicographicalOrder, + InRandomOrder + }; }; + struct UseColour { enum YesOrNo { + Auto, + Yes, + No + }; }; + struct WaitForKeypress { enum When { + Never, + BeforeStart = 1, + BeforeExit = 2, + BeforeStartAndExit = BeforeStart | BeforeExit + }; }; + + class TestSpec; + + struct IConfig : NonCopyable { + + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + virtual std::ostream& stream() const = 0; + virtual std::string name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual bool warnAboutNoTests() const = 0; + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations::OrNot showDurations() const = 0; + virtual TestSpec const& testSpec() const = 0; + virtual bool hasTestFilters() const = 0; + virtual RunTests::InWhatOrder runOrder() const = 0; + virtual unsigned int rngSeed() const = 0; + virtual int benchmarkResolutionMultiple() const = 0; + virtual UseColour::YesOrNo useColour() const = 0; + virtual std::vector const& getSectionsToRun() const = 0; + virtual Verbosity verbosity() const = 0; + }; + + using IConfigPtr = std::shared_ptr; +} + +// end catch_interfaces_config.h +// Libstdc++ doesn't like incomplete classes for unique_ptr + +#include +#include +#include + +#ifndef CATCH_CONFIG_CONSOLE_WIDTH +#define CATCH_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { + + struct IStream; + + struct ConfigData { + bool listTests = false; + bool listTags = false; + bool listReporters = false; + bool listTestNamesOnly = false; + + bool showSuccessfulTests = false; + bool shouldDebugBreak = false; + bool noThrow = false; + bool showHelp = false; + bool showInvisibles = false; + bool filenamesAsTags = false; + bool libIdentify = false; + + int abortAfter = -1; + unsigned int rngSeed = 0; + int benchmarkResolutionMultiple = 100; + + Verbosity verbosity = Verbosity::Normal; + WarnAbout::What warnings = WarnAbout::Nothing; + ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; + RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder; + UseColour::YesOrNo useColour = UseColour::Auto; + WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; + + std::string outputFilename; + std::string name; + std::string processName; +#ifndef CATCH_CONFIG_DEFAULT_REPORTER +#define CATCH_CONFIG_DEFAULT_REPORTER "console" +#endif + std::string reporterName = CATCH_CONFIG_DEFAULT_REPORTER; +#undef CATCH_CONFIG_DEFAULT_REPORTER + + std::vector testsOrTags; + std::vector sectionsToRun; + }; + + class Config : public IConfig { + public: + + Config() = default; + Config( ConfigData const& data ); + virtual ~Config() = default; + + std::string const& getFilename() const; + + bool listTests() const; + bool listTestNamesOnly() const; + bool listTags() const; + bool listReporters() const; + + std::string getProcessName() const; + std::string const& getReporterName() const; + + std::vector const& getTestsOrTags() const; + std::vector const& getSectionsToRun() const override; + + virtual TestSpec const& testSpec() const override; + bool hasTestFilters() const override; + + bool showHelp() const; + + // IConfig interface + bool allowThrows() const override; + std::ostream& stream() const override; + std::string name() const override; + bool includeSuccessfulResults() const override; + bool warnAboutMissingAssertions() const override; + bool warnAboutNoTests() const override; + ShowDurations::OrNot showDurations() const override; + RunTests::InWhatOrder runOrder() const override; + unsigned int rngSeed() const override; + int benchmarkResolutionMultiple() const override; + UseColour::YesOrNo useColour() const override; + bool shouldDebugBreak() const override; + int abortAfter() const override; + bool showInvisibles() const override; + Verbosity verbosity() const override; + + private: + + IStream const* openStream(); + ConfigData m_data; + + std::unique_ptr m_stream; + TestSpec m_testSpec; + bool m_hasTestFilters = false; + }; + +} // end namespace Catch + +// end catch_config.hpp +// start catch_assertionresult.h + +#include + +namespace Catch { + + struct AssertionResultData + { + AssertionResultData() = delete; + + AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); + + std::string message; + mutable std::string reconstructedExpression; + LazyExpression lazyExpression; + ResultWas::OfType resultType; + + std::string reconstructExpression() const; + }; + + class AssertionResult { + public: + AssertionResult() = delete; + AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); + + bool isOk() const; + bool succeeded() const; + ResultWas::OfType getResultType() const; + bool hasExpression() const; + bool hasMessage() const; + std::string getExpression() const; + std::string getExpressionInMacro() const; + bool hasExpandedExpression() const; + std::string getExpandedExpression() const; + std::string getMessage() const; + SourceLineInfo getSourceInfo() const; + StringRef getTestMacroName() const; + + //protected: + AssertionInfo m_info; + AssertionResultData m_resultData; + }; + +} // end namespace Catch + +// end catch_assertionresult.h +// start catch_option.hpp + +namespace Catch { + + // An optional type + template + class Option { + public: + Option() : nullableValue( nullptr ) {} + Option( T const& _value ) + : nullableValue( new( storage ) T( _value ) ) + {} + Option( Option const& _other ) + : nullableValue( _other ? new( storage ) T( *_other ) : nullptr ) + {} + + ~Option() { + reset(); + } + + Option& operator= ( Option const& _other ) { + if( &_other != this ) { + reset(); + if( _other ) + nullableValue = new( storage ) T( *_other ); + } + return *this; + } + Option& operator = ( T const& _value ) { + reset(); + nullableValue = new( storage ) T( _value ); + return *this; + } + + void reset() { + if( nullableValue ) + nullableValue->~T(); + nullableValue = nullptr; + } + + T& operator*() { return *nullableValue; } + T const& operator*() const { return *nullableValue; } + T* operator->() { return nullableValue; } + const T* operator->() const { return nullableValue; } + + T valueOr( T const& defaultValue ) const { + return nullableValue ? *nullableValue : defaultValue; + } + + bool some() const { return nullableValue != nullptr; } + bool none() const { return nullableValue == nullptr; } + + bool operator !() const { return nullableValue == nullptr; } + explicit operator bool() const { + return some(); + } + + private: + T *nullableValue; + alignas(alignof(T)) char storage[sizeof(T)]; + }; + +} // end namespace Catch + +// end catch_option.hpp +#include +#include +#include +#include +#include + +namespace Catch { + + struct ReporterConfig { + explicit ReporterConfig( IConfigPtr const& _fullConfig ); + + ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ); + + std::ostream& stream() const; + IConfigPtr fullConfig() const; + + private: + std::ostream* m_stream; + IConfigPtr m_fullConfig; + }; + + struct ReporterPreferences { + bool shouldRedirectStdOut = false; + bool shouldReportAllAssertions = false; + }; + + template + struct LazyStat : Option { + LazyStat& operator=( T const& _value ) { + Option::operator=( _value ); + used = false; + return *this; + } + void reset() { + Option::reset(); + used = false; + } + bool used = false; + }; + + struct TestRunInfo { + TestRunInfo( std::string const& _name ); + std::string name; + }; + struct GroupInfo { + GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ); + + std::string name; + std::size_t groupIndex; + std::size_t groupsCounts; + }; + + struct AssertionStats { + AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ); + + AssertionStats( AssertionStats const& ) = default; + AssertionStats( AssertionStats && ) = default; + AssertionStats& operator = ( AssertionStats const& ) = default; + AssertionStats& operator = ( AssertionStats && ) = default; + virtual ~AssertionStats(); + + AssertionResult assertionResult; + std::vector infoMessages; + Totals totals; + }; + + struct SectionStats { + SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ); + SectionStats( SectionStats const& ) = default; + SectionStats( SectionStats && ) = default; + SectionStats& operator = ( SectionStats const& ) = default; + SectionStats& operator = ( SectionStats && ) = default; + virtual ~SectionStats(); + + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; + }; + + struct TestCaseStats { + TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ); + + TestCaseStats( TestCaseStats const& ) = default; + TestCaseStats( TestCaseStats && ) = default; + TestCaseStats& operator = ( TestCaseStats const& ) = default; + TestCaseStats& operator = ( TestCaseStats && ) = default; + virtual ~TestCaseStats(); + + TestCaseInfo testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; + + struct TestGroupStats { + TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ); + TestGroupStats( GroupInfo const& _groupInfo ); + + TestGroupStats( TestGroupStats const& ) = default; + TestGroupStats( TestGroupStats && ) = default; + TestGroupStats& operator = ( TestGroupStats const& ) = default; + TestGroupStats& operator = ( TestGroupStats && ) = default; + virtual ~TestGroupStats(); + + GroupInfo groupInfo; + Totals totals; + bool aborting; + }; + + struct TestRunStats { + TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ); + + TestRunStats( TestRunStats const& ) = default; + TestRunStats( TestRunStats && ) = default; + TestRunStats& operator = ( TestRunStats const& ) = default; + TestRunStats& operator = ( TestRunStats && ) = default; + virtual ~TestRunStats(); + + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; + + struct BenchmarkInfo { + std::string name; + }; + struct BenchmarkStats { + BenchmarkInfo info; + std::size_t iterations; + uint64_t elapsedTimeInNanoseconds; + }; + + struct IStreamingReporter { + virtual ~IStreamingReporter() = default; + + // Implementing class must also provide the following static methods: + // static std::string getDescription(); + // static std::set getSupportedVerbosities() + + virtual ReporterPreferences getPreferences() const = 0; + + virtual void noMatchingTestCases( std::string const& spec ) = 0; + + virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; + virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; + virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; + + // *** experimental *** + virtual void benchmarkStarting( BenchmarkInfo const& ) {} + + virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; + + // The return value indicates if the messages buffer should be cleared: + virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; + + // *** experimental *** + virtual void benchmarkEnded( BenchmarkStats const& ) {} + + virtual void sectionEnded( SectionStats const& sectionStats ) = 0; + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; + virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; + + virtual void skipTest( TestCaseInfo const& testInfo ) = 0; + + // Default empty implementation provided + virtual void fatalErrorEncountered( StringRef name ); + + virtual bool isMulti() const; + }; + using IStreamingReporterPtr = std::unique_ptr; + + struct IReporterFactory { + virtual ~IReporterFactory(); + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0; + virtual std::string getDescription() const = 0; + }; + using IReporterFactoryPtr = std::shared_ptr; + + struct IReporterRegistry { + using FactoryMap = std::map; + using Listeners = std::vector; + + virtual ~IReporterRegistry(); + virtual IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const = 0; + virtual FactoryMap const& getFactories() const = 0; + virtual Listeners const& getListeners() const = 0; + }; + +} // end namespace Catch + +// end catch_interfaces_reporter.h +#include +#include +#include +#include +#include +#include +#include + +namespace Catch { + void prepareExpandedExpression(AssertionResult& result); + + // Returns double formatted as %.3f (format expected on output) + std::string getFormattedDuration( double duration ); + + template + struct StreamingReporterBase : IStreamingReporter { + + StreamingReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) + CATCH_ERROR( "Verbosity level not supported by this reporter" ); + } + + ReporterPreferences getPreferences() const override { + return m_reporterPrefs; + } + + static std::set getSupportedVerbosities() { + return { Verbosity::Normal }; + } + + ~StreamingReporterBase() override = default; + + void noMatchingTestCases(std::string const&) override {} + + void testRunStarting(TestRunInfo const& _testRunInfo) override { + currentTestRunInfo = _testRunInfo; + } + void testGroupStarting(GroupInfo const& _groupInfo) override { + currentGroupInfo = _groupInfo; + } + + void testCaseStarting(TestCaseInfo const& _testInfo) override { + currentTestCaseInfo = _testInfo; + } + void sectionStarting(SectionInfo const& _sectionInfo) override { + m_sectionStack.push_back(_sectionInfo); + } + + void sectionEnded(SectionStats const& /* _sectionStats */) override { + m_sectionStack.pop_back(); + } + void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override { + currentTestCaseInfo.reset(); + } + void testGroupEnded(TestGroupStats const& /* _testGroupStats */) override { + currentGroupInfo.reset(); + } + void testRunEnded(TestRunStats const& /* _testRunStats */) override { + currentTestCaseInfo.reset(); + currentGroupInfo.reset(); + currentTestRunInfo.reset(); + } + + void skipTest(TestCaseInfo const&) override { + // Don't do anything with this by default. + // It can optionally be overridden in the derived class. + } + + IConfigPtr m_config; + std::ostream& stream; + + LazyStat currentTestRunInfo; + LazyStat currentGroupInfo; + LazyStat currentTestCaseInfo; + + std::vector m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template + struct CumulativeReporterBase : IStreamingReporter { + template + struct Node { + explicit Node( T const& _value ) : value( _value ) {} + virtual ~Node() {} + + using ChildNodes = std::vector>; + T value; + ChildNodes children; + }; + struct SectionNode { + explicit SectionNode(SectionStats const& _stats) : stats(_stats) {} + virtual ~SectionNode() = default; + + bool operator == (SectionNode const& other) const { + return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; + } + bool operator == (std::shared_ptr const& other) const { + return operator==(*other); + } + + SectionStats stats; + using ChildSections = std::vector>; + using Assertions = std::vector; + ChildSections childSections; + Assertions assertions; + std::string stdOut; + std::string stdErr; + }; + + struct BySectionInfo { + BySectionInfo( SectionInfo const& other ) : m_other( other ) {} + BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} + bool operator() (std::shared_ptr const& node) const { + return ((node->stats.sectionInfo.name == m_other.name) && + (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); + } + void operator=(BySectionInfo const&) = delete; + + private: + SectionInfo const& m_other; + }; + + using TestCaseNode = Node; + using TestGroupNode = Node; + using TestRunNode = Node; + + CumulativeReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) + CATCH_ERROR( "Verbosity level not supported by this reporter" ); + } + ~CumulativeReporterBase() override = default; + + ReporterPreferences getPreferences() const override { + return m_reporterPrefs; + } + + static std::set getSupportedVerbosities() { + return { Verbosity::Normal }; + } + + void testRunStarting( TestRunInfo const& ) override {} + void testGroupStarting( GroupInfo const& ) override {} + + void testCaseStarting( TestCaseInfo const& ) override {} + + void sectionStarting( SectionInfo const& sectionInfo ) override { + SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); + std::shared_ptr node; + if( m_sectionStack.empty() ) { + if( !m_rootSection ) + m_rootSection = std::make_shared( incompleteStats ); + node = m_rootSection; + } + else { + SectionNode& parentNode = *m_sectionStack.back(); + auto it = + std::find_if( parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo( sectionInfo ) ); + if( it == parentNode.childSections.end() ) { + node = std::make_shared( incompleteStats ); + parentNode.childSections.push_back( node ); + } + else + node = *it; + } + m_sectionStack.push_back( node ); + m_deepestSection = std::move(node); + } + + void assertionStarting(AssertionInfo const&) override {} + + bool assertionEnded(AssertionStats const& assertionStats) override { + assert(!m_sectionStack.empty()); + // AssertionResult holds a pointer to a temporary DecomposedExpression, + // which getExpandedExpression() calls to build the expression string. + // Our section stack copy of the assertionResult will likely outlive the + // temporary, so it must be expanded or discarded now to avoid calling + // a destroyed object later. + prepareExpandedExpression(const_cast( assertionStats.assertionResult ) ); + SectionNode& sectionNode = *m_sectionStack.back(); + sectionNode.assertions.push_back(assertionStats); + return true; + } + void sectionEnded(SectionStats const& sectionStats) override { + assert(!m_sectionStack.empty()); + SectionNode& node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } + void testCaseEnded(TestCaseStats const& testCaseStats) override { + auto node = std::make_shared(testCaseStats); + assert(m_sectionStack.size() == 0); + node->children.push_back(m_rootSection); + m_testCases.push_back(node); + m_rootSection.reset(); + + assert(m_deepestSection); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } + void testGroupEnded(TestGroupStats const& testGroupStats) override { + auto node = std::make_shared(testGroupStats); + node->children.swap(m_testCases); + m_testGroups.push_back(node); + } + void testRunEnded(TestRunStats const& testRunStats) override { + auto node = std::make_shared(testRunStats); + node->children.swap(m_testGroups); + m_testRuns.push_back(node); + testRunEndedCumulative(); + } + virtual void testRunEndedCumulative() = 0; + + void skipTest(TestCaseInfo const&) override {} + + IConfigPtr m_config; + std::ostream& stream; + std::vector m_assertions; + std::vector>> m_sections; + std::vector> m_testCases; + std::vector> m_testGroups; + + std::vector> m_testRuns; + + std::shared_ptr m_rootSection; + std::shared_ptr m_deepestSection; + std::vector> m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template + char const* getLineOfChars() { + static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; + if( !*line ) { + std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); + line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; + } + return line; + } + + struct TestEventListenerBase : StreamingReporterBase { + TestEventListenerBase( ReporterConfig const& _config ); + + static std::set getSupportedVerbosities(); + + void assertionStarting(AssertionInfo const&) override; + bool assertionEnded(AssertionStats const&) override; + }; + +} // end namespace Catch + +// end catch_reporter_bases.hpp +// start catch_console_colour.h + +namespace Catch { + + struct Colour { + enum Code { + None = 0, + + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, + + Bright = 0x10, + + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White, + BrightYellow = Bright | Yellow, + + // By intention + FileName = LightGrey, + Warning = BrightYellow, + ResultError = BrightRed, + ResultSuccess = BrightGreen, + ResultExpectedFailure = Warning, + + Error = BrightRed, + Success = Green, + + OriginalExpression = Cyan, + ReconstructedExpression = BrightYellow, + + SecondaryText = LightGrey, + Headers = White + }; + + // Use constructed object for RAII guard + Colour( Code _colourCode ); + Colour( Colour&& other ) noexcept; + Colour& operator=( Colour&& other ) noexcept; + ~Colour(); + + // Use static method for one-shot changes + static void use( Code _colourCode ); + + private: + bool m_moved = false; + }; + + std::ostream& operator << ( std::ostream& os, Colour const& ); + +} // end namespace Catch + +// end catch_console_colour.h +// start catch_reporter_registrars.hpp + + +namespace Catch { + + template + class ReporterRegistrar { + + class ReporterFactory : public IReporterFactory { + + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr( new T( config ) ); + } + + virtual std::string getDescription() const override { + return T::getDescription(); + } + }; + + public: + + explicit ReporterRegistrar( std::string const& name ) { + getMutableRegistryHub().registerReporter( name, std::make_shared() ); + } + }; + + template + class ListenerRegistrar { + + class ListenerFactory : public IReporterFactory { + + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr( new T( config ) ); + } + virtual std::string getDescription() const override { + return std::string(); + } + }; + + public: + + ListenerRegistrar() { + getMutableRegistryHub().registerListener( std::make_shared() ); + } + }; +} + +#if !defined(CATCH_CONFIG_DISABLE) + +#define CATCH_REGISTER_REPORTER( name, reporterType ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + +#define CATCH_REGISTER_LISTENER( listenerType ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#else // CATCH_CONFIG_DISABLE + +#define CATCH_REGISTER_REPORTER(name, reporterType) +#define CATCH_REGISTER_LISTENER(listenerType) + +#endif // CATCH_CONFIG_DISABLE + +// end catch_reporter_registrars.hpp +// Allow users to base their work off existing reporters +// start catch_reporter_compact.h + +namespace Catch { + + struct CompactReporter : StreamingReporterBase { + + using StreamingReporterBase::StreamingReporterBase; + + ~CompactReporter() override; + + static std::string getDescription(); + + ReporterPreferences getPreferences() const override; + + void noMatchingTestCases(std::string const& spec) override; + + void assertionStarting(AssertionInfo const&) override; + + bool assertionEnded(AssertionStats const& _assertionStats) override; + + void sectionEnded(SectionStats const& _sectionStats) override; + + void testRunEnded(TestRunStats const& _testRunStats) override; + + }; + +} // end namespace Catch + +// end catch_reporter_compact.h +// start catch_reporter_console.h + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif + +namespace Catch { + // Fwd decls + struct SummaryColumn; + class TablePrinter; + + struct ConsoleReporter : StreamingReporterBase { + std::unique_ptr m_tablePrinter; + + ConsoleReporter(ReporterConfig const& config); + ~ConsoleReporter() override; + static std::string getDescription(); + + void noMatchingTestCases(std::string const& spec) override; + + void assertionStarting(AssertionInfo const&) override; + + bool assertionEnded(AssertionStats const& _assertionStats) override; + + void sectionStarting(SectionInfo const& _sectionInfo) override; + void sectionEnded(SectionStats const& _sectionStats) override; + + void benchmarkStarting(BenchmarkInfo const& info) override; + void benchmarkEnded(BenchmarkStats const& stats) override; + + void testCaseEnded(TestCaseStats const& _testCaseStats) override; + void testGroupEnded(TestGroupStats const& _testGroupStats) override; + void testRunEnded(TestRunStats const& _testRunStats) override; + + private: + + void lazyPrint(); + + void lazyPrintWithoutClosingBenchmarkTable(); + void lazyPrintRunInfo(); + void lazyPrintGroupInfo(); + void printTestCaseAndSectionHeader(); + + void printClosedHeader(std::string const& _name); + void printOpenHeader(std::string const& _name); + + // if string has a : in first line will set indent to follow it on + // subsequent lines + void printHeaderString(std::string const& _string, std::size_t indent = 0); + + void printTotals(Totals const& totals); + void printSummaryRow(std::string const& label, std::vector const& cols, std::size_t row); + + void printTotalsDivider(Totals const& totals); + void printSummaryDivider(); + + private: + bool m_headerPrinted = false; + }; + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +// end catch_reporter_console.h +// start catch_reporter_junit.h + +// start catch_xmlwriter.h + +#include + +namespace Catch { + + class XmlEncode { + public: + enum ForWhat { ForTextNodes, ForAttributes }; + + XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ); + + void encodeTo( std::ostream& os ) const; + + friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ); + + private: + std::string m_str; + ForWhat m_forWhat; + }; + + class XmlWriter { + public: + + class ScopedElement { + public: + ScopedElement( XmlWriter* writer ); + + ScopedElement( ScopedElement&& other ) noexcept; + ScopedElement& operator=( ScopedElement&& other ) noexcept; + + ~ScopedElement(); + + ScopedElement& writeText( std::string const& text, bool indent = true ); + + template + ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { + m_writer->writeAttribute( name, attribute ); + return *this; + } + + private: + mutable XmlWriter* m_writer = nullptr; + }; + + XmlWriter( std::ostream& os = Catch::cout() ); + ~XmlWriter(); + + XmlWriter( XmlWriter const& ) = delete; + XmlWriter& operator=( XmlWriter const& ) = delete; + + XmlWriter& startElement( std::string const& name ); + + ScopedElement scopedElement( std::string const& name ); + + XmlWriter& endElement(); + + XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ); + + XmlWriter& writeAttribute( std::string const& name, bool attribute ); + + template + XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { + ReusableStringStream rss; + rss << attribute; + return writeAttribute( name, rss.str() ); + } + + XmlWriter& writeText( std::string const& text, bool indent = true ); + + XmlWriter& writeComment( std::string const& text ); + + void writeStylesheetRef( std::string const& url ); + + XmlWriter& writeBlankLine(); + + void ensureTagClosed(); + + private: + + void writeDeclaration(); + + void newlineIfNecessary(); + + bool m_tagIsOpen = false; + bool m_needsNewline = false; + std::vector m_tags; + std::string m_indent; + std::ostream& m_os; + }; + +} + +// end catch_xmlwriter.h +namespace Catch { + + class JunitReporter : public CumulativeReporterBase { + public: + JunitReporter(ReporterConfig const& _config); + + ~JunitReporter() override; + + static std::string getDescription(); + + void noMatchingTestCases(std::string const& /*spec*/) override; + + void testRunStarting(TestRunInfo const& runInfo) override; + + void testGroupStarting(GroupInfo const& groupInfo) override; + + void testCaseStarting(TestCaseInfo const& testCaseInfo) override; + bool assertionEnded(AssertionStats const& assertionStats) override; + + void testCaseEnded(TestCaseStats const& testCaseStats) override; + + void testGroupEnded(TestGroupStats const& testGroupStats) override; + + void testRunEndedCumulative() override; + + void writeGroup(TestGroupNode const& groupNode, double suiteTime); + + void writeTestCase(TestCaseNode const& testCaseNode); + + void writeSection(std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode); + + void writeAssertions(SectionNode const& sectionNode); + void writeAssertion(AssertionStats const& stats); + + XmlWriter xml; + Timer suiteTimer; + std::string stdOutForSuite; + std::string stdErrForSuite; + unsigned int unexpectedExceptions = 0; + bool m_okToFail = false; + }; + +} // end namespace Catch + +// end catch_reporter_junit.h +// start catch_reporter_xml.h + +namespace Catch { + class XmlReporter : public StreamingReporterBase { + public: + XmlReporter(ReporterConfig const& _config); + + ~XmlReporter() override; + + static std::string getDescription(); + + virtual std::string getStylesheetRef() const; + + void writeSourceInfo(SourceLineInfo const& sourceInfo); + + public: // StreamingReporterBase + + void noMatchingTestCases(std::string const& s) override; + + void testRunStarting(TestRunInfo const& testInfo) override; + + void testGroupStarting(GroupInfo const& groupInfo) override; + + void testCaseStarting(TestCaseInfo const& testInfo) override; + + void sectionStarting(SectionInfo const& sectionInfo) override; + + void assertionStarting(AssertionInfo const&) override; + + bool assertionEnded(AssertionStats const& assertionStats) override; + + void sectionEnded(SectionStats const& sectionStats) override; + + void testCaseEnded(TestCaseStats const& testCaseStats) override; + + void testGroupEnded(TestGroupStats const& testGroupStats) override; + + void testRunEnded(TestRunStats const& testRunStats) override; + + private: + Timer m_testCaseTimer; + XmlWriter m_xml; + int m_sectionDepth = 0; + }; + +} // end namespace Catch + +// end catch_reporter_xml.h + +// end catch_external_interfaces.h +#endif + +#endif // ! CATCH_CONFIG_IMPL_ONLY + +#ifdef CATCH_IMPL +// start catch_impl.hpp + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#endif + +// Keep these here for external reporters +// start catch_test_case_tracker.h + +#include +#include +#include + +namespace Catch { +namespace TestCaseTracking { + + struct NameAndLocation { + std::string name; + SourceLineInfo location; + + NameAndLocation( std::string const& _name, SourceLineInfo const& _location ); + }; + + struct ITracker; + + using ITrackerPtr = std::shared_ptr; + + struct ITracker { + virtual ~ITracker(); + + // static queries + virtual NameAndLocation const& nameAndLocation() const = 0; + + // dynamic queries + virtual bool isComplete() const = 0; // Successfully completed or failed + virtual bool isSuccessfullyCompleted() const = 0; + virtual bool isOpen() const = 0; // Started but not complete + virtual bool hasChildren() const = 0; + + virtual ITracker& parent() = 0; + + // actions + virtual void close() = 0; // Successfully complete + virtual void fail() = 0; + virtual void markAsNeedingAnotherRun() = 0; + + virtual void addChild( ITrackerPtr const& child ) = 0; + virtual ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) = 0; + virtual void openChild() = 0; + + // Debug/ checking + virtual bool isSectionTracker() const = 0; + virtual bool isIndexTracker() const = 0; + }; + + class TrackerContext { + + enum RunState { + NotStarted, + Executing, + CompletedCycle + }; + + ITrackerPtr m_rootTracker; + ITracker* m_currentTracker = nullptr; + RunState m_runState = NotStarted; + + public: + + static TrackerContext& instance(); + + ITracker& startRun(); + void endRun(); + + void startCycle(); + void completeCycle(); + + bool completedCycle() const; + ITracker& currentTracker(); + void setCurrentTracker( ITracker* tracker ); + }; + + class TrackerBase : public ITracker { + protected: + enum CycleState { + NotStarted, + Executing, + ExecutingChildren, + NeedsAnotherRun, + CompletedSuccessfully, + Failed + }; + + using Children = std::vector; + NameAndLocation m_nameAndLocation; + TrackerContext& m_ctx; + ITracker* m_parent; + Children m_children; + CycleState m_runState = NotStarted; + + public: + TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); + + NameAndLocation const& nameAndLocation() const override; + bool isComplete() const override; + bool isSuccessfullyCompleted() const override; + bool isOpen() const override; + bool hasChildren() const override; + + void addChild( ITrackerPtr const& child ) override; + + ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) override; + ITracker& parent() override; + + void openChild() override; + + bool isSectionTracker() const override; + bool isIndexTracker() const override; + + void open(); + + void close() override; + void fail() override; + void markAsNeedingAnotherRun() override; + + private: + void moveToParent(); + void moveToThis(); + }; + + class SectionTracker : public TrackerBase { + std::vector m_filters; + public: + SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); + + bool isSectionTracker() const override; + + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ); + + void tryOpen(); + + void addInitialFilters( std::vector const& filters ); + void addNextFilters( std::vector const& filters ); + }; + + class IndexTracker : public TrackerBase { + int m_size; + int m_index = -1; + public: + IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ); + + bool isIndexTracker() const override; + void close() override; + + static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ); + + int index() const; + + void moveNext(); + }; + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; +using TestCaseTracking::IndexTracker; + +} // namespace Catch + +// end catch_test_case_tracker.h + +// start catch_leak_detector.h + +namespace Catch { + + struct LeakDetector { + LeakDetector(); + ~LeakDetector(); + }; + +} +// end catch_leak_detector.h +// Cpp files will be included in the single-header file here +// start catch_approx.cpp + +#include +#include + +namespace { + +// Performs equivalent check of std::fabs(lhs - rhs) <= margin +// But without the subtraction to allow for INFINITY in comparison +bool marginComparison(double lhs, double rhs, double margin) { + return (lhs + margin >= rhs) && (rhs + margin >= lhs); +} + +} + +namespace Catch { +namespace Detail { + + Approx::Approx ( double value ) + : m_epsilon( std::numeric_limits::epsilon()*100 ), + m_margin( 0.0 ), + m_scale( 0.0 ), + m_value( value ) + {} + + Approx Approx::custom() { + return Approx( 0 ); + } + + Approx Approx::operator-() const { + auto temp(*this); + temp.m_value = -temp.m_value; + return temp; + } + + std::string Approx::toString() const { + ReusableStringStream rss; + rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; + return rss.str(); + } + + bool Approx::equalityComparisonImpl(const double other) const { + // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value + // Thanks to Richard Harris for his help refining the scaled margin value + return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(m_value))); + } + + void Approx::setMargin(double margin) { + CATCH_ENFORCE(margin >= 0, + "Invalid Approx::margin: " << margin << '.' + << " Approx::Margin has to be non-negative."); + m_margin = margin; + } + + void Approx::setEpsilon(double epsilon) { + CATCH_ENFORCE(epsilon >= 0 && epsilon <= 1.0, + "Invalid Approx::epsilon: " << epsilon << '.' + << " Approx::epsilon has to be in [0, 1]"); + m_epsilon = epsilon; + } + +} // end namespace Detail + +namespace literals { + Detail::Approx operator "" _a(long double val) { + return Detail::Approx(val); + } + Detail::Approx operator "" _a(unsigned long long val) { + return Detail::Approx(val); + } +} // end namespace literals + +std::string StringMaker::convert(Catch::Detail::Approx const& value) { + return value.toString(); +} + +} // end namespace Catch +// end catch_approx.cpp +// start catch_assertionhandler.cpp + +// start catch_context.h + +#include + +namespace Catch { + + struct IResultCapture; + struct IRunner; + struct IConfig; + struct IMutableContext; + + using IConfigPtr = std::shared_ptr; + + struct IContext + { + virtual ~IContext(); + + virtual IResultCapture* getResultCapture() = 0; + virtual IRunner* getRunner() = 0; + virtual IConfigPtr const& getConfig() const = 0; + }; + + struct IMutableContext : IContext + { + virtual ~IMutableContext(); + virtual void setResultCapture( IResultCapture* resultCapture ) = 0; + virtual void setRunner( IRunner* runner ) = 0; + virtual void setConfig( IConfigPtr const& config ) = 0; + + private: + static IMutableContext *currentContext; + friend IMutableContext& getCurrentMutableContext(); + friend void cleanUpContext(); + static void createContext(); + }; + + inline IMutableContext& getCurrentMutableContext() + { + if( !IMutableContext::currentContext ) + IMutableContext::createContext(); + return *IMutableContext::currentContext; + } + + inline IContext& getCurrentContext() + { + return getCurrentMutableContext(); + } + + void cleanUpContext(); +} + +// end catch_context.h +// start catch_debugger.h + +namespace Catch { + bool isDebuggerActive(); +} + +#ifdef CATCH_PLATFORM_MAC + + #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ + +#elif defined(CATCH_PLATFORM_LINUX) + // If we can use inline assembler, do it because this allows us to break + // directly at the location of the failing check instead of breaking inside + // raise() called from it, i.e. one stack frame below. + #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) + #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */ + #else // Fall back to the generic way. + #include + + #define CATCH_TRAP() raise(SIGTRAP) + #endif +#elif defined(_MSC_VER) + #define CATCH_TRAP() __debugbreak() +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) void __stdcall DebugBreak(); + #define CATCH_TRAP() DebugBreak() +#endif + +#ifdef CATCH_TRAP + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } +#else + namespace Catch { + inline void doNothing() {} + } + #define CATCH_BREAK_INTO_DEBUGGER() Catch::doNothing() +#endif + +// end catch_debugger.h +// start catch_run_context.h + +// start catch_fatal_condition.h + +// start catch_windows_h_proxy.h + + +#if defined(CATCH_PLATFORM_WINDOWS) + +#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +# define CATCH_DEFINED_NOMINMAX +# define NOMINMAX +#endif +#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __AFXDLL +#include +#else +#include +#endif + +#ifdef CATCH_DEFINED_NOMINMAX +# undef NOMINMAX +#endif +#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN +# undef WIN32_LEAN_AND_MEAN +#endif + +#endif // defined(CATCH_PLATFORM_WINDOWS) + +// end catch_windows_h_proxy.h +#if defined( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + + struct FatalConditionHandler { + + static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo); + FatalConditionHandler(); + static void reset(); + ~FatalConditionHandler(); + + private: + static bool isSet; + static ULONG guaranteeSize; + static PVOID exceptionHandlerHandle; + }; + +} // namespace Catch + +#elif defined ( CATCH_CONFIG_POSIX_SIGNALS ) + +#include + +namespace Catch { + + struct FatalConditionHandler { + + static bool isSet; + static struct sigaction oldSigActions[]; + static stack_t oldSigStack; + static char altStackMem[]; + + static void handleSignal( int sig ); + + FatalConditionHandler(); + ~FatalConditionHandler(); + static void reset(); + }; + +} // namespace Catch + +#else + +namespace Catch { + struct FatalConditionHandler { + void reset(); + }; +} + +#endif + +// end catch_fatal_condition.h +#include + +namespace Catch { + + struct IMutableContext; + + /////////////////////////////////////////////////////////////////////////// + + class RunContext : public IResultCapture, public IRunner { + + public: + RunContext( RunContext const& ) = delete; + RunContext& operator =( RunContext const& ) = delete; + + explicit RunContext( IConfigPtr const& _config, IStreamingReporterPtr&& reporter ); + + ~RunContext() override; + + void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ); + void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ); + + Totals runTest(TestCase const& testCase); + + IConfigPtr config() const; + IStreamingReporter& reporter() const; + + public: // IResultCapture + + // Assertion handlers + void handleExpr + ( AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction ) override; + void handleMessage + ( AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction ) override; + void handleUnexpectedExceptionNotThrown + ( AssertionInfo const& info, + AssertionReaction& reaction ) override; + void handleUnexpectedInflightException + ( AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction ) override; + void handleIncomplete + ( AssertionInfo const& info ) override; + void handleNonExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction ) override; + + bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override; + + void sectionEnded( SectionEndInfo const& endInfo ) override; + void sectionEndedEarly( SectionEndInfo const& endInfo ) override; + + auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override; + + void benchmarkStarting( BenchmarkInfo const& info ) override; + void benchmarkEnded( BenchmarkStats const& stats ) override; + + void pushScopedMessage( MessageInfo const& message ) override; + void popScopedMessage( MessageInfo const& message ) override; + + std::string getCurrentTestName() const override; + + const AssertionResult* getLastResult() const override; + + void exceptionEarlyReported() override; + + void handleFatalErrorCondition( StringRef message ) override; + + bool lastAssertionPassed() override; + + void assertionPassed() override; + + public: + // !TBD We need to do this another way! + bool aborting() const final; + + private: + + void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ); + void invokeActiveTestCase(); + + void resetAssertionInfo(); + bool testForMissingAssertions( Counts& assertions ); + + void assertionEnded( AssertionResult const& result ); + void reportExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, + bool negated ); + + void populateReaction( AssertionReaction& reaction ); + + private: + + void handleUnfinishedSections(); + + TestRunInfo m_runInfo; + IMutableContext& m_context; + TestCase const* m_activeTestCase = nullptr; + ITracker* m_testCaseTracker; + Option m_lastResult; + + IConfigPtr m_config; + Totals m_totals; + IStreamingReporterPtr m_reporter; + std::vector m_messages; + AssertionInfo m_lastAssertionInfo; + std::vector m_unfinishedSections; + std::vector m_activeSections; + TrackerContext m_trackerContext; + bool m_lastAssertionPassed = false; + bool m_shouldReportUnexpected = true; + bool m_includeSuccessfulResults; + }; + +} // end namespace Catch + +// end catch_run_context.h +namespace Catch { + + namespace { + auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& { + expr.streamReconstructedExpression( os ); + return os; + } + } + + LazyExpression::LazyExpression( bool isNegated ) + : m_isNegated( isNegated ) + {} + + LazyExpression::LazyExpression( LazyExpression const& other ) : m_isNegated( other.m_isNegated ) {} + + LazyExpression::operator bool() const { + return m_transientExpression != nullptr; + } + + auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream& { + if( lazyExpr.m_isNegated ) + os << "!"; + + if( lazyExpr ) { + if( lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression() ) + os << "(" << *lazyExpr.m_transientExpression << ")"; + else + os << *lazyExpr.m_transientExpression; + } + else { + os << "{** error - unchecked empty expression requested **}"; + } + return os; + } + + AssertionHandler::AssertionHandler + ( StringRef const& macroName, + SourceLineInfo const& lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition ) + : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }, + m_resultCapture( getResultCapture() ) + {} + + void AssertionHandler::handleExpr( ITransientExpression const& expr ) { + m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction ); + } + void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const& message) { + m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction ); + } + + auto AssertionHandler::allowThrows() const -> bool { + return getCurrentContext().getConfig()->allowThrows(); + } + + void AssertionHandler::complete() { + setCompleted(); + if( m_reaction.shouldDebugBreak ) { + + // If you find your debugger stopping you here then go one level up on the + // call-stack for the code that caused it (typically a failed assertion) + + // (To go back to the test and change execution, jump over the throw, next) + CATCH_BREAK_INTO_DEBUGGER(); + } + if (m_reaction.shouldThrow) { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + throw Catch::TestFailureException(); +#else + CATCH_ERROR( "Test failure requires aborting test!" ); +#endif + } + } + void AssertionHandler::setCompleted() { + m_completed = true; + } + + void AssertionHandler::handleUnexpectedInflightException() { + m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction ); + } + + void AssertionHandler::handleExceptionThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + void AssertionHandler::handleExceptionNotThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + + void AssertionHandler::handleUnexpectedExceptionNotThrown() { + m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction ); + } + + void AssertionHandler::handleThrowingCallSkipped() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + + // This is the overload that takes a string and infers the Equals matcher from it + // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString ) { + handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString ); + } + +} // namespace Catch +// end catch_assertionhandler.cpp +// start catch_assertionresult.cpp + +namespace Catch { + AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression): + lazyExpression(_lazyExpression), + resultType(_resultType) {} + + std::string AssertionResultData::reconstructExpression() const { + + if( reconstructedExpression.empty() ) { + if( lazyExpression ) { + ReusableStringStream rss; + rss << lazyExpression; + reconstructedExpression = rss.str(); + } + } + return reconstructedExpression; + } + + AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) + : m_info( info ), + m_resultData( data ) + {} + + // Result was a success + bool AssertionResult::succeeded() const { + return Catch::isOk( m_resultData.resultType ); + } + + // Result was a success, or failure is suppressed + bool AssertionResult::isOk() const { + return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); + } + + ResultWas::OfType AssertionResult::getResultType() const { + return m_resultData.resultType; + } + + bool AssertionResult::hasExpression() const { + return m_info.capturedExpression[0] != 0; + } + + bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); + } + + std::string AssertionResult::getExpression() const { + if( isFalseTest( m_info.resultDisposition ) ) + return "!(" + m_info.capturedExpression + ")"; + else + return m_info.capturedExpression; + } + + std::string AssertionResult::getExpressionInMacro() const { + std::string expr; + if( m_info.macroName[0] == 0 ) + expr = m_info.capturedExpression; + else { + expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); + expr += m_info.macroName; + expr += "( "; + expr += m_info.capturedExpression; + expr += " )"; + } + return expr; + } + + bool AssertionResult::hasExpandedExpression() const { + return hasExpression() && getExpandedExpression() != getExpression(); + } + + std::string AssertionResult::getExpandedExpression() const { + std::string expr = m_resultData.reconstructExpression(); + return expr.empty() + ? getExpression() + : expr; + } + + std::string AssertionResult::getMessage() const { + return m_resultData.message; + } + SourceLineInfo AssertionResult::getSourceInfo() const { + return m_info.lineInfo; + } + + StringRef AssertionResult::getTestMacroName() const { + return m_info.macroName; + } + +} // end namespace Catch +// end catch_assertionresult.cpp +// start catch_benchmark.cpp + +namespace Catch { + + auto BenchmarkLooper::getResolution() -> uint64_t { + return getEstimatedClockResolution() * getCurrentContext().getConfig()->benchmarkResolutionMultiple(); + } + + void BenchmarkLooper::reportStart() { + getResultCapture().benchmarkStarting( { m_name } ); + } + auto BenchmarkLooper::needsMoreIterations() -> bool { + auto elapsed = m_timer.getElapsedNanoseconds(); + + // Exponentially increasing iterations until we're confident in our timer resolution + if( elapsed < m_resolution ) { + m_iterationsToRun *= 10; + return true; + } + + getResultCapture().benchmarkEnded( { { m_name }, m_count, elapsed } ); + return false; + } + +} // end namespace Catch +// end catch_benchmark.cpp +// start catch_capture_matchers.cpp + +namespace Catch { + + using StringMatcher = Matchers::Impl::MatcherBase; + + // This is the general overload that takes a any string matcher + // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers + // the Equals matcher (so the header does not mention matchers) + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ) { + std::string exceptionMessage = Catch::translateActiveException(); + MatchExpr expr( exceptionMessage, matcher, matcherString ); + handler.handleExpr( expr ); + } + +} // namespace Catch +// end catch_capture_matchers.cpp +// start catch_commandline.cpp + +// start catch_commandline.h + +// start catch_clara.h + +// Use Catch's value for console width (store Clara's off to the side, if present) +#ifdef CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#undef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#endif +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH-1 + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic ignored "-Wshadow" +#endif + +// start clara.hpp +// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// See https://github.com/philsquared/Clara for more details + +// Clara v1.1.5 + + +#ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80 +#endif + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +#ifndef CLARA_CONFIG_OPTIONAL_TYPE +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L +#include +#define CLARA_CONFIG_OPTIONAL_TYPE std::optional +#endif +#endif +#endif + +// ----------- #included from clara_textflow.hpp ----------- + +// TextFlowCpp +// +// A single-header library for wrapping and laying out basic text, by Phil Nash +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// This project is hosted at https://github.com/philsquared/textflowcpp + + +#include +#include +#include +#include + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { +namespace clara { +namespace TextFlow { + +inline auto isWhitespace(char c) -> bool { + static std::string chars = " \t\n\r"; + return chars.find(c) != std::string::npos; +} +inline auto isBreakableBefore(char c) -> bool { + static std::string chars = "[({<|"; + return chars.find(c) != std::string::npos; +} +inline auto isBreakableAfter(char c) -> bool { + static std::string chars = "])}>.,:;*+-=&/\\"; + return chars.find(c) != std::string::npos; +} + +class Columns; + +class Column { + std::vector m_strings; + size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; + size_t m_indent = 0; + size_t m_initialIndent = std::string::npos; + +public: + class iterator { + friend Column; + + Column const& m_column; + size_t m_stringIndex = 0; + size_t m_pos = 0; + + size_t m_len = 0; + size_t m_end = 0; + bool m_suffix = false; + + iterator(Column const& column, size_t stringIndex) + : m_column(column), + m_stringIndex(stringIndex) {} + + auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } + + auto isBoundary(size_t at) const -> bool { + assert(at > 0); + assert(at <= line().size()); + + return at == line().size() || + (isWhitespace(line()[at]) && !isWhitespace(line()[at - 1])) || + isBreakableBefore(line()[at]) || + isBreakableAfter(line()[at - 1]); + } + + void calcLength() { + assert(m_stringIndex < m_column.m_strings.size()); + + m_suffix = false; + auto width = m_column.m_width - indent(); + m_end = m_pos; + while (m_end < line().size() && line()[m_end] != '\n') + ++m_end; + + if (m_end < m_pos + width) { + m_len = m_end - m_pos; + } else { + size_t len = width; + while (len > 0 && !isBoundary(m_pos + len)) + --len; + while (len > 0 && isWhitespace(line()[m_pos + len - 1])) + --len; + + if (len > 0) { + m_len = len; + } else { + m_suffix = true; + m_len = width - 1; + } + } + } + + auto indent() const -> size_t { + auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; + return initial == std::string::npos ? m_column.m_indent : initial; + } + + auto addIndentAndSuffix(std::string const &plain) const -> std::string { + return std::string(indent(), ' ') + (m_suffix ? plain + "-" : plain); + } + + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::forward_iterator_tag; + + explicit iterator(Column const& column) : m_column(column) { + assert(m_column.m_width > m_column.m_indent); + assert(m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent); + calcLength(); + if (m_len == 0) + m_stringIndex++; // Empty string + } + + auto operator *() const -> std::string { + assert(m_stringIndex < m_column.m_strings.size()); + assert(m_pos <= m_end); + return addIndentAndSuffix(line().substr(m_pos, m_len)); + } + + auto operator ++() -> iterator& { + m_pos += m_len; + if (m_pos < line().size() && line()[m_pos] == '\n') + m_pos += 1; + else + while (m_pos < line().size() && isWhitespace(line()[m_pos])) + ++m_pos; + + if (m_pos == line().size()) { + m_pos = 0; + ++m_stringIndex; + } + if (m_stringIndex < m_column.m_strings.size()) + calcLength(); + return *this; + } + auto operator ++(int) -> iterator { + iterator prev(*this); + operator++(); + return prev; + } + + auto operator ==(iterator const& other) const -> bool { + return + m_pos == other.m_pos && + m_stringIndex == other.m_stringIndex && + &m_column == &other.m_column; + } + auto operator !=(iterator const& other) const -> bool { + return !operator==(other); + } + }; + using const_iterator = iterator; + + explicit Column(std::string const& text) { m_strings.push_back(text); } + + auto width(size_t newWidth) -> Column& { + assert(newWidth > 0); + m_width = newWidth; + return *this; + } + auto indent(size_t newIndent) -> Column& { + m_indent = newIndent; + return *this; + } + auto initialIndent(size_t newIndent) -> Column& { + m_initialIndent = newIndent; + return *this; + } + + auto width() const -> size_t { return m_width; } + auto begin() const -> iterator { return iterator(*this); } + auto end() const -> iterator { return { *this, m_strings.size() }; } + + inline friend std::ostream& operator << (std::ostream& os, Column const& col) { + bool first = true; + for (auto line : col) { + if (first) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto operator + (Column const& other)->Columns; + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } +}; + +class Spacer : public Column { + +public: + explicit Spacer(size_t spaceWidth) : Column("") { + width(spaceWidth); + } +}; + +class Columns { + std::vector m_columns; + +public: + + class iterator { + friend Columns; + struct EndTag {}; + + std::vector const& m_columns; + std::vector m_iterators; + size_t m_activeIterators; + + iterator(Columns const& columns, EndTag) + : m_columns(columns.m_columns), + m_activeIterators(0) { + m_iterators.reserve(m_columns.size()); + + for (auto const& col : m_columns) + m_iterators.push_back(col.end()); + } + + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::forward_iterator_tag; + + explicit iterator(Columns const& columns) + : m_columns(columns.m_columns), + m_activeIterators(m_columns.size()) { + m_iterators.reserve(m_columns.size()); + + for (auto const& col : m_columns) + m_iterators.push_back(col.begin()); + } + + auto operator ==(iterator const& other) const -> bool { + return m_iterators == other.m_iterators; + } + auto operator !=(iterator const& other) const -> bool { + return m_iterators != other.m_iterators; + } + auto operator *() const -> std::string { + std::string row, padding; + + for (size_t i = 0; i < m_columns.size(); ++i) { + auto width = m_columns[i].width(); + if (m_iterators[i] != m_columns[i].end()) { + std::string col = *m_iterators[i]; + row += padding + col; + if (col.size() < width) + padding = std::string(width - col.size(), ' '); + else + padding = ""; + } else { + padding += std::string(width, ' '); + } + } + return row; + } + auto operator ++() -> iterator& { + for (size_t i = 0; i < m_columns.size(); ++i) { + if (m_iterators[i] != m_columns[i].end()) + ++m_iterators[i]; + } + return *this; + } + auto operator ++(int) -> iterator { + iterator prev(*this); + operator++(); + return prev; + } + }; + using const_iterator = iterator; + + auto begin() const -> iterator { return iterator(*this); } + auto end() const -> iterator { return { *this, iterator::EndTag() }; } + + auto operator += (Column const& col) -> Columns& { + m_columns.push_back(col); + return *this; + } + auto operator + (Column const& col) -> Columns { + Columns combined = *this; + combined += col; + return combined; + } + + inline friend std::ostream& operator << (std::ostream& os, Columns const& cols) { + + bool first = true; + for (auto line : cols) { + if (first) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } +}; + +inline auto Column::operator + (Column const& other) -> Columns { + Columns cols; + cols += *this; + cols += other; + return cols; +} +} + +} +} + +// ----------- end of #include from clara_textflow.hpp ----------- +// ........... back in clara.hpp + +#include +#include +#include +#include + +#if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) +#define CATCH_PLATFORM_WINDOWS +#endif + +namespace Catch { namespace clara { +namespace detail { + + // Traits for extracting arg and return type of lambdas (for single argument lambdas) + template + struct UnaryLambdaTraits : UnaryLambdaTraits {}; + + template + struct UnaryLambdaTraits { + static const bool isValid = false; + }; + + template + struct UnaryLambdaTraits { + static const bool isValid = true; + using ArgType = typename std::remove_const::type>::type; + using ReturnType = ReturnT; + }; + + class TokenStream; + + // Transport for raw args (copied from main args, or supplied via init list for testing) + class Args { + friend TokenStream; + std::string m_exeName; + std::vector m_args; + + public: + Args( int argc, char const* const* argv ) + : m_exeName(argv[0]), + m_args(argv + 1, argv + argc) {} + + Args( std::initializer_list args ) + : m_exeName( *args.begin() ), + m_args( args.begin()+1, args.end() ) + {} + + auto exeName() const -> std::string { + return m_exeName; + } + }; + + // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string + // may encode an option + its argument if the : or = form is used + enum class TokenType { + Option, Argument + }; + struct Token { + TokenType type; + std::string token; + }; + + inline auto isOptPrefix( char c ) -> bool { + return c == '-' +#ifdef CATCH_PLATFORM_WINDOWS + || c == '/' +#endif + ; + } + + // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled + class TokenStream { + using Iterator = std::vector::const_iterator; + Iterator it; + Iterator itEnd; + std::vector m_tokenBuffer; + + void loadBuffer() { + m_tokenBuffer.resize( 0 ); + + // Skip any empty strings + while( it != itEnd && it->empty() ) + ++it; + + if( it != itEnd ) { + auto const &next = *it; + if( isOptPrefix( next[0] ) ) { + auto delimiterPos = next.find_first_of( " :=" ); + if( delimiterPos != std::string::npos ) { + m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); + m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } ); + } else { + if( next[1] != '-' && next.size() > 2 ) { + std::string opt = "- "; + for( size_t i = 1; i < next.size(); ++i ) { + opt[1] = next[i]; + m_tokenBuffer.push_back( { TokenType::Option, opt } ); + } + } else { + m_tokenBuffer.push_back( { TokenType::Option, next } ); + } + } + } else { + m_tokenBuffer.push_back( { TokenType::Argument, next } ); + } + } + } + + public: + explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {} + + TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) { + loadBuffer(); + } + + explicit operator bool() const { + return !m_tokenBuffer.empty() || it != itEnd; + } + + auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } + + auto operator*() const -> Token { + assert( !m_tokenBuffer.empty() ); + return m_tokenBuffer.front(); + } + + auto operator->() const -> Token const * { + assert( !m_tokenBuffer.empty() ); + return &m_tokenBuffer.front(); + } + + auto operator++() -> TokenStream & { + if( m_tokenBuffer.size() >= 2 ) { + m_tokenBuffer.erase( m_tokenBuffer.begin() ); + } else { + if( it != itEnd ) + ++it; + loadBuffer(); + } + return *this; + } + }; + + class ResultBase { + public: + enum Type { + Ok, LogicError, RuntimeError + }; + + protected: + ResultBase( Type type ) : m_type( type ) {} + virtual ~ResultBase() = default; + + virtual void enforceOk() const = 0; + + Type m_type; + }; + + template + class ResultValueBase : public ResultBase { + public: + auto value() const -> T const & { + enforceOk(); + return m_value; + } + + protected: + ResultValueBase( Type type ) : ResultBase( type ) {} + + ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) { + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + } + + ResultValueBase( Type, T const &value ) : ResultBase( Ok ) { + new( &m_value ) T( value ); + } + + auto operator=( ResultValueBase const &other ) -> ResultValueBase & { + if( m_type == ResultBase::Ok ) + m_value.~T(); + ResultBase::operator=(other); + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + return *this; + } + + ~ResultValueBase() override { + if( m_type == Ok ) + m_value.~T(); + } + + union { + T m_value; + }; + }; + + template<> + class ResultValueBase : public ResultBase { + protected: + using ResultBase::ResultBase; + }; + + template + class BasicResult : public ResultValueBase { + public: + template + explicit BasicResult( BasicResult const &other ) + : ResultValueBase( other.type() ), + m_errorMessage( other.errorMessage() ) + { + assert( type() != ResultBase::Ok ); + } + + template + static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; } + static auto ok() -> BasicResult { return { ResultBase::Ok }; } + static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; } + static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; } + + explicit operator bool() const { return m_type == ResultBase::Ok; } + auto type() const -> ResultBase::Type { return m_type; } + auto errorMessage() const -> std::string { return m_errorMessage; } + + protected: + void enforceOk() const override { + + // Errors shouldn't reach this point, but if they do + // the actual error message will be in m_errorMessage + assert( m_type != ResultBase::LogicError ); + assert( m_type != ResultBase::RuntimeError ); + if( m_type != ResultBase::Ok ) + std::abort(); + } + + std::string m_errorMessage; // Only populated if resultType is an error + + BasicResult( ResultBase::Type type, std::string const &message ) + : ResultValueBase(type), + m_errorMessage(message) + { + assert( m_type != ResultBase::Ok ); + } + + using ResultValueBase::ResultValueBase; + using ResultBase::m_type; + }; + + enum class ParseResultType { + Matched, NoMatch, ShortCircuitAll, ShortCircuitSame + }; + + class ParseState { + public: + + ParseState( ParseResultType type, TokenStream const &remainingTokens ) + : m_type(type), + m_remainingTokens( remainingTokens ) + {} + + auto type() const -> ParseResultType { return m_type; } + auto remainingTokens() const -> TokenStream { return m_remainingTokens; } + + private: + ParseResultType m_type; + TokenStream m_remainingTokens; + }; + + using Result = BasicResult; + using ParserResult = BasicResult; + using InternalParseResult = BasicResult; + + struct HelpColumns { + std::string left; + std::string right; + }; + + template + inline auto convertInto( std::string const &source, T& target ) -> ParserResult { + std::stringstream ss; + ss << source; + ss >> target; + if( ss.fail() ) + return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult { + target = source; + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { + std::string srcLC = source; + std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast( ::tolower(c) ); } ); + if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") + target = true; + else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") + target = false; + else + return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + } +#ifdef CLARA_CONFIG_OPTIONAL_TYPE + template + inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE& target ) -> ParserResult { + T temp; + auto result = convertInto( source, temp ); + if( result ) + target = std::move(temp); + return result; + } +#endif // CLARA_CONFIG_OPTIONAL_TYPE + + struct NonCopyable { + NonCopyable() = default; + NonCopyable( NonCopyable const & ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable &operator=( NonCopyable const & ) = delete; + NonCopyable &operator=( NonCopyable && ) = delete; + }; + + struct BoundRef : NonCopyable { + virtual ~BoundRef() = default; + virtual auto isContainer() const -> bool { return false; } + virtual auto isFlag() const -> bool { return false; } + }; + struct BoundValueRefBase : BoundRef { + virtual auto setValue( std::string const &arg ) -> ParserResult = 0; + }; + struct BoundFlagRefBase : BoundRef { + virtual auto setFlag( bool flag ) -> ParserResult = 0; + virtual auto isFlag() const -> bool { return true; } + }; + + template + struct BoundValueRef : BoundValueRefBase { + T &m_ref; + + explicit BoundValueRef( T &ref ) : m_ref( ref ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return convertInto( arg, m_ref ); + } + }; + + template + struct BoundValueRef> : BoundValueRefBase { + std::vector &m_ref; + + explicit BoundValueRef( std::vector &ref ) : m_ref( ref ) {} + + auto isContainer() const -> bool override { return true; } + + auto setValue( std::string const &arg ) -> ParserResult override { + T temp; + auto result = convertInto( arg, temp ); + if( result ) + m_ref.push_back( temp ); + return result; + } + }; + + struct BoundFlagRef : BoundFlagRefBase { + bool &m_ref; + + explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {} + + auto setFlag( bool flag ) -> ParserResult override { + m_ref = flag; + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template + struct LambdaInvoker { + static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); + + template + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + return lambda( arg ); + } + }; + + template<> + struct LambdaInvoker { + template + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + lambda( arg ); + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template + inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { + ArgType temp{}; + auto result = convertInto( arg, temp ); + return !result + ? result + : LambdaInvoker::ReturnType>::invoke( lambda, temp ); + } + + template + struct BoundLambda : BoundValueRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); + explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return invokeLambda::ArgType>( m_lambda, arg ); + } + }; + + template + struct BoundFlagLambda : BoundFlagRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); + static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); + + explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setFlag( bool flag ) -> ParserResult override { + return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); + } + }; + + enum class Optionality { Optional, Required }; + + struct Parser; + + class ParserBase { + public: + virtual ~ParserBase() = default; + virtual auto validate() const -> Result { return Result::ok(); } + virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0; + virtual auto cardinality() const -> size_t { return 1; } + + auto parse( Args const &args ) const -> InternalParseResult { + return parse( args.exeName(), TokenStream( args ) ); + } + }; + + template + class ComposableParserImpl : public ParserBase { + public: + template + auto operator|( T const &other ) const -> Parser; + + template + auto operator+( T const &other ) const -> Parser; + }; + + // Common code and state for Args and Opts + template + class ParserRefImpl : public ComposableParserImpl { + protected: + Optionality m_optionality = Optionality::Optional; + std::shared_ptr m_ref; + std::string m_hint; + std::string m_description; + + explicit ParserRefImpl( std::shared_ptr const &ref ) : m_ref( ref ) {} + + public: + template + ParserRefImpl( T &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint( hint ) + {} + + template + ParserRefImpl( LambdaT const &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint(hint) + {} + + auto operator()( std::string const &description ) -> DerivedT & { + m_description = description; + return static_cast( *this ); + } + + auto optional() -> DerivedT & { + m_optionality = Optionality::Optional; + return static_cast( *this ); + }; + + auto required() -> DerivedT & { + m_optionality = Optionality::Required; + return static_cast( *this ); + }; + + auto isOptional() const -> bool { + return m_optionality == Optionality::Optional; + } + + auto cardinality() const -> size_t override { + if( m_ref->isContainer() ) + return 0; + else + return 1; + } + + auto hint() const -> std::string { return m_hint; } + }; + + class ExeName : public ComposableParserImpl { + std::shared_ptr m_name; + std::shared_ptr m_ref; + + template + static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { + return std::make_shared>( lambda) ; + } + + public: + ExeName() : m_name( std::make_shared( "" ) ) {} + + explicit ExeName( std::string &ref ) : ExeName() { + m_ref = std::make_shared>( ref ); + } + + template + explicit ExeName( LambdaT const& lambda ) : ExeName() { + m_ref = std::make_shared>( lambda ); + } + + // The exe name is not parsed out of the normal tokens, but is handled specially + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + } + + auto name() const -> std::string { return *m_name; } + auto set( std::string const& newName ) -> ParserResult { + + auto lastSlash = newName.find_last_of( "\\/" ); + auto filename = ( lastSlash == std::string::npos ) + ? newName + : newName.substr( lastSlash+1 ); + + *m_name = filename; + if( m_ref ) + return m_ref->setValue( filename ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + class Arg : public ParserRefImpl { + public: + using ParserRefImpl::ParserRefImpl; + + auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + auto const &token = *remainingTokens; + if( token.type != TokenType::Argument ) + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + + assert( !m_ref->isFlag() ); + auto valueRef = static_cast( m_ref.get() ); + + auto result = valueRef->setValue( remainingTokens->token ); + if( !result ) + return InternalParseResult( result ); + else + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + }; + + inline auto normaliseOpt( std::string const &optName ) -> std::string { +#ifdef CATCH_PLATFORM_WINDOWS + if( optName[0] == '/' ) + return "-" + optName.substr( 1 ); + else +#endif + return optName; + } + + class Opt : public ParserRefImpl { + protected: + std::vector m_optNames; + + public: + template + explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared>( ref ) ) {} + + explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared( ref ) ) {} + + template + Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + template + Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + auto operator[]( std::string const &optName ) -> Opt & { + m_optNames.push_back( optName ); + return *this; + } + + auto getHelpColumns() const -> std::vector { + std::ostringstream oss; + bool first = true; + for( auto const &opt : m_optNames ) { + if (first) + first = false; + else + oss << ", "; + oss << opt; + } + if( !m_hint.empty() ) + oss << " <" << m_hint << ">"; + return { { oss.str(), m_description } }; + } + + auto isMatch( std::string const &optToken ) const -> bool { + auto normalisedToken = normaliseOpt( optToken ); + for( auto const &name : m_optNames ) { + if( normaliseOpt( name ) == normalisedToken ) + return true; + } + return false; + } + + using ParserBase::parse; + + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + if( remainingTokens && remainingTokens->type == TokenType::Option ) { + auto const &token = *remainingTokens; + if( isMatch(token.token ) ) { + if( m_ref->isFlag() ) { + auto flagRef = static_cast( m_ref.get() ); + auto result = flagRef->setFlag( true ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } else { + auto valueRef = static_cast( m_ref.get() ); + ++remainingTokens; + if( !remainingTokens ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto const &argToken = *remainingTokens; + if( argToken.type != TokenType::Argument ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto result = valueRef->setValue( argToken.token ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + } + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + } + + auto validate() const -> Result override { + if( m_optNames.empty() ) + return Result::logicError( "No options supplied to Opt" ); + for( auto const &name : m_optNames ) { + if( name.empty() ) + return Result::logicError( "Option name cannot be empty" ); +#ifdef CATCH_PLATFORM_WINDOWS + if( name[0] != '-' && name[0] != '/' ) + return Result::logicError( "Option name must begin with '-' or '/'" ); +#else + if( name[0] != '-' ) + return Result::logicError( "Option name must begin with '-'" ); +#endif + } + return ParserRefImpl::validate(); + } + }; + + struct Help : Opt { + Help( bool &showHelpFlag ) + : Opt([&]( bool flag ) { + showHelpFlag = flag; + return ParserResult::ok( ParseResultType::ShortCircuitAll ); + }) + { + static_cast( *this ) + ("display usage information") + ["-?"]["-h"]["--help"] + .optional(); + } + }; + + struct Parser : ParserBase { + + mutable ExeName m_exeName; + std::vector m_options; + std::vector m_args; + + auto operator|=( ExeName const &exeName ) -> Parser & { + m_exeName = exeName; + return *this; + } + + auto operator|=( Arg const &arg ) -> Parser & { + m_args.push_back(arg); + return *this; + } + + auto operator|=( Opt const &opt ) -> Parser & { + m_options.push_back(opt); + return *this; + } + + auto operator|=( Parser const &other ) -> Parser & { + m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); + m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); + return *this; + } + + template + auto operator|( T const &other ) const -> Parser { + return Parser( *this ) |= other; + } + + // Forward deprecated interface with '+' instead of '|' + template + auto operator+=( T const &other ) -> Parser & { return operator|=( other ); } + template + auto operator+( T const &other ) const -> Parser { return operator|( other ); } + + auto getHelpColumns() const -> std::vector { + std::vector cols; + for (auto const &o : m_options) { + auto childCols = o.getHelpColumns(); + cols.insert( cols.end(), childCols.begin(), childCols.end() ); + } + return cols; + } + + void writeToStream( std::ostream &os ) const { + if (!m_exeName.name().empty()) { + os << "usage:\n" << " " << m_exeName.name() << " "; + bool required = true, first = true; + for( auto const &arg : m_args ) { + if (first) + first = false; + else + os << " "; + if( arg.isOptional() && required ) { + os << "["; + required = false; + } + os << "<" << arg.hint() << ">"; + if( arg.cardinality() == 0 ) + os << " ... "; + } + if( !required ) + os << "]"; + if( !m_options.empty() ) + os << " options"; + os << "\n\nwhere options are:" << std::endl; + } + + auto rows = getHelpColumns(); + size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH; + size_t optWidth = 0; + for( auto const &cols : rows ) + optWidth = (std::max)(optWidth, cols.left.size() + 2); + + optWidth = (std::min)(optWidth, consoleWidth/2); + + for( auto const &cols : rows ) { + auto row = + TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + + TextFlow::Spacer(4) + + TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth ); + os << row << std::endl; + } + } + + friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& { + parser.writeToStream( os ); + return os; + } + + auto validate() const -> Result override { + for( auto const &opt : m_options ) { + auto result = opt.validate(); + if( !result ) + return result; + } + for( auto const &arg : m_args ) { + auto result = arg.validate(); + if( !result ) + return result; + } + return Result::ok(); + } + + using ParserBase::parse; + + auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override { + + struct ParserInfo { + ParserBase const* parser = nullptr; + size_t count = 0; + }; + const size_t totalParsers = m_options.size() + m_args.size(); + assert( totalParsers < 512 ); + // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do + ParserInfo parseInfos[512]; + + { + size_t i = 0; + for (auto const &opt : m_options) parseInfos[i++].parser = &opt; + for (auto const &arg : m_args) parseInfos[i++].parser = &arg; + } + + m_exeName.set( exeName ); + + auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + while( result.value().remainingTokens() ) { + bool tokenParsed = false; + + for( size_t i = 0; i < totalParsers; ++i ) { + auto& parseInfo = parseInfos[i]; + if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { + result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); + if (!result) + return result; + if (result.value().type() != ParseResultType::NoMatch) { + tokenParsed = true; + ++parseInfo.count; + break; + } + } + } + + if( result.value().type() == ParseResultType::ShortCircuitAll ) + return result; + if( !tokenParsed ) + return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token ); + } + // !TBD Check missing required options + return result; + } + }; + + template + template + auto ComposableParserImpl::operator|( T const &other ) const -> Parser { + return Parser() | static_cast( *this ) | other; + } +} // namespace detail + +// A Combined parser +using detail::Parser; + +// A parser for options +using detail::Opt; + +// A parser for arguments +using detail::Arg; + +// Wrapper for argc, argv from main() +using detail::Args; + +// Specifies the name of the executable +using detail::ExeName; + +// Convenience wrapper for option parser that specifies the help option +using detail::Help; + +// enum of result types from a parse +using detail::ParseResultType; + +// Result type for parser operation +using detail::ParserResult; + +}} // namespace Catch::clara + +// end clara.hpp +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// Restore Clara's value for console width, if present +#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +// end catch_clara.h +namespace Catch { + + clara::Parser makeCommandLineParser( ConfigData& config ); + +} // end namespace Catch + +// end catch_commandline.h +#include +#include + +namespace Catch { + + clara::Parser makeCommandLineParser( ConfigData& config ) { + + using namespace clara; + + auto const setWarning = [&]( std::string const& warning ) { + auto warningSet = [&]() { + if( warning == "NoAssertions" ) + return WarnAbout::NoAssertions; + + if ( warning == "NoTests" ) + return WarnAbout::NoTests; + + return WarnAbout::Nothing; + }(); + + if (warningSet == WarnAbout::Nothing) + return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" ); + config.warnings = static_cast( config.warnings | warningSet ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const loadTestNamesFromFile = [&]( std::string const& filename ) { + std::ifstream f( filename.c_str() ); + if( !f.is_open() ) + return ParserResult::runtimeError( "Unable to load input file: '" + filename + "'" ); + + std::string line; + while( std::getline( f, line ) ) { + line = trim(line); + if( !line.empty() && !startsWith( line, '#' ) ) { + if( !startsWith( line, '"' ) ) + line = '"' + line + '"'; + config.testsOrTags.push_back( line + ',' ); + } + } + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setTestOrder = [&]( std::string const& order ) { + if( startsWith( "declared", order ) ) + config.runOrder = RunTests::InDeclarationOrder; + else if( startsWith( "lexical", order ) ) + config.runOrder = RunTests::InLexicographicalOrder; + else if( startsWith( "random", order ) ) + config.runOrder = RunTests::InRandomOrder; + else + return clara::ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setRngSeed = [&]( std::string const& seed ) { + if( seed != "time" ) + return clara::detail::convertInto( seed, config.rngSeed ); + config.rngSeed = static_cast( std::time(nullptr) ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setColourUsage = [&]( std::string const& useColour ) { + auto mode = toLower( useColour ); + + if( mode == "yes" ) + config.useColour = UseColour::Yes; + else if( mode == "no" ) + config.useColour = UseColour::No; + else if( mode == "auto" ) + config.useColour = UseColour::Auto; + else + return ParserResult::runtimeError( "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setWaitForKeypress = [&]( std::string const& keypress ) { + auto keypressLc = toLower( keypress ); + if( keypressLc == "start" ) + config.waitForKeypress = WaitForKeypress::BeforeStart; + else if( keypressLc == "exit" ) + config.waitForKeypress = WaitForKeypress::BeforeExit; + else if( keypressLc == "both" ) + config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; + else + return ParserResult::runtimeError( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setVerbosity = [&]( std::string const& verbosity ) { + auto lcVerbosity = toLower( verbosity ); + if( lcVerbosity == "quiet" ) + config.verbosity = Verbosity::Quiet; + else if( lcVerbosity == "normal" ) + config.verbosity = Verbosity::Normal; + else if( lcVerbosity == "high" ) + config.verbosity = Verbosity::High; + else + return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setReporter = [&]( std::string const& reporter ) { + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + + auto lcReporter = toLower( reporter ); + auto result = factories.find( lcReporter ); + + if( factories.end() != result ) + config.reporterName = lcReporter; + else + return ParserResult::runtimeError( "Unrecognized reporter, '" + reporter + "'. Check available with --list-reporters" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + + auto cli + = ExeName( config.processName ) + | Help( config.showHelp ) + | Opt( config.listTests ) + ["-l"]["--list-tests"] + ( "list all/matching test cases" ) + | Opt( config.listTags ) + ["-t"]["--list-tags"] + ( "list all/matching tags" ) + | Opt( config.showSuccessfulTests ) + ["-s"]["--success"] + ( "include successful tests in output" ) + | Opt( config.shouldDebugBreak ) + ["-b"]["--break"] + ( "break into debugger on failure" ) + | Opt( config.noThrow ) + ["-e"]["--nothrow"] + ( "skip exception tests" ) + | Opt( config.showInvisibles ) + ["-i"]["--invisibles"] + ( "show invisibles (tabs, newlines)" ) + | Opt( config.outputFilename, "filename" ) + ["-o"]["--out"] + ( "output filename" ) + | Opt( setReporter, "name" ) + ["-r"]["--reporter"] + ( "reporter to use (defaults to console)" ) + | Opt( config.name, "name" ) + ["-n"]["--name"] + ( "suite name" ) + | Opt( [&]( bool ){ config.abortAfter = 1; } ) + ["-a"]["--abort"] + ( "abort at first failure" ) + | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" ) + ["-x"]["--abortx"] + ( "abort after x failures" ) + | Opt( setWarning, "warning name" ) + ["-w"]["--warn"] + ( "enable warnings" ) + | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" ) + ["-d"]["--durations"] + ( "show test durations" ) + | Opt( loadTestNamesFromFile, "filename" ) + ["-f"]["--input-file"] + ( "load test names to run from a file" ) + | Opt( config.filenamesAsTags ) + ["-#"]["--filenames-as-tags"] + ( "adds a tag for the filename" ) + | Opt( config.sectionsToRun, "section name" ) + ["-c"]["--section"] + ( "specify section to run" ) + | Opt( setVerbosity, "quiet|normal|high" ) + ["-v"]["--verbosity"] + ( "set output verbosity" ) + | Opt( config.listTestNamesOnly ) + ["--list-test-names-only"] + ( "list all/matching test cases names only" ) + | Opt( config.listReporters ) + ["--list-reporters"] + ( "list all reporters" ) + | Opt( setTestOrder, "decl|lex|rand" ) + ["--order"] + ( "test case order (defaults to decl)" ) + | Opt( setRngSeed, "'time'|number" ) + ["--rng-seed"] + ( "set a specific seed for random numbers" ) + | Opt( setColourUsage, "yes|no" ) + ["--use-colour"] + ( "should output be colourised" ) + | Opt( config.libIdentify ) + ["--libidentify"] + ( "report name and version according to libidentify standard" ) + | Opt( setWaitForKeypress, "start|exit|both" ) + ["--wait-for-keypress"] + ( "waits for a keypress before exiting" ) + | Opt( config.benchmarkResolutionMultiple, "multiplier" ) + ["--benchmark-resolution-multiple"] + ( "multiple of clock resolution to run benchmarks" ) + + | Arg( config.testsOrTags, "test name|pattern|tags" ) + ( "which test or tests to use" ); + + return cli; + } + +} // end namespace Catch +// end catch_commandline.cpp +// start catch_common.cpp + +#include +#include + +namespace Catch { + + bool SourceLineInfo::empty() const noexcept { + return file[0] == '\0'; + } + bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept { + return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); + } + bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept { + // We can assume that the same file will usually have the same pointer. + // Thus, if the pointers are the same, there is no point in calling the strcmp + return line < other.line || ( line == other.line && file != other.file && (std::strcmp(file, other.file) < 0)); + } + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { +#ifndef __GNUG__ + os << info.file << '(' << info.line << ')'; +#else + os << info.file << ':' << info.line; +#endif + return os; + } + + std::string StreamEndStop::operator+() const { + return std::string(); + } + + NonCopyable::NonCopyable() = default; + NonCopyable::~NonCopyable() = default; + +} +// end catch_common.cpp +// start catch_config.cpp + +namespace Catch { + + Config::Config( ConfigData const& data ) + : m_data( data ), + m_stream( openStream() ) + { + TestSpecParser parser(ITagAliasRegistry::get()); + if (data.testsOrTags.empty()) { + parser.parse("~[.]"); // All not hidden tests + } + else { + m_hasTestFilters = true; + for( auto const& testOrTags : data.testsOrTags ) + parser.parse( testOrTags ); + } + m_testSpec = parser.testSpec(); + } + + std::string const& Config::getFilename() const { + return m_data.outputFilename ; + } + + bool Config::listTests() const { return m_data.listTests; } + bool Config::listTestNamesOnly() const { return m_data.listTestNamesOnly; } + bool Config::listTags() const { return m_data.listTags; } + bool Config::listReporters() const { return m_data.listReporters; } + + std::string Config::getProcessName() const { return m_data.processName; } + std::string const& Config::getReporterName() const { return m_data.reporterName; } + + std::vector const& Config::getTestsOrTags() const { return m_data.testsOrTags; } + std::vector const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } + + TestSpec const& Config::testSpec() const { return m_testSpec; } + bool Config::hasTestFilters() const { return m_hasTestFilters; } + + bool Config::showHelp() const { return m_data.showHelp; } + + // IConfig interface + bool Config::allowThrows() const { return !m_data.noThrow; } + std::ostream& Config::stream() const { return m_stream->stream(); } + std::string Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } + bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; } + bool Config::warnAboutMissingAssertions() const { return !!(m_data.warnings & WarnAbout::NoAssertions); } + bool Config::warnAboutNoTests() const { return !!(m_data.warnings & WarnAbout::NoTests); } + ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } + RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; } + unsigned int Config::rngSeed() const { return m_data.rngSeed; } + int Config::benchmarkResolutionMultiple() const { return m_data.benchmarkResolutionMultiple; } + UseColour::YesOrNo Config::useColour() const { return m_data.useColour; } + bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; } + int Config::abortAfter() const { return m_data.abortAfter; } + bool Config::showInvisibles() const { return m_data.showInvisibles; } + Verbosity Config::verbosity() const { return m_data.verbosity; } + + IStream const* Config::openStream() { + return Catch::makeStream(m_data.outputFilename); + } + +} // end namespace Catch +// end catch_config.cpp +// start catch_console_colour.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +// start catch_errno_guard.h + +namespace Catch { + + class ErrnoGuard { + public: + ErrnoGuard(); + ~ErrnoGuard(); + private: + int m_oldErrno; + }; + +} + +// end catch_errno_guard.h +#include + +namespace Catch { + namespace { + + struct IColourImpl { + virtual ~IColourImpl() = default; + virtual void use( Colour::Code _colourCode ) = 0; + }; + + struct NoColourImpl : IColourImpl { + void use( Colour::Code ) {} + + static IColourImpl* instance() { + static NoColourImpl s_instance; + return &s_instance; + } + }; + + } // anon namespace +} // namespace Catch + +#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) +# ifdef CATCH_PLATFORM_WINDOWS +# define CATCH_CONFIG_COLOUR_WINDOWS +# else +# define CATCH_CONFIG_COLOUR_ANSI +# endif +#endif + +#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// + +namespace Catch { +namespace { + + class Win32ColourImpl : public IColourImpl { + public: + Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) + { + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); + originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); + originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); + } + + virtual void use( Colour::Code _colourCode ) override { + switch( _colourCode ) { + case Colour::None: return setTextAttribute( originalForegroundAttributes ); + case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::Red: return setTextAttribute( FOREGROUND_RED ); + case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); + case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); + case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); + case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); + case Colour::Grey: return setTextAttribute( 0 ); + + case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); + case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); + case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); + case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::BrightYellow: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN ); + + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); + + default: + CATCH_ERROR( "Unknown colour requested" ); + } + } + + private: + void setTextAttribute( WORD _textAttribute ) { + SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes ); + } + HANDLE stdoutHandle; + WORD originalForegroundAttributes; + WORD originalBackgroundAttributes; + }; + + IColourImpl* platformColourInstance() { + static Win32ColourImpl s_instance; + + IConfigPtr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if( colourMode == UseColour::Auto ) + colourMode = UseColour::Yes; + return colourMode == UseColour::Yes + ? &s_instance + : NoColourImpl::instance(); + } + +} // end anon namespace +} // end namespace Catch + +#elif defined( CATCH_CONFIG_COLOUR_ANSI ) ////////////////////////////////////// + +#include + +namespace Catch { +namespace { + + // use POSIX/ ANSI console terminal codes + // Thanks to Adam Strzelecki for original contribution + // (http://github.com/nanoant) + // https://github.com/philsquared/Catch/pull/131 + class PosixColourImpl : public IColourImpl { + public: + virtual void use( Colour::Code _colourCode ) override { + switch( _colourCode ) { + case Colour::None: + case Colour::White: return setColour( "[0m" ); + case Colour::Red: return setColour( "[0;31m" ); + case Colour::Green: return setColour( "[0;32m" ); + case Colour::Blue: return setColour( "[0;34m" ); + case Colour::Cyan: return setColour( "[0;36m" ); + case Colour::Yellow: return setColour( "[0;33m" ); + case Colour::Grey: return setColour( "[1;30m" ); + + case Colour::LightGrey: return setColour( "[0;37m" ); + case Colour::BrightRed: return setColour( "[1;31m" ); + case Colour::BrightGreen: return setColour( "[1;32m" ); + case Colour::BrightWhite: return setColour( "[1;37m" ); + case Colour::BrightYellow: return setColour( "[1;33m" ); + + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); + default: CATCH_INTERNAL_ERROR( "Unknown colour requested" ); + } + } + static IColourImpl* instance() { + static PosixColourImpl s_instance; + return &s_instance; + } + + private: + void setColour( const char* _escapeCode ) { + Catch::cout() << '\033' << _escapeCode; + } + }; + + bool useColourOnPlatform() { + return +#ifdef CATCH_PLATFORM_MAC + !isDebuggerActive() && +#endif +#if !(defined(__DJGPP__) && defined(__STRICT_ANSI__)) + isatty(STDOUT_FILENO) +#else + false +#endif + ; + } + IColourImpl* platformColourInstance() { + ErrnoGuard guard; + IConfigPtr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if( colourMode == UseColour::Auto ) + colourMode = useColourOnPlatform() + ? UseColour::Yes + : UseColour::No; + return colourMode == UseColour::Yes + ? PosixColourImpl::instance() + : NoColourImpl::instance(); + } + +} // end anon namespace +} // end namespace Catch + +#else // not Windows or ANSI /////////////////////////////////////////////// + +namespace Catch { + + static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } + +} // end namespace Catch + +#endif // Windows/ ANSI/ None + +namespace Catch { + + Colour::Colour( Code _colourCode ) { use( _colourCode ); } + Colour::Colour( Colour&& rhs ) noexcept { + m_moved = rhs.m_moved; + rhs.m_moved = true; + } + Colour& Colour::operator=( Colour&& rhs ) noexcept { + m_moved = rhs.m_moved; + rhs.m_moved = true; + return *this; + } + + Colour::~Colour(){ if( !m_moved ) use( None ); } + + void Colour::use( Code _colourCode ) { + static IColourImpl* impl = platformColourInstance(); + impl->use( _colourCode ); + } + + std::ostream& operator << ( std::ostream& os, Colour const& ) { + return os; + } + +} // end namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +// end catch_console_colour.cpp +// start catch_context.cpp + +namespace Catch { + + class Context : public IMutableContext, NonCopyable { + + public: // IContext + virtual IResultCapture* getResultCapture() override { + return m_resultCapture; + } + virtual IRunner* getRunner() override { + return m_runner; + } + + virtual IConfigPtr const& getConfig() const override { + return m_config; + } + + virtual ~Context() override; + + public: // IMutableContext + virtual void setResultCapture( IResultCapture* resultCapture ) override { + m_resultCapture = resultCapture; + } + virtual void setRunner( IRunner* runner ) override { + m_runner = runner; + } + virtual void setConfig( IConfigPtr const& config ) override { + m_config = config; + } + + friend IMutableContext& getCurrentMutableContext(); + + private: + IConfigPtr m_config; + IRunner* m_runner = nullptr; + IResultCapture* m_resultCapture = nullptr; + }; + + IMutableContext *IMutableContext::currentContext = nullptr; + + void IMutableContext::createContext() + { + currentContext = new Context(); + } + + void cleanUpContext() { + delete IMutableContext::currentContext; + IMutableContext::currentContext = nullptr; + } + IContext::~IContext() = default; + IMutableContext::~IMutableContext() = default; + Context::~Context() = default; +} +// end catch_context.cpp +// start catch_debug_console.cpp + +// start catch_debug_console.h + +#include + +namespace Catch { + void writeToDebugConsole( std::string const& text ); +} + +// end catch_debug_console.h +#ifdef CATCH_PLATFORM_WINDOWS + + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + ::OutputDebugStringA( text.c_str() ); + } + } + +#else + + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + // !TBD: Need a version for Mac/ XCode and other IDEs + Catch::cout() << text; + } + } + +#endif // Platform +// end catch_debug_console.cpp +// start catch_debugger.cpp + +#ifdef CATCH_PLATFORM_MAC + +# include +# include +# include +# include +# include +# include +# include + +namespace Catch { + + // The following function is taken directly from the following technical note: + // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html + + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive(){ + + int mib[4]; + struct kinfo_proc info; + std::size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) { + Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; + return false; + } + + // We're being debugged if the P_TRACED flag is set. + + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + } + } // namespace Catch + +#elif defined(CATCH_PLATFORM_LINUX) + #include + #include + + namespace Catch{ + // The standard POSIX way of detecting a debugger is to attempt to + // ptrace() the process, but this needs to be done from a child and not + // this process itself to still allow attaching to this process later + // if wanted, so is rather heavy. Under Linux we have the PID of the + // "debugger" (which doesn't need to be gdb, of course, it could also + // be strace, for example) in /proc/$PID/status, so just get it from + // there instead. + bool isDebuggerActive(){ + // Libstdc++ has a bug, where std::ifstream sets errno to 0 + // This way our users can properly assert over errno values + ErrnoGuard guard; + std::ifstream in("/proc/self/status"); + for( std::string line; std::getline(in, line); ) { + static const int PREFIX_LEN = 11; + if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { + // We're traced if the PID is not 0 and no other PID starts + // with 0 digit, so it's enough to check for just a single + // character. + return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; + } + } + + return false; + } + } // namespace Catch +#elif defined(_MSC_VER) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#else + namespace Catch { + bool isDebuggerActive() { return false; } + } +#endif // Platform +// end catch_debugger.cpp +// start catch_decomposer.cpp + +namespace Catch { + + ITransientExpression::~ITransientExpression() = default; + + void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) { + if( lhs.size() + rhs.size() < 40 && + lhs.find('\n') == std::string::npos && + rhs.find('\n') == std::string::npos ) + os << lhs << " " << op << " " << rhs; + else + os << lhs << "\n" << op << "\n" << rhs; + } +} +// end catch_decomposer.cpp +// start catch_enforce.cpp + +namespace Catch { +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER) + [[noreturn]] + void throw_exception(std::exception const& e) { + Catch::cerr() << "Catch will terminate because it needed to throw an exception.\n" + << "The message was: " << e.what() << '\n'; + std::terminate(); + } +#endif +} // namespace Catch; +// end catch_enforce.cpp +// start catch_errno_guard.cpp + +#include + +namespace Catch { + ErrnoGuard::ErrnoGuard():m_oldErrno(errno){} + ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; } +} +// end catch_errno_guard.cpp +// start catch_exception_translator_registry.cpp + +// start catch_exception_translator_registry.h + +#include +#include +#include + +namespace Catch { + + class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { + public: + ~ExceptionTranslatorRegistry(); + virtual void registerTranslator( const IExceptionTranslator* translator ); + virtual std::string translateActiveException() const override; + std::string tryTranslators() const; + + private: + std::vector> m_translators; + }; +} + +// end catch_exception_translator_registry.h +#ifdef __OBJC__ +#import "Foundation/Foundation.h" +#endif + +namespace Catch { + + ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { + } + + void ExceptionTranslatorRegistry::registerTranslator( const IExceptionTranslator* translator ) { + m_translators.push_back( std::unique_ptr( translator ) ); + } + +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + std::string ExceptionTranslatorRegistry::translateActiveException() const { + try { +#ifdef __OBJC__ + // In Objective-C try objective-c exceptions first + @try { + return tryTranslators(); + } + @catch (NSException *exception) { + return Catch::Detail::stringify( [exception description] ); + } +#else + // Compiling a mixed mode project with MSVC means that CLR + // exceptions will be caught in (...) as well. However, these + // do not fill-in std::current_exception and thus lead to crash + // when attempting rethrow. + // /EHa switch also causes structured exceptions to be caught + // here, but they fill-in current_exception properly, so + // at worst the output should be a little weird, instead of + // causing a crash. + if (std::current_exception() == nullptr) { + return "Non C++ exception. Possibly a CLR exception."; + } + return tryTranslators(); +#endif + } + catch( TestFailureException& ) { + std::rethrow_exception(std::current_exception()); + } + catch( std::exception& ex ) { + return ex.what(); + } + catch( std::string& msg ) { + return msg; + } + catch( const char* msg ) { + return msg; + } + catch(...) { + return "Unknown exception"; + } + } + +#else // ^^ Exceptions are enabled // Exceptions are disabled vv + std::string ExceptionTranslatorRegistry::translateActiveException() const { + CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); + } +#endif + + std::string ExceptionTranslatorRegistry::tryTranslators() const { + if( m_translators.empty() ) + std::rethrow_exception(std::current_exception()); + else + return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); + } +} +// end catch_exception_translator_registry.cpp +// start catch_fatal_condition.cpp + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS ) + +namespace { + // Report the error condition + void reportFatal( char const * const message ) { + Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); + } +} + +#endif // signals/SEH handling + +#if defined( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + struct SignalDefs { DWORD id; const char* name; }; + + // There is no 1-1 mapping between signals and windows exceptions. + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + static SignalDefs signalDefs[] = { + { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, + { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, + //{ EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, + { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, + }; + + LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { + for (auto const& def : signalDefs) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { + reportFatal(def.name); + } + } + // If its not an exception we care about, pass it along. + // This stops us from eating debugger breaks etc. + return EXCEPTION_CONTINUE_SEARCH; + } + + FatalConditionHandler::FatalConditionHandler() { + isSet = true; + // 32k seems enough for Catch to handle stack overflow, + // but the value was found experimentally, so there is no strong guarantee + guaranteeSize = 32 * 1024; + exceptionHandlerHandle = nullptr; + // Register as first handler in current chain + exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); + // Pass in guarantee size to be filled + SetThreadStackGuarantee(&guaranteeSize); + } + + void FatalConditionHandler::reset() { + if (isSet) { + RemoveVectoredExceptionHandler(exceptionHandlerHandle); + SetThreadStackGuarantee(&guaranteeSize); + exceptionHandlerHandle = nullptr; + isSet = false; + } + } + + FatalConditionHandler::~FatalConditionHandler() { + reset(); + } + +bool FatalConditionHandler::isSet = false; +ULONG FatalConditionHandler::guaranteeSize = 0; +PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; + +} // namespace Catch + +#elif defined( CATCH_CONFIG_POSIX_SIGNALS ) + +namespace Catch { + + struct SignalDefs { + int id; + const char* name; + }; + + // 32kb for the alternate stack seems to be sufficient. However, this value + // is experimentally determined, so that's not guaranteed. + constexpr static std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ; + + static SignalDefs signalDefs[] = { + { SIGINT, "SIGINT - Terminal interrupt signal" }, + { SIGILL, "SIGILL - Illegal instruction signal" }, + { SIGFPE, "SIGFPE - Floating point error signal" }, + { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, + { SIGTERM, "SIGTERM - Termination request signal" }, + { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } + }; + + void FatalConditionHandler::handleSignal( int sig ) { + char const * name = ""; + for (auto const& def : signalDefs) { + if (sig == def.id) { + name = def.name; + break; + } + } + reset(); + reportFatal(name); + raise( sig ); + } + + FatalConditionHandler::FatalConditionHandler() { + isSet = true; + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = sigStackSize; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = { }; + + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); + } + } + + FatalConditionHandler::~FatalConditionHandler() { + reset(); + } + + void FatalConditionHandler::reset() { + if( isSet ) { + // Set signals back to previous values -- hopefully nobody overwrote them in the meantime + for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { + sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); + } + // Return the old stack + sigaltstack(&oldSigStack, nullptr); + isSet = false; + } + } + + bool FatalConditionHandler::isSet = false; + struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; + stack_t FatalConditionHandler::oldSigStack = {}; + char FatalConditionHandler::altStackMem[sigStackSize] = {}; + +} // namespace Catch + +#else + +namespace Catch { + void FatalConditionHandler::reset() {} +} + +#endif // signals/SEH handling + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif +// end catch_fatal_condition.cpp +// start catch_generators.cpp + +// start catch_random_number_generator.h + +#include +#include + +namespace Catch { + + struct IConfig; + + std::mt19937& rng(); + void seedRng( IConfig const& config ); + unsigned int rngSeed(); + +} + +// end catch_random_number_generator.h +#include +#include + +namespace Catch { + +IGeneratorTracker::~IGeneratorTracker() {} + +namespace Generators { + + GeneratorBase::~GeneratorBase() {} + + std::vector randomiseIndices( size_t selectionSize, size_t sourceSize ) { + + assert( selectionSize <= sourceSize ); + std::vector indices; + indices.reserve( selectionSize ); + std::uniform_int_distribution uid( 0, sourceSize-1 ); + + std::set seen; + // !TBD: improve this algorithm + while( indices.size() < selectionSize ) { + auto index = uid( rng() ); + if( seen.insert( index ).second ) + indices.push_back( index ); + } + return indices; + } + + auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + return getResultCapture().acquireGeneratorTracker( lineInfo ); + } + + template<> + auto all() -> Generator { + return range( std::numeric_limits::min(), std::numeric_limits::max() ); + } + +} // namespace Generators +} // namespace Catch +// end catch_generators.cpp +// start catch_interfaces_capture.cpp + +namespace Catch { + IResultCapture::~IResultCapture() = default; +} +// end catch_interfaces_capture.cpp +// start catch_interfaces_config.cpp + +namespace Catch { + IConfig::~IConfig() = default; +} +// end catch_interfaces_config.cpp +// start catch_interfaces_exception.cpp + +namespace Catch { + IExceptionTranslator::~IExceptionTranslator() = default; + IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; +} +// end catch_interfaces_exception.cpp +// start catch_interfaces_registry_hub.cpp + +namespace Catch { + IRegistryHub::~IRegistryHub() = default; + IMutableRegistryHub::~IMutableRegistryHub() = default; +} +// end catch_interfaces_registry_hub.cpp +// start catch_interfaces_reporter.cpp + +// start catch_reporter_listening.h + +namespace Catch { + + class ListeningReporter : public IStreamingReporter { + using Reporters = std::vector; + Reporters m_listeners; + IStreamingReporterPtr m_reporter = nullptr; + ReporterPreferences m_preferences; + + public: + ListeningReporter(); + + void addListener( IStreamingReporterPtr&& listener ); + void addReporter( IStreamingReporterPtr&& reporter ); + + public: // IStreamingReporter + + ReporterPreferences getPreferences() const override; + + void noMatchingTestCases( std::string const& spec ) override; + + static std::set getSupportedVerbosities(); + + void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override; + void benchmarkEnded( BenchmarkStats const& benchmarkStats ) override; + + void testRunStarting( TestRunInfo const& testRunInfo ) override; + void testGroupStarting( GroupInfo const& groupInfo ) override; + void testCaseStarting( TestCaseInfo const& testInfo ) override; + void sectionStarting( SectionInfo const& sectionInfo ) override; + void assertionStarting( AssertionInfo const& assertionInfo ) override; + + // The return value indicates if the messages buffer should be cleared: + bool assertionEnded( AssertionStats const& assertionStats ) override; + void sectionEnded( SectionStats const& sectionStats ) override; + void testCaseEnded( TestCaseStats const& testCaseStats ) override; + void testGroupEnded( TestGroupStats const& testGroupStats ) override; + void testRunEnded( TestRunStats const& testRunStats ) override; + + void skipTest( TestCaseInfo const& testInfo ) override; + bool isMulti() const override; + + }; + +} // end namespace Catch + +// end catch_reporter_listening.h +namespace Catch { + + ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig ) + : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} + + ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ) + : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} + + std::ostream& ReporterConfig::stream() const { return *m_stream; } + IConfigPtr ReporterConfig::fullConfig() const { return m_fullConfig; } + + TestRunInfo::TestRunInfo( std::string const& _name ) : name( _name ) {} + + GroupInfo::GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ) + : name( _name ), + groupIndex( _groupIndex ), + groupsCounts( _groupsCount ) + {} + + AssertionStats::AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ) + : assertionResult( _assertionResult ), + infoMessages( _infoMessages ), + totals( _totals ) + { + assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression; + + if( assertionResult.hasMessage() ) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); + + infoMessages.push_back( builder.m_info ); + } + } + + AssertionStats::~AssertionStats() = default; + + SectionStats::SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ) + : sectionInfo( _sectionInfo ), + assertions( _assertions ), + durationInSeconds( _durationInSeconds ), + missingAssertions( _missingAssertions ) + {} + + SectionStats::~SectionStats() = default; + + TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ) + : testInfo( _testInfo ), + totals( _totals ), + stdOut( _stdOut ), + stdErr( _stdErr ), + aborting( _aborting ) + {} + + TestCaseStats::~TestCaseStats() = default; + + TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ) + : groupInfo( _groupInfo ), + totals( _totals ), + aborting( _aborting ) + {} + + TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo ) + : groupInfo( _groupInfo ), + aborting( false ) + {} + + TestGroupStats::~TestGroupStats() = default; + + TestRunStats::TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ) + : runInfo( _runInfo ), + totals( _totals ), + aborting( _aborting ) + {} + + TestRunStats::~TestRunStats() = default; + + void IStreamingReporter::fatalErrorEncountered( StringRef ) {} + bool IStreamingReporter::isMulti() const { return false; } + + IReporterFactory::~IReporterFactory() = default; + IReporterRegistry::~IReporterRegistry() = default; + +} // end namespace Catch +// end catch_interfaces_reporter.cpp +// start catch_interfaces_runner.cpp + +namespace Catch { + IRunner::~IRunner() = default; +} +// end catch_interfaces_runner.cpp +// start catch_interfaces_testcase.cpp + +namespace Catch { + ITestInvoker::~ITestInvoker() = default; + ITestCaseRegistry::~ITestCaseRegistry() = default; +} +// end catch_interfaces_testcase.cpp +// start catch_leak_detector.cpp + +#ifdef CATCH_CONFIG_WINDOWS_CRTDBG +#include + +namespace Catch { + + LeakDetector::LeakDetector() { + int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + flag |= _CRTDBG_LEAK_CHECK_DF; + flag |= _CRTDBG_ALLOC_MEM_DF; + _CrtSetDbgFlag(flag); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + // Change this to leaking allocation's number to break there + _CrtSetBreakAlloc(-1); + } +} + +#else + + Catch::LeakDetector::LeakDetector() {} + +#endif + +Catch::LeakDetector::~LeakDetector() { + Catch::cleanUp(); +} +// end catch_leak_detector.cpp +// start catch_list.cpp + +// start catch_list.h + +#include + +namespace Catch { + + std::size_t listTests( Config const& config ); + + std::size_t listTestsNamesOnly( Config const& config ); + + struct TagInfo { + void add( std::string const& spelling ); + std::string all() const; + + std::set spellings; + std::size_t count = 0; + }; + + std::size_t listTags( Config const& config ); + + std::size_t listReporters(); + + Option list( Config const& config ); + +} // end namespace Catch + +// end catch_list.h +// start catch_text.h + +namespace Catch { + using namespace clara::TextFlow; +} + +// end catch_text.h +#include +#include +#include + +namespace Catch { + + std::size_t listTests( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.hasTestFilters() ) + Catch::cout() << "Matching test cases:\n"; + else { + Catch::cout() << "All available test cases:\n"; + } + + auto matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCaseInfo : matchedTestCases ) { + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + Colour colourGuard( colour ); + + Catch::cout() << Column( testCaseInfo.name ).initialIndent( 2 ).indent( 4 ) << "\n"; + if( config.verbosity() >= Verbosity::High ) { + Catch::cout() << Column( Catch::Detail::stringify( testCaseInfo.lineInfo ) ).indent(4) << std::endl; + std::string description = testCaseInfo.description; + if( description.empty() ) + description = "(NO DESCRIPTION)"; + Catch::cout() << Column( description ).indent(4) << std::endl; + } + if( !testCaseInfo.tags.empty() ) + Catch::cout() << Column( testCaseInfo.tagsAsString() ).indent( 6 ) << "\n"; + } + + if( !config.hasTestFilters() ) + Catch::cout() << pluralise( matchedTestCases.size(), "test case" ) << '\n' << std::endl; + else + Catch::cout() << pluralise( matchedTestCases.size(), "matching test case" ) << '\n' << std::endl; + return matchedTestCases.size(); + } + + std::size_t listTestsNamesOnly( Config const& config ) { + TestSpec testSpec = config.testSpec(); + std::size_t matchedTests = 0; + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCaseInfo : matchedTestCases ) { + matchedTests++; + if( startsWith( testCaseInfo.name, '#' ) ) + Catch::cout() << '"' << testCaseInfo.name << '"'; + else + Catch::cout() << testCaseInfo.name; + if ( config.verbosity() >= Verbosity::High ) + Catch::cout() << "\t@" << testCaseInfo.lineInfo; + Catch::cout() << std::endl; + } + return matchedTests; + } + + void TagInfo::add( std::string const& spelling ) { + ++count; + spellings.insert( spelling ); + } + + std::string TagInfo::all() const { + std::string out; + for( auto const& spelling : spellings ) + out += "[" + spelling + "]"; + return out; + } + + std::size_t listTags( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.hasTestFilters() ) + Catch::cout() << "Tags for matching test cases:\n"; + else { + Catch::cout() << "All available tags:\n"; + } + + std::map tagCounts; + + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCase : matchedTestCases ) { + for( auto const& tagName : testCase.getTestCaseInfo().tags ) { + std::string lcaseTagName = toLower( tagName ); + auto countIt = tagCounts.find( lcaseTagName ); + if( countIt == tagCounts.end() ) + countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; + countIt->second.add( tagName ); + } + } + + for( auto const& tagCount : tagCounts ) { + ReusableStringStream rss; + rss << " " << std::setw(2) << tagCount.second.count << " "; + auto str = rss.str(); + auto wrapper = Column( tagCount.second.all() ) + .initialIndent( 0 ) + .indent( str.size() ) + .width( CATCH_CONFIG_CONSOLE_WIDTH-10 ); + Catch::cout() << str << wrapper << '\n'; + } + Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; + return tagCounts.size(); + } + + std::size_t listReporters() { + Catch::cout() << "Available reporters:\n"; + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + std::size_t maxNameLen = 0; + for( auto const& factoryKvp : factories ) + maxNameLen = (std::max)( maxNameLen, factoryKvp.first.size() ); + + for( auto const& factoryKvp : factories ) { + Catch::cout() + << Column( factoryKvp.first + ":" ) + .indent(2) + .width( 5+maxNameLen ) + + Column( factoryKvp.second->getDescription() ) + .initialIndent(0) + .indent(2) + .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) + << "\n"; + } + Catch::cout() << std::endl; + return factories.size(); + } + + Option list( Config const& config ) { + Option listedCount; + if( config.listTests() ) + listedCount = listedCount.valueOr(0) + listTests( config ); + if( config.listTestNamesOnly() ) + listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); + if( config.listTags() ) + listedCount = listedCount.valueOr(0) + listTags( config ); + if( config.listReporters() ) + listedCount = listedCount.valueOr(0) + listReporters(); + return listedCount; + } + +} // end namespace Catch +// end catch_list.cpp +// start catch_matchers.cpp + +namespace Catch { +namespace Matchers { + namespace Impl { + + std::string MatcherUntypedBase::toString() const { + if( m_cachedToString.empty() ) + m_cachedToString = describe(); + return m_cachedToString; + } + + MatcherUntypedBase::~MatcherUntypedBase() = default; + + } // namespace Impl +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch +// end catch_matchers.cpp +// start catch_matchers_floating.cpp + +// start catch_polyfills.hpp + +namespace Catch { + bool isnan(float f); + bool isnan(double d); +} + +// end catch_polyfills.hpp +// start catch_to_string.hpp + +#include + +namespace Catch { + template + std::string to_string(T const& t) { +#if defined(CATCH_CONFIG_CPP11_TO_STRING) + return std::to_string(t); +#else + ReusableStringStream rss; + rss << t; + return rss.str(); +#endif + } +} // end namespace Catch + +// end catch_to_string.hpp +#include +#include +#include + +namespace Catch { +namespace Matchers { +namespace Floating { +enum class FloatingPointKind : uint8_t { + Float, + Double +}; +} +} +} + +namespace { + +template +struct Converter; + +template <> +struct Converter { + static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated"); + Converter(float f) { + std::memcpy(&i, &f, sizeof(f)); + } + int32_t i; +}; + +template <> +struct Converter { + static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated"); + Converter(double d) { + std::memcpy(&i, &d, sizeof(d)); + } + int64_t i; +}; + +template +auto convert(T t) -> Converter { + return Converter(t); +} + +template +bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) { + // Comparison with NaN should always be false. + // This way we can rule it out before getting into the ugly details + if (Catch::isnan(lhs) || Catch::isnan(rhs)) { + return false; + } + + auto lc = convert(lhs); + auto rc = convert(rhs); + + if ((lc.i < 0) != (rc.i < 0)) { + // Potentially we can have +0 and -0 + return lhs == rhs; + } + + auto ulpDiff = std::abs(lc.i - rc.i); + return ulpDiff <= maxUlpDiff; +} + +} + +namespace Catch { +namespace Matchers { +namespace Floating { + WithinAbsMatcher::WithinAbsMatcher(double target, double margin) + :m_target{ target }, m_margin{ margin } { + CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.' + << " Margin has to be non-negative."); + } + + // Performs equivalent check of std::fabs(lhs - rhs) <= margin + // But without the subtraction to allow for INFINITY in comparison + bool WithinAbsMatcher::match(double const& matchee) const { + return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee); + } + + std::string WithinAbsMatcher::describe() const { + return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target); + } + + WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType) + :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } { + CATCH_ENFORCE(ulps >= 0, "Invalid ULP setting: " << ulps << '.' + << " ULPs have to be non-negative."); + } + +#if defined(__clang__) +#pragma clang diagnostic push +// Clang <3.5 reports on the default branch in the switch below +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + + bool WithinUlpsMatcher::match(double const& matchee) const { + switch (m_type) { + case FloatingPointKind::Float: + return almostEqualUlps(static_cast(matchee), static_cast(m_target), m_ulps); + case FloatingPointKind::Double: + return almostEqualUlps(matchee, m_target, m_ulps); + default: + CATCH_INTERNAL_ERROR( "Unknown FloatingPointKind value" ); + } + } + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + + std::string WithinUlpsMatcher::describe() const { + return "is within " + Catch::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : ""); + } + +}// namespace Floating + +Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double); +} + +Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float); +} + +Floating::WithinAbsMatcher WithinAbs(double target, double margin) { + return Floating::WithinAbsMatcher(target, margin); +} + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_floating.cpp +// start catch_matchers_generic.cpp + +std::string Catch::Matchers::Generic::Detail::finalizeDescription(const std::string& desc) { + if (desc.empty()) { + return "matches undescribed predicate"; + } else { + return "matches predicate: \"" + desc + '"'; + } +} +// end catch_matchers_generic.cpp +// start catch_matchers_string.cpp + +#include + +namespace Catch { +namespace Matchers { + + namespace StdString { + + CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_str( adjustString( str ) ) + {} + std::string CasedString::adjustString( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No + ? toLower( str ) + : str; + } + std::string CasedString::caseSensitivitySuffix() const { + return m_caseSensitivity == CaseSensitive::No + ? " (case insensitive)" + : std::string(); + } + + StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator ) + : m_comparator( comparator ), + m_operation( operation ) { + } + + std::string StringMatcherBase::describe() const { + std::string description; + description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + + m_comparator.caseSensitivitySuffix().size()); + description += m_operation; + description += ": \""; + description += m_comparator.m_str; + description += "\""; + description += m_comparator.caseSensitivitySuffix(); + return description; + } + + EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {} + + bool EqualsMatcher::match( std::string const& source ) const { + return m_comparator.adjustString( source ) == m_comparator.m_str; + } + + ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {} + + bool ContainsMatcher::match( std::string const& source ) const { + return contains( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {} + + bool StartsWithMatcher::match( std::string const& source ) const { + return startsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {} + + bool EndsWithMatcher::match( std::string const& source ) const { + return endsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + RegexMatcher::RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity): m_regex(std::move(regex)), m_caseSensitivity(caseSensitivity) {} + + bool RegexMatcher::match(std::string const& matchee) const { + auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway + if (m_caseSensitivity == CaseSensitive::Choice::No) { + flags |= std::regex::icase; + } + auto reg = std::regex(m_regex, flags); + return std::regex_match(matchee, reg); + } + + std::string RegexMatcher::describe() const { + return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Choice::Yes)? " case sensitively" : " case insensitively"); + } + + } // namespace StdString + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } + + StdString::RegexMatcher Matches(std::string const& regex, CaseSensitive::Choice caseSensitivity) { + return StdString::RegexMatcher(regex, caseSensitivity); + } + +} // namespace Matchers +} // namespace Catch +// end catch_matchers_string.cpp +// start catch_message.cpp + +// start catch_uncaught_exceptions.h + +namespace Catch { + bool uncaught_exceptions(); +} // end namespace Catch + +// end catch_uncaught_exceptions.h +#include +#include + +namespace Catch { + + MessageInfo::MessageInfo( StringRef const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ) + : macroName( _macroName ), + lineInfo( _lineInfo ), + type( _type ), + sequence( ++globalCount ) + {} + + bool MessageInfo::operator==( MessageInfo const& other ) const { + return sequence == other.sequence; + } + + bool MessageInfo::operator<( MessageInfo const& other ) const { + return sequence < other.sequence; + } + + // This may need protecting if threading support is added + unsigned int MessageInfo::globalCount = 0; + + //////////////////////////////////////////////////////////////////////////// + + Catch::MessageBuilder::MessageBuilder( StringRef const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ) + :m_info(macroName, lineInfo, type) {} + + //////////////////////////////////////////////////////////////////////////// + + ScopedMessage::ScopedMessage( MessageBuilder const& builder ) + : m_info( builder.m_info ) + { + m_info.message = builder.m_stream.str(); + getResultCapture().pushScopedMessage( m_info ); + } + + ScopedMessage::~ScopedMessage() { + if ( !uncaught_exceptions() ){ + getResultCapture().popScopedMessage(m_info); + } + } + + Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) { + auto trimmed = [&] (size_t start, size_t end) { + while (names[start] == ',' || isspace(names[start])) { + ++start; + } + while (names[end] == ',' || isspace(names[end])) { + --end; + } + return names.substr(start, end - start + 1); + }; + + size_t start = 0; + std::stack openings; + for (size_t pos = 0; pos < names.size(); ++pos) { + char c = names[pos]; + switch (c) { + case '[': + case '{': + case '(': + // It is basically impossible to disambiguate between + // comparison and start of template args in this context +// case '<': + openings.push(c); + break; + case ']': + case '}': + case ')': +// case '>': + openings.pop(); + break; + case ',': + if (start != pos && openings.size() == 0) { + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message = trimmed(start, pos); + m_messages.back().message += " := "; + start = pos; + } + } + } + assert(openings.size() == 0 && "Mismatched openings"); + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message = trimmed(start, names.size() - 1); + m_messages.back().message += " := "; + } + Capturer::~Capturer() { + if ( !uncaught_exceptions() ){ + assert( m_captured == m_messages.size() ); + for( size_t i = 0; i < m_captured; ++i ) + m_resultCapture.popScopedMessage( m_messages[i] ); + } + } + + void Capturer::captureValue( size_t index, std::string const& value ) { + assert( index < m_messages.size() ); + m_messages[index].message += value; + m_resultCapture.pushScopedMessage( m_messages[index] ); + m_captured++; + } + +} // end namespace Catch +// end catch_message.cpp +// start catch_output_redirect.cpp + +// start catch_output_redirect.h +#ifndef TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H +#define TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H + +#include +#include +#include + +namespace Catch { + + class RedirectedStream { + std::ostream& m_originalStream; + std::ostream& m_redirectionStream; + std::streambuf* m_prevBuf; + + public: + RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ); + ~RedirectedStream(); + }; + + class RedirectedStdOut { + ReusableStringStream m_rss; + RedirectedStream m_cout; + public: + RedirectedStdOut(); + auto str() const -> std::string; + }; + + // StdErr has two constituent streams in C++, std::cerr and std::clog + // This means that we need to redirect 2 streams into 1 to keep proper + // order of writes + class RedirectedStdErr { + ReusableStringStream m_rss; + RedirectedStream m_cerr; + RedirectedStream m_clog; + public: + RedirectedStdErr(); + auto str() const -> std::string; + }; + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + + // Windows's implementation of std::tmpfile is terrible (it tries + // to create a file inside system folder, thus requiring elevated + // privileges for the binary), so we have to use tmpnam(_s) and + // create the file ourselves there. + class TempFile { + public: + TempFile(TempFile const&) = delete; + TempFile& operator=(TempFile const&) = delete; + TempFile(TempFile&&) = delete; + TempFile& operator=(TempFile&&) = delete; + + TempFile(); + ~TempFile(); + + std::FILE* getFile(); + std::string getContents(); + + private: + std::FILE* m_file = nullptr; + #if defined(_MSC_VER) + char m_buffer[L_tmpnam] = { 0 }; + #endif + }; + + class OutputRedirect { + public: + OutputRedirect(OutputRedirect const&) = delete; + OutputRedirect& operator=(OutputRedirect const&) = delete; + OutputRedirect(OutputRedirect&&) = delete; + OutputRedirect& operator=(OutputRedirect&&) = delete; + + OutputRedirect(std::string& stdout_dest, std::string& stderr_dest); + ~OutputRedirect(); + + private: + int m_originalStdout = -1; + int m_originalStderr = -1; + TempFile m_stdoutFile; + TempFile m_stderrFile; + std::string& m_stdoutDest; + std::string& m_stderrDest; + }; + +#endif + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H +// end catch_output_redirect.h +#include +#include +#include +#include +#include + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + #if defined(_MSC_VER) + #include //_dup and _dup2 + #define dup _dup + #define dup2 _dup2 + #define fileno _fileno + #else + #include // dup and dup2 + #endif +#endif + +namespace Catch { + + RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) + : m_originalStream( originalStream ), + m_redirectionStream( redirectionStream ), + m_prevBuf( m_originalStream.rdbuf() ) + { + m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); + } + + RedirectedStream::~RedirectedStream() { + m_originalStream.rdbuf( m_prevBuf ); + } + + RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {} + auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); } + + RedirectedStdErr::RedirectedStdErr() + : m_cerr( Catch::cerr(), m_rss.get() ), + m_clog( Catch::clog(), m_rss.get() ) + {} + auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); } + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + +#if defined(_MSC_VER) + TempFile::TempFile() { + if (tmpnam_s(m_buffer)) { + CATCH_RUNTIME_ERROR("Could not get a temp filename"); + } + if (fopen_s(&m_file, m_buffer, "w")) { + char buffer[100]; + if (strerror_s(buffer, errno)) { + CATCH_RUNTIME_ERROR("Could not translate errno to a string"); + } + CATCH_RUNTIME_ERROR("Coul dnot open the temp file: '" << m_buffer << "' because: " << buffer); + } + } +#else + TempFile::TempFile() { + m_file = std::tmpfile(); + if (!m_file) { + CATCH_RUNTIME_ERROR("Could not create a temp file."); + } + } + +#endif + + TempFile::~TempFile() { + // TBD: What to do about errors here? + std::fclose(m_file); + // We manually create the file on Windows only, on Linux + // it will be autodeleted +#if defined(_MSC_VER) + std::remove(m_buffer); +#endif + } + + FILE* TempFile::getFile() { + return m_file; + } + + std::string TempFile::getContents() { + std::stringstream sstr; + char buffer[100] = {}; + std::rewind(m_file); + while (std::fgets(buffer, sizeof(buffer), m_file)) { + sstr << buffer; + } + return sstr.str(); + } + + OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) : + m_originalStdout(dup(1)), + m_originalStderr(dup(2)), + m_stdoutDest(stdout_dest), + m_stderrDest(stderr_dest) { + dup2(fileno(m_stdoutFile.getFile()), 1); + dup2(fileno(m_stderrFile.getFile()), 2); + } + + OutputRedirect::~OutputRedirect() { + Catch::cout() << std::flush; + fflush(stdout); + // Since we support overriding these streams, we flush cerr + // even though std::cerr is unbuffered + Catch::cerr() << std::flush; + Catch::clog() << std::flush; + fflush(stderr); + + dup2(m_originalStdout, 1); + dup2(m_originalStderr, 2); + + m_stdoutDest += m_stdoutFile.getContents(); + m_stderrDest += m_stderrFile.getContents(); + } + +#endif // CATCH_CONFIG_NEW_CAPTURE + +} // namespace Catch + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + #if defined(_MSC_VER) + #undef dup + #undef dup2 + #undef fileno + #endif +#endif +// end catch_output_redirect.cpp +// start catch_polyfills.cpp + +#include + +namespace Catch { + +#if !defined(CATCH_CONFIG_POLYFILL_ISNAN) + bool isnan(float f) { + return std::isnan(f); + } + bool isnan(double d) { + return std::isnan(d); + } +#else + // For now we only use this for embarcadero + bool isnan(float f) { + return std::_isnan(f); + } + bool isnan(double d) { + return std::_isnan(d); + } +#endif + +} // end namespace Catch +// end catch_polyfills.cpp +// start catch_random_number_generator.cpp + +namespace Catch { + + std::mt19937& rng() { + static std::mt19937 s_rng; + return s_rng; + } + + void seedRng( IConfig const& config ) { + if( config.rngSeed() != 0 ) { + std::srand( config.rngSeed() ); + rng().seed( config.rngSeed() ); + } + } + + unsigned int rngSeed() { + return getCurrentContext().getConfig()->rngSeed(); + } +} +// end catch_random_number_generator.cpp +// start catch_registry_hub.cpp + +// start catch_test_case_registry_impl.h + +#include +#include +#include +#include + +namespace Catch { + + class TestCase; + struct IConfig; + + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + + void enforceNoDuplicateTestCases( std::vector const& functions ); + + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + + class TestRegistry : public ITestCaseRegistry { + public: + virtual ~TestRegistry() = default; + + virtual void registerTest( TestCase const& testCase ); + + std::vector const& getAllTests() const override; + std::vector const& getAllTestsSorted( IConfig const& config ) const override; + + private: + std::vector m_functions; + mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder; + mutable std::vector m_sortedFunctions; + std::size_t m_unnamedCount = 0; + std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised + }; + + /////////////////////////////////////////////////////////////////////////// + + class TestInvokerAsFunction : public ITestInvoker { + void(*m_testAsFunction)(); + public: + TestInvokerAsFunction( void(*testAsFunction)() ) noexcept; + + void invoke() const override; + }; + + std::string extractClassName( StringRef const& classOrQualifiedMethodName ); + + /////////////////////////////////////////////////////////////////////////// + +} // end namespace Catch + +// end catch_test_case_registry_impl.h +// start catch_reporter_registry.h + +#include + +namespace Catch { + + class ReporterRegistry : public IReporterRegistry { + + public: + + ~ReporterRegistry() override; + + IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const override; + + void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ); + void registerListener( IReporterFactoryPtr const& factory ); + + FactoryMap const& getFactories() const override; + Listeners const& getListeners() const override; + + private: + FactoryMap m_factories; + Listeners m_listeners; + }; +} + +// end catch_reporter_registry.h +// start catch_tag_alias_registry.h + +// start catch_tag_alias.h + +#include + +namespace Catch { + + struct TagAlias { + TagAlias(std::string const& _tag, SourceLineInfo _lineInfo); + + std::string tag; + SourceLineInfo lineInfo; + }; + +} // end namespace Catch + +// end catch_tag_alias.h +#include + +namespace Catch { + + class TagAliasRegistry : public ITagAliasRegistry { + public: + ~TagAliasRegistry() override; + TagAlias const* find( std::string const& alias ) const override; + std::string expandAliases( std::string const& unexpandedTestSpec ) const override; + void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); + + private: + std::map m_registry; + }; + +} // end namespace Catch + +// end catch_tag_alias_registry.h +// start catch_startup_exception_registry.h + +#include +#include + +namespace Catch { + + class StartupExceptionRegistry { + public: + void add(std::exception_ptr const& exception) noexcept; + std::vector const& getExceptions() const noexcept; + private: + std::vector m_exceptions; + }; + +} // end namespace Catch + +// end catch_startup_exception_registry.h +// start catch_singletons.hpp + +namespace Catch { + + struct ISingleton { + virtual ~ISingleton(); + }; + + void addSingleton( ISingleton* singleton ); + void cleanupSingletons(); + + template + class Singleton : SingletonImplT, public ISingleton { + + static auto getInternal() -> Singleton* { + static Singleton* s_instance = nullptr; + if( !s_instance ) { + s_instance = new Singleton; + addSingleton( s_instance ); + } + return s_instance; + } + + public: + static auto get() -> InterfaceT const& { + return *getInternal(); + } + static auto getMutable() -> MutableInterfaceT& { + return *getInternal(); + } + }; + +} // namespace Catch + +// end catch_singletons.hpp +namespace Catch { + + namespace { + + class RegistryHub : public IRegistryHub, public IMutableRegistryHub, + private NonCopyable { + + public: // IRegistryHub + RegistryHub() = default; + IReporterRegistry const& getReporterRegistry() const override { + return m_reporterRegistry; + } + ITestCaseRegistry const& getTestCaseRegistry() const override { + return m_testCaseRegistry; + } + IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const override { + return m_exceptionTranslatorRegistry; + } + ITagAliasRegistry const& getTagAliasRegistry() const override { + return m_tagAliasRegistry; + } + StartupExceptionRegistry const& getStartupExceptionRegistry() const override { + return m_exceptionRegistry; + } + + public: // IMutableRegistryHub + void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) override { + m_reporterRegistry.registerReporter( name, factory ); + } + void registerListener( IReporterFactoryPtr const& factory ) override { + m_reporterRegistry.registerListener( factory ); + } + void registerTest( TestCase const& testInfo ) override { + m_testCaseRegistry.registerTest( testInfo ); + } + void registerTranslator( const IExceptionTranslator* translator ) override { + m_exceptionTranslatorRegistry.registerTranslator( translator ); + } + void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override { + m_tagAliasRegistry.add( alias, tag, lineInfo ); + } + void registerStartupException() noexcept override { + m_exceptionRegistry.add(std::current_exception()); + } + + private: + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + TagAliasRegistry m_tagAliasRegistry; + StartupExceptionRegistry m_exceptionRegistry; + }; + } + + using RegistryHubSingleton = Singleton; + + IRegistryHub const& getRegistryHub() { + return RegistryHubSingleton::get(); + } + IMutableRegistryHub& getMutableRegistryHub() { + return RegistryHubSingleton::getMutable(); + } + void cleanUp() { + cleanupSingletons(); + cleanUpContext(); + } + std::string translateActiveException() { + return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); + } + +} // end namespace Catch +// end catch_registry_hub.cpp +// start catch_reporter_registry.cpp + +namespace Catch { + + ReporterRegistry::~ReporterRegistry() = default; + + IStreamingReporterPtr ReporterRegistry::create( std::string const& name, IConfigPtr const& config ) const { + auto it = m_factories.find( name ); + if( it == m_factories.end() ) + return nullptr; + return it->second->create( ReporterConfig( config ) ); + } + + void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) { + m_factories.emplace(name, factory); + } + void ReporterRegistry::registerListener( IReporterFactoryPtr const& factory ) { + m_listeners.push_back( factory ); + } + + IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const { + return m_factories; + } + IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const { + return m_listeners; + } + +} +// end catch_reporter_registry.cpp +// start catch_result_type.cpp + +namespace Catch { + + bool isOk( ResultWas::OfType resultType ) { + return ( resultType & ResultWas::FailureBit ) == 0; + } + bool isJustInfo( int flags ) { + return flags == ResultWas::Info; + } + + ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { + return static_cast( static_cast( lhs ) | static_cast( rhs ) ); + } + + bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } + bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } + +} // end namespace Catch +// end catch_result_type.cpp +// start catch_run_context.cpp + +#include +#include +#include + +namespace Catch { + + namespace Generators { + struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { + size_t m_index = static_cast( -1 ); + GeneratorBasePtr m_generator; + + GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + {} + ~GeneratorTracker(); + + static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) { + std::shared_ptr tracker; + + ITracker& currentTracker = ctx.currentTracker(); + if( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isIndexTracker() ); + tracker = std::static_pointer_cast( childTracker ); + } + else { + tracker = std::make_shared( nameAndLocation, ctx, ¤tTracker ); + currentTracker.addChild( tracker ); + } + + if( !ctx.completedCycle() && !tracker->isComplete() ) { + if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) + tracker->moveNext(); + tracker->open(); + } + + return *tracker; + } + + void moveNext() { + m_index++; + m_children.clear(); + } + + // TrackerBase interface + bool isIndexTracker() const override { return true; } + auto hasGenerator() const -> bool override { + return !!m_generator; + } + void close() override { + TrackerBase::close(); + if( m_runState == CompletedSuccessfully && m_index < m_generator->size()-1 ) + m_runState = Executing; + } + + // IGeneratorTracker interface + auto getGenerator() const -> GeneratorBasePtr const& override { + return m_generator; + } + void setGenerator( GeneratorBasePtr&& generator ) override { + m_generator = std::move( generator ); + } + auto getIndex() const -> size_t override { + return m_index; + } + }; + GeneratorTracker::~GeneratorTracker() {} + } + + RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter) + : m_runInfo(_config->name()), + m_context(getCurrentMutableContext()), + m_config(_config), + m_reporter(std::move(reporter)), + m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal }, + m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions ) + { + m_context.setRunner(this); + m_context.setConfig(m_config); + m_context.setResultCapture(this); + m_reporter->testRunStarting(m_runInfo); + } + + RunContext::~RunContext() { + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); + } + + void RunContext::testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount)); + } + + void RunContext::testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting())); + } + + Totals RunContext::runTest(TestCase const& testCase) { + Totals prevTotals = m_totals; + + std::string redirectedCout; + std::string redirectedCerr; + + auto const& testInfo = testCase.getTestCaseInfo(); + + m_reporter->testCaseStarting(testInfo); + + m_activeTestCase = &testCase; + + ITracker& rootTracker = m_trackerContext.startRun(); + assert(rootTracker.isSectionTracker()); + static_cast(rootTracker).addInitialFilters(m_config->getSectionsToRun()); + do { + m_trackerContext.startCycle(); + m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo)); + runCurrentTest(redirectedCout, redirectedCerr); + } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); + + Totals deltaTotals = m_totals.delta(prevTotals); + if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) { + deltaTotals.assertions.failed++; + deltaTotals.testCases.passed--; + deltaTotals.testCases.failed++; + } + m_totals.testCases += deltaTotals.testCases; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + redirectedCout, + redirectedCerr, + aborting())); + + m_activeTestCase = nullptr; + m_testCaseTracker = nullptr; + + return deltaTotals; + } + + IConfigPtr RunContext::config() const { + return m_config; + } + + IStreamingReporter& RunContext::reporter() const { + return *m_reporter; + } + + void RunContext::assertionEnded(AssertionResult const & result) { + if (result.getResultType() == ResultWas::Ok) { + m_totals.assertions.passed++; + m_lastAssertionPassed = true; + } else if (!result.isOk()) { + m_lastAssertionPassed = false; + if( m_activeTestCase->getTestCaseInfo().okToFail() ) + m_totals.assertions.failedButOk++; + else + m_totals.assertions.failed++; + } + else { + m_lastAssertionPassed = true; + } + + // We have no use for the return value (whether messages should be cleared), because messages were made scoped + // and should be let to clear themselves out. + static_cast(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); + + // Reset working state + resetAssertionInfo(); + m_lastResult = result; + } + void RunContext::resetAssertionInfo() { + m_lastAssertionInfo.macroName = StringRef(); + m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; + } + + bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) { + ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo)); + if (!sectionTracker.isOpen()) + return false; + m_activeSections.push_back(§ionTracker); + + m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + + m_reporter->sectionStarting(sectionInfo); + + assertions = m_totals.assertions; + + return true; + } + auto RunContext::acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + using namespace Generators; + GeneratorTracker& tracker = GeneratorTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( "generator", lineInfo ) ); + assert( tracker.isOpen() ); + m_lastAssertionInfo.lineInfo = lineInfo; + return tracker; + } + + bool RunContext::testForMissingAssertions(Counts& assertions) { + if (assertions.total() != 0) + return false; + if (!m_config->warnAboutMissingAssertions()) + return false; + if (m_trackerContext.currentTracker().hasChildren()) + return false; + m_totals.assertions.failed++; + assertions.failed++; + return true; + } + + void RunContext::sectionEnded(SectionEndInfo const & endInfo) { + Counts assertions = m_totals.assertions - endInfo.prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + if (!m_activeSections.empty()) { + m_activeSections.back()->close(); + m_activeSections.pop_back(); + } + + m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); + m_messages.clear(); + } + + void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) { + if (m_unfinishedSections.empty()) + m_activeSections.back()->fail(); + else + m_activeSections.back()->close(); + m_activeSections.pop_back(); + + m_unfinishedSections.push_back(endInfo); + } + void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { + m_reporter->benchmarkStarting( info ); + } + void RunContext::benchmarkEnded( BenchmarkStats const& stats ) { + m_reporter->benchmarkEnded( stats ); + } + + void RunContext::pushScopedMessage(MessageInfo const & message) { + m_messages.push_back(message); + } + + void RunContext::popScopedMessage(MessageInfo const & message) { + m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); + } + + std::string RunContext::getCurrentTestName() const { + return m_activeTestCase + ? m_activeTestCase->getTestCaseInfo().name + : std::string(); + } + + const AssertionResult * RunContext::getLastResult() const { + return &(*m_lastResult); + } + + void RunContext::exceptionEarlyReported() { + m_shouldReportUnexpected = false; + } + + void RunContext::handleFatalErrorCondition( StringRef message ) { + // First notify reporter that bad things happened + m_reporter->fatalErrorEncountered(message); + + // Don't rebuild the result -- the stringification itself can cause more fatal errors + // Instead, fake a result data. + AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } ); + tempResult.message = message; + AssertionResult result(m_lastAssertionInfo, tempResult); + + assertionEnded(result); + + handleUnfinishedSections(); + + // Recreate section for test case (as we will lose the one that was in scope) + auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + + Counts assertions; + assertions.failed = 1; + SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false); + m_reporter->sectionEnded(testCaseSectionStats); + + auto const& testInfo = m_activeTestCase->getTestCaseInfo(); + + Totals deltaTotals; + deltaTotals.testCases.failed = 1; + deltaTotals.assertions.failed = 1; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + std::string(), + std::string(), + false)); + m_totals.testCases.failed++; + testGroupEnded(std::string(), m_totals, 1, 1); + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); + } + + bool RunContext::lastAssertionPassed() { + return m_lastAssertionPassed; + } + + void RunContext::assertionPassed() { + m_lastAssertionPassed = true; + ++m_totals.assertions.passed; + resetAssertionInfo(); + } + + bool RunContext::aborting() const { + return m_totals.assertions.failed >= static_cast(m_config->abortAfter()); + } + + void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) { + auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + m_reporter->sectionStarting(testCaseSection); + Counts prevAssertions = m_totals.assertions; + double duration = 0; + m_shouldReportUnexpected = true; + m_lastAssertionInfo = { "TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal }; + + seedRng(*m_config); + + Timer timer; + CATCH_TRY { + if (m_reporter->getPreferences().shouldRedirectStdOut) { +#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) + RedirectedStdOut redirectedStdOut; + RedirectedStdErr redirectedStdErr; + + timer.start(); + invokeActiveTestCase(); + redirectedCout += redirectedStdOut.str(); + redirectedCerr += redirectedStdErr.str(); +#else + OutputRedirect r(redirectedCout, redirectedCerr); + timer.start(); + invokeActiveTestCase(); +#endif + } else { + timer.start(); + invokeActiveTestCase(); + } + duration = timer.getElapsedSeconds(); + } CATCH_CATCH_ANON (TestFailureException&) { + // This just means the test was aborted due to failure + } CATCH_CATCH_ALL { + // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions + // are reported without translation at the point of origin. + if( m_shouldReportUnexpected ) { + AssertionReaction dummyReaction; + handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction ); + } + } + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + m_testCaseTracker->close(); + handleUnfinishedSections(); + m_messages.clear(); + + SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); + m_reporter->sectionEnded(testCaseSectionStats); + } + + void RunContext::invokeActiveTestCase() { + FatalConditionHandler fatalConditionHandler; // Handle signals + m_activeTestCase->invoke(); + fatalConditionHandler.reset(); + } + + void RunContext::handleUnfinishedSections() { + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down outside the unwind process. + for (auto it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); + it != itEnd; + ++it) + sectionEnded(*it); + m_unfinishedSections.clear(); + } + + void RunContext::handleExpr( + AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction + ) { + m_reporter->assertionStarting( info ); + + bool negated = isFalseTest( info.resultDisposition ); + bool result = expr.getResult() != negated; + + if( result ) { + if (!m_includeSuccessfulResults) { + assertionPassed(); + } + else { + reportExpr(info, ResultWas::Ok, &expr, negated); + } + } + else { + reportExpr(info, ResultWas::ExpressionFailed, &expr, negated ); + populateReaction( reaction ); + } + } + void RunContext::reportExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, + bool negated ) { + + m_lastAssertionInfo = info; + AssertionResultData data( resultType, LazyExpression( negated ) ); + + AssertionResult assertionResult{ info, data }; + assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; + + assertionEnded( assertionResult ); + } + + void RunContext::handleMessage( + AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction + ) { + m_reporter->assertionStarting( info ); + + m_lastAssertionInfo = info; + + AssertionResultData data( resultType, LazyExpression( false ) ); + data.message = message; + AssertionResult assertionResult{ m_lastAssertionInfo, data }; + assertionEnded( assertionResult ); + if( !assertionResult.isOk() ) + populateReaction( reaction ); + } + void RunContext::handleUnexpectedExceptionNotThrown( + AssertionInfo const& info, + AssertionReaction& reaction + ) { + handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction); + } + + void RunContext::handleUnexpectedInflightException( + AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); + data.message = message; + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + populateReaction( reaction ); + } + + void RunContext::populateReaction( AssertionReaction& reaction ) { + reaction.shouldDebugBreak = m_config->shouldDebugBreak(); + reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal); + } + + void RunContext::handleIncomplete( + AssertionInfo const& info + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); + data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + } + void RunContext::handleNonExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( resultType, LazyExpression( false ) ); + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + + if( !assertionResult.isOk() ) + populateReaction( reaction ); + } + + IResultCapture& getResultCapture() { + if (auto* capture = getCurrentContext().getResultCapture()) + return *capture; + else + CATCH_INTERNAL_ERROR("No result capture instance"); + } +} +// end catch_run_context.cpp +// start catch_section.cpp + +namespace Catch { + + Section::Section( SectionInfo const& info ) + : m_info( info ), + m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) + { + m_timer.start(); + } + + Section::~Section() { + if( m_sectionIncluded ) { + SectionEndInfo endInfo{ m_info, m_assertions, m_timer.getElapsedSeconds() }; + if( uncaught_exceptions() ) + getResultCapture().sectionEndedEarly( endInfo ); + else + getResultCapture().sectionEnded( endInfo ); + } + } + + // This indicates whether the section should be executed or not + Section::operator bool() const { + return m_sectionIncluded; + } + +} // end namespace Catch +// end catch_section.cpp +// start catch_section_info.cpp + +namespace Catch { + + SectionInfo::SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name ) + : name( _name ), + lineInfo( _lineInfo ) + {} + +} // end namespace Catch +// end catch_section_info.cpp +// start catch_session.cpp + +// start catch_session.h + +#include + +namespace Catch { + + class Session : NonCopyable { + public: + + Session(); + ~Session() override; + + void showHelp() const; + void libIdentify(); + + int applyCommandLine( int argc, char const * const * argv ); + #if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE) + int applyCommandLine( int argc, wchar_t const * const * argv ); + #endif + + void useConfigData( ConfigData const& configData ); + + template + int run(int argc, CharT const * const argv[]) { + if (m_startupExceptions) + return 1; + int returnCode = applyCommandLine(argc, argv); + if (returnCode == 0) + returnCode = run(); + return returnCode; + } + + int run(); + + clara::Parser const& cli() const; + void cli( clara::Parser const& newParser ); + ConfigData& configData(); + Config& config(); + private: + int runInternal(); + + clara::Parser m_cli; + ConfigData m_configData; + std::shared_ptr m_config; + bool m_startupExceptions = false; + }; + +} // end namespace Catch + +// end catch_session.h +// start catch_version.h + +#include + +namespace Catch { + + // Versioning information + struct Version { + Version( Version const& ) = delete; + Version& operator=( Version const& ) = delete; + Version( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const * const _branchName, + unsigned int _buildNumber ); + + unsigned int const majorVersion; + unsigned int const minorVersion; + unsigned int const patchNumber; + + // buildNumber is only used if branchName is not null + char const * const branchName; + unsigned int const buildNumber; + + friend std::ostream& operator << ( std::ostream& os, Version const& version ); + }; + + Version const& libraryVersion(); +} + +// end catch_version.h +#include +#include + +namespace Catch { + + namespace { + const int MaxExitCode = 255; + + IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) { + auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config); + CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'"); + + return reporter; + } + + IStreamingReporterPtr makeReporter(std::shared_ptr const& config) { + if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) { + return createReporter(config->getReporterName(), config); + } + + auto multi = std::unique_ptr(new ListeningReporter); + + auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); + for (auto const& listener : listeners) { + multi->addListener(listener->create(Catch::ReporterConfig(config))); + } + multi->addReporter(createReporter(config->getReporterName(), config)); + return std::move(multi); + } + + Catch::Totals runTests(std::shared_ptr const& config) { + auto reporter = makeReporter(config); + + RunContext context(config, std::move(reporter)); + + Totals totals; + + context.testGroupStarting(config->name(), 1, 1); + + TestSpec testSpec = config->testSpec(); + + auto const& allTestCases = getAllTestCasesSorted(*config); + for (auto const& testCase : allTestCases) { + if (!context.aborting() && matchTest(testCase, testSpec, *config)) + totals += context.runTest(testCase); + else + context.reporter().skipTest(testCase); + } + + if (config->warnAboutNoTests() && totals.testCases.total() == 0) { + ReusableStringStream testConfig; + + bool first = true; + for (const auto& input : config->getTestsOrTags()) { + if (!first) { testConfig << ' '; } + first = false; + testConfig << input; + } + + context.reporter().noMatchingTestCases(testConfig.str()); + totals.error = -1; + } + + context.testGroupEnded(config->name(), totals, 1, 1); + return totals; + } + + void applyFilenamesAsTags(Catch::IConfig const& config) { + auto& tests = const_cast&>(getAllTestCasesSorted(config)); + for (auto& testCase : tests) { + auto tags = testCase.tags; + + std::string filename = testCase.lineInfo.file; + auto lastSlash = filename.find_last_of("\\/"); + if (lastSlash != std::string::npos) { + filename.erase(0, lastSlash); + filename[0] = '#'; + } + + auto lastDot = filename.find_last_of('.'); + if (lastDot != std::string::npos) { + filename.erase(lastDot); + } + + tags.push_back(std::move(filename)); + setTags(testCase, tags); + } + } + + } // anon namespace + + Session::Session() { + static bool alreadyInstantiated = false; + if( alreadyInstantiated ) { + CATCH_TRY { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); } + CATCH_CATCH_ALL { getMutableRegistryHub().registerStartupException(); } + } + + // There cannot be exceptions at startup in no-exception mode. +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions(); + if ( !exceptions.empty() ) { + m_startupExceptions = true; + Colour colourGuard( Colour::Red ); + Catch::cerr() << "Errors occurred during startup!" << '\n'; + // iterate over all exceptions and notify user + for ( const auto& ex_ptr : exceptions ) { + try { + std::rethrow_exception(ex_ptr); + } catch ( std::exception const& ex ) { + Catch::cerr() << Column( ex.what() ).indent(2) << '\n'; + } + } + } +#endif + + alreadyInstantiated = true; + m_cli = makeCommandLineParser( m_configData ); + } + Session::~Session() { + Catch::cleanUp(); + } + + void Session::showHelp() const { + Catch::cout() + << "\nCatch v" << libraryVersion() << "\n" + << m_cli << std::endl + << "For more detailed usage please see the project docs\n" << std::endl; + } + void Session::libIdentify() { + Catch::cout() + << std::left << std::setw(16) << "description: " << "A Catch test executable\n" + << std::left << std::setw(16) << "category: " << "testframework\n" + << std::left << std::setw(16) << "framework: " << "Catch Test\n" + << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; + } + + int Session::applyCommandLine( int argc, char const * const * argv ) { + if( m_startupExceptions ) + return 1; + + auto result = m_cli.parse( clara::Args( argc, argv ) ); + if( !result ) { + Catch::cerr() + << Colour( Colour::Red ) + << "\nError(s) in input:\n" + << Column( result.errorMessage() ).indent( 2 ) + << "\n\n"; + Catch::cerr() << "Run with -? for usage\n" << std::endl; + return MaxExitCode; + } + + if( m_configData.showHelp ) + showHelp(); + if( m_configData.libIdentify ) + libIdentify(); + m_config.reset(); + return 0; + } + +#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE) + int Session::applyCommandLine( int argc, wchar_t const * const * argv ) { + + char **utf8Argv = new char *[ argc ]; + + for ( int i = 0; i < argc; ++i ) { + int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL ); + + utf8Argv[ i ] = new char[ bufSize ]; + + WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL ); + } + + int returnCode = applyCommandLine( argc, utf8Argv ); + + for ( int i = 0; i < argc; ++i ) + delete [] utf8Argv[ i ]; + + delete [] utf8Argv; + + return returnCode; + } +#endif + + void Session::useConfigData( ConfigData const& configData ) { + m_configData = configData; + m_config.reset(); + } + + int Session::run() { + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before starting" << std::endl; + static_cast(std::getchar()); + } + int exitCode = runInternal(); + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; + static_cast(std::getchar()); + } + return exitCode; + } + + clara::Parser const& Session::cli() const { + return m_cli; + } + void Session::cli( clara::Parser const& newParser ) { + m_cli = newParser; + } + ConfigData& Session::configData() { + return m_configData; + } + Config& Session::config() { + if( !m_config ) + m_config = std::make_shared( m_configData ); + return *m_config; + } + + int Session::runInternal() { + if( m_startupExceptions ) + return 1; + + if (m_configData.showHelp || m_configData.libIdentify) { + return 0; + } + + CATCH_TRY { + config(); // Force config to be constructed + + seedRng( *m_config ); + + if( m_configData.filenamesAsTags ) + applyFilenamesAsTags( *m_config ); + + // Handle list request + if( Option listed = list( config() ) ) + return static_cast( *listed ); + + auto totals = runTests( m_config ); + // Note that on unices only the lower 8 bits are usually used, clamping + // the return value to 255 prevents false negative when some multiple + // of 256 tests has failed + return (std::min) (MaxExitCode, (std::max) (totals.error, static_cast(totals.assertions.failed))); + } +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + catch( std::exception& ex ) { + Catch::cerr() << ex.what() << std::endl; + return MaxExitCode; + } +#endif + } + +} // end namespace Catch +// end catch_session.cpp +// start catch_singletons.cpp + +#include + +namespace Catch { + + namespace { + static auto getSingletons() -> std::vector*& { + static std::vector* g_singletons = nullptr; + if( !g_singletons ) + g_singletons = new std::vector(); + return g_singletons; + } + } + + ISingleton::~ISingleton() {} + + void addSingleton(ISingleton* singleton ) { + getSingletons()->push_back( singleton ); + } + void cleanupSingletons() { + auto& singletons = getSingletons(); + for( auto singleton : *singletons ) + delete singleton; + delete singletons; + singletons = nullptr; + } + +} // namespace Catch +// end catch_singletons.cpp +// start catch_startup_exception_registry.cpp + +namespace Catch { +void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { + CATCH_TRY { + m_exceptions.push_back(exception); + } CATCH_CATCH_ALL { + // If we run out of memory during start-up there's really not a lot more we can do about it + std::terminate(); + } + } + + std::vector const& StartupExceptionRegistry::getExceptions() const noexcept { + return m_exceptions; + } + +} // end namespace Catch +// end catch_startup_exception_registry.cpp +// start catch_stream.cpp + +#include +#include +#include +#include +#include +#include + +namespace Catch { + + Catch::IStream::~IStream() = default; + + namespace detail { namespace { + template + class StreamBufImpl : public std::streambuf { + char data[bufferSize]; + WriterF m_writer; + + public: + StreamBufImpl() { + setp( data, data + sizeof(data) ); + } + + ~StreamBufImpl() noexcept { + StreamBufImpl::sync(); + } + + private: + int overflow( int c ) override { + sync(); + + if( c != EOF ) { + if( pbase() == epptr() ) + m_writer( std::string( 1, static_cast( c ) ) ); + else + sputc( static_cast( c ) ); + } + return 0; + } + + int sync() override { + if( pbase() != pptr() ) { + m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); + setp( pbase(), epptr() ); + } + return 0; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + struct OutputDebugWriter { + + void operator()( std::string const&str ) { + writeToDebugConsole( str ); + } + }; + + /////////////////////////////////////////////////////////////////////////// + + class FileStream : public IStream { + mutable std::ofstream m_ofs; + public: + FileStream( StringRef filename ) { + m_ofs.open( filename.c_str() ); + CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" ); + } + ~FileStream() override = default; + public: // IStream + std::ostream& stream() const override { + return m_ofs; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + class CoutStream : public IStream { + mutable std::ostream m_os; + public: + // Store the streambuf from cout up-front because + // cout may get redirected when running tests + CoutStream() : m_os( Catch::cout().rdbuf() ) {} + ~CoutStream() override = default; + + public: // IStream + std::ostream& stream() const override { return m_os; } + }; + + /////////////////////////////////////////////////////////////////////////// + + class DebugOutStream : public IStream { + std::unique_ptr> m_streamBuf; + mutable std::ostream m_os; + public: + DebugOutStream() + : m_streamBuf( new StreamBufImpl() ), + m_os( m_streamBuf.get() ) + {} + + ~DebugOutStream() override = default; + + public: // IStream + std::ostream& stream() const override { return m_os; } + }; + + }} // namespace anon::detail + + /////////////////////////////////////////////////////////////////////////// + + auto makeStream( StringRef const &filename ) -> IStream const* { + if( filename.empty() ) + return new detail::CoutStream(); + else if( filename[0] == '%' ) { + if( filename == "%debug" ) + return new detail::DebugOutStream(); + else + CATCH_ERROR( "Unrecognised stream: '" << filename << "'" ); + } + else + return new detail::FileStream( filename ); + } + + // This class encapsulates the idea of a pool of ostringstreams that can be reused. + struct StringStreams { + std::vector> m_streams; + std::vector m_unused; + std::ostringstream m_referenceStream; // Used for copy state/ flags from + + auto add() -> std::size_t { + if( m_unused.empty() ) { + m_streams.push_back( std::unique_ptr( new std::ostringstream ) ); + return m_streams.size()-1; + } + else { + auto index = m_unused.back(); + m_unused.pop_back(); + return index; + } + } + + void release( std::size_t index ) { + m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state + m_unused.push_back(index); + } + }; + + ReusableStringStream::ReusableStringStream() + : m_index( Singleton::getMutable().add() ), + m_oss( Singleton::getMutable().m_streams[m_index].get() ) + {} + + ReusableStringStream::~ReusableStringStream() { + static_cast( m_oss )->str(""); + m_oss->clear(); + Singleton::getMutable().release( m_index ); + } + + auto ReusableStringStream::str() const -> std::string { + return static_cast( m_oss )->str(); + } + + /////////////////////////////////////////////////////////////////////////// + +#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions + std::ostream& cout() { return std::cout; } + std::ostream& cerr() { return std::cerr; } + std::ostream& clog() { return std::clog; } +#endif +} +// end catch_stream.cpp +// start catch_string_manip.cpp + +#include +#include +#include +#include + +namespace Catch { + + namespace { + char toLowerCh(char c) { + return static_cast( std::tolower( c ) ); + } + } + + bool startsWith( std::string const& s, std::string const& prefix ) { + return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); + } + bool startsWith( std::string const& s, char prefix ) { + return !s.empty() && s[0] == prefix; + } + bool endsWith( std::string const& s, std::string const& suffix ) { + return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); + } + bool endsWith( std::string const& s, char suffix ) { + return !s.empty() && s[s.size()-1] == suffix; + } + bool contains( std::string const& s, std::string const& infix ) { + return s.find( infix ) != std::string::npos; + } + void toLowerInPlace( std::string& s ) { + std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); + } + std::string toLower( std::string const& s ) { + std::string lc = s; + toLowerInPlace( lc ); + return lc; + } + std::string trim( std::string const& str ) { + static char const* whitespaceChars = "\n\r\t "; + std::string::size_type start = str.find_first_not_of( whitespaceChars ); + std::string::size_type end = str.find_last_not_of( whitespaceChars ); + + return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); + } + + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { + bool replaced = false; + std::size_t i = str.find( replaceThis ); + while( i != std::string::npos ) { + replaced = true; + str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); + if( i < str.size()-withThis.size() ) + i = str.find( replaceThis, i+withThis.size() ); + else + i = std::string::npos; + } + return replaced; + } + + pluralise::pluralise( std::size_t count, std::string const& label ) + : m_count( count ), + m_label( label ) + {} + + std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { + os << pluraliser.m_count << ' ' << pluraliser.m_label; + if( pluraliser.m_count != 1 ) + os << 's'; + return os; + } + +} +// end catch_string_manip.cpp +// start catch_stringref.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +#include +#include +#include + +namespace { + const uint32_t byte_2_lead = 0xC0; + const uint32_t byte_3_lead = 0xE0; + const uint32_t byte_4_lead = 0xF0; +} + +namespace Catch { + StringRef::StringRef( char const* rawChars ) noexcept + : StringRef( rawChars, static_cast(std::strlen(rawChars) ) ) + {} + + StringRef::operator std::string() const { + return std::string( m_start, m_size ); + } + + void StringRef::swap( StringRef& other ) noexcept { + std::swap( m_start, other.m_start ); + std::swap( m_size, other.m_size ); + std::swap( m_data, other.m_data ); + } + + auto StringRef::c_str() const -> char const* { + if( isSubstring() ) + const_cast( this )->takeOwnership(); + return m_start; + } + auto StringRef::currentData() const noexcept -> char const* { + return m_start; + } + + auto StringRef::isOwned() const noexcept -> bool { + return m_data != nullptr; + } + auto StringRef::isSubstring() const noexcept -> bool { + return m_start[m_size] != '\0'; + } + + void StringRef::takeOwnership() { + if( !isOwned() ) { + m_data = new char[m_size+1]; + memcpy( m_data, m_start, m_size ); + m_data[m_size] = '\0'; + m_start = m_data; + } + } + auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef { + if( start < m_size ) + return StringRef( m_start+start, size ); + else + return StringRef(); + } + auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool { + return + size() == other.size() && + (std::strncmp( m_start, other.m_start, size() ) == 0); + } + auto StringRef::operator != ( StringRef const& other ) const noexcept -> bool { + return !operator==( other ); + } + + auto StringRef::operator[](size_type index) const noexcept -> char { + return m_start[index]; + } + + auto StringRef::numberOfCharacters() const noexcept -> size_type { + size_type noChars = m_size; + // Make adjustments for uft encodings + for( size_type i=0; i < m_size; ++i ) { + char c = m_start[i]; + if( ( c & byte_2_lead ) == byte_2_lead ) { + noChars--; + if (( c & byte_3_lead ) == byte_3_lead ) + noChars--; + if( ( c & byte_4_lead ) == byte_4_lead ) + noChars--; + } + } + return noChars; + } + + auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string { + std::string str; + str.reserve( lhs.size() + rhs.size() ); + str += lhs; + str += rhs; + return str; + } + auto operator + ( StringRef const& lhs, const char* rhs ) -> std::string { + return std::string( lhs ) + std::string( rhs ); + } + auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string { + return std::string( lhs ) + std::string( rhs ); + } + + auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& { + return os.write(str.currentData(), str.size()); + } + + auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& { + lhs.append(rhs.currentData(), rhs.size()); + return lhs; + } + +} // namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif +// end catch_stringref.cpp +// start catch_tag_alias.cpp + +namespace Catch { + TagAlias::TagAlias(std::string const & _tag, SourceLineInfo _lineInfo): tag(_tag), lineInfo(_lineInfo) {} +} +// end catch_tag_alias.cpp +// start catch_tag_alias_autoregistrar.cpp + +namespace Catch { + + RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) { + CATCH_TRY { + getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); + } CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } + +} +// end catch_tag_alias_autoregistrar.cpp +// start catch_tag_alias_registry.cpp + +#include + +namespace Catch { + + TagAliasRegistry::~TagAliasRegistry() {} + + TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { + auto it = m_registry.find( alias ); + if( it != m_registry.end() ) + return &(it->second); + else + return nullptr; + } + + std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { + std::string expandedTestSpec = unexpandedTestSpec; + for( auto const& registryKvp : m_registry ) { + std::size_t pos = expandedTestSpec.find( registryKvp.first ); + if( pos != std::string::npos ) { + expandedTestSpec = expandedTestSpec.substr( 0, pos ) + + registryKvp.second.tag + + expandedTestSpec.substr( pos + registryKvp.first.size() ); + } + } + return expandedTestSpec; + } + + void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { + CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'), + "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo ); + + CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second, + "error: tag alias, '" << alias << "' already registered.\n" + << "\tFirst seen at: " << find(alias)->lineInfo << "\n" + << "\tRedefined at: " << lineInfo ); + } + + ITagAliasRegistry::~ITagAliasRegistry() {} + + ITagAliasRegistry const& ITagAliasRegistry::get() { + return getRegistryHub().getTagAliasRegistry(); + } + +} // end namespace Catch +// end catch_tag_alias_registry.cpp +// start catch_test_case_info.cpp + +#include +#include +#include +#include + +namespace Catch { + + namespace { + TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { + if( startsWith( tag, '.' ) || + tag == "!hide" ) + return TestCaseInfo::IsHidden; + else if( tag == "!throws" ) + return TestCaseInfo::Throws; + else if( tag == "!shouldfail" ) + return TestCaseInfo::ShouldFail; + else if( tag == "!mayfail" ) + return TestCaseInfo::MayFail; + else if( tag == "!nonportable" ) + return TestCaseInfo::NonPortable; + else if( tag == "!benchmark" ) + return static_cast( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden ); + else + return TestCaseInfo::None; + } + bool isReservedTag( std::string const& tag ) { + return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( static_cast(tag[0]) ); + } + void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { + CATCH_ENFORCE( !isReservedTag(tag), + "Tag name: [" << tag << "] is not allowed.\n" + << "Tag names starting with non alpha-numeric characters are reserved\n" + << _lineInfo ); + } + } + + TestCase makeTestCase( ITestInvoker* _testCase, + std::string const& _className, + NameAndTags const& nameAndTags, + SourceLineInfo const& _lineInfo ) + { + bool isHidden = false; + + // Parse out tags + std::vector tags; + std::string desc, tag; + bool inTag = false; + std::string _descOrTags = nameAndTags.tags; + for (char c : _descOrTags) { + if( !inTag ) { + if( c == '[' ) + inTag = true; + else + desc += c; + } + else { + if( c == ']' ) { + TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); + if( ( prop & TestCaseInfo::IsHidden ) != 0 ) + isHidden = true; + else if( prop == TestCaseInfo::None ) + enforceNotReservedTag( tag, _lineInfo ); + + tags.push_back( tag ); + tag.clear(); + inTag = false; + } + else + tag += c; + } + } + if( isHidden ) { + tags.push_back( "." ); + } + + TestCaseInfo info( nameAndTags.name, _className, desc, tags, _lineInfo ); + return TestCase( _testCase, std::move(info) ); + } + + void setTags( TestCaseInfo& testCaseInfo, std::vector tags ) { + std::sort(begin(tags), end(tags)); + tags.erase(std::unique(begin(tags), end(tags)), end(tags)); + testCaseInfo.lcaseTags.clear(); + + for( auto const& tag : tags ) { + std::string lcaseTag = toLower( tag ); + testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); + testCaseInfo.lcaseTags.push_back( lcaseTag ); + } + testCaseInfo.tags = std::move(tags); + } + + TestCaseInfo::TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::vector const& _tags, + SourceLineInfo const& _lineInfo ) + : name( _name ), + className( _className ), + description( _description ), + lineInfo( _lineInfo ), + properties( None ) + { + setTags( *this, _tags ); + } + + bool TestCaseInfo::isHidden() const { + return ( properties & IsHidden ) != 0; + } + bool TestCaseInfo::throws() const { + return ( properties & Throws ) != 0; + } + bool TestCaseInfo::okToFail() const { + return ( properties & (ShouldFail | MayFail ) ) != 0; + } + bool TestCaseInfo::expectedToFail() const { + return ( properties & (ShouldFail ) ) != 0; + } + + std::string TestCaseInfo::tagsAsString() const { + std::string ret; + // '[' and ']' per tag + std::size_t full_size = 2 * tags.size(); + for (const auto& tag : tags) { + full_size += tag.size(); + } + ret.reserve(full_size); + for (const auto& tag : tags) { + ret.push_back('['); + ret.append(tag); + ret.push_back(']'); + } + + return ret; + } + + TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo&& info ) : TestCaseInfo( std::move(info) ), test( testCase ) {} + + TestCase TestCase::withName( std::string const& _newName ) const { + TestCase other( *this ); + other.name = _newName; + return other; + } + + void TestCase::invoke() const { + test->invoke(); + } + + bool TestCase::operator == ( TestCase const& other ) const { + return test.get() == other.test.get() && + name == other.name && + className == other.className; + } + + bool TestCase::operator < ( TestCase const& other ) const { + return name < other.name; + } + + TestCaseInfo const& TestCase::getTestCaseInfo() const + { + return *this; + } + +} // end namespace Catch +// end catch_test_case_info.cpp +// start catch_test_case_registry_impl.cpp + +#include + +namespace Catch { + + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { + + std::vector sorted = unsortedTestCases; + + switch( config.runOrder() ) { + case RunTests::InLexicographicalOrder: + std::sort( sorted.begin(), sorted.end() ); + break; + case RunTests::InRandomOrder: + seedRng( config ); + std::shuffle( sorted.begin(), sorted.end(), rng() ); + break; + case RunTests::InDeclarationOrder: + // already in declaration order + break; + } + return sorted; + } + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { + return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); + } + + void enforceNoDuplicateTestCases( std::vector const& functions ) { + std::set seenFunctions; + for( auto const& function : functions ) { + auto prev = seenFunctions.insert( function ); + CATCH_ENFORCE( prev.second, + "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << function.getTestCaseInfo().lineInfo ); + } + } + + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { + std::vector filtered; + filtered.reserve( testCases.size() ); + for( auto const& testCase : testCases ) + if( matchTest( testCase, testSpec, config ) ) + filtered.push_back( testCase ); + return filtered; + } + std::vector const& getAllTestCasesSorted( IConfig const& config ) { + return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); + } + + void TestRegistry::registerTest( TestCase const& testCase ) { + std::string name = testCase.getTestCaseInfo().name; + if( name.empty() ) { + ReusableStringStream rss; + rss << "Anonymous test case " << ++m_unnamedCount; + return registerTest( testCase.withName( rss.str() ) ); + } + m_functions.push_back( testCase ); + } + + std::vector const& TestRegistry::getAllTests() const { + return m_functions; + } + std::vector const& TestRegistry::getAllTestsSorted( IConfig const& config ) const { + if( m_sortedFunctions.empty() ) + enforceNoDuplicateTestCases( m_functions ); + + if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { + m_sortedFunctions = sortTests( config, m_functions ); + m_currentSortOrder = config.runOrder(); + } + return m_sortedFunctions; + } + + /////////////////////////////////////////////////////////////////////////// + TestInvokerAsFunction::TestInvokerAsFunction( void(*testAsFunction)() ) noexcept : m_testAsFunction( testAsFunction ) {} + + void TestInvokerAsFunction::invoke() const { + m_testAsFunction(); + } + + std::string extractClassName( StringRef const& classOrQualifiedMethodName ) { + std::string className = classOrQualifiedMethodName; + if( startsWith( className, '&' ) ) + { + std::size_t lastColons = className.rfind( "::" ); + std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); + if( penultimateColons == std::string::npos ) + penultimateColons = 1; + className = className.substr( penultimateColons, lastColons-penultimateColons ); + } + return className; + } + +} // end namespace Catch +// end catch_test_case_registry_impl.cpp +// start catch_test_case_tracker.cpp + +#include +#include +#include +#include +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +namespace Catch { +namespace TestCaseTracking { + + NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) + : name( _name ), + location( _location ) + {} + + ITracker::~ITracker() = default; + + TrackerContext& TrackerContext::instance() { + static TrackerContext s_instance; + return s_instance; + } + + ITracker& TrackerContext::startRun() { + m_rootTracker = std::make_shared( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr ); + m_currentTracker = nullptr; + m_runState = Executing; + return *m_rootTracker; + } + + void TrackerContext::endRun() { + m_rootTracker.reset(); + m_currentTracker = nullptr; + m_runState = NotStarted; + } + + void TrackerContext::startCycle() { + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; + } + void TrackerContext::completeCycle() { + m_runState = CompletedCycle; + } + + bool TrackerContext::completedCycle() const { + return m_runState == CompletedCycle; + } + ITracker& TrackerContext::currentTracker() { + return *m_currentTracker; + } + void TrackerContext::setCurrentTracker( ITracker* tracker ) { + m_currentTracker = tracker; + } + + TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : m_nameAndLocation( nameAndLocation ), + m_ctx( ctx ), + m_parent( parent ) + {} + + NameAndLocation const& TrackerBase::nameAndLocation() const { + return m_nameAndLocation; + } + bool TrackerBase::isComplete() const { + return m_runState == CompletedSuccessfully || m_runState == Failed; + } + bool TrackerBase::isSuccessfullyCompleted() const { + return m_runState == CompletedSuccessfully; + } + bool TrackerBase::isOpen() const { + return m_runState != NotStarted && !isComplete(); + } + bool TrackerBase::hasChildren() const { + return !m_children.empty(); + } + + void TrackerBase::addChild( ITrackerPtr const& child ) { + m_children.push_back( child ); + } + + ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) { + auto it = std::find_if( m_children.begin(), m_children.end(), + [&nameAndLocation]( ITrackerPtr const& tracker ){ + return + tracker->nameAndLocation().location == nameAndLocation.location && + tracker->nameAndLocation().name == nameAndLocation.name; + } ); + return( it != m_children.end() ) + ? *it + : nullptr; + } + ITracker& TrackerBase::parent() { + assert( m_parent ); // Should always be non-null except for root + return *m_parent; + } + + void TrackerBase::openChild() { + if( m_runState != ExecutingChildren ) { + m_runState = ExecutingChildren; + if( m_parent ) + m_parent->openChild(); + } + } + + bool TrackerBase::isSectionTracker() const { return false; } + bool TrackerBase::isIndexTracker() const { return false; } + + void TrackerBase::open() { + m_runState = Executing; + moveToThis(); + if( m_parent ) + m_parent->openChild(); + } + + void TrackerBase::close() { + + // Close any still open children (e.g. generators) + while( &m_ctx.currentTracker() != this ) + m_ctx.currentTracker().close(); + + switch( m_runState ) { + case NeedsAnotherRun: + break; + + case Executing: + m_runState = CompletedSuccessfully; + break; + case ExecutingChildren: + if( m_children.empty() || m_children.back()->isComplete() ) + m_runState = CompletedSuccessfully; + break; + + case NotStarted: + case CompletedSuccessfully: + case Failed: + CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState ); + + default: + CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState ); + } + moveToParent(); + m_ctx.completeCycle(); + } + void TrackerBase::fail() { + m_runState = Failed; + if( m_parent ) + m_parent->markAsNeedingAnotherRun(); + moveToParent(); + m_ctx.completeCycle(); + } + void TrackerBase::markAsNeedingAnotherRun() { + m_runState = NeedsAnotherRun; + } + + void TrackerBase::moveToParent() { + assert( m_parent ); + m_ctx.setCurrentTracker( m_parent ); + } + void TrackerBase::moveToThis() { + m_ctx.setCurrentTracker( this ); + } + + SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + { + if( parent ) { + while( !parent->isSectionTracker() ) + parent = &parent->parent(); + + SectionTracker& parentSection = static_cast( *parent ); + addNextFilters( parentSection.m_filters ); + } + } + + bool SectionTracker::isSectionTracker() const { return true; } + + SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { + std::shared_ptr section; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isSectionTracker() ); + section = std::static_pointer_cast( childTracker ); + } + else { + section = std::make_shared( nameAndLocation, ctx, ¤tTracker ); + currentTracker.addChild( section ); + } + if( !ctx.completedCycle() ) + section->tryOpen(); + return *section; + } + + void SectionTracker::tryOpen() { + if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) + open(); + } + + void SectionTracker::addInitialFilters( std::vector const& filters ) { + if( !filters.empty() ) { + m_filters.push_back(""); // Root - should never be consulted + m_filters.push_back(""); // Test Case - not a section filter + m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); + } + } + void SectionTracker::addNextFilters( std::vector const& filters ) { + if( filters.size() > 1 ) + m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); + } + + IndexTracker::IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) + : TrackerBase( nameAndLocation, ctx, parent ), + m_size( size ) + {} + + bool IndexTracker::isIndexTracker() const { return true; } + + IndexTracker& IndexTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { + std::shared_ptr tracker; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isIndexTracker() ); + tracker = std::static_pointer_cast( childTracker ); + } + else { + tracker = std::make_shared( nameAndLocation, ctx, ¤tTracker, size ); + currentTracker.addChild( tracker ); + } + + if( !ctx.completedCycle() && !tracker->isComplete() ) { + if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) + tracker->moveNext(); + tracker->open(); + } + + return *tracker; + } + + int IndexTracker::index() const { return m_index; } + + void IndexTracker::moveNext() { + m_index++; + m_children.clear(); + } + + void IndexTracker::close() { + TrackerBase::close(); + if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) + m_runState = Executing; + } + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; +using TestCaseTracking::IndexTracker; + +} // namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif +// end catch_test_case_tracker.cpp +// start catch_test_registry.cpp + +namespace Catch { + + auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsFunction( testAsFunction ); + } + + NameAndTags::NameAndTags( StringRef const& name_ , StringRef const& tags_ ) noexcept : name( name_ ), tags( tags_ ) {} + + AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept { + CATCH_TRY { + getMutableRegistryHub() + .registerTest( + makeTestCase( + invoker, + extractClassName( classOrMethod ), + nameAndTags, + lineInfo)); + } CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } + + AutoReg::~AutoReg() = default; +} +// end catch_test_registry.cpp +// start catch_test_spec.cpp + +#include +#include +#include +#include + +namespace Catch { + + TestSpec::Pattern::~Pattern() = default; + TestSpec::NamePattern::~NamePattern() = default; + TestSpec::TagPattern::~TagPattern() = default; + TestSpec::ExcludedPattern::~ExcludedPattern() = default; + + TestSpec::NamePattern::NamePattern( std::string const& name ) + : m_wildcardPattern( toLower( name ), CaseSensitive::No ) + {} + bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const { + return m_wildcardPattern.matches( toLower( testCase.name ) ); + } + + TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} + bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const { + return std::find(begin(testCase.lcaseTags), + end(testCase.lcaseTags), + m_tag) != end(testCase.lcaseTags); + } + + TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} + bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } + + bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const { + // All patterns in a filter must match for the filter to be a match + for( auto const& pattern : m_patterns ) { + if( !pattern->matches( testCase ) ) + return false; + } + return true; + } + + bool TestSpec::hasFilters() const { + return !m_filters.empty(); + } + bool TestSpec::matches( TestCaseInfo const& testCase ) const { + // A TestSpec matches if any filter matches + for( auto const& filter : m_filters ) + if( filter.matches( testCase ) ) + return true; + return false; + } +} +// end catch_test_spec.cpp +// start catch_test_spec_parser.cpp + +namespace Catch { + + TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + + TestSpecParser& TestSpecParser::parse( std::string const& arg ) { + m_mode = None; + m_exclusion = false; + m_start = std::string::npos; + m_arg = m_tagAliases->expandAliases( arg ); + m_escapeChars.clear(); + for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) + visitChar( m_arg[m_pos] ); + if( m_mode == Name ) + addPattern(); + return *this; + } + TestSpec TestSpecParser::testSpec() { + addFilter(); + return m_testSpec; + } + + void TestSpecParser::visitChar( char c ) { + if( m_mode == None ) { + switch( c ) { + case ' ': return; + case '~': m_exclusion = true; return; + case '[': return startNewMode( Tag, ++m_pos ); + case '"': return startNewMode( QuotedName, ++m_pos ); + case '\\': return escape(); + default: startNewMode( Name, m_pos ); break; + } + } + if( m_mode == Name ) { + if( c == ',' ) { + addPattern(); + addFilter(); + } + else if( c == '[' ) { + if( subString() == "exclude:" ) + m_exclusion = true; + else + addPattern(); + startNewMode( Tag, ++m_pos ); + } + else if( c == '\\' ) + escape(); + } + else if( m_mode == EscapedName ) + m_mode = Name; + else if( m_mode == QuotedName && c == '"' ) + addPattern(); + else if( m_mode == Tag && c == ']' ) + addPattern(); + } + void TestSpecParser::startNewMode( Mode mode, std::size_t start ) { + m_mode = mode; + m_start = start; + } + void TestSpecParser::escape() { + if( m_mode == None ) + m_start = m_pos; + m_mode = EscapedName; + m_escapeChars.push_back( m_pos ); + } + std::string TestSpecParser::subString() const { return m_arg.substr( m_start, m_pos - m_start ); } + + void TestSpecParser::addFilter() { + if( !m_currentFilter.m_patterns.empty() ) { + m_testSpec.m_filters.push_back( m_currentFilter ); + m_currentFilter = TestSpec::Filter(); + } + } + + TestSpec parseTestSpec( std::string const& arg ) { + return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); + } + +} // namespace Catch +// end catch_test_spec_parser.cpp +// start catch_timer.cpp + +#include + +static const uint64_t nanosecondsInSecond = 1000000000; + +namespace Catch { + + auto getCurrentNanosecondsSinceEpoch() -> uint64_t { + return std::chrono::duration_cast( std::chrono::high_resolution_clock::now().time_since_epoch() ).count(); + } + + namespace { + auto estimateClockResolution() -> uint64_t { + uint64_t sum = 0; + static const uint64_t iterations = 1000000; + + auto startTime = getCurrentNanosecondsSinceEpoch(); + + for( std::size_t i = 0; i < iterations; ++i ) { + + uint64_t ticks; + uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); + do { + ticks = getCurrentNanosecondsSinceEpoch(); + } while( ticks == baseTicks ); + + auto delta = ticks - baseTicks; + sum += delta; + + // If we have been calibrating for over 3 seconds -- the clock + // is terrible and we should move on. + // TBD: How to signal that the measured resolution is probably wrong? + if (ticks > startTime + 3 * nanosecondsInSecond) { + return sum / i; + } + } + + // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers + // - and potentially do more iterations if there's a high variance. + return sum/iterations; + } + } + auto getEstimatedClockResolution() -> uint64_t { + static auto s_resolution = estimateClockResolution(); + return s_resolution; + } + + void Timer::start() { + m_nanoseconds = getCurrentNanosecondsSinceEpoch(); + } + auto Timer::getElapsedNanoseconds() const -> uint64_t { + return getCurrentNanosecondsSinceEpoch() - m_nanoseconds; + } + auto Timer::getElapsedMicroseconds() const -> uint64_t { + return getElapsedNanoseconds()/1000; + } + auto Timer::getElapsedMilliseconds() const -> unsigned int { + return static_cast(getElapsedMicroseconds()/1000); + } + auto Timer::getElapsedSeconds() const -> double { + return getElapsedMicroseconds()/1000000.0; + } + +} // namespace Catch +// end catch_timer.cpp +// start catch_tostring.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +# pragma clang diagnostic ignored "-Wglobal-constructors" +#endif + +// Enable specific decls locally +#if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#endif + +#include +#include + +namespace Catch { + +namespace Detail { + + const std::string unprintableString = "{?}"; + + namespace { + const int hexThreshold = 255; + + struct Endianness { + enum Arch { Big, Little }; + + static Arch which() { + union _{ + int asInt; + char asChar[sizeof (int)]; + } u; + + u.asInt = 1; + return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; + } + }; + } + + std::string rawMemoryToString( const void *object, std::size_t size ) { + // Reverse order for little endian architectures + int i = 0, end = static_cast( size ), inc = 1; + if( Endianness::which() == Endianness::Little ) { + i = end-1; + end = inc = -1; + } + + unsigned char const *bytes = static_cast(object); + ReusableStringStream rss; + rss << "0x" << std::setfill('0') << std::hex; + for( ; i != end; i += inc ) + rss << std::setw(2) << static_cast(bytes[i]); + return rss.str(); + } +} + +template +std::string fpToString( T value, int precision ) { + if (Catch::isnan(value)) { + return "nan"; + } + + ReusableStringStream rss; + rss << std::setprecision( precision ) + << std::fixed + << value; + std::string d = rss.str(); + std::size_t i = d.find_last_not_of( '0' ); + if( i != std::string::npos && i != d.size()-1 ) { + if( d[i] == '.' ) + i++; + d = d.substr( 0, i+1 ); + } + return d; +} + +//// ======================================================= //// +// +// Out-of-line defs for full specialization of StringMaker +// +//// ======================================================= //// + +std::string StringMaker::convert(const std::string& str) { + if (!getCurrentContext().getConfig()->showInvisibles()) { + return '"' + str + '"'; + } + + std::string s("\""); + for (char c : str) { + switch (c) { + case '\n': + s.append("\\n"); + break; + case '\t': + s.append("\\t"); + break; + default: + s.push_back(c); + break; + } + } + s.append("\""); + return s; +} + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +std::string StringMaker::convert(std::string_view str) { + return ::Catch::Detail::stringify(std::string{ str }); +} +#endif + +std::string StringMaker::convert(char const* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker::convert(char* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } +} + +#ifdef CATCH_CONFIG_WCHAR +std::string StringMaker::convert(const std::wstring& wstr) { + std::string s; + s.reserve(wstr.size()); + for (auto c : wstr) { + s += (c <= 0xff) ? static_cast(c) : '?'; + } + return ::Catch::Detail::stringify(s); +} + +# ifdef CATCH_CONFIG_CPP17_STRING_VIEW +std::string StringMaker::convert(std::wstring_view str) { + return StringMaker::convert(std::wstring(str)); +} +# endif + +std::string StringMaker::convert(wchar_t const * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker::convert(wchar_t * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} +#endif + +std::string StringMaker::convert(int value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); +} + +std::string StringMaker::convert(unsigned int value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(unsigned long value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(unsigned long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); +} + +std::string StringMaker::convert(bool b) { + return b ? "true" : "false"; +} + +std::string StringMaker::convert(signed char value) { + if (value == '\r') { + return "'\\r'"; + } else if (value == '\f') { + return "'\\f'"; + } else if (value == '\n') { + return "'\\n'"; + } else if (value == '\t') { + return "'\\t'"; + } else if ('\0' <= value && value < ' ') { + return ::Catch::Detail::stringify(static_cast(value)); + } else { + char chstr[] = "' '"; + chstr[1] = value; + return chstr; + } +} +std::string StringMaker::convert(char c) { + return ::Catch::Detail::stringify(static_cast(c)); +} +std::string StringMaker::convert(unsigned char c) { + return ::Catch::Detail::stringify(static_cast(c)); +} + +std::string StringMaker::convert(std::nullptr_t) { + return "nullptr"; +} + +std::string StringMaker::convert(float value) { + return fpToString(value, 5) + 'f'; +} +std::string StringMaker::convert(double value) { + return fpToString(value, 10); +} + +std::string ratio_string::symbol() { return "a"; } +std::string ratio_string::symbol() { return "f"; } +std::string ratio_string::symbol() { return "p"; } +std::string ratio_string::symbol() { return "n"; } +std::string ratio_string::symbol() { return "u"; } +std::string ratio_string::symbol() { return "m"; } + +} // end namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +// end catch_tostring.cpp +// start catch_totals.cpp + +namespace Catch { + + Counts Counts::operator - ( Counts const& other ) const { + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + diff.failedButOk = failedButOk - other.failedButOk; + return diff; + } + + Counts& Counts::operator += ( Counts const& other ) { + passed += other.passed; + failed += other.failed; + failedButOk += other.failedButOk; + return *this; + } + + std::size_t Counts::total() const { + return passed + failed + failedButOk; + } + bool Counts::allPassed() const { + return failed == 0 && failedButOk == 0; + } + bool Counts::allOk() const { + return failed == 0; + } + + Totals Totals::operator - ( Totals const& other ) const { + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; + } + + Totals& Totals::operator += ( Totals const& other ) { + assertions += other.assertions; + testCases += other.testCases; + return *this; + } + + Totals Totals::delta( Totals const& prevTotals ) const { + Totals diff = *this - prevTotals; + if( diff.assertions.failed > 0 ) + ++diff.testCases.failed; + else if( diff.assertions.failedButOk > 0 ) + ++diff.testCases.failedButOk; + else + ++diff.testCases.passed; + return diff; + } + +} +// end catch_totals.cpp +// start catch_uncaught_exceptions.cpp + +#include + +namespace Catch { + bool uncaught_exceptions() { +#if defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + return std::uncaught_exceptions() > 0; +#else + return std::uncaught_exception(); +#endif + } +} // end namespace Catch +// end catch_uncaught_exceptions.cpp +// start catch_version.cpp + +#include + +namespace Catch { + + Version::Version + ( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const * const _branchName, + unsigned int _buildNumber ) + : majorVersion( _majorVersion ), + minorVersion( _minorVersion ), + patchNumber( _patchNumber ), + branchName( _branchName ), + buildNumber( _buildNumber ) + {} + + std::ostream& operator << ( std::ostream& os, Version const& version ) { + os << version.majorVersion << '.' + << version.minorVersion << '.' + << version.patchNumber; + // branchName is never null -> 0th char is \0 if it is empty + if (version.branchName[0]) { + os << '-' << version.branchName + << '.' << version.buildNumber; + } + return os; + } + + Version const& libraryVersion() { + static Version version( 2, 5, 0, "", 0 ); + return version; + } + +} +// end catch_version.cpp +// start catch_wildcard_pattern.cpp + +#include + +namespace Catch { + + WildcardPattern::WildcardPattern( std::string const& pattern, + CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_pattern( adjustCase( pattern ) ) + { + if( startsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 1 ); + m_wildcard = WildcardAtStart; + } + if( endsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); + m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); + } + } + + bool WildcardPattern::matches( std::string const& str ) const { + switch( m_wildcard ) { + case NoWildcard: + return m_pattern == adjustCase( str ); + case WildcardAtStart: + return endsWith( adjustCase( str ), m_pattern ); + case WildcardAtEnd: + return startsWith( adjustCase( str ), m_pattern ); + case WildcardAtBothEnds: + return contains( adjustCase( str ), m_pattern ); + default: + CATCH_INTERNAL_ERROR( "Unknown enum" ); + } + } + + std::string WildcardPattern::adjustCase( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; + } +} +// end catch_wildcard_pattern.cpp +// start catch_xmlwriter.cpp + +#include + +using uchar = unsigned char; + +namespace Catch { + +namespace { + + size_t trailingBytes(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return 2; + } + if ((c & 0xF0) == 0xE0) { + return 3; + } + if ((c & 0xF8) == 0xF0) { + return 4; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + uint32_t headerValue(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return c & 0x1F; + } + if ((c & 0xF0) == 0xE0) { + return c & 0x0F; + } + if ((c & 0xF8) == 0xF0) { + return c & 0x07; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + void hexEscapeChar(std::ostream& os, unsigned char c) { + os << "\\x" + << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast(c); + } + +} // anonymous namespace + + XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) + : m_str( str ), + m_forWhat( forWhat ) + {} + + void XmlEncode::encodeTo( std::ostream& os ) const { + // Apostrophe escaping not necessary if we always use " to write attributes + // (see: http://www.w3.org/TR/xml/#syntax) + + for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) { + uchar c = m_str[idx]; + switch (c) { + case '<': os << "<"; break; + case '&': os << "&"; break; + + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') + os << ">"; + else + os << c; + break; + + case '\"': + if (m_forWhat == ForAttributes) + os << """; + else + os << c; + break; + + default: + // Check for control characters and invalid utf-8 + + // Escape control characters in standard ascii + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { + hexEscapeChar(os, c); + break; + } + + // Plain ASCII: Write it to stream + if (c < 0x7F) { + os << c; + break; + } + + // UTF-8 territory + // Check if the encoding is valid and if it is not, hex escape bytes. + // Important: We do not check the exact decoded values for validity, only the encoding format + // First check that this bytes is a valid lead byte: + // This means that it is not encoded as 1111 1XXX + // Or as 10XX XXXX + if (c < 0xC0 || + c >= 0xF8) { + hexEscapeChar(os, c); + break; + } + + auto encBytes = trailingBytes(c); + // Are there enough bytes left to avoid accessing out-of-bounds memory? + if (idx + encBytes - 1 >= m_str.size()) { + hexEscapeChar(os, c); + break; + } + // The header is valid, check data + // The next encBytes bytes must together be a valid utf-8 + // This means: bitpattern 10XX XXXX and the extracted value is sane (ish) + bool valid = true; + uint32_t value = headerValue(c); + for (std::size_t n = 1; n < encBytes; ++n) { + uchar nc = m_str[idx + n]; + valid &= ((nc & 0xC0) == 0x80); + value = (value << 6) | (nc & 0x3F); + } + + if ( + // Wrong bit pattern of following bytes + (!valid) || + // Overlong encodings + (value < 0x80) || + (0x80 <= value && value < 0x800 && encBytes > 2) || + (0x800 < value && value < 0x10000 && encBytes > 3) || + // Encoded value out of range + (value >= 0x110000) + ) { + hexEscapeChar(os, c); + break; + } + + // If we got here, this is in fact a valid(ish) utf-8 sequence + for (std::size_t n = 0; n < encBytes; ++n) { + os << m_str[idx + n]; + } + idx += encBytes - 1; + break; + } + } + } + + std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { + xmlEncode.encodeTo( os ); + return os; + } + + XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer ) + : m_writer( writer ) + {} + + XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept + : m_writer( other.m_writer ){ + other.m_writer = nullptr; + } + XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept { + if ( m_writer ) { + m_writer->endElement(); + } + m_writer = other.m_writer; + other.m_writer = nullptr; + return *this; + } + + XmlWriter::ScopedElement::~ScopedElement() { + if( m_writer ) + m_writer->endElement(); + } + + XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) { + m_writer->writeText( text, indent ); + return *this; + } + + XmlWriter::XmlWriter( std::ostream& os ) : m_os( os ) + { + writeDeclaration(); + } + + XmlWriter::~XmlWriter() { + while( !m_tags.empty() ) + endElement(); + } + + XmlWriter& XmlWriter::startElement( std::string const& name ) { + ensureTagClosed(); + newlineIfNecessary(); + m_os << m_indent << '<' << name; + m_tags.push_back( name ); + m_indent += " "; + m_tagIsOpen = true; + return *this; + } + + XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) { + ScopedElement scoped( this ); + startElement( name ); + return scoped; + } + + XmlWriter& XmlWriter::endElement() { + newlineIfNecessary(); + m_indent = m_indent.substr( 0, m_indent.size()-2 ); + if( m_tagIsOpen ) { + m_os << "/>"; + m_tagIsOpen = false; + } + else { + m_os << m_indent << ""; + } + m_os << std::endl; + m_tags.pop_back(); + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) { + if( !name.empty() && !attribute.empty() ) + m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) { + m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) { + if( !text.empty() ){ + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if( tagWasOpen && indent ) + m_os << m_indent; + m_os << XmlEncode( text ); + m_needsNewline = true; + } + return *this; + } + + XmlWriter& XmlWriter::writeComment( std::string const& text ) { + ensureTagClosed(); + m_os << m_indent << ""; + m_needsNewline = true; + return *this; + } + + void XmlWriter::writeStylesheetRef( std::string const& url ) { + m_os << "\n"; + } + + XmlWriter& XmlWriter::writeBlankLine() { + ensureTagClosed(); + m_os << '\n'; + return *this; + } + + void XmlWriter::ensureTagClosed() { + if( m_tagIsOpen ) { + m_os << ">" << std::endl; + m_tagIsOpen = false; + } + } + + void XmlWriter::writeDeclaration() { + m_os << "\n"; + } + + void XmlWriter::newlineIfNecessary() { + if( m_needsNewline ) { + m_os << std::endl; + m_needsNewline = false; + } + } +} +// end catch_xmlwriter.cpp +// start catch_reporter_bases.cpp + +#include +#include +#include +#include +#include + +namespace Catch { + void prepareExpandedExpression(AssertionResult& result) { + result.getExpandedExpression(); + } + + // Because formatting using c++ streams is stateful, drop down to C is required + // Alternatively we could use stringstream, but its performance is... not good. + std::string getFormattedDuration( double duration ) { + // Max exponent + 1 is required to represent the whole part + // + 1 for decimal point + // + 3 for the 3 decimal places + // + 1 for null terminator + const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; + char buffer[maxDoubleSize]; + + // Save previous errno, to prevent sprintf from overwriting it + ErrnoGuard guard; +#ifdef _MSC_VER + sprintf_s(buffer, "%.3f", duration); +#else + sprintf(buffer, "%.3f", duration); +#endif + return std::string(buffer); + } + + TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config) + :StreamingReporterBase(_config) {} + + std::set TestEventListenerBase::getSupportedVerbosities() { + return { Verbosity::Quiet, Verbosity::Normal, Verbosity::High }; + } + + void TestEventListenerBase::assertionStarting(AssertionInfo const &) {} + + bool TestEventListenerBase::assertionEnded(AssertionStats const &) { + return false; + } + +} // end namespace Catch +// end catch_reporter_bases.cpp +// start catch_reporter_compact.cpp + +namespace { + +#ifdef CATCH_PLATFORM_MAC + const char* failedString() { return "FAILED"; } + const char* passedString() { return "PASSED"; } +#else + const char* failedString() { return "failed"; } + const char* passedString() { return "passed"; } +#endif + + // Colour::LightGrey + Catch::Colour::Code dimColour() { return Catch::Colour::FileName; } + + std::string bothOrAll( std::size_t count ) { + return count == 1 ? std::string() : + count == 2 ? "both " : "all " ; + } + +} // anon namespace + +namespace Catch { +namespace { +// Colour, message variants: +// - white: No tests ran. +// - red: Failed [both/all] N test cases, failed [both/all] M assertions. +// - white: Passed [both/all] N test cases (no assertions). +// - red: Failed N tests cases, failed M assertions. +// - green: Passed [both/all] N tests cases with M assertions. +void printTotals(std::ostream& out, const Totals& totals) { + if (totals.testCases.total() == 0) { + out << "No tests ran."; + } else if (totals.testCases.failed == totals.testCases.total()) { + Colour colour(Colour::ResultError); + const std::string qualify_assertions_failed = + totals.assertions.failed == totals.assertions.total() ? + bothOrAll(totals.assertions.failed) : std::string(); + out << + "Failed " << bothOrAll(totals.testCases.failed) + << pluralise(totals.testCases.failed, "test case") << ", " + "failed " << qualify_assertions_failed << + pluralise(totals.assertions.failed, "assertion") << '.'; + } else if (totals.assertions.total() == 0) { + out << + "Passed " << bothOrAll(totals.testCases.total()) + << pluralise(totals.testCases.total(), "test case") + << " (no assertions)."; + } else if (totals.assertions.failed) { + Colour colour(Colour::ResultError); + out << + "Failed " << pluralise(totals.testCases.failed, "test case") << ", " + "failed " << pluralise(totals.assertions.failed, "assertion") << '.'; + } else { + Colour colour(Colour::ResultSuccess); + out << + "Passed " << bothOrAll(totals.testCases.passed) + << pluralise(totals.testCases.passed, "test case") << + " with " << pluralise(totals.assertions.passed, "assertion") << '.'; + } +} + +// Implementation of CompactReporter formatting +class AssertionPrinter { +public: + AssertionPrinter& operator= (AssertionPrinter const&) = delete; + AssertionPrinter(AssertionPrinter const&) = delete; + AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) + : stream(_stream) + , result(_stats.assertionResult) + , messages(_stats.infoMessages) + , itMessage(_stats.infoMessages.begin()) + , printInfoMessages(_printInfoMessages) {} + + void print() { + printSourceInfo(); + + itMessage = messages.begin(); + + switch (result.getResultType()) { + case ResultWas::Ok: + printResultType(Colour::ResultSuccess, passedString()); + printOriginalExpression(); + printReconstructedExpression(); + if (!result.hasExpression()) + printRemainingMessages(Colour::None); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) + printResultType(Colour::ResultSuccess, failedString() + std::string(" - but was ok")); + else + printResultType(Colour::Error, failedString()); + printOriginalExpression(); + printReconstructedExpression(); + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType(Colour::Error, failedString()); + printIssue("unexpected exception with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::FatalErrorCondition: + printResultType(Colour::Error, failedString()); + printIssue("fatal error condition with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType(Colour::Error, failedString()); + printIssue("expected exception, got none"); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType(Colour::None, "info"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType(Colour::None, "warning"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType(Colour::Error, failedString()); + printIssue("explicitly"); + printRemainingMessages(Colour::None); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType(Colour::Error, "** internal error **"); + break; + } + } + +private: + void printSourceInfo() const { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ':'; + } + + void printResultType(Colour::Code colour, std::string const& passOrFail) const { + if (!passOrFail.empty()) { + { + Colour colourGuard(colour); + stream << ' ' << passOrFail; + } + stream << ':'; + } + } + + void printIssue(std::string const& issue) const { + stream << ' ' << issue; + } + + void printExpressionWas() { + if (result.hasExpression()) { + stream << ';'; + { + Colour colour(dimColour()); + stream << " expression was:"; + } + printOriginalExpression(); + } + } + + void printOriginalExpression() const { + if (result.hasExpression()) { + stream << ' ' << result.getExpression(); + } + } + + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + { + Colour colour(dimColour()); + stream << " for: "; + } + stream << result.getExpandedExpression(); + } + } + + void printMessage() { + if (itMessage != messages.end()) { + stream << " '" << itMessage->message << '\''; + ++itMessage; + } + } + + void printRemainingMessages(Colour::Code colour = dimColour()) { + if (itMessage == messages.end()) + return; + + // using messages.end() directly yields (or auto) compilation error: + std::vector::const_iterator itEnd = messages.end(); + const std::size_t N = static_cast(std::distance(itMessage, itEnd)); + + { + Colour colourGuard(colour); + stream << " with " << pluralise(N, "message") << ':'; + } + + for (; itMessage != itEnd; ) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || itMessage->type != ResultWas::Info) { + stream << " '" << itMessage->message << '\''; + if (++itMessage != itEnd) { + Colour colourGuard(dimColour()); + stream << " and"; + } + } + } + } + +private: + std::ostream& stream; + AssertionResult const& result; + std::vector messages; + std::vector::const_iterator itMessage; + bool printInfoMessages; +}; + +} // anon namespace + + std::string CompactReporter::getDescription() { + return "Reports test results on a single line, suitable for IDEs"; + } + + ReporterPreferences CompactReporter::getPreferences() const { + return m_reporterPrefs; + } + + void CompactReporter::noMatchingTestCases( std::string const& spec ) { + stream << "No test cases matched '" << spec << '\'' << std::endl; + } + + void CompactReporter::assertionStarting( AssertionInfo const& ) {} + + bool CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if( !m_config->includeSuccessfulResults() && result.isOk() ) { + if( result.getResultType() != ResultWas::Warning ) + return false; + printInfoMessages = false; + } + + AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + printer.print(); + + stream << std::endl; + return true; + } + + void CompactReporter::sectionEnded(SectionStats const& _sectionStats) { + if (m_config->showDurations() == ShowDurations::Always) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + } + + void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) { + printTotals( stream, _testRunStats.totals ); + stream << '\n' << std::endl; + StreamingReporterBase::testRunEnded( _testRunStats ); + } + + CompactReporter::~CompactReporter() {} + + CATCH_REGISTER_REPORTER( "compact", CompactReporter ) + +} // end namespace Catch +// end catch_reporter_compact.cpp +// start catch_reporter_console.cpp + +#include +#include + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif + +namespace Catch { + +namespace { + +// Formatter impl for ConsoleReporter +class ConsoleAssertionPrinter { +public: + ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter const&) = delete; + ConsoleAssertionPrinter(ConsoleAssertionPrinter const&) = delete; + ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) + : stream(_stream), + stats(_stats), + result(_stats.assertionResult), + colour(Colour::None), + message(result.getMessage()), + messages(_stats.infoMessages), + printInfoMessages(_printInfoMessages) { + switch (result.getResultType()) { + case ResultWas::Ok: + colour = Colour::Success; + passOrFail = "PASSED"; + //if( result.hasMessage() ) + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) { + colour = Colour::Success; + passOrFail = "FAILED - but was ok"; + } else { + colour = Colour::Error; + passOrFail = "FAILED"; + } + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ThrewException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to unexpected exception with "; + if (_stats.infoMessages.size() == 1) + messageLabel += "message"; + if (_stats.infoMessages.size() > 1) + messageLabel += "messages"; + break; + case ResultWas::FatalErrorCondition: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to a fatal error condition"; + break; + case ResultWas::DidntThrowException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "because no exception was thrown where one was expected"; + break; + case ResultWas::Info: + messageLabel = "info"; + break; + case ResultWas::Warning: + messageLabel = "warning"; + break; + case ResultWas::ExplicitFailure: + passOrFail = "FAILED"; + colour = Colour::Error; + if (_stats.infoMessages.size() == 1) + messageLabel = "explicitly with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "explicitly with messages"; + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + passOrFail = "** internal error **"; + colour = Colour::Error; + break; + } + } + + void print() const { + printSourceInfo(); + if (stats.totals.assertions.total() > 0) { + printResultType(); + printOriginalExpression(); + printReconstructedExpression(); + } else { + stream << '\n'; + } + printMessage(); + } + +private: + void printResultType() const { + if (!passOrFail.empty()) { + Colour colourGuard(colour); + stream << passOrFail << ":\n"; + } + } + void printOriginalExpression() const { + if (result.hasExpression()) { + Colour colourGuard(Colour::OriginalExpression); + stream << " "; + stream << result.getExpressionInMacro(); + stream << '\n'; + } + } + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + stream << "with expansion:\n"; + Colour colourGuard(Colour::ReconstructedExpression); + stream << Column(result.getExpandedExpression()).indent(2) << '\n'; + } + } + void printMessage() const { + if (!messageLabel.empty()) + stream << messageLabel << ':' << '\n'; + for (auto const& msg : messages) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || msg.type != ResultWas::Info) + stream << Column(msg.message).indent(2) << '\n'; + } + } + void printSourceInfo() const { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ": "; + } + + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + Colour::Code colour; + std::string passOrFail; + std::string messageLabel; + std::string message; + std::vector messages; + bool printInfoMessages; +}; + +std::size_t makeRatio(std::size_t number, std::size_t total) { + std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; + return (ratio == 0 && number > 0) ? 1 : ratio; +} + +std::size_t& findMax(std::size_t& i, std::size_t& j, std::size_t& k) { + if (i > j && i > k) + return i; + else if (j > k) + return j; + else + return k; +} + +struct ColumnInfo { + enum Justification { Left, Right }; + std::string name; + int width; + Justification justification; +}; +struct ColumnBreak {}; +struct RowBreak {}; + +class Duration { + enum class Unit { + Auto, + Nanoseconds, + Microseconds, + Milliseconds, + Seconds, + Minutes + }; + static const uint64_t s_nanosecondsInAMicrosecond = 1000; + static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond; + static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond; + static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond; + + uint64_t m_inNanoseconds; + Unit m_units; + +public: + explicit Duration(uint64_t inNanoseconds, Unit units = Unit::Auto) + : m_inNanoseconds(inNanoseconds), + m_units(units) { + if (m_units == Unit::Auto) { + if (m_inNanoseconds < s_nanosecondsInAMicrosecond) + m_units = Unit::Nanoseconds; + else if (m_inNanoseconds < s_nanosecondsInAMillisecond) + m_units = Unit::Microseconds; + else if (m_inNanoseconds < s_nanosecondsInASecond) + m_units = Unit::Milliseconds; + else if (m_inNanoseconds < s_nanosecondsInAMinute) + m_units = Unit::Seconds; + else + m_units = Unit::Minutes; + } + + } + + auto value() const -> double { + switch (m_units) { + case Unit::Microseconds: + return m_inNanoseconds / static_cast(s_nanosecondsInAMicrosecond); + case Unit::Milliseconds: + return m_inNanoseconds / static_cast(s_nanosecondsInAMillisecond); + case Unit::Seconds: + return m_inNanoseconds / static_cast(s_nanosecondsInASecond); + case Unit::Minutes: + return m_inNanoseconds / static_cast(s_nanosecondsInAMinute); + default: + return static_cast(m_inNanoseconds); + } + } + auto unitsAsString() const -> std::string { + switch (m_units) { + case Unit::Nanoseconds: + return "ns"; + case Unit::Microseconds: + return "µs"; + case Unit::Milliseconds: + return "ms"; + case Unit::Seconds: + return "s"; + case Unit::Minutes: + return "m"; + default: + return "** internal error **"; + } + + } + friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& { + return os << duration.value() << " " << duration.unitsAsString(); + } +}; +} // end anon namespace + +class TablePrinter { + std::ostream& m_os; + std::vector m_columnInfos; + std::ostringstream m_oss; + int m_currentColumn = -1; + bool m_isOpen = false; + +public: + TablePrinter( std::ostream& os, std::vector columnInfos ) + : m_os( os ), + m_columnInfos( std::move( columnInfos ) ) {} + + auto columnInfos() const -> std::vector const& { + return m_columnInfos; + } + + void open() { + if (!m_isOpen) { + m_isOpen = true; + *this << RowBreak(); + for (auto const& info : m_columnInfos) + *this << info.name << ColumnBreak(); + *this << RowBreak(); + m_os << Catch::getLineOfChars<'-'>() << "\n"; + } + } + void close() { + if (m_isOpen) { + *this << RowBreak(); + m_os << std::endl; + m_isOpen = false; + } + } + + template + friend TablePrinter& operator << (TablePrinter& tp, T const& value) { + tp.m_oss << value; + return tp; + } + + friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) { + auto colStr = tp.m_oss.str(); + // This takes account of utf8 encodings + auto strSize = Catch::StringRef(colStr).numberOfCharacters(); + tp.m_oss.str(""); + tp.open(); + if (tp.m_currentColumn == static_cast(tp.m_columnInfos.size() - 1)) { + tp.m_currentColumn = -1; + tp.m_os << "\n"; + } + tp.m_currentColumn++; + + auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; + auto padding = (strSize + 2 < static_cast(colInfo.width)) + ? std::string(colInfo.width - (strSize + 2), ' ') + : std::string(); + if (colInfo.justification == ColumnInfo::Left) + tp.m_os << colStr << padding << " "; + else + tp.m_os << padding << colStr << " "; + return tp; + } + + friend TablePrinter& operator << (TablePrinter& tp, RowBreak) { + if (tp.m_currentColumn > 0) { + tp.m_os << "\n"; + tp.m_currentColumn = -1; + } + return tp; + } +}; + +ConsoleReporter::ConsoleReporter(ReporterConfig const& config) + : StreamingReporterBase(config), + m_tablePrinter(new TablePrinter(config.stream(), + { + { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 32, ColumnInfo::Left }, + { "iters", 8, ColumnInfo::Right }, + { "elapsed ns", 14, ColumnInfo::Right }, + { "average", 14, ColumnInfo::Right } + })) {} +ConsoleReporter::~ConsoleReporter() = default; + +std::string ConsoleReporter::getDescription() { + return "Reports test results as plain lines of text"; +} + +void ConsoleReporter::noMatchingTestCases(std::string const& spec) { + stream << "No test cases matched '" << spec << '\'' << std::endl; +} + +void ConsoleReporter::assertionStarting(AssertionInfo const&) {} + +bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + + // Drop out if result was successful but we're not printing them. + if (!includeResults && result.getResultType() != ResultWas::Warning) + return false; + + lazyPrint(); + + ConsoleAssertionPrinter printer(stream, _assertionStats, includeResults); + printer.print(); + stream << std::endl; + return true; +} + +void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) { + m_headerPrinted = false; + StreamingReporterBase::sectionStarting(_sectionInfo); +} +void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) { + m_tablePrinter->close(); + if (_sectionStats.missingAssertions) { + lazyPrint(); + Colour colour(Colour::ResultError); + if (m_sectionStack.size() > 1) + stream << "\nNo assertions in section"; + else + stream << "\nNo assertions in test case"; + stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; + } + if (m_config->showDurations() == ShowDurations::Always) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + if (m_headerPrinted) { + m_headerPrinted = false; + } + StreamingReporterBase::sectionEnded(_sectionStats); +} + +void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { + lazyPrintWithoutClosingBenchmarkTable(); + + auto nameCol = Column( info.name ).width( static_cast( m_tablePrinter->columnInfos()[0].width - 2 ) ); + + bool firstLine = true; + for (auto line : nameCol) { + if (!firstLine) + (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak(); + else + firstLine = false; + + (*m_tablePrinter) << line << ColumnBreak(); + } +} +void ConsoleReporter::benchmarkEnded(BenchmarkStats const& stats) { + Duration average(stats.elapsedTimeInNanoseconds / stats.iterations); + (*m_tablePrinter) + << stats.iterations << ColumnBreak() + << stats.elapsedTimeInNanoseconds << ColumnBreak() + << average << ColumnBreak(); +} + +void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { + m_tablePrinter->close(); + StreamingReporterBase::testCaseEnded(_testCaseStats); + m_headerPrinted = false; +} +void ConsoleReporter::testGroupEnded(TestGroupStats const& _testGroupStats) { + if (currentGroupInfo.used) { + printSummaryDivider(); + stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; + printTotals(_testGroupStats.totals); + stream << '\n' << std::endl; + } + StreamingReporterBase::testGroupEnded(_testGroupStats); +} +void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) { + printTotalsDivider(_testRunStats.totals); + printTotals(_testRunStats.totals); + stream << std::endl; + StreamingReporterBase::testRunEnded(_testRunStats); +} + +void ConsoleReporter::lazyPrint() { + + m_tablePrinter->close(); + lazyPrintWithoutClosingBenchmarkTable(); +} + +void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() { + + if (!currentTestRunInfo.used) + lazyPrintRunInfo(); + if (!currentGroupInfo.used) + lazyPrintGroupInfo(); + + if (!m_headerPrinted) { + printTestCaseAndSectionHeader(); + m_headerPrinted = true; + } +} +void ConsoleReporter::lazyPrintRunInfo() { + stream << '\n' << getLineOfChars<'~'>() << '\n'; + Colour colour(Colour::SecondaryText); + stream << currentTestRunInfo->name + << " is a Catch v" << libraryVersion() << " host application.\n" + << "Run with -? for options\n\n"; + + if (m_config->rngSeed() != 0) + stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; + + currentTestRunInfo.used = true; +} +void ConsoleReporter::lazyPrintGroupInfo() { + if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) { + printClosedHeader("Group: " + currentGroupInfo->name); + currentGroupInfo.used = true; + } +} +void ConsoleReporter::printTestCaseAndSectionHeader() { + assert(!m_sectionStack.empty()); + printOpenHeader(currentTestCaseInfo->name); + + if (m_sectionStack.size() > 1) { + Colour colourGuard(Colour::Headers); + + auto + it = m_sectionStack.begin() + 1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for (; it != itEnd; ++it) + printHeaderString(it->name, 2); + } + + SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; + + if (!lineInfo.empty()) { + stream << getLineOfChars<'-'>() << '\n'; + Colour colourGuard(Colour::FileName); + stream << lineInfo << '\n'; + } + stream << getLineOfChars<'.'>() << '\n' << std::endl; +} + +void ConsoleReporter::printClosedHeader(std::string const& _name) { + printOpenHeader(_name); + stream << getLineOfChars<'.'>() << '\n'; +} +void ConsoleReporter::printOpenHeader(std::string const& _name) { + stream << getLineOfChars<'-'>() << '\n'; + { + Colour colourGuard(Colour::Headers); + printHeaderString(_name); + } +} + +// if string has a : in first line will set indent to follow it on +// subsequent lines +void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t indent) { + std::size_t i = _string.find(": "); + if (i != std::string::npos) + i += 2; + else + i = 0; + stream << Column(_string).indent(indent + i).initialIndent(indent) << '\n'; +} + +struct SummaryColumn { + + SummaryColumn( std::string _label, Colour::Code _colour ) + : label( std::move( _label ) ), + colour( _colour ) {} + SummaryColumn addRow( std::size_t count ) { + ReusableStringStream rss; + rss << count; + std::string row = rss.str(); + for (auto& oldRow : rows) { + while (oldRow.size() < row.size()) + oldRow = ' ' + oldRow; + while (oldRow.size() > row.size()) + row = ' ' + row; + } + rows.push_back(row); + return *this; + } + + std::string label; + Colour::Code colour; + std::vector rows; + +}; + +void ConsoleReporter::printTotals( Totals const& totals ) { + if (totals.testCases.total() == 0) { + stream << Colour(Colour::Warning) << "No tests ran\n"; + } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) { + stream << Colour(Colour::ResultSuccess) << "All tests passed"; + stream << " (" + << pluralise(totals.assertions.passed, "assertion") << " in " + << pluralise(totals.testCases.passed, "test case") << ')' + << '\n'; + } else { + + std::vector columns; + columns.push_back(SummaryColumn("", Colour::None) + .addRow(totals.testCases.total()) + .addRow(totals.assertions.total())); + columns.push_back(SummaryColumn("passed", Colour::Success) + .addRow(totals.testCases.passed) + .addRow(totals.assertions.passed)); + columns.push_back(SummaryColumn("failed", Colour::ResultError) + .addRow(totals.testCases.failed) + .addRow(totals.assertions.failed)); + columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure) + .addRow(totals.testCases.failedButOk) + .addRow(totals.assertions.failedButOk)); + + printSummaryRow("test cases", columns, 0); + printSummaryRow("assertions", columns, 1); + } +} +void ConsoleReporter::printSummaryRow(std::string const& label, std::vector const& cols, std::size_t row) { + for (auto col : cols) { + std::string value = col.rows[row]; + if (col.label.empty()) { + stream << label << ": "; + if (value != "0") + stream << value; + else + stream << Colour(Colour::Warning) << "- none -"; + } else if (value != "0") { + stream << Colour(Colour::LightGrey) << " | "; + stream << Colour(col.colour) + << value << ' ' << col.label; + } + } + stream << '\n'; +} + +void ConsoleReporter::printTotalsDivider(Totals const& totals) { + if (totals.testCases.total() > 0) { + std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total()); + std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total()); + std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total()); + while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)++; + while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)--; + + stream << Colour(Colour::Error) << std::string(failedRatio, '='); + stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '='); + if (totals.testCases.allPassed()) + stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '='); + else + stream << Colour(Colour::Success) << std::string(passedRatio, '='); + } else { + stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '='); + } + stream << '\n'; +} +void ConsoleReporter::printSummaryDivider() { + stream << getLineOfChars<'-'>() << '\n'; +} + +CATCH_REGISTER_REPORTER("console", ConsoleReporter) + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +// end catch_reporter_console.cpp +// start catch_reporter_junit.cpp + +#include +#include +#include +#include + +namespace Catch { + + namespace { + std::string getCurrentTimestamp() { + // Beware, this is not reentrant because of backward compatibility issues + // Also, UTC only, again because of backward compatibility (%z is C++11) + time_t rawtime; + std::time(&rawtime); + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &rawtime); +#else + std::tm* timeInfo; + timeInfo = std::gmtime(&rawtime); +#endif + + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } + + std::string fileNameTag(const std::vector &tags) { + auto it = std::find_if(begin(tags), + end(tags), + [] (std::string const& tag) {return tag.front() == '#'; }); + if (it != tags.end()) + return it->substr(1); + return std::string(); + } + } // anonymous namespace + + JunitReporter::JunitReporter( ReporterConfig const& _config ) + : CumulativeReporterBase( _config ), + xml( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; + } + + JunitReporter::~JunitReporter() {} + + std::string JunitReporter::getDescription() { + return "Reports test results in an XML format that looks like Ant's junitreport target"; + } + + void JunitReporter::noMatchingTestCases( std::string const& /*spec*/ ) {} + + void JunitReporter::testRunStarting( TestRunInfo const& runInfo ) { + CumulativeReporterBase::testRunStarting( runInfo ); + xml.startElement( "testsuites" ); + } + + void JunitReporter::testGroupStarting( GroupInfo const& groupInfo ) { + suiteTimer.start(); + stdOutForSuite.clear(); + stdErrForSuite.clear(); + unexpectedExceptions = 0; + CumulativeReporterBase::testGroupStarting( groupInfo ); + } + + void JunitReporter::testCaseStarting( TestCaseInfo const& testCaseInfo ) { + m_okToFail = testCaseInfo.okToFail(); + } + + bool JunitReporter::assertionEnded( AssertionStats const& assertionStats ) { + if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) + unexpectedExceptions++; + return CumulativeReporterBase::assertionEnded( assertionStats ); + } + + void JunitReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + stdOutForSuite += testCaseStats.stdOut; + stdErrForSuite += testCaseStats.stdErr; + CumulativeReporterBase::testCaseEnded( testCaseStats ); + } + + void JunitReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + double suiteTime = suiteTimer.getElapsedSeconds(); + CumulativeReporterBase::testGroupEnded( testGroupStats ); + writeGroup( *m_testGroups.back(), suiteTime ); + } + + void JunitReporter::testRunEndedCumulative() { + xml.endElement(); + } + + void JunitReporter::writeGroup( TestGroupNode const& groupNode, double suiteTime ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); + TestGroupStats const& stats = groupNode.value; + xml.writeAttribute( "name", stats.groupInfo.name ); + xml.writeAttribute( "errors", unexpectedExceptions ); + xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); + xml.writeAttribute( "tests", stats.totals.assertions.total() ); + xml.writeAttribute( "hostname", "tbd" ); // !TBD + if( m_config->showDurations() == ShowDurations::Never ) + xml.writeAttribute( "time", "" ); + else + xml.writeAttribute( "time", suiteTime ); + xml.writeAttribute( "timestamp", getCurrentTimestamp() ); + + // Write test cases + for( auto const& child : groupNode.children ) + writeTestCase( *child ); + + xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), false ); + xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), false ); + } + + void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) { + TestCaseStats const& stats = testCaseNode.value; + + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert( testCaseNode.children.size() == 1 ); + SectionNode const& rootSection = *testCaseNode.children.front(); + + std::string className = stats.testInfo.className; + + if( className.empty() ) { + className = fileNameTag(stats.testInfo.tags); + if ( className.empty() ) + className = "global"; + } + + if ( !m_config->name().empty() ) + className = m_config->name() + "." + className; + + writeSection( className, "", rootSection ); + } + + void JunitReporter::writeSection( std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode ) { + std::string name = trim( sectionNode.stats.sectionInfo.name ); + if( !rootName.empty() ) + name = rootName + '/' + name; + + if( !sectionNode.assertions.empty() || + !sectionNode.stdOut.empty() || + !sectionNode.stdErr.empty() ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); + if( className.empty() ) { + xml.writeAttribute( "classname", name ); + xml.writeAttribute( "name", "root" ); + } + else { + xml.writeAttribute( "classname", className ); + xml.writeAttribute( "name", name ); + } + xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) ); + + writeAssertions( sectionNode ); + + if( !sectionNode.stdOut.empty() ) + xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); + if( !sectionNode.stdErr.empty() ) + xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); + } + for( auto const& childNode : sectionNode.childSections ) + if( className.empty() ) + writeSection( name, "", *childNode ); + else + writeSection( className, name, *childNode ); + } + + void JunitReporter::writeAssertions( SectionNode const& sectionNode ) { + for( auto const& assertion : sectionNode.assertions ) + writeAssertion( assertion ); + } + + void JunitReporter::writeAssertion( AssertionStats const& stats ) { + AssertionResult const& result = stats.assertionResult; + if( !result.isOk() ) { + std::string elementName; + switch( result.getResultType() ) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + elementName = "failure"; + break; + case ResultWas::ExpressionFailed: + elementName = "failure"; + break; + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; + } + + XmlWriter::ScopedElement e = xml.scopedElement( elementName ); + + xml.writeAttribute( "message", result.getExpandedExpression() ); + xml.writeAttribute( "type", result.getTestMacroName() ); + + ReusableStringStream rss; + if( !result.getMessage().empty() ) + rss << result.getMessage() << '\n'; + for( auto const& msg : stats.infoMessages ) + if( msg.type == ResultWas::Info ) + rss << msg.message << '\n'; + + rss << "at " << result.getSourceInfo(); + xml.writeText( rss.str(), false ); + } + } + + CATCH_REGISTER_REPORTER( "junit", JunitReporter ) + +} // end namespace Catch +// end catch_reporter_junit.cpp +// start catch_reporter_listening.cpp + +#include + +namespace Catch { + + ListeningReporter::ListeningReporter() { + // We will assume that listeners will always want all assertions + m_preferences.shouldReportAllAssertions = true; + } + + void ListeningReporter::addListener( IStreamingReporterPtr&& listener ) { + m_listeners.push_back( std::move( listener ) ); + } + + void ListeningReporter::addReporter(IStreamingReporterPtr&& reporter) { + assert(!m_reporter && "Listening reporter can wrap only 1 real reporter"); + m_reporter = std::move( reporter ); + m_preferences.shouldRedirectStdOut = m_reporter->getPreferences().shouldRedirectStdOut; + } + + ReporterPreferences ListeningReporter::getPreferences() const { + return m_preferences; + } + + std::set ListeningReporter::getSupportedVerbosities() { + return std::set{ }; + } + + void ListeningReporter::noMatchingTestCases( std::string const& spec ) { + for ( auto const& listener : m_listeners ) { + listener->noMatchingTestCases( spec ); + } + m_reporter->noMatchingTestCases( spec ); + } + + void ListeningReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { + for ( auto const& listener : m_listeners ) { + listener->benchmarkStarting( benchmarkInfo ); + } + m_reporter->benchmarkStarting( benchmarkInfo ); + } + void ListeningReporter::benchmarkEnded( BenchmarkStats const& benchmarkStats ) { + for ( auto const& listener : m_listeners ) { + listener->benchmarkEnded( benchmarkStats ); + } + m_reporter->benchmarkEnded( benchmarkStats ); + } + + void ListeningReporter::testRunStarting( TestRunInfo const& testRunInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testRunStarting( testRunInfo ); + } + m_reporter->testRunStarting( testRunInfo ); + } + + void ListeningReporter::testGroupStarting( GroupInfo const& groupInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testGroupStarting( groupInfo ); + } + m_reporter->testGroupStarting( groupInfo ); + } + + void ListeningReporter::testCaseStarting( TestCaseInfo const& testInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testCaseStarting( testInfo ); + } + m_reporter->testCaseStarting( testInfo ); + } + + void ListeningReporter::sectionStarting( SectionInfo const& sectionInfo ) { + for ( auto const& listener : m_listeners ) { + listener->sectionStarting( sectionInfo ); + } + m_reporter->sectionStarting( sectionInfo ); + } + + void ListeningReporter::assertionStarting( AssertionInfo const& assertionInfo ) { + for ( auto const& listener : m_listeners ) { + listener->assertionStarting( assertionInfo ); + } + m_reporter->assertionStarting( assertionInfo ); + } + + // The return value indicates if the messages buffer should be cleared: + bool ListeningReporter::assertionEnded( AssertionStats const& assertionStats ) { + for( auto const& listener : m_listeners ) { + static_cast( listener->assertionEnded( assertionStats ) ); + } + return m_reporter->assertionEnded( assertionStats ); + } + + void ListeningReporter::sectionEnded( SectionStats const& sectionStats ) { + for ( auto const& listener : m_listeners ) { + listener->sectionEnded( sectionStats ); + } + m_reporter->sectionEnded( sectionStats ); + } + + void ListeningReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + for ( auto const& listener : m_listeners ) { + listener->testCaseEnded( testCaseStats ); + } + m_reporter->testCaseEnded( testCaseStats ); + } + + void ListeningReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + for ( auto const& listener : m_listeners ) { + listener->testGroupEnded( testGroupStats ); + } + m_reporter->testGroupEnded( testGroupStats ); + } + + void ListeningReporter::testRunEnded( TestRunStats const& testRunStats ) { + for ( auto const& listener : m_listeners ) { + listener->testRunEnded( testRunStats ); + } + m_reporter->testRunEnded( testRunStats ); + } + + void ListeningReporter::skipTest( TestCaseInfo const& testInfo ) { + for ( auto const& listener : m_listeners ) { + listener->skipTest( testInfo ); + } + m_reporter->skipTest( testInfo ); + } + + bool ListeningReporter::isMulti() const { + return true; + } + +} // end namespace Catch +// end catch_reporter_listening.cpp +// start catch_reporter_xml.cpp + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif + +namespace Catch { + XmlReporter::XmlReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ), + m_xml(_config.stream()) + { + m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; + } + + XmlReporter::~XmlReporter() = default; + + std::string XmlReporter::getDescription() { + return "Reports test results as an XML document"; + } + + std::string XmlReporter::getStylesheetRef() const { + return std::string(); + } + + void XmlReporter::writeSourceInfo( SourceLineInfo const& sourceInfo ) { + m_xml + .writeAttribute( "filename", sourceInfo.file ) + .writeAttribute( "line", sourceInfo.line ); + } + + void XmlReporter::noMatchingTestCases( std::string const& s ) { + StreamingReporterBase::noMatchingTestCases( s ); + } + + void XmlReporter::testRunStarting( TestRunInfo const& testInfo ) { + StreamingReporterBase::testRunStarting( testInfo ); + std::string stylesheetRef = getStylesheetRef(); + if( !stylesheetRef.empty() ) + m_xml.writeStylesheetRef( stylesheetRef ); + m_xml.startElement( "Catch" ); + if( !m_config->name().empty() ) + m_xml.writeAttribute( "name", m_config->name() ); + if( m_config->rngSeed() != 0 ) + m_xml.scopedElement( "Randomness" ) + .writeAttribute( "seed", m_config->rngSeed() ); + } + + void XmlReporter::testGroupStarting( GroupInfo const& groupInfo ) { + StreamingReporterBase::testGroupStarting( groupInfo ); + m_xml.startElement( "Group" ) + .writeAttribute( "name", groupInfo.name ); + } + + void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) { + StreamingReporterBase::testCaseStarting(testInfo); + m_xml.startElement( "TestCase" ) + .writeAttribute( "name", trim( testInfo.name ) ) + .writeAttribute( "description", testInfo.description ) + .writeAttribute( "tags", testInfo.tagsAsString() ); + + writeSourceInfo( testInfo.lineInfo ); + + if ( m_config->showDurations() == ShowDurations::Always ) + m_testCaseTimer.start(); + m_xml.ensureTagClosed(); + } + + void XmlReporter::sectionStarting( SectionInfo const& sectionInfo ) { + StreamingReporterBase::sectionStarting( sectionInfo ); + if( m_sectionDepth++ > 0 ) { + m_xml.startElement( "Section" ) + .writeAttribute( "name", trim( sectionInfo.name ) ); + writeSourceInfo( sectionInfo.lineInfo ); + m_xml.ensureTagClosed(); + } + } + + void XmlReporter::assertionStarting( AssertionInfo const& ) { } + + bool XmlReporter::assertionEnded( AssertionStats const& assertionStats ) { + + AssertionResult const& result = assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + + if( includeResults || result.getResultType() == ResultWas::Warning ) { + // Print any info messages in tags. + for( auto const& msg : assertionStats.infoMessages ) { + if( msg.type == ResultWas::Info && includeResults ) { + m_xml.scopedElement( "Info" ) + .writeText( msg.message ); + } else if ( msg.type == ResultWas::Warning ) { + m_xml.scopedElement( "Warning" ) + .writeText( msg.message ); + } + } + } + + // Drop out if result was successful but we're not printing them. + if( !includeResults && result.getResultType() != ResultWas::Warning ) + return true; + + // Print the expression if there is one. + if( result.hasExpression() ) { + m_xml.startElement( "Expression" ) + .writeAttribute( "success", result.succeeded() ) + .writeAttribute( "type", result.getTestMacroName() ); + + writeSourceInfo( result.getSourceInfo() ); + + m_xml.scopedElement( "Original" ) + .writeText( result.getExpression() ); + m_xml.scopedElement( "Expanded" ) + .writeText( result.getExpandedExpression() ); + } + + // And... Print a result applicable to each result type. + switch( result.getResultType() ) { + case ResultWas::ThrewException: + m_xml.startElement( "Exception" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + case ResultWas::FatalErrorCondition: + m_xml.startElement( "FatalErrorCondition" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + case ResultWas::Info: + m_xml.scopedElement( "Info" ) + .writeText( result.getMessage() ); + break; + case ResultWas::Warning: + // Warning will already have been written + break; + case ResultWas::ExplicitFailure: + m_xml.startElement( "Failure" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + default: + break; + } + + if( result.hasExpression() ) + m_xml.endElement(); + + return true; + } + + void XmlReporter::sectionEnded( SectionStats const& sectionStats ) { + StreamingReporterBase::sectionEnded( sectionStats ); + if( --m_sectionDepth > 0 ) { + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); + e.writeAttribute( "successes", sectionStats.assertions.passed ); + e.writeAttribute( "failures", sectionStats.assertions.failed ); + e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); + + m_xml.endElement(); + } + } + + void XmlReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + StreamingReporterBase::testCaseEnded( testCaseStats ); + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); + e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); + + if( !testCaseStats.stdOut.empty() ) + m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); + if( !testCaseStats.stdErr.empty() ) + m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); + + m_xml.endElement(); + } + + void XmlReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + StreamingReporterBase::testGroupEnded( testGroupStats ); + // TODO: Check testGroupStats.aborting and act accordingly. + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) + .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + + void XmlReporter::testRunEnded( TestRunStats const& testRunStats ) { + StreamingReporterBase::testRunEnded( testRunStats ); + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testRunStats.totals.assertions.passed ) + .writeAttribute( "failures", testRunStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + + CATCH_REGISTER_REPORTER( "xml", XmlReporter ) + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +// end catch_reporter_xml.cpp + +namespace Catch { + LeakDetector leakDetector; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_impl.hpp +#endif + +#ifdef CATCH_CONFIG_MAIN +// start catch_default_main.hpp + +#ifndef __OBJC__ + +#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) +// Standard C/C++ Win32 Unicode wmain entry point +extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) { +#else +// Standard C/C++ main entry point +int main (int argc, char * argv[]) { +#endif + + return Catch::Session().run( argc, argv ); +} + +#else // __OBJC__ + +// Objective-C entry point +int main (int argc, char * const argv[]) { +#if !CATCH_ARC_ENABLED + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; +#endif + + Catch::registerTestMethods(); + int result = Catch::Session().run( argc, (char**)argv ); + +#if !CATCH_ARC_ENABLED + [pool drain]; +#endif + + return result; +} + +#endif // __OBJC__ + +// end catch_default_main.hpp +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) + +#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED +# undef CLARA_CONFIG_MAIN +#endif + +#if !defined(CATCH_CONFIG_DISABLE) +////// +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) + +#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", __VA_ARGS__ ) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) +#endif// CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) + +#define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +#define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) + +#define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", __VA_ARGS__ ) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) + +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) +#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE",__VA_ARGS__ ) + +#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) +#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) +#define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) +#define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) +#define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#else +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#endif + +#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) +#define CATCH_STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__ , #__VA_ARGS__ ); CATCH_SUCCEED( #__VA_ARGS__ ) +#define CATCH_STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ ) +#else +#define CATCH_STATIC_REQUIRE( ... ) CATCH_REQUIRE( __VA_ARGS__ ) +#define CATCH_STATIC_REQUIRE_FALSE( ... ) CATCH_REQUIRE_FALSE( __VA_ARGS__ ) +#endif + +// "BDD-style" convenience wrappers +#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) +#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) +#define CATCH_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) +#define CATCH_AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) +#define CATCH_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) +#define CATCH_AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) +#define CATCH_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) +#define CATCH_AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) + +#define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) + +#define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +#define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) + +#define CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) + +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) +#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE",__VA_ARGS__ ) + +#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) +#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) +#define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) +#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) +#define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#else +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#endif + +#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) +#define STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ ) +#define STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" ) +#else +#define STATIC_REQUIRE( ... ) REQUIRE( __VA_ARGS__ ) +#define STATIC_REQUIRE_FALSE( ... ) REQUIRE_FALSE( __VA_ARGS__ ) +#endif + +#endif + +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) + +// "BDD-style" convenience wrappers +#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) +#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) + +#define GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) +#define AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) +#define WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) +#define AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) +#define THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) +#define AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) + +using Catch::Detail::Approx; + +#else // CATCH_CONFIG_DISABLE + +////// +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#define CATCH_REQUIRE( ... ) (void)(0) +#define CATCH_REQUIRE_FALSE( ... ) (void)(0) + +#define CATCH_REQUIRE_THROWS( ... ) (void)(0) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif// CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_REQUIRE_NOTHROW( ... ) (void)(0) + +#define CATCH_CHECK( ... ) (void)(0) +#define CATCH_CHECK_FALSE( ... ) (void)(0) +#define CATCH_CHECKED_IF( ... ) if (__VA_ARGS__) +#define CATCH_CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) +#define CATCH_CHECK_NOFAIL( ... ) (void)(0) + +#define CATCH_CHECK_THROWS( ... ) (void)(0) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW( ... ) (void)(0) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THAT( arg, matcher ) (void)(0) + +#define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define CATCH_INFO( msg ) (void)(0) +#define CATCH_WARN( msg ) (void)(0) +#define CATCH_CAPTURE( msg ) (void)(0) + +#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_METHOD_AS_TEST_CASE( method, ... ) +#define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) +#define CATCH_SECTION( ... ) +#define CATCH_DYNAMIC_SECTION( ... ) +#define CATCH_FAIL( ... ) (void)(0) +#define CATCH_FAIL_CHECK( ... ) (void)(0) +#define CATCH_SUCCEED( ... ) (void)(0) + +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) +#else +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) ) +#endif + +// "BDD-style" convenience wrappers +#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) +#define CATCH_GIVEN( desc ) +#define CATCH_AND_GIVEN( desc ) +#define CATCH_WHEN( desc ) +#define CATCH_AND_WHEN( desc ) +#define CATCH_THEN( desc ) +#define CATCH_AND_THEN( desc ) + +#define CATCH_STATIC_REQUIRE( ... ) (void)(0) +#define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0) + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE( ... ) (void)(0) +#define REQUIRE_FALSE( ... ) (void)(0) + +#define REQUIRE_THROWS( ... ) (void)(0) +#define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) +#define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define REQUIRE_NOTHROW( ... ) (void)(0) + +#define CHECK( ... ) (void)(0) +#define CHECK_FALSE( ... ) (void)(0) +#define CHECKED_IF( ... ) if (__VA_ARGS__) +#define CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) +#define CHECK_NOFAIL( ... ) (void)(0) + +#define CHECK_THROWS( ... ) (void)(0) +#define CHECK_THROWS_AS( expr, exceptionType ) (void)(0) +#define CHECK_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW( ... ) (void)(0) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THAT( arg, matcher ) (void)(0) + +#define REQUIRE_THAT( arg, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define INFO( msg ) (void)(0) +#define WARN( msg ) (void)(0) +#define CAPTURE( msg ) (void)(0) + +#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define METHOD_AS_TEST_CASE( method, ... ) +#define REGISTER_TEST_CASE( Function, ... ) (void)(0) +#define SECTION( ... ) +#define DYNAMIC_SECTION( ... ) +#define FAIL( ... ) (void)(0) +#define FAIL_CHECK( ... ) (void)(0) +#define SUCCEED( ... ) (void)(0) +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) +#else +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) ) +#endif + +#define STATIC_REQUIRE( ... ) (void)(0) +#define STATIC_REQUIRE_FALSE( ... ) (void)(0) + +#endif + +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) + +// "BDD-style" convenience wrappers +#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) ) +#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) + +#define GIVEN( desc ) +#define AND_GIVEN( desc ) +#define WHEN( desc ) +#define AND_WHEN( desc ) +#define THEN( desc ) +#define AND_THEN( desc ) + +using Catch::Detail::Approx; + +#endif + +#endif // ! CATCH_CONFIG_IMPL_ONLY + +// start catch_reenable_warnings.h + + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(pop) +# else +# pragma clang diagnostic pop +# endif +#elif defined __GNUC__ +# pragma GCC diagnostic pop +#endif + +// end catch_reenable_warnings.h +// end catch.hpp +#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED diff --git a/src/lib/data/NodeType.cpp b/src/lib/data/NodeType.cpp index 07787d71..61e845e6 100644 --- a/src/lib/data/NodeType.cpp +++ b/src/lib/data/NodeType.cpp @@ -159,6 +159,11 @@ bool NodeType::operator==(const NodeType& o) const return m_type == o.m_type; } +bool NodeType::operator!=(const NodeType& o) const +{ + return !operator==(o); +} + bool NodeType::operator<(const NodeType& o) const { return m_type < o.m_type; diff --git a/src/lib/data/NodeType.h b/src/lib/data/NodeType.h index c48dadcb..d42ca653 100644 --- a/src/lib/data/NodeType.h +++ b/src/lib/data/NodeType.h @@ -88,6 +88,7 @@ public: NodeType(Type type); bool operator==(const NodeType& o) const; + bool operator!=(const NodeType& o) const; bool operator<(const NodeType& o) const; Type getType() const; diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 9f7c0e9a..beb1325b 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -5,43 +5,41 @@ add_files( helper/TestFileRegister.h helper/TestIntermediateStorage.h - TestSuiteFixture.cpp - TestSuiteFixture.h - - CommandlineTestSuite.h - ConfigManagerTestSuite.h - CxxIncludeProcessingTestSuite.h - CxxIndexSampleProjectsTestSuite.h - CxxParserTestSuite.h - CxxTypeNameTestSuite.h - FileManagerTestSuite.h - FilePathFilterTestSuite.h - FilePathTestSuite.h - FileSystemTestSuite.h - GraphTestSuite.h - LogManagerTestSuite.h - LowMemoryStringMapTestSuite.h - MatrixBaseTestSuite.h - MessageQueueTestSuite.h - NetworkProtocolHelperTestSuite.h - RefreshInfoGeneratorTestSuite.h - SearchIndexTestSuite.h - SettingsMigratorTestSuite.h - SettingsTestSuite.h - SharedMemoryTestSuite.h - SourceLocationCollectionTestSuite.h - SqliteBookmarkStorageTestSuite.h - SqliteIndexStorageTestSuite.h - StorageTestSuite.h - TaskSchedulerTestSuite.h - TextAccessTestSuite.h - UtilityMavenTestSuite.h - UtilityStringTestSuite.h - UtilityTestSuite.h - Vector2TestSuite.h - -# Java tests need to be executed last because of some linux related issues. - JavaParserTestSuite.h - JavaIndexSampleProjectsTestSuite.h - SourceGroupTestSuite.h + test_main.cpp + + CommandlineTestSuite.cpp + ConfigManagerTestSuite.cpp + CxxIncludeProcessingTestSuite.cpp + CxxIndexSampleProjectsTestSuite.cpp + CxxParserTestSuite.cpp + CxxTypeNameTestSuite.cpp + FileManagerTestSuite.cpp + FilePathFilterTestSuite.cpp + FilePathTestSuite.cpp + FileSystemTestSuite.cpp + GraphTestSuite.cpp + JavaIndexSampleProjectsTestSuite.cpp + JavaParserTestSuite.cpp + LogManagerTestSuite.cpp + LowMemoryStringMapTestSuite.cpp + MatrixBaseTestSuite.cpp + MatrixDynamicBaseTestSuite.cpp + MessageQueueTestSuite.cpp + NetworkProtocolHelperTestSuite.cpp + RefreshInfoGeneratorTestSuite.cpp + SearchIndexTestSuite.cpp + SettingsMigratorTestSuite.cpp + SettingsTestSuite.cpp + SharedMemoryTestSuite.cpp + SourceGroupTestSuite.cpp + SourceLocationCollectionTestSuite.cpp + SqliteBookmarkStorageTestSuite.cpp + SqliteIndexStorageTestSuite.cpp + StorageTestSuite.cpp + TaskSchedulerTestSuite.cpp + TextAccessTestSuite.cpp + UtilityMavenTestSuite.cpp + UtilityStringTestSuite.cpp + UtilityTestSuite.cpp + Vector2TestSuite.cpp ) diff --git a/src/test/CommandlineTestSuite.h b/src/test/CommandlineTestSuite.cpp similarity index 61% rename from src/test/CommandlineTestSuite.h rename to src/test/CommandlineTestSuite.cpp index 4d85dc79..c869cc6c 100644 --- a/src/test/CommandlineTestSuite.h +++ b/src/test/CommandlineTestSuite.cpp @@ -1,4 +1,4 @@ -#include +#include "catch.hpp" #include "CommandLineParser.h" #include "ApplicationSettings.h" @@ -7,21 +7,12 @@ #include #include -class CommandlineTestSuite: public CxxTest::TestSuite +TEST_CASE("command line") { -public: - void setUp() - { - m_appSettingsPath = ApplicationSettings::getInstance()->getFilePath(); - ApplicationSettings::getInstance()->load(FilePath(L"data/CommandlineTestSuite/settings.xml")); - } + FilePath appSettingsPath = ApplicationSettings::getInstance()->getFilePath(); + ApplicationSettings::getInstance()->load(FilePath(L"data/CommandlineTestSuite/settings.xml")); - void tearDown() - { - ApplicationSettings::getInstance()->load(m_appSettingsPath); - } - - void test_commandline_version() + SECTION("commandline version") { std::vector args({"--version", "help"}); @@ -34,15 +25,15 @@ public: std::cout.rdbuf( oldBuf ); - TS_ASSERT_EQUALS(redStream.str(), "Sourcetrail Version 2016.1\n"); + REQUIRE(redStream.str() == "Sourcetrail Version 2016.1\n"); } - void test_command_config_help() + SECTION("command config help") { } - void test_command_config_filepathVector() + SECTION("command config filepathVector") { std::vector args( { @@ -60,12 +51,12 @@ public: parser.parse(); std::vector paths = ApplicationSettings::getInstance()->getHeaderSearchPaths(); - TS_ASSERT_EQUALS(paths[0].wstr(), L"/usr") - TS_ASSERT_EQUALS(paths[1].wstr(), L"/usr/share/include") - TS_ASSERT_EQUALS(paths[2].wstr(), L"/opt/test/include") + REQUIRE(paths[0].wstr() == L"/usr"); + REQUIRE(paths[1].wstr() == L"/usr/share/include"); + REQUIRE(paths[2].wstr() == L"/opt/test/include"); } - void test_command_config_string_filepath_option() + SECTION("command config string filepath option") { std::vector args( { @@ -84,11 +75,11 @@ public: std::cout.rdbuf( oldBuf ); FilePath path = ApplicationSettings::getInstance()->getMavenPath(); - TS_ASSERT_EQUALS( path.wstr(), L"/opt/testpath/mvn") + REQUIRE( path.wstr() == L"/opt/testpath/mvn"); } - void test_command_config_filepathVector_comma_separated() + SECTION("command config filepathVector comma separated") { std::vector args( { @@ -102,14 +93,14 @@ public: parser.parse(); std::vector paths = ApplicationSettings::getInstance()->getHeaderSearchPaths(); - TS_ASSERT_EQUALS( paths[0].wstr(), L"/usr") - TS_ASSERT_EQUALS( paths[1].wstr(), L"/usr/include") - TS_ASSERT_EQUALS( paths[2].wstr(), L"/include") - TS_ASSERT_EQUALS( paths[3].wstr(), L"/opt/include") + REQUIRE( paths[0].wstr() == L"/usr"); + REQUIRE( paths[1].wstr() == L"/usr/include"); + REQUIRE( paths[2].wstr() == L"/include"); + REQUIRE( paths[3].wstr() == L"/opt/include"); } - void test_command_config_bool_options() + SECTION("command config bool options") { std::vector args( { @@ -123,7 +114,7 @@ public: parser.parse(); bool processes = ApplicationSettings::getInstance()->getMultiProcessIndexingEnabled(); - TS_ASSERT_EQUALS( processes, 0); + REQUIRE( processes == 0); std::vector args1( { @@ -136,9 +127,8 @@ public: parser.parse(); processes = ApplicationSettings::getInstance()->getMultiProcessIndexingEnabled(); - TS_ASSERT_EQUALS( processes, 1); + REQUIRE( processes == 1); } -private: - FilePath m_appSettingsPath; -}; + ApplicationSettings::getInstance()->load(appSettingsPath); +} diff --git a/src/test/ConfigManagerTestSuite.cpp b/src/test/ConfigManagerTestSuite.cpp new file mode 100644 index 00000000..f7e958e7 --- /dev/null +++ b/src/test/ConfigManagerTestSuite.cpp @@ -0,0 +1,172 @@ +#include "catch.hpp" + +#include "ConfigManager.h" +#include "TextAccess.h" + +namespace +{ + std::shared_ptr getConfigTextAccess() + { + std::string text = + "\n" + "\n" + " \n" + " \n" + " 0\n" + " 1\n" + " 42\n" + " \n" + " \n" + " \n" + " 4\n" + " 2\n" + " 5\n" + " 8\n" + " \n" + "\n"; + return TextAccess::createFromString(text); + } +} + +TEST_CASE("config manager returns true when key is found") +{ + std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); + + float value; + bool success = config->getValue("path/to/single_value", value); + + REQUIRE(success); +} + + +TEST_CASE("config manager returns false when key is not found") +{ + std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); + + float value; + bool success = config->getValue("path/to/nowhere", value); + + REQUIRE(!success); +} + + +TEST_CASE("config manager returns correct string for key") +{ + std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); + + std::wstring value; + config->getValue("path/to/single_value", value); + + REQUIRE(L"42" == value); +} + + +TEST_CASE("config manager returns correct float for key") +{ + std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); + + float value; + config->getValue("path/to/single_value", value); + + REQUIRE(value == Approx(42.0f)); +} + +TEST_CASE("config manager returns correct bool for key if value is true") +{ + std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); + + float value; + bool success(config->getValue("path/to/bool_that_is_true", value)); + + REQUIRE(success); + REQUIRE(value); +} + +TEST_CASE("config manager returns correct bool for key if value is false") +{ + std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); + + float value; + bool success(config->getValue("path/to/bool_that_is_false", value)); + + REQUIRE(success); + REQUIRE(!value); +} + +TEST_CASE("config manager adds new key when empty") +{ + std::shared_ptr config = ConfigManager::createEmpty(); + + config->setValue("path/to/true_bool", true); + + bool value = false; + bool success(config->getValue("path/to/true_bool", value)); + + REQUIRE(success); + REQUIRE(value); +} + +TEST_CASE("config manager adds new key when not empty") +{ + std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); + + config->setValue("path/to/true_bool", true); + + bool value = false; + bool success(config->getValue("path/to/true_bool", value)); + + REQUIRE(success); + REQUIRE(value); +} + +TEST_CASE("config manager returns correct list for key") +{ + std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); + + std::vector values; + + bool success(config->getValues("paths/path", values)); + + REQUIRE(success); + REQUIRE(values.size() == 3); + REQUIRE(values[0] == 2); + REQUIRE(values[1] == 5); + REQUIRE(values[2] == 8); +} + +TEST_CASE("config manager save and load configuration and compare") +{ + const FilePath path(L"data/ConfigManagerTestSuite/temp.xml"); + + std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); + config->save(path.str()); + std::shared_ptr config2 = ConfigManager::createAndLoad(TextAccess::createFromFile(path)); + REQUIRE(config->toString() == config2->toString()); +} + +TEST_CASE("config manager loads special character") +{ + std::shared_ptr config = ConfigManager::createAndLoad( + TextAccess::createFromFile(FilePath(L"data/ConfigManagerTestSuite/test_data.xml")) + ); + std::wstring loadedSpecialCharacter; + config->getValue("path/to/special_character", loadedSpecialCharacter); + + REQUIRE(loadedSpecialCharacter.size() == 1); + REQUIRE(loadedSpecialCharacter[0] == wchar_t(252));; // special character needs to be encoded as ASCII code because + // otherwise python and cxx compiler may be complaining +} + +TEST_CASE("config manager save and load special character and compare") +{ + const FilePath path(L"data/ConfigManagerTestSuite/temp.xml"); + std::wstring specialCharacter; + specialCharacter.push_back(wchar_t(252)); + + std::shared_ptr config = ConfigManager::createEmpty(); + config->setValue("path/to/special_character", specialCharacter); + config->save(path.str()); + + std::shared_ptr config2 = ConfigManager::createAndLoad(TextAccess::createFromFile(path)); + REQUIRE(config->toString() == config2->toString()); +} diff --git a/src/test/ConfigManagerTestSuite.h b/src/test/ConfigManagerTestSuite.h deleted file mode 100644 index fc607abb..00000000 --- a/src/test/ConfigManagerTestSuite.h +++ /dev/null @@ -1,174 +0,0 @@ -#include - -#include "ConfigManager.h" -#include "TextAccess.h" - -class ConfigManagerTestSuite: public CxxTest::TestSuite -{ -public: - void test_config_manager_returns_true_when_key_is_found() - { - std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); - - float value; - bool success = config->getValue("path/to/single_value", value); - - TS_ASSERT(success); - } - - - void test_config_manager_returns_false_when_key_is_not_found() - { - std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); - - float value; - bool success = config->getValue("path/to/nowhere", value); - - TS_ASSERT(!success); - } - - - void test_config_manager_returns_correct_string_for_key() - { - std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); - - std::wstring value; - config->getValue("path/to/single_value", value); - - TS_ASSERT_EQUALS(L"42", value); - } - - - void test_config_manager_returns_correct_float_for_key() - { - std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); - - float value; - config->getValue("path/to/single_value", value); - - TS_ASSERT_DELTA(42.0f, value, 0.0001f); - } - - void test_config_manager_returns_correct_bool_for_key_if_value_is_true() - { - std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); - - float value; - bool success(config->getValue("path/to/bool_that_is_true", value)); - - TS_ASSERT(success); - TS_ASSERT(value); - } - - void test_config_manager_returns_correct_bool_for_key_if_value_is_false() - { - std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); - - float value; - bool success(config->getValue("path/to/bool_that_is_false", value)); - - TS_ASSERT(success); - TS_ASSERT(!value); - } - - void test_config_manager_adds_new_key_when_empty() - { - std::shared_ptr config = ConfigManager::createEmpty(); - - config->setValue("path/to/true_bool", true); - - bool value = false; - bool success(config->getValue("path/to/true_bool", value)); - - TS_ASSERT(success); - TS_ASSERT(value); - } - - void test_config_manager_adds_new_key_when_not_empty() - { - std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); - - config->setValue("path/to/true_bool", true); - - bool value = false; - bool success(config->getValue("path/to/true_bool", value)); - - TS_ASSERT(success); - TS_ASSERT(value); - } - - void test_config_manager_returns_correct_list_for_key() - { - std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); - - std::vector values; - - bool success(config->getValues("paths/path", values)); - - TS_ASSERT(success); - TS_ASSERT_EQUALS(values.size(), 3); - TS_ASSERT_EQUALS(values[0], 2); - TS_ASSERT_EQUALS(values[1], 5); - TS_ASSERT_EQUALS(values[2], 8); - } - - void test_config_manager_save_and_load_configuration_and_compare() - { - const FilePath path(L"data/ConfigManagerTestSuite/temp.xml"); - - std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); - config->save(path.str()); - std::shared_ptr config2 = ConfigManager::createAndLoad(TextAccess::createFromFile(path)); - TS_ASSERT_EQUALS(config->toString(), config2->toString()); - } - - void test_config_manager_loads_special_character() - { - std::shared_ptr config = ConfigManager::createAndLoad( - TextAccess::createFromFile(FilePath(L"data/ConfigManagerTestSuite/test_data.xml")) - ); - std::wstring loadedSpecialCharacter; - config->getValue("path/to/special_character", loadedSpecialCharacter); - - TS_ASSERT_EQUALS(loadedSpecialCharacter.size(), 1); - TS_ASSERT_EQUALS(loadedSpecialCharacter[0], wchar_t(252)); // special character needs to be encoded as ASCII code because - // otherwise python and cxx compiler may be complaining - } - - void test_config_manager_save_and_load_special_character_and_compare() - { - const FilePath path(L"data/ConfigManagerTestSuite/temp.xml"); - std::wstring specialCharacter; - specialCharacter.push_back(wchar_t(252)); - - std::shared_ptr config = ConfigManager::createEmpty(); - config->setValue("path/to/special_character", specialCharacter); - config->save(path.str()); - - std::shared_ptr config2 = ConfigManager::createAndLoad(TextAccess::createFromFile(path)); - TS_ASSERT_EQUALS(config->toString(), config2->toString()); - } - -private: - std::shared_ptr getConfigTextAccess() - { - std::string text = - "\n" - "\n" - " \n" - " \n" - " 0\n" - " 1\n" - " 42\n" - " \n" - " \n" - " \n" - " 4\n" - " 2\n" - " 5\n" - " 8\n" - " \n" - "\n"; - return TextAccess::createFromString(text); - } -}; diff --git a/src/test/CxxIncludeProcessingTestSuite.cpp b/src/test/CxxIncludeProcessingTestSuite.cpp new file mode 100644 index 00000000..54d46894 --- /dev/null +++ b/src/test/CxxIncludeProcessingTestSuite.cpp @@ -0,0 +1,145 @@ +#include "catch.hpp" + +#include "TextAccess.h" +#include "IncludeDirective.h" +#include "IncludeProcessing.h" +#include "utility.h" + +TEST_CASE("include detection finds include with quotes") +{ + std::vector includeDirectives = IncludeProcessing::getIncludeDirectives(TextAccess::createFromString( + "#include \"foo.h\"", + FilePath(L"foo.cpp") + )); + + REQUIRE(!includeDirectives.empty()); + + if (!includeDirectives.empty()) + { + REQUIRE(L"foo.h" == includeDirectives.front().getIncludedFile().wstr()); + REQUIRE(L"foo.cpp" == includeDirectives.front().getIncludingFile().wstr()); + } +} + +TEST_CASE("include detection finds include with angle brackets") +{ + std::vector includeDirectives = IncludeProcessing::getIncludeDirectives(TextAccess::createFromString( + "#include ", + FilePath(L"foo.cpp") + )); + + REQUIRE(!includeDirectives.empty()); + + if (!includeDirectives.empty()) + { + REQUIRE(L"foo.h" == includeDirectives.front().getIncludedFile().wstr()); + REQUIRE(L"foo.cpp" == includeDirectives.front().getIncludingFile().wstr()); + } +} +TEST_CASE("include detection finds include with quotes and space before keyword") +{ + std::vector includeDirectives = IncludeProcessing::getIncludeDirectives(TextAccess::createFromString( + "# include \"foo.h\"", + FilePath(L"foo.cpp") + )); + + REQUIRE(!includeDirectives.empty()); + + if (!includeDirectives.empty()) + { + REQUIRE(L"foo.h" == includeDirectives.front().getIncludedFile().wstr()); + REQUIRE(L"foo.cpp" == includeDirectives.front().getIncludingFile().wstr()); + } +} + +TEST_CASE("include detection does not find include in empty file") +{ + REQUIRE(IncludeProcessing::getIncludeDirectives(TextAccess::createFromString("")).empty()); +} + +TEST_CASE("include detection does not find include in file without preprocessor directive") +{ + REQUIRE(IncludeProcessing::getIncludeDirectives(TextAccess::createFromString("foo")).empty()); +} + +TEST_CASE("include detection does not find include in file without include preprocessor directive") +{ + REQUIRE(IncludeProcessing::getIncludeDirectives(TextAccess::createFromString("#ifdef xx\n#endif")).empty()); +} + +TEST_CASE("header search path detection does not find path relative to including file") +{ + std::vector headerSearchDirectories = utility::toVector(IncludeProcessing::getHeaderSearchDirectories( + { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_does_not_find_path_relative_to_including_file/a.cpp") }, + { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_does_not_find_path_relative_to_including_file") }, + { }, + 1, [](float) {} + )); + + REQUIRE(headerSearchDirectories.empty()); +} + +TEST_CASE("header search path detection finds path inside sub directory") +{ + std::vector headerSearchDirectories = utility::toVector(IncludeProcessing::getHeaderSearchDirectories( + { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_inside_sub_directory/a.cpp") }, + { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_inside_sub_directory") }, + {}, + 1, [](float) {} + )); + + REQUIRE(utility::containsElement( + headerSearchDirectories, + FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_inside_sub_directory/include").makeAbsolute() + )); +} + +TEST_CASE("header search path detection finds path relative to sub directory") +{ + std::vector headerSearchDirectories = utility::toVector(IncludeProcessing::getHeaderSearchDirectories( + { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_relative_to_sub_directory/a.cpp") }, + { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_relative_to_sub_directory") }, + {}, + 1, [](float) {} + )); + + REQUIRE(utility::containsElement( + headerSearchDirectories, + FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_relative_to_sub_directory/include").makeAbsolute() + )); +} + +TEST_CASE("header search path detection finds path included in header search path") +{ + std::vector headerSearchDirectories = utility::toVector(IncludeProcessing::getHeaderSearchDirectories( + { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_included_in_header_search_path/a.cpp") }, + { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_included_in_header_search_path/include_b") }, + { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_included_in_header_search_path/include_a") }, + 1, [](float) {} + )); + + + REQUIRE(utility::containsElement( + headerSearchDirectories, + FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_included_in_header_search_path/include_b").makeAbsolute() + )); +} + +TEST_CASE("header search path detection finds path included in future header search path") +{ + std::vector headerSearchDirectories = utility::toVector(IncludeProcessing::getHeaderSearchDirectories( + { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_included_in_future_header_search_path/a.cpp") }, + { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_included_in_future_header_search_path") }, + { }, + 1, [](float) {} + )); + + REQUIRE(utility::containsElement( + headerSearchDirectories, + FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_included_in_future_header_search_path/include_a").makeAbsolute() + )); + REQUIRE(utility::containsElement( + headerSearchDirectories, + FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_included_in_future_header_search_path/include_b").makeAbsolute() + )); +} diff --git a/src/test/CxxIncludeProcessingTestSuite.h b/src/test/CxxIncludeProcessingTestSuite.h deleted file mode 100644 index 33721fc6..00000000 --- a/src/test/CxxIncludeProcessingTestSuite.h +++ /dev/null @@ -1,149 +0,0 @@ -#include - -#include "TextAccess.h" -#include "IncludeDirective.h" -#include "IncludeProcessing.h" -#include "utility.h" - -class CxxIncludeProcessingTestSuite: public CxxTest::TestSuite -{ -public: - void test_include_detection_finds_include_with_quotes() - { - std::vector includeDirectives = IncludeProcessing::getIncludeDirectives(TextAccess::createFromString( - "#include \"foo.h\"", - FilePath(L"foo.cpp") - )); - - TS_ASSERT(!includeDirectives.empty()); - - if (!includeDirectives.empty()) - { - TS_ASSERT_EQUALS(L"foo.h", includeDirectives.front().getIncludedFile().wstr()); - TS_ASSERT_EQUALS(L"foo.cpp", includeDirectives.front().getIncludingFile().wstr()); - } - } - - void test_include_detection_finds_include_with_angle_brackets() - { - std::vector includeDirectives = IncludeProcessing::getIncludeDirectives(TextAccess::createFromString( - "#include ", - FilePath(L"foo.cpp") - )); - - TS_ASSERT(!includeDirectives.empty()); - - if (!includeDirectives.empty()) - { - TS_ASSERT_EQUALS(L"foo.h", includeDirectives.front().getIncludedFile().wstr()); - TS_ASSERT_EQUALS(L"foo.cpp", includeDirectives.front().getIncludingFile().wstr()); - } - } - void test_include_detection_finds_include_with_quotes_and_space_before_keyword() - { - std::vector includeDirectives = IncludeProcessing::getIncludeDirectives(TextAccess::createFromString( - "# include \"foo.h\"", - FilePath(L"foo.cpp") - )); - - TS_ASSERT(!includeDirectives.empty()); - - if (!includeDirectives.empty()) - { - TS_ASSERT_EQUALS(L"foo.h", includeDirectives.front().getIncludedFile().wstr()); - TS_ASSERT_EQUALS(L"foo.cpp", includeDirectives.front().getIncludingFile().wstr()); - } - } - - void test_include_detection_does_not_find_include_in_empty_file() - { - TS_ASSERT(IncludeProcessing::getIncludeDirectives(TextAccess::createFromString("")).empty()); - } - - void test_include_detection_does_not_find_include_in_file_without_preprocessor_directive() - { - TS_ASSERT(IncludeProcessing::getIncludeDirectives(TextAccess::createFromString("foo")).empty()); - } - - void test_include_detection_does_not_find_include_in_file_without_include_preprocessor_directive() - { - TS_ASSERT(IncludeProcessing::getIncludeDirectives(TextAccess::createFromString("#ifdef xx\n#endif")).empty()); - } - - void test_header_search_path_detection_does_not_find_path_relative_to_including_file() - { - std::vector headerSearchDirectories = utility::toVector(IncludeProcessing::getHeaderSearchDirectories( - { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_does_not_find_path_relative_to_including_file/a.cpp") }, - { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_does_not_find_path_relative_to_including_file") }, - { }, - 1, [](float) {} - )); - - TS_ASSERT(headerSearchDirectories.empty()); - } - - void test_header_search_path_detection_finds_path_inside_sub_directory() - { - std::vector headerSearchDirectories = utility::toVector(IncludeProcessing::getHeaderSearchDirectories( - { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_inside_sub_directory/a.cpp") }, - { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_inside_sub_directory") }, - {}, - 1, [](float) {} - )); - - TS_ASSERT(utility::containsElement( - headerSearchDirectories, - FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_inside_sub_directory/include").makeAbsolute() - )); - } - - void test_header_search_path_detection_finds_path_relative_to_sub_directory() - { - std::vector headerSearchDirectories = utility::toVector(IncludeProcessing::getHeaderSearchDirectories( - { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_relative_to_sub_directory/a.cpp") }, - { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_relative_to_sub_directory") }, - {}, - 1, [](float) {} - )); - - TS_ASSERT(utility::containsElement( - headerSearchDirectories, - FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_relative_to_sub_directory/include").makeAbsolute() - )); - } - - void test_header_search_path_detection_finds_path_included_in_header_search_path() - { - std::vector headerSearchDirectories = utility::toVector(IncludeProcessing::getHeaderSearchDirectories( - { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_included_in_header_search_path/a.cpp") }, - { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_included_in_header_search_path/include_b") }, - { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_included_in_header_search_path/include_a") }, - 1, [](float) {} - )); - - - TS_ASSERT(utility::containsElement( - headerSearchDirectories, - FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_included_in_header_search_path/include_b").makeAbsolute() - )); - } - - void test_header_search_path_detection_finds_path_included_in_future_header_search_path() - { - std::vector headerSearchDirectories = utility::toVector(IncludeProcessing::getHeaderSearchDirectories( - { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_included_in_future_header_search_path/a.cpp") }, - { FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_included_in_future_header_search_path") }, - { }, - 1, [](float) {} - )); - - TS_ASSERT(utility::containsElement( - headerSearchDirectories, - FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_included_in_future_header_search_path/include_a").makeAbsolute() - )); - TS_ASSERT(utility::containsElement( - headerSearchDirectories, - FilePath(L"data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_included_in_future_header_search_path/include_b").makeAbsolute() - )); - } -}; diff --git a/src/test/CxxIndexSampleProjectsTestSuite.cpp b/src/test/CxxIndexSampleProjectsTestSuite.cpp new file mode 100644 index 00000000..527ac27c --- /dev/null +++ b/src/test/CxxIndexSampleProjectsTestSuite.cpp @@ -0,0 +1,221 @@ +#include "catch.hpp" + +#include + +#include "ApplicationSettings.h" +#include "CxxParser.h" +#include "FileRegister.h" +#include "IndexerCommandCxx.h" +#include "IndexerStateInfo.h" +#include "ParserClientImpl.h" +#include "TestIntermediateStorage.h" +#include "TextAccess.h" +#include "TimeStamp.h" +#include "utility.h" +#include "utilityString.h" + +#define REQUIRE_MESSAGE(msg, cond) do { INFO(msg); REQUIRE(cond); } while((void)0, 0) + +namespace +{ + const bool updateExpectedOutput = false; + const bool trackTime = true; + size_t duration; + + std::shared_ptr parseCode(const FilePath& sourceFilePath, const FilePath& projectDataSrcRoot) + { + const std::set indexedPaths = { projectDataSrcRoot.getCanonical() }; + const std::set excludedFilters = {}; + const std::set includedFilters = {}; + const FilePath workingDirectory(L"."); + + std::vector compilerFlags; + utility::append(compilerFlags, IndexerCommandCxx::getCompilerFlagsForSystemHeaderSearchPaths( + utility::concat(std::vector { projectDataSrcRoot }, ApplicationSettings::getInstance()->getHeaderSearchPathsExpanded()) + )); + utility::append(compilerFlags, IndexerCommandCxx::getCompilerFlagsForFrameworkSearchPaths( + ApplicationSettings::getInstance()->getFrameworkSearchPathsExpanded() + )); +#ifdef _WIN32 + // compilerFlags.emplace_back(L"--target=x86_64-pc-windows-msvc"); +#else // _WIN32 + compilerFlags.emplace_back(L"-xc++"); +#endif // _WIN32 + compilerFlags.emplace_back(L"-std=c++1z"); + compilerFlags.emplace_back(sourceFilePath.wstr()); + + std::shared_ptr fileRegister = std::make_shared( + sourceFilePath, + indexedPaths, + excludedFilters + ); + + TestIntermediateStorage storage; + CxxParser parser(std::make_shared(&storage), fileRegister, std::make_shared()); + + std::shared_ptr command = std::make_shared( + sourceFilePath, + indexedPaths, + excludedFilters, + includedFilters, + workingDirectory, + compilerFlags + ); + + TimeStamp startTime = TimeStamp::now(); + parser.buildIndex(command); + duration += TimeStamp::now().deltaMS(startTime); + + storage.generateStringLists(); + + return TextAccess::createFromLines(storage.m_lines); + } + + void processSourceFile(const std::wstring& projectName, const FilePath& sourceFilePath) + { + const FilePath projectDataRoot = FilePath(L"data/CxxIndexSampleProjectsTestSuite/" + projectName).makeAbsolute(); + const FilePath projectDataSrcRoot = projectDataRoot.getConcatenated(L"src"); + const FilePath projectDataExpectedOutputRoot = projectDataRoot.getConcatenated(L"expected_output"); + + std::shared_ptr output = parseCode(projectDataSrcRoot.getConcatenated(sourceFilePath), projectDataSrcRoot); + + FilePath expectedOutputFilePath = projectDataExpectedOutputRoot.getConcatenated(utility::replace(sourceFilePath.withoutExtension().wstr() + L".txt", L"/", L"_")); + if (updateExpectedOutput || !expectedOutputFilePath.exists()) + { + std::ofstream expectedOutputFile; + expectedOutputFile.open(expectedOutputFilePath.str()); + expectedOutputFile << output->getText(); + expectedOutputFile.close(); + } + else + { + std::shared_ptr expectedOutput = TextAccess::createFromFile(expectedOutputFilePath); + REQUIRE_MESSAGE(("Output does not match the expected line count for file " + sourceFilePath.str() + " in project " + utility::encodeToUtf8(projectName)).c_str(), expectedOutput->getLineCount() == output->getLineCount()); + if (expectedOutput->getLineCount() == output->getLineCount()) + { + for (size_t i = 1; i <= expectedOutput->getLineCount(); i++) + { + REQUIRE(expectedOutput->getLine(i) == output->getLine(i)); + } + } + } + } + + void processSourceFiles(const std::wstring& projectName, const std::vector& sourceFilePaths) + { + duration = 0; + for (const FilePath& filePath : sourceFilePaths) + { + processSourceFile(projectName, filePath); + } + if (trackTime) + { + const FilePath projectDataRoot = FilePath(L"data/CxxIndexSampleProjectsTestSuite/" + projectName).makeAbsolute(); + + std::ofstream outfile; + outfile.open(FilePath(projectDataRoot.wstr() + L"/" + projectName + L".timing").str(), std::ios_base::app); + outfile << TimeStamp::now().toString() << " - " << duration << " ms\n"; + outfile.close(); + } + } +} + +TEST_CASE("index tictactoe project") +{ +#ifdef NDEBUG + processSourceFiles( + L"TicTacToe", + { + FilePath("artificial_player.cpp"), + FilePath("field.cpp"), + FilePath("player.cpp"), + FilePath("tictactoe.cpp"), + FilePath("human_player.cpp"), + FilePath("main.cpp"), + } + ); +#endif // NDEBUG +} + +TEST_CASE("index box2d project") +{ +#ifdef _WIN32 +#ifdef NDEBUG + processSourceFiles( + L"Box2D", + { + FilePath("Box2D/Collision/b2BroadPhase.cpp"), + FilePath("Box2D/Collision/b2CollideCircle.cpp"), + FilePath("Box2D/Collision/b2CollideEdge.cpp"), + FilePath("Box2D/Collision/b2CollidePolygon.cpp"), + FilePath("Box2D/Collision/b2Collision.cpp"), + FilePath("Box2D/Collision/b2Distance.cpp"), + FilePath("Box2D/Collision/b2DynamicTree.cpp"), + FilePath("Box2D/Collision/b2TimeOfImpact.cpp"), + FilePath("Box2D/Collision/Shapes/b2ChainShape.cpp"), + FilePath("Box2D/Collision/Shapes/b2CircleShape.cpp"), + FilePath("Box2D/Collision/Shapes/b2EdgeShape.cpp"), + FilePath("Box2D/Collision/Shapes/b2PolygonShape.cpp"), + FilePath("Box2D/Common/b2BlockAllocator.cpp"), + FilePath("Box2D/Common/b2Draw.cpp"), + FilePath("Box2D/Common/b2Math.cpp"), + FilePath("Box2D/Common/b2Settings.cpp"), + FilePath("Box2D/Common/b2StackAllocator.cpp"), + FilePath("Box2D/Common/b2Timer.cpp"), + FilePath("Box2D/Dynamics/b2Body.cpp"), + FilePath("Box2D/Dynamics/b2ContactManager.cpp"), + FilePath("Box2D/Dynamics/b2Fixture.cpp"), + FilePath("Box2D/Dynamics/b2Island.cpp"), + FilePath("Box2D/Dynamics/b2World.cpp"), + FilePath("Box2D/Dynamics/b2WorldCallbacks.cpp"), + FilePath("Box2D/Dynamics/Contacts/b2ChainAndCircleContact.cpp"), + FilePath("Box2D/Dynamics/Contacts/b2ChainAndPolygonContact.cpp"), + FilePath("Box2D/Dynamics/Contacts/b2CircleContact.cpp"), + FilePath("Box2D/Dynamics/Contacts/b2Contact.cpp"), + FilePath("Box2D/Dynamics/Contacts/b2ContactSolver.cpp"), + FilePath("Box2D/Dynamics/Contacts/b2EdgeAndCircleContact.cpp"), + FilePath("Box2D/Dynamics/Contacts/b2EdgeAndPolygonContact.cpp"), + FilePath("Box2D/Dynamics/Contacts/b2PolygonAndCircleContact.cpp"), + FilePath("Box2D/Dynamics/Contacts/b2PolygonContact.cpp"), + FilePath("Box2D/Dynamics/Joints/b2DistanceJoint.cpp"), + FilePath("Box2D/Dynamics/Joints/b2FrictionJoint.cpp"), + FilePath("Box2D/Dynamics/Joints/b2GearJoint.cpp"), + FilePath("Box2D/Dynamics/Joints/b2Joint.cpp"), + FilePath("Box2D/Dynamics/Joints/b2MotorJoint.cpp"), + FilePath("Box2D/Dynamics/Joints/b2MouseJoint.cpp"), + FilePath("Box2D/Dynamics/Joints/b2PrismaticJoint.cpp"), + FilePath("Box2D/Dynamics/Joints/b2PulleyJoint.cpp"), + FilePath("Box2D/Dynamics/Joints/b2RevoluteJoint.cpp"), + FilePath("Box2D/Dynamics/Joints/b2RopeJoint.cpp"), + FilePath("Box2D/Dynamics/Joints/b2WeldJoint.cpp"), + FilePath("Box2D/Dynamics/Joints/b2WheelJoint.cpp"), + FilePath("Box2D/Rope/b2Rope.cpp") + } + ); +#endif // NDEBUG +#endif // _WIN32 +} + +TEST_CASE("index bullet3 project") +{ +#ifdef _WIN32 +#ifdef NDEBUG + processSourceFiles( + L"Bullet3", + { + FilePath("Bullet3Collision/BroadPhaseCollision/b3DynamicBvh.cpp"), + FilePath("Bullet3Collision/BroadPhaseCollision/b3DynamicBvhBroadphase.cpp"), + FilePath("Bullet3Collision/BroadPhaseCollision/b3OverlappingPairCache.cpp"), + FilePath("Bullet3Collision/NarrowPhaseCollision/b3ConvexUtility.cpp"), + FilePath("Bullet3Collision/NarrowPhaseCollision/b3CpuNarrowPhase.cpp"), + FilePath("Bullet3Common/b3AlignedAllocator.cpp"), + FilePath("Bullet3Common/b3Logging.cpp"), + FilePath("Bullet3Common/b3Vector3.cpp"), + FilePath("Bullet3Geometry/b3ConvexHullComputer.cpp"), + FilePath("Bullet3Geometry/b3GeometryUtil.cpp") + } + ); +#endif // NDEBUG +#endif // _WIN32 +} + diff --git a/src/test/CxxIndexSampleProjectsTestSuite.h b/src/test/CxxIndexSampleProjectsTestSuite.h deleted file mode 100644 index a4c5499d..00000000 --- a/src/test/CxxIndexSampleProjectsTestSuite.h +++ /dev/null @@ -1,223 +0,0 @@ -#include - -#include -#include - -#include "ApplicationSettings.h" -#include "CxxParser.h" -#include "FileRegister.h" -#include "IndexerCommandCxx.h" -#include "IndexerStateInfo.h" -#include "ParserClientImpl.h" -#include "TestIntermediateStorage.h" -#include "TextAccess.h" -#include "TimeStamp.h" -#include "utility.h" -#include "utilityString.h" - -class CxxIndexSampleProjectsTestSuite : public CxxTest::TestSuite -{ -public: - static const bool s_updateExpectedOutput = false; - static const bool s_trackTime = true; - - void test_index_tictactoe_project() - { -#ifdef NDEBUG - processSourceFiles( - L"TicTacToe", - { - FilePath("artificial_player.cpp"), - FilePath("field.cpp"), - FilePath("player.cpp"), - FilePath("tictactoe.cpp"), - FilePath("human_player.cpp"), - FilePath("main.cpp"), - } - ); -#endif // NDEBUG - } - - void test_index_box2d_project() - { -#ifdef _WIN32 -#ifdef NDEBUG - processSourceFiles( - L"Box2D", - { - FilePath("Box2D/Collision/b2BroadPhase.cpp"), - FilePath("Box2D/Collision/b2CollideCircle.cpp"), - FilePath("Box2D/Collision/b2CollideEdge.cpp"), - FilePath("Box2D/Collision/b2CollidePolygon.cpp"), - FilePath("Box2D/Collision/b2Collision.cpp"), - FilePath("Box2D/Collision/b2Distance.cpp"), - FilePath("Box2D/Collision/b2DynamicTree.cpp"), - FilePath("Box2D/Collision/b2TimeOfImpact.cpp"), - FilePath("Box2D/Collision/Shapes/b2ChainShape.cpp"), - FilePath("Box2D/Collision/Shapes/b2CircleShape.cpp"), - FilePath("Box2D/Collision/Shapes/b2EdgeShape.cpp"), - FilePath("Box2D/Collision/Shapes/b2PolygonShape.cpp"), - FilePath("Box2D/Common/b2BlockAllocator.cpp"), - FilePath("Box2D/Common/b2Draw.cpp"), - FilePath("Box2D/Common/b2Math.cpp"), - FilePath("Box2D/Common/b2Settings.cpp"), - FilePath("Box2D/Common/b2StackAllocator.cpp"), - FilePath("Box2D/Common/b2Timer.cpp"), - FilePath("Box2D/Dynamics/b2Body.cpp"), - FilePath("Box2D/Dynamics/b2ContactManager.cpp"), - FilePath("Box2D/Dynamics/b2Fixture.cpp"), - FilePath("Box2D/Dynamics/b2Island.cpp"), - FilePath("Box2D/Dynamics/b2World.cpp"), - FilePath("Box2D/Dynamics/b2WorldCallbacks.cpp"), - FilePath("Box2D/Dynamics/Contacts/b2ChainAndCircleContact.cpp"), - FilePath("Box2D/Dynamics/Contacts/b2ChainAndPolygonContact.cpp"), - FilePath("Box2D/Dynamics/Contacts/b2CircleContact.cpp"), - FilePath("Box2D/Dynamics/Contacts/b2Contact.cpp"), - FilePath("Box2D/Dynamics/Contacts/b2ContactSolver.cpp"), - FilePath("Box2D/Dynamics/Contacts/b2EdgeAndCircleContact.cpp"), - FilePath("Box2D/Dynamics/Contacts/b2EdgeAndPolygonContact.cpp"), - FilePath("Box2D/Dynamics/Contacts/b2PolygonAndCircleContact.cpp"), - FilePath("Box2D/Dynamics/Contacts/b2PolygonContact.cpp"), - FilePath("Box2D/Dynamics/Joints/b2DistanceJoint.cpp"), - FilePath("Box2D/Dynamics/Joints/b2FrictionJoint.cpp"), - FilePath("Box2D/Dynamics/Joints/b2GearJoint.cpp"), - FilePath("Box2D/Dynamics/Joints/b2Joint.cpp"), - FilePath("Box2D/Dynamics/Joints/b2MotorJoint.cpp"), - FilePath("Box2D/Dynamics/Joints/b2MouseJoint.cpp"), - FilePath("Box2D/Dynamics/Joints/b2PrismaticJoint.cpp"), - FilePath("Box2D/Dynamics/Joints/b2PulleyJoint.cpp"), - FilePath("Box2D/Dynamics/Joints/b2RevoluteJoint.cpp"), - FilePath("Box2D/Dynamics/Joints/b2RopeJoint.cpp"), - FilePath("Box2D/Dynamics/Joints/b2WeldJoint.cpp"), - FilePath("Box2D/Dynamics/Joints/b2WheelJoint.cpp"), - FilePath("Box2D/Rope/b2Rope.cpp") - } - ); -#endif // NDEBUG -#endif // _WIN32 - } - - void test_index_bullet3_project() - { -#ifdef _WIN32 -#ifdef NDEBUG - processSourceFiles( - L"Bullet3", - { - FilePath("Bullet3Collision/BroadPhaseCollision/b3DynamicBvh.cpp"), - FilePath("Bullet3Collision/BroadPhaseCollision/b3DynamicBvhBroadphase.cpp"), - FilePath("Bullet3Collision/BroadPhaseCollision/b3OverlappingPairCache.cpp"), - FilePath("Bullet3Collision/NarrowPhaseCollision/b3ConvexUtility.cpp"), - FilePath("Bullet3Collision/NarrowPhaseCollision/b3CpuNarrowPhase.cpp"), - FilePath("Bullet3Common/b3AlignedAllocator.cpp"), - FilePath("Bullet3Common/b3Logging.cpp"), - FilePath("Bullet3Common/b3Vector3.cpp"), - FilePath("Bullet3Geometry/b3ConvexHullComputer.cpp"), - FilePath("Bullet3Geometry/b3GeometryUtil.cpp") - } - ); -#endif // NDEBUG -#endif // _WIN32 - } - -private: - - void processSourceFiles(const std::wstring& projectName, const std::vector& sourceFilePaths) - { - m_duration = 0; - for (const FilePath& filePath : sourceFilePaths) - { - processSourceFile(projectName, filePath); - } - if (s_trackTime) - { - const FilePath projectDataRoot = FilePath(L"data/CxxIndexSampleProjectsTestSuite/" + projectName).makeAbsolute(); - - std::ofstream outfile; - outfile.open(FilePath(projectDataRoot.wstr() + L"/" + projectName + L".timing").str(), std::ios_base::app); - outfile << TimeStamp::now().toString() << " - " << m_duration << " ms\n"; - outfile.close(); - } - } - - void processSourceFile(const std::wstring& projectName, const FilePath& sourceFilePath) - { - const FilePath projectDataRoot = FilePath(L"data/CxxIndexSampleProjectsTestSuite/" + projectName).makeAbsolute(); - const FilePath projectDataSrcRoot = projectDataRoot.getConcatenated(L"src"); - const FilePath projectDataExpectedOutputRoot = projectDataRoot.getConcatenated(L"expected_output"); - - std::shared_ptr output = parseCode(projectDataSrcRoot.getConcatenated(sourceFilePath), projectDataSrcRoot); - - FilePath expectedOutputFilePath = projectDataExpectedOutputRoot.getConcatenated(utility::replace(sourceFilePath.withoutExtension().wstr() + L".txt", L"/", L"_")); - if (s_updateExpectedOutput || !expectedOutputFilePath.exists()) - { - std::ofstream expectedOutputFile; - expectedOutputFile.open(expectedOutputFilePath.str()); - expectedOutputFile << output->getText(); - expectedOutputFile.close(); - } - else - { - std::shared_ptr expectedOutput = TextAccess::createFromFile(expectedOutputFilePath); - TSM_ASSERT_EQUALS(L"Output does not match the expected line count for file " + sourceFilePath.wstr() + L" in project " + projectName, expectedOutput->getLineCount(), output->getLineCount()); - if (expectedOutput->getLineCount() == output->getLineCount()) - { - for (size_t i = 1; i <= expectedOutput->getLineCount(); i++) - { - TS_ASSERT_EQUALS(expectedOutput->getLine(i), output->getLine(i)); - } - } - } - } - - std::shared_ptr parseCode(const FilePath& sourceFilePath, const FilePath& projectDataSrcRoot) - { - const std::set indexedPaths = { projectDataSrcRoot.getCanonical() }; - const std::set excludedFilters = {}; - const std::set includedFilters = {}; - const FilePath workingDirectory(L"."); - - std::vector compilerFlags; - utility::append(compilerFlags, IndexerCommandCxx::getCompilerFlagsForSystemHeaderSearchPaths( - utility::concat(std::vector { projectDataSrcRoot }, ApplicationSettings::getInstance()->getHeaderSearchPathsExpanded()) - )); - utility::append(compilerFlags, IndexerCommandCxx::getCompilerFlagsForFrameworkSearchPaths( - ApplicationSettings::getInstance()->getFrameworkSearchPathsExpanded() - )); -#ifdef _WIN32 - // compilerFlags.emplace_back(L"--target=x86_64-pc-windows-msvc"); -#else // _WIN32 - compilerFlags.emplace_back(L"-xc++"); -#endif // _WIN32 - compilerFlags.emplace_back(L"-std=c++1z"); - compilerFlags.emplace_back(sourceFilePath.wstr()); - - std::shared_ptr fileRegister = std::make_shared( - sourceFilePath, - indexedPaths, - excludedFilters - ); - - TestIntermediateStorage storage; - CxxParser parser(std::make_shared(&storage), fileRegister, std::make_shared()); - - std::shared_ptr command = std::make_shared( - sourceFilePath, - indexedPaths, - excludedFilters, - includedFilters, - workingDirectory, - compilerFlags - ); - - TimeStamp startTime = TimeStamp::now(); - parser.buildIndex(command); - m_duration += TimeStamp::now().deltaMS(startTime); - - storage.generateStringLists(); - - return TextAccess::createFromLines(storage.m_lines); - } - - size_t m_duration; -}; diff --git a/src/test/CxxParserTestSuite.cpp b/src/test/CxxParserTestSuite.cpp new file mode 100644 index 00000000..a50df072 --- /dev/null +++ b/src/test/CxxParserTestSuite.cpp @@ -0,0 +1,4724 @@ +#include "catch.hpp" +#include "TextAccess.h" +#include "utility.h" +#include "utilityString.h" + +#include "IndexerCommandCxx.h" +#include "IndexerStateInfo.h" +#include "CxxParser.h" +#include "ParserClientImpl.h" + +#include "TestFileRegister.h" +#include "TestIntermediateStorage.h" + +namespace +{ + std::shared_ptr parseCode(std::string code, std::vector compilerFlags = {}) + { + std::shared_ptr storage = std::make_shared(); + CxxParser parser(std::make_shared(storage.get()), std::make_shared(), std::make_shared()); + parser.buildIndex(L"input.cc", TextAccess::createFromString(code), utility::concat(compilerFlags, std::vector(1, L"-std=c++1z"))); + storage->generateStringLists(); + return storage; + } +} + +TEST_CASE("cxx parser finds global variable declaration") +{ + std::shared_ptr client = parseCode( + "int x;\n" + ); + + REQUIRE(utility::containsElement( + client->globalVariables, L"int x <1:5 1:5>" + )); +} + +TEST_CASE("cxx parser finds static global variable declaration") +{ + std::shared_ptr client = parseCode( + "static int x;\n" + ); + + REQUIRE(utility::containsElement( + client->globalVariables, L"int x (input.cc) <1:12 1:12>" + )); +} + +TEST_CASE("cxx parser finds static const global variable declaration") +{ + std::shared_ptr client = parseCode( + "static const int x;\n" + ); + + REQUIRE(utility::containsElement( + client->globalVariables, L"const int x (input.cc) <1:18 1:18>" + )); +} + +TEST_CASE("cxx parser finds global class definition") +{ + std::shared_ptr client = parseCode( + "class A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->classes, L"A <1:1 <1:7 1:7> 3:1>" + )); +} + +TEST_CASE("cxx parser finds global class declaration") +{ + std::shared_ptr client = parseCode( + "class A;\n" + ); + + REQUIRE(utility::containsElement( + client->classes, L"A <1:7 1:7>" + )); +} + +TEST_CASE("cxx parser finds global struct definition") +{ + std::shared_ptr client = parseCode( + "struct A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->structs, L"A <1:1 <1:8 1:8> 3:1>" + )); +} + +TEST_CASE("cxx parser finds global struct declaration") +{ + std::shared_ptr client = parseCode( + "struct A;\n" + ); + + REQUIRE(utility::containsElement( + client->structs, L"A <1:8 1:8>" + )); +} + +TEST_CASE("cxx parser finds variable definitions in global scope") +{ + std::shared_ptr client = parseCode( + "int x;\n" + ); + + REQUIRE(utility::containsElement( + client->globalVariables, L"int x <1:5 1:5>" + )); +} + +TEST_CASE("cxx parser finds fields in class with access type") +{ + std::shared_ptr client = parseCode( + "class A\n" + "{\n" + " int a;\n" + "public:\n" + " A() : d(0) {};\n" + " int b;\n" + "protected:\n" + " static int c;\n" + "private:\n" + " const int d;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->fields, L"private int A::a <3:6 3:6>" + )); + REQUIRE(utility::containsElement( + client->fields, L"public int A::b <6:6 6:6>" + )); + REQUIRE(utility::containsElement( + client->fields, L"protected static int A::c <8:13 8:13>" + )); + REQUIRE(utility::containsElement( + client->fields, L"private const int A::d <10:12 10:12>" + )); +} + +TEST_CASE("cxx parser finds function declaration") +{ + std::shared_ptr client = parseCode( + "int ceil(float a)\n" + "{\n" + " return 1;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->functions, L"int ceil(float) <1:1 <1:1 <1:5 1:8> 1:17> 4:1>" + )); +} + +TEST_CASE("cxx parser finds static function declaration") +{ + std::shared_ptr client = parseCode( + "static int ceil(float a)\n" + "{\n" + " return static_cast(a) + 1;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->functions, L"static int ceil(float) (input.cc) <1:1 <1:1 <1:12 1:15> 1:24> 4:1>" + )); +} + +TEST_CASE("cxx parser finds method declaration") +{ + std::shared_ptr client = parseCode( + "class B\n" + "{\n" + "public:\n" + " B();\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->methods, L"public void B::B() <4:2 <4:2 4:2> 4:4>" + )); +} + +TEST_CASE("cxx parser finds overloaded operator declaration") +{ + std::shared_ptr client = parseCode( + "class B\n" + "{\n" + "public:\n" + " B& operator=(const B& other);\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->methods, L"public B & B::operator=(const B &) <4:2 <4:5 4:13> 4:29>" + )); +} + +TEST_CASE("cxx parser finds method declaration and definition") +{ + std::shared_ptr client = parseCode( + "class B\n" + "{\n" + "public:\n" + " B();\n" + "};\n" + "B::B()\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->methods, L"public void B::B() <6:1 <6:4 6:4> 8:1>" + )); +} + +TEST_CASE("cxx parser finds virtual method declaration") +{ + std::shared_ptr client = parseCode( + "class B\n" + "{\n" + "public:\n" + " virtual void process();\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->methods, L"public void B::process() <4:2 <4:15 4:21> 4:23>" + )); +} + +TEST_CASE("cxx parser finds pure virtual method declaration") +{ + std::shared_ptr client = parseCode( + "class B\n" + "{\n" + "protected:\n" + " virtual void process() = 0;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->methods, L"protected void B::process() <4:2 <4:15 4:21> 4:27>" + )); +} + +TEST_CASE("cxx parser finds named namespace declaration") +{ + std::shared_ptr client = parseCode( + "namespace A\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->namespaces, L"A <1:1 <1:11 1:11> 3:1>" + )); +} + +TEST_CASE("cxx parser finds anonymous namespace declaration") +{ + std::shared_ptr client = parseCode( + "namespace\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->namespaces, L"anonymous namespace (input.cc<1:1>) <1:1 <2:1 2:1> 3:1>" + )); +} + +TEST_CASE("cxx parser finds multiple anonymous namespace declarations as same symbol") +{ + std::shared_ptr client = parseCode( + "namespace\n" + "{\n" + "}\n" + "namespace\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->namespaces, L"anonymous namespace (input.cc<1:1>) <1:1 <2:1 2:1> 3:1>" + )); + + REQUIRE(utility::containsElement( + client->namespaces, L"anonymous namespace (input.cc<1:1>) <4:1 <5:1 5:1> 6:1>" + )); +} + +TEST_CASE("cxx parser finds multiple nested anonymous namespace declarations as different symbol") +{ + std::shared_ptr client = parseCode( + "namespace\n" + "{\n" + " namespace\n" + " {\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->namespaces, L"anonymous namespace (input.cc<1:1>) <1:1 <2:1 2:1> 6:1>" + )); + + REQUIRE(utility::containsElement( + client->namespaces, L"anonymous namespace (input.cc<1:1>)::anonymous namespace (input.cc<3:2>) <3:2 <4:2 4:2> 5:2>" + )); +} + +TEST_CASE("cxx parser finds anonymous namespace declarations nested inside namespaces with different name as different symbol") +{ + std::shared_ptr client = parseCode( + "namespace a\n" + "{\n" + " namespace\n" + " {\n" + " }\n" + "}\n" + "namespace b\n" + "{\n" + " namespace\n" + " {\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->namespaces, L"a <1:1 <1:11 1:11> 6:1>" + )); + + REQUIRE(utility::containsElement( + client->namespaces, L"a::anonymous namespace (input.cc<3:2>) <3:2 <4:2 4:2> 5:2>" + )); + + REQUIRE(utility::containsElement( + client->namespaces, L"b <7:1 <7:11 7:11> 12:1>" + )); + + REQUIRE(utility::containsElement( + client->namespaces, L"b::anonymous namespace (input.cc<9:2>) <9:2 <10:2 10:2> 11:2>" + )); +} + +TEST_CASE("cxx parser finds anonymous namespace declarations nested inside namespaces with same name as same symbol") +{ + std::shared_ptr client = parseCode( + "namespace a\n" + "{\n" + " namespace\n" + " {\n" + " }\n" + "}\n" + "namespace a\n" + "{\n" + " namespace\n" + " {\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->namespaces, L"a <1:1 <1:11 1:11> 6:1>" + )); + + REQUIRE(utility::containsElement( + client->namespaces, L"a::anonymous namespace (input.cc<3:2>) <3:2 <4:2 4:2> 5:2>" + )); + + REQUIRE(utility::containsElement( + client->namespaces, L"a <7:1 <7:11 7:11> 12:1>" + )); + + REQUIRE(utility::containsElement( + client->namespaces, L"a::anonymous namespace (input.cc<3:2>) <9:2 <10:2 10:2> 11:2>" + )); +} + +TEST_CASE("cxx parser finds anonymous struct declaration") +{ + std::shared_ptr client = parseCode( + "typedef struct\n" + "{\n" + " int x;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->structs, L"anonymous struct (input.cc<1:9>) <1:9 <1:9 1:14> 4:1>" + )); +} + +TEST_CASE("cxx parser finds multiple anonymous struct declarations as distinct elements") +{ + std::shared_ptr client = parseCode( + "typedef struct\n" + "{\n" + " int x;\n" + "};\n" + "typedef struct\n" + "{\n" + " float x;\n" + "};\n" + ); + + REQUIRE(client->structs.size() == 2); + REQUIRE(client->fields.size() == 2); + REQUIRE(utility::substrBeforeLast(client->fields[0], '<') != utility::substrBeforeLast(client->fields[1], '<')); +} + +TEST_CASE("cxx parser finds anonymous union declaration") +{ + std::shared_ptr client = parseCode( + "typedef union\n" + "{\n" + " int i;\n" + " float f;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->unions, L"anonymous union (input.cc<1:9>) <1:9 <1:9 1:13> 5:1>" + )); +} + +TEST_CASE("cxx parser finds name of anonymous struct declared inside typedef") +{ + std::shared_ptr client = parseCode( + "typedef struct\n" + "{\n" + " int x;\n" + "} Foo;\n" + ); + + REQUIRE(utility::containsElement( + client->structs, L"Foo <1:9 <1:9 1:14> 4:1>" + )); + REQUIRE(utility::containsElement( + client->structs, L"Foo <4:3 4:5>" + )); +} + +TEST_CASE("cxx parser finds name of anonymous class declared inside typedef") +{ + std::shared_ptr client = parseCode( + "typedef class\n" + "{\n" + " int x;\n" + "} Foo;\n" + ); + + REQUIRE(utility::containsElement( + client->classes, L"Foo <1:9 <1:9 1:13> 4:1>" + )); + REQUIRE(utility::containsElement( + client->classes, L"Foo <4:3 4:5>" + )); +} + +TEST_CASE("cxx parser finds name of anonymous enum declared inside typedef") +{ + std::shared_ptr client = parseCode( + "typedef enum\n" + "{\n" + " CONSTANT_1;\n" + "} Foo;\n" + ); + + REQUIRE(utility::containsElement( + client->enums, L"Foo <1:9 <1:9 1:12> 4:1>" + )); + REQUIRE(utility::containsElement( + client->enums, L"Foo <4:3 4:5>" + )); +} + +TEST_CASE("cxx parser finds name of anonymous union declared inside typedef") +{ + std::shared_ptr client = parseCode( + "typedef union\n" + "{\n" + " int x;\n" + " float y;\n" + "} Foo;\n" + ); + + REQUIRE(utility::containsElement( + client->unions, L"Foo <1:9 <1:9 1:13> 5:1>" + )); + REQUIRE(utility::containsElement( + client->unions, L"Foo <5:3 5:5>" + )); +} + +TEST_CASE("cxx parser finds name of anonymous struct declared inside type alias") +{ + std::shared_ptr client = parseCode( + "using Foo = struct\n" + "{\n" + " int x;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->structs, L"Foo <1:13 <1:13 1:18> 4:1>" + )); + REQUIRE(utility::containsElement( + client->structs, L"Foo <1:7 1:9>" + )); +} + +TEST_CASE("cxx parser finds name of anonymous class declared inside type alias") +{ + std::shared_ptr client = parseCode( + "using Foo = class\n" + "{\n" + " int x;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->classes, L"Foo <1:13 <1:13 1:17> 4:1>" + )); + REQUIRE(utility::containsElement( + client->classes, L"Foo <1:7 1:9>" + )); +} + +TEST_CASE("cxx parser finds name of anonymous enum declared inside type alias") +{ + std::shared_ptr client = parseCode( + "using Foo = enum\n" + "{\n" + " CONSTANT_1;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->enums, L"Foo <1:13 <1:13 1:16> 4:1>" + )); + REQUIRE(utility::containsElement( + client->enums, L"Foo <1:7 1:9>" + )); +} + +TEST_CASE("cxx parser finds name of anonymous union declared inside type alias") +{ + std::shared_ptr client = parseCode( + "using Foo = union\n" + "{\n" + " int x;\n" + " float y;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->unions, L"Foo <1:13 <1:13 1:17> 5:1>" + )); + REQUIRE(utility::containsElement( + client->unions, L"Foo <1:7 1:9>" + )); +} + +TEST_CASE("cxx parser finds enum defined in global namespace") +{ + std::shared_ptr client = parseCode( + "enum E\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->enums, L"E <1:1 <1:6 1:6> 3:1>" + )); +} + +TEST_CASE("cxx parser finds enum constant in global enum") +{ + std::shared_ptr client = parseCode( + "enum E\n" + "{\n" + " P\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->enumConstants, L"E::P <3:2 3:2>" + )); +} + +TEST_CASE("cxx parser finds typedef in global namespace") +{ + std::shared_ptr client = parseCode( + "typedef unsigned int uint;\n" + ); + + REQUIRE(utility::containsElement( + client->typedefs, L"uint <1:22 1:25>" + )); +} + +TEST_CASE("cxx parser finds typedef in named namespace") +{ + std::shared_ptr client = parseCode( + "namespace test\n" + "{\n" + " typedef unsigned int uint;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typedefs, L"test::uint <3:23 3:26>" + )); +} + +TEST_CASE("cxx parser finds typedef in anonymous namespace") +{ + std::shared_ptr client = parseCode( + "namespace\n" + "{\n" + " typedef unsigned int uint;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typedefs, L"anonymous namespace (input.cc<1:1>)::uint <3:23 3:26>" + )); +} + +TEST_CASE("cxx parser finds type alias in class") +{ + std::shared_ptr client = parseCode( + "class Foo\n" + "{\n" + " using Bar = Foo;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->typedefs, L"private Foo::Bar <3:8 3:10>" + )); +} + +TEST_CASE("cxx parser finds macro define") +{ + std::shared_ptr client = parseCode( + "#define PI\n" + "void test()\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->macros, L"PI <1:9 1:10>" + )); +} + +TEST_CASE("cxx parser finds macro undefine") +{ + std::shared_ptr client = parseCode( + "#undef PI\n" + "void test()\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->macroUses, L"input.cc -> PI <1:8 1:9>" + )); +} + +TEST_CASE("cxx parser finds macro in ifdef") +{ + std::shared_ptr client = parseCode( + "#define PI\n" + "#ifdef PI\n" + "void test()\n" + "{\n" + "};\n" + "#endif\n" + ); + + REQUIRE(utility::containsElement( + client->macroUses, L"input.cc -> PI <2:8 2:9>" + )); +} + +TEST_CASE("cxx parser finds macro in ifndef") +{ + std::shared_ptr client = parseCode( + "#define PI\n" + "#ifndef PI\n" + "void test()\n" + "{\n" + "};\n" + "#endif\n" + ); + + REQUIRE(utility::containsElement( + client->macroUses, L"input.cc -> PI <2:9 2:10>" + )); +} + +TEST_CASE("cxx parser finds macro in ifdefined") +{ + std::shared_ptr client = parseCode( + "#define PI\n" + "#if defined(PI)\n" + "void test()\n" + "{\n" + "};\n" + "#endif\n" + ); + + REQUIRE(utility::containsElement( + client->macroUses, L"input.cc -> PI <2:13 2:14>" + )); +} + +TEST_CASE("cxx parser finds macro expand") +{ + std::shared_ptr client = parseCode( + "#define PI 3.14159265359\n" + "void test()\n" + "{\n" + "double i = PI;" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->macroUses, L"input.cc -> PI <4:12 4:13>" + )); +} + +TEST_CASE("cxx parser finds macro expand within macro") +{ + std::shared_ptr client = parseCode( + "#define PI 3.14159265359\n" + "#define TAU (2 * PI)\n" + "void test()\n" + "{\n" + "double i = TAU;" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->macroUses, L"input.cc -> PI <2:18 2:19>" + )); +} + +TEST_CASE("cxx parser finds macro define scope") +{ + std::shared_ptr client = parseCode( + "#define MAX(a,b) \\\n" + " ((a)>(b)?(a):(b))" + ); + + REQUIRE(utility::containsElement( + client->macros, L"MAX <1:9 <1:9 1:11> 2:17>" + )); +} + +TEST_CASE("cxx parser finds type template parameter definition of template type alias") +{ + std::shared_ptr client = parseCode( + "template\n" + "using MyType = int;\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:16> <1:16 1:16>" + )); +} + +TEST_CASE("cxx parser finds type template parameter definition of class template") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:20> <1:20 1:20>" + )); +} + +TEST_CASE("cxx parser finds type template parameter definition of explicit partial class template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "};\n" + "template \n" + "class A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<5:20> <5:20 5:20>" + )); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<5:20> <6:9 6:9>" + )); +} + +TEST_CASE("cxx parser finds type template parameter definition of variable template") +{ + std::shared_ptr client = parseCode( + "template \n" + "T v;\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:20> <1:20 1:20>" + )); +} + +TEST_CASE("cxx parser finds type template parameter definition of explicit partial variable template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "T t = Q(5);\n" + "\n" + "template \n" + "int t = 9;\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<4:20> <4:20 4:20>" + )); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<4:20> <5:12 5:12>" + )); +} + +TEST_CASE("cxx parser finds type template parameter defined with class keyword") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:17> <1:17 1:17>" + )); +} + +TEST_CASE("cxx parser finds non type int template parameter definition of template class") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:15> <1:15 1:15>" + )); +} + +TEST_CASE("cxx parser finds non type bool template parameter definition of template class") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:16> <1:16 1:16>" + )); +} + +TEST_CASE("cxx parser finds non type custom pointer template parameter definition of template class") +{ + std::shared_ptr client = parseCode( + "class P\n" + "{};\n" + "template \n" + "class A\n" + "{};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<3:14> <3:14 3:14>" + )); +} + +TEST_CASE("cxx parser finds non type custom reference template parameter definition of template class") +{ + std::shared_ptr client = parseCode( + "class P\n" + "{};\n" + "template \n" + "class A\n" + "{};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<3:14> <3:14 3:14>" + )); +} + +TEST_CASE("cxx parser finds non type template parameter definition that depends on type template parameter of template class") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:28> <1:28 1:29>" + )); + + // and usage + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:20> <1:24 1:25>" + )); +} + +TEST_CASE("cxx parser finds non type template parameter definition that depends on template template parameter of template class") +{ + std::shared_ptr client = parseCode( + "template class T1, T1& T2>\n" + "class A\n" + "{};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:49> <1:49 1:50>" + )); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:36> <1:40 1:41>" + )); +} + +TEST_CASE("cxx parser finds non type template parameter definition that depends on type template parameter of template template parameter") +{ + std::shared_ptr client = parseCode( + "template typename S>\n" + "class A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:45> <1:45 1:45>" + )); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:29> <1:32 1:32>" + )); +} + +TEST_CASE("cxx parser finds template argument of dependent non type template parameter") +{ + std::shared_ptr client = parseCode( + "template class T1, T1& T2>\n" + "class A\n" + "{};\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"A typename T1, T1 & T2> -> int <1:43 1:45>" + )); +} + +//void _test_foofoo() +//{ +// std::shared_ptr client = parseCode( +// "template \n" +// "class vector { };\n" +// "\n" +// "template\n" +// "struct Alloc { };\n" +// "\n" +// "template\n" +// "using Vec = vector>;\n" +// "\n" +// "Vec v;\n" +// ); + +// TS_ASSERT(utility::containsElement( +// client->typeUses, // TODO: record edge between vector> and Alloc (this is an issue because we dont have any typeloc for this edge -.- +// )); +//} + +TEST_CASE("cxx parser finds template template parameter of template class") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{};\n" + "template class T>\n" + "class B\n" + "{};\n" + "int main()\n" + "{\n" + " B ba;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<4:36> <4:36 4:36>" + )); +} + +TEST_CASE("cxx parser finds type template parameter pack type of template class") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:23> <1:23 1:23>" + )); +} + +TEST_CASE("cxx parser finds non type int template parameter pack type of template class") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:18> <1:18 1:18>" + )); +} + +TEST_CASE("cxx parser finds template template parameter pack type of template class") +{ + std::shared_ptr client = parseCode( + "template typename... T>\n" + "class A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:42> <1:42 1:42>" + )); +} + +TEST_CASE("cxx parser finds type template parameters of template class with multiple parameters") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:20> <1:20 1:20>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:32> <1:32 1:32>" + )); +} + +TEST_CASE("cxx parser skips creating node for template parameter without a name") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" // local symbol for brace + "};\n" // local symbol for brace + ); + + REQUIRE(client->localSymbols.size() == 2); + REQUIRE(utility::containsElement( + client->classes, L"A <1:1 <2:7 2:7> 4:1>" + )); +} + +TEST_CASE("cxx parser finds type template parameter of template method definition outside template class") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + " template \n" + " U foo();\n" + "};\n" + "template \n" + "template \n" + "U A::foo()\n" + "{}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<8:20> <8:20 8:20>" + )); +} + +TEST_CASE("cxx parser finds explicit class template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "};\n" + "template <>\n" + "class A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->classes, L"A <5:1 <6:7 6:7> 8:1>" + )); +} + +TEST_CASE("cxx parser finds explicit variable template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "T t = T(5);\n" + "\n" + "template <>\n" + "int t = 99;\n" + ); + + REQUIRE(utility::containsElement( + client->globalVariables, L"int t <5:5 5:5>" + )); +} + +TEST_CASE("cxx parser finds explicit partial class template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "};\n" + "template \n" + "class A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->classes, L"A <5:1 <6:7 6:7> 8:1>" + )); +} + +TEST_CASE("cxx parser finds explicit partial variable template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "T t = Q(5);\n" + "\n" + "template \n" + "int t = 9;\n" + ); + + REQUIRE(utility::containsElement( + client->globalVariables, L"int t <5:5 5:5>" + )); +} + +TEST_CASE("cxx parser finds correct field member name of template class in declaration") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + " int foo;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->fields, L"private int A::foo <4:6 4:8>" + )); +} + +TEST_CASE("cxx parser finds correct type of field member of template class in declaration") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + " T foo;\n" + "};\n" + "A a; \n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"int A::foo -> int <4:2 4:2>" + )); + + REQUIRE(utility::containsElement( + client->typeUses, L"A a -> A <6:1 6:1>" + )); + + REQUIRE(utility::containsElement( + client->typeUses, L"A a -> int <6:3 6:5>" + )); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:20> <4:2 4:2>" + )); +} + +TEST_CASE("cxx parser finds correct method member name of template class in declaration") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + " int foo();\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->methods, L"private int A::foo() <4:2 <4:6 4:8> 4:10>" + )); +} + +TEST_CASE("cxx parser finds type template parameter definition of template function") +{ + std::shared_ptr client = parseCode( + "template \n" + "T test(T a)\n" + "{\n" + " return a;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:20> <1:20 1:20>" + )); +} + +TEST_CASE("cxx parser finds non type int template parameter definition of template function") +{ + std::shared_ptr client = parseCode( + "template \n" + "int test(int a)\n" + "{\n" + " return a + T;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:15> <1:15 1:15>" + )); +} + +TEST_CASE("cxx parser finds non type bool template parameter definition of template function") +{ + std::shared_ptr client = parseCode( + "template \n" + "int test(int a)\n" + "{\n" + " return T ? a : 0;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:16> <1:16 1:16>" + )); +} + +TEST_CASE("cxx parser finds non type custom pointer template parameter definition of template function") +{ + std::shared_ptr client = parseCode( + "class P\n" + "{};\n" + "template \n" + "int test(int a)\n" + "{\n" + " return a;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<3:14> <3:14 3:14>" + )); +} + +TEST_CASE("cxx parser finds non type custom reference template parameter definition of template function") +{ + std::shared_ptr client = parseCode( + "class P\n" + "{};\n" + "template \n" + "int test(int a)\n" + "{\n" + " return a;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<3:14> <3:14 3:14>" + )); +} + +TEST_CASE("cxx parser finds template template parameter definition of template function") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{};\n" + "template class T>\n" + "int test(int a)\n" + "{\n" + " return a;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<4:36> <4:36 4:36>" + )); +} + +TEST_CASE("cxx parser finds function for implicit instantiation of template function") +{ + std::shared_ptr client = parseCode( + "template \n" + "T test(T a)\n" + "{\n" + " return a;\n" + "};\n" + "\n" + "int main()\n" + "{\n" + " return test(1);\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->functions, L"int test(int) <2:1 <2:1 <2:3 2:6> 2:11> 5:1>" + )); +} + +TEST_CASE("cxx parser skips implicit template method definition of implicit template class instantiation") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "public:\n" + " template \n" + " void foo() {}\n" + "};\n" + "\n" + "int main()\n" + "{\n" + " A().foo();\n" + " return 0;\n" + "}\n" + ); + + REQUIRE( /*NOT!*/ !utility::containsElement( + client->methods, L"public void A::foo() <6:2 <6:7 6:9> 6:14>" + )); + REQUIRE(utility::containsElement( + client->templateSpecializations, L"void A::foo() -> void A::foo() <6:7 6:9>" + )); +} + +TEST_CASE("cxx parser finds lambda definition and call in function") +{ + std::shared_ptr client = parseCode( + "void lambdaCaller()\n" + "{\n" + " [](){}();\n" + "}\n" + ); + + // TODO: fix + // TS_ASSERT(utility::containsElement( + // client->functions, L"void lambdaCaller::lambda at 3:2() const <3:5 <3:2 3:2> 3:7>" + // )); + REQUIRE(utility::containsElement( + client->calls, L"void lambdaCaller() -> void lambdaCaller::lambda at 3:2() const <3:8 3:8>" + )); +} + +TEST_CASE("cxx parser finds mutable lambda definition") +{ + std::shared_ptr client = parseCode( + "void lambdaWrapper()\n" + "{\n" + " [](int foo) mutable { return foo; };\n" + "}\n" + ); + + // TODO: fix + // TS_ASSERT(utility::containsElement( + // client->functions, L"int lambdaWrapper::lambda at 3:2(int) <3:14 <3:2 3:2> 3:36>" + // )); +} + +TEST_CASE("cxx parser finds local variable declared in lambda capture") +{ + std::shared_ptr client = parseCode( + "void lambdaWrapper()\n" + "{\n" + " [x(42)]() { return x; };\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<3:3> <3:3 3:3>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<3:3> <3:21 3:21>" + )); +} + +TEST_CASE("cxx parser finds definition of local symbol in function parameter list") +{ + std::shared_ptr client = parseCode( + "void test(int a)\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:15> <1:15 1:15>" + )); +} + +TEST_CASE("cxx parser finds definition of local symbol in function scope") +{ + std::shared_ptr client = parseCode( + "void test()\n" + "{\n" + " int a;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<3:6> <3:6 3:6>" + )); +} + +/////////////////////////////////////////////////////////////////////////////// +// test finding nested symbol definitions and declarations + +TEST_CASE("cxx parser finds class definition in class") +{ + std::shared_ptr client = parseCode( + "class A\n" + "{\n" + "public:\n" + " class B;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->classes, L"public A::B <4:8 4:8>" + )); +} + +TEST_CASE("cxx parser finds class definition in namespace") +{ + std::shared_ptr client = parseCode( + "namespace a\n" + "{\n" + " class B;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->classes, L"a::B <3:8 3:8>" + )); +} + +TEST_CASE("cxx parser finds struct definition in class") +{ + std::shared_ptr client = parseCode( + "class A\n" + "{\n" + " struct B\n" + " {\n" + " };\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->structs, L"private A::B <3:2 <3:9 3:9> 5:2>" + )); +} + +TEST_CASE("cxx parser finds struct definition in namespace") +{ + std::shared_ptr client = parseCode( + "namespace A\n" + "{\n" + " struct B\n" + " {\n" + " };\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->structs, L"A::B <3:2 <3:9 3:9> 5:2>" + )); +} + +TEST_CASE("cxx parser finds struct definition in function") +{ + std::shared_ptr client = parseCode( + "void foo(int)\n" + "{\n" + " struct B\n" + " {\n" + " };\n" + "};\n" + "void foo(float)\n" + "{\n" + " struct B\n" + " {\n" + " };\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->structs, L"foo::B <3:2 <3:9 3:9> 5:2>" + )); + REQUIRE(utility::containsElement( + client->structs, L"foo::B <9:2 <9:9 9:9> 11:2>" + )); +} + +TEST_CASE("cxx parser finds variable definitions in namespace scope") +{ + std::shared_ptr client = parseCode( + "namespace n" + "{\n" + " int x;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->globalVariables, L"int n::x <2:6 2:6>" + )); +} + +TEST_CASE("cxx parser finds field in nested class") +{ + std::shared_ptr client = parseCode( + "class B\n" + "{\n" + "public:\n" + " class C\n" + " {\n" + " private:\n" + " static const int amount;\n" + " };\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->fields, L"private static const int B::C::amount <7:20 7:25>" + )); +} + +TEST_CASE("cxx parser finds function in anonymous namespace") +{ + std::shared_ptr client = parseCode( + "namespace\n" + "{\n" + " int sum(int a, int b);\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->functions, L"int anonymous namespace (input.cc<1:1>)::sum(int, int) <3:2 <3:6 3:8> 3:22>" + )); +} + +TEST_CASE("cxx parser finds method declared in nested class") +{ + std::shared_ptr client = parseCode( + "class B\n" + "{\n" + " class C\n" + " {\n" + " bool isGreat() const;\n" + " };\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->methods, L"private bool B::C::isGreat() const <5:3 <5:8 5:14> 5:22>" + )); +} + +TEST_CASE("cxx parser finds nested named namespace") +{ + std::shared_ptr client = parseCode( + "namespace A\n" + "{\n" + " namespace B\n" + " {\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->namespaces, L"A::B <3:2 <3:12 3:12> 5:2>" + )); +} + +TEST_CASE("cxx parser finds enum defined in class") +{ + std::shared_ptr client = parseCode( + "class B\n" + "{\n" + "public:\n" + " enum Z\n" + " {\n" + " };\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->enums, L"public B::Z <4:2 <4:7 4:7> 6:2>" + )); +} + +TEST_CASE("cxx parser finds enum defined in namespace") +{ + std::shared_ptr client = parseCode( + "namespace n\n" + "{\n" + " enum Z\n" + " {\n" + " };\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->enums, L"n::Z <3:2 <3:7 3:7> 5:2>" + )); +} + +TEST_CASE("cxx parser finds enum definition in template class") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + " enum TestType\n" + " {\n" + " TEST_ONE,\n" + " TEST_TWO\n" + " };\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->enums, L"private A::TestType <4:2 <4:7 4:14> 8:2>" + )); +} + +TEST_CASE("cxx parser finds enum constants in template class") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + " enum TestType\n" + " {\n" + " TEST_ONE,\n" + " TEST_TWO\n" + " };\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->enumConstants, L"A::TestType::TEST_ONE <6:3 6:10>" + )); +} + +/////////////////////////////////////////////////////////////////////////////// +// test qualifier locations + +TEST_CASE("cxx parser finds qualifier of access to global variable defined in namespace") +{ + std::shared_ptr client = parseCode( + "namespace foo {\n" + " namespace bar {\n" + " int x;\n" + " }\n" + "}\n" + "void f() {\n" + " foo::bar::x = 9;\n" + "}\n" + ); + + REQUIRE(utility::containsElement(client->qualifiers, L"foo <7:2 7:4>")); + REQUIRE(utility::containsElement(client->qualifiers, L"foo::bar <7:7 7:9>")); +} + +TEST_CASE("cxx parser finds qualifier of access to static field") +{ + std::shared_ptr client = parseCode( + "class Foo {\n" + "public:\n" + " struct Bar {\n" + " public:\n" + " static int x;\n" + " };\n" + "};\n" + "void f() {\n" + " Foo::Bar::x = 9;\n" + "}\n" + ); + + REQUIRE(utility::containsElement(client->qualifiers, L"Foo <9:2 9:4>")); + REQUIRE(utility::containsElement(client->qualifiers, L"Foo::Bar <9:7 9:9>")); +} + +TEST_CASE("cxx parser finds qualifier of access to enum constant") +{ + std::shared_ptr client = parseCode( + "enum Foo {\n" + " FOO_V\n" + "};\n" + "void f() {\n" + " Foo v = Foo::FOO_V;\n" + "}\n" + ); + + REQUIRE(utility::containsElement(client->qualifiers, L"Foo <5:10 5:12>")); +} + +TEST_CASE("cxx parser finds qualifier of reference to method") +{ + std::shared_ptr client = parseCode( + "class Foo {\n" + "public:\n" + " static void my_int_func(int x) {\n" + " }\n" + "};\n" + "\n" + "void test() {\n" + " void(*foo)(int);\n" + " foo = &Foo::my_int_func;\n" + "}\n" + ); + + REQUIRE(utility::containsElement(client->qualifiers, L"Foo <9:9 9:11>")); +} + +TEST_CASE("cxx parser finds qualifier of constructor call") +{ + std::shared_ptr client = parseCode( + "class Foo {\n" + "public:\n" + " Foo(int i) {}\n" + "};\n" + "\n" + "class Bar : public Foo {\n" + "public:\n" + " Bar() : Foo::Foo(4) {}\n" + "};\n" + ); + + REQUIRE(utility::containsElement(client->qualifiers, L"Foo <8:10 8:12>")); +} + +/////////////////////////////////////////////////////////////////////////////// +// test implicit symbols + +TEST_CASE("cxx parser finds builtin types") +{ + std::shared_ptr client = parseCode( + "void t1(int v) {}\n" + "void t2(float v) {}\n" + "void t3(double v) {}\n" + "void t4(bool v) {}\n" + ); + + REQUIRE(utility::containsElement(client->builtinTypes, L"void")); + REQUIRE(utility::containsElement(client->builtinTypes, L"int")); + REQUIRE(utility::containsElement(client->builtinTypes, L"float")); + REQUIRE(utility::containsElement(client->builtinTypes, L"double")); + REQUIRE(utility::containsElement(client->builtinTypes, L"bool")); +} + +TEST_CASE("cxx parser finds implicit copy constructor") +{ + std::shared_ptr client = parseCode( + "class TestClass {}\n" + "void foo()\n" + "{\n" + " TestClass a;\n" + " TestClass b(a);\n" + "}\n" + ); + + REQUIRE(utility::containsElement(client->methods, L"public void TestClass::TestClass() <1:7 <1:7 1:15> 1:15>")); + REQUIRE(utility::containsElement(client->methods, L"public void TestClass::TestClass(const TestClass &) <1:7 <1:7 1:15> 1:15>")); + REQUIRE(utility::containsElement(client->methods, L"public void TestClass::TestClass(TestClass &&) <1:7 <1:7 1:15> 1:15>")); +} + +/////////////////////////////////////////////////////////////////////////////// +// test finding usages of symbols + +TEST_CASE("cxx parser finds enum usage in template class") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + " enum TestType\n" + " {\n" + " TEST_ONE,\n" + " TEST_TWO\n" + " };\n" + " TestType foo;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"A::TestType A::foo -> A::TestType <9:2 9:9>" + )); +} + +TEST_CASE("cxx parser finds correct field member type of nested template class in declaration") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "public:\n" + " class B\n" + " {\n" + " T foo;\n" + " };\n" + "};\n" + "A a;\n" + "A::B b;\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:20> <7:3 7:3>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"int A::B::foo -> int <7:3 7:3>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"A a -> A <10:1 10:1>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"A -> int <10:3 10:5>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"A a -> int <10:3 10:5>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"A::B b -> A::B <11:9 11:9>" + )); +} + +TEST_CASE("cxx parser finds type usage of global variable") +{ + std::shared_ptr client = parseCode( + "int x;\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"int x -> int <1:1 1:3>" + )); +} + +TEST_CASE("cxx parser finds typedefs type use") +{ + std::shared_ptr client = parseCode( + "typedef unsigned int uint;\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"uint -> unsigned int <1:9 1:16>" + )); +} + +TEST_CASE("cxx parser finds typedef that uses type defined in named namespace") +{ + std::shared_ptr client = parseCode( + "namespace test\n" + "{\n" + " struct TestStruct{};\n" + "}\n" + "typedef test::TestStruct globalTestStruct;\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"globalTestStruct -> test::TestStruct <5:15 5:24>" + )); +} + +TEST_CASE("cxx parser finds type use of typedef") +{ + std::shared_ptr client = parseCode( + "typedef unsigned int uint;\n" + "uint number;\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"uint number -> uint <2:1 2:4>" + )); +} + +TEST_CASE("cxx parser finds class default private inheritance") +{ + std::shared_ptr client = parseCode( + "class A {};\n" + "class B : A {};\n" + ); + + REQUIRE(utility::containsElement( + client->inheritances, L"B -> A <2:11 2:11>" + )); +} + +TEST_CASE("cxx parser finds class public inheritance") +{ + std::shared_ptr client = parseCode( + "class A {};\n" + "class B : public A {};\n" + ); + + REQUIRE(utility::containsElement( + client->inheritances, L"B -> A <2:18 2:18>" + )); +} + +TEST_CASE("cxx parser finds class protected inheritance") +{ + std::shared_ptr client = parseCode( + "class A {};\n" + "class B : protected A {};\n" + ); + + REQUIRE(utility::containsElement( + client->inheritances, L"B -> A <2:21 2:21>" + )); +} + +TEST_CASE("cxx parser finds class private inheritance") +{ + std::shared_ptr client = parseCode( + "class A {};\n" + "class B : private A {};\n" + ); + + REQUIRE(utility::containsElement( + client->inheritances, L"B -> A <2:19 2:19>" + )); +} + +TEST_CASE("cxx parser finds class multiple inheritance") +{ + std::shared_ptr client = parseCode( + "class A {};\n" + "class B {};\n" + "class C\n" + " : public A\n" + " , private B\n" + "{};\n" + ); + + REQUIRE(utility::containsElement( + client->inheritances, L"C -> A <4:11 4:11>" + )); + REQUIRE(utility::containsElement( + client->inheritances, L"C -> B <5:12 5:12>" + )); +} + +TEST_CASE("cxx parser finds struct default public inheritance") +{ + std::shared_ptr client = parseCode( + "struct A {};\n" + "struct B : A {};\n" + ); + + REQUIRE(utility::containsElement( + client->inheritances, L"B -> A <2:12 2:12>" + )); +} + +TEST_CASE("cxx parser finds struct public inheritance") +{ + std::shared_ptr client = parseCode( + "struct A {};\n" + "struct B : public A {};\n" + ); + + REQUIRE(utility::containsElement( + client->inheritances, L"B -> A <2:19 2:19>" + )); +} + +TEST_CASE("cxx parser finds struct protected inheritance") +{ + std::shared_ptr client = parseCode( + "struct A {};\n" + "struct B : protected A {};\n" + ); + + REQUIRE(utility::containsElement( + client->inheritances, L"B -> A <2:22 2:22>" + )); +} + +TEST_CASE("cxx parser finds struct private inheritance") +{ + std::shared_ptr client = parseCode( + "struct A {};\n" + "struct B : private A {};\n" + ); + + REQUIRE(utility::containsElement( + client->inheritances, L"B -> A <2:20 2:20>" + )); +} + +TEST_CASE("cxx parser finds struct multiple inheritance") +{ + std::shared_ptr client = parseCode( + "struct A {};\n" + "struct B {};\n" + "struct C\n" + " : public A\n" + " , private B\n" + "{};\n" + ); + + REQUIRE(utility::containsElement( + client->inheritances, L"C -> A <4:11 4:11>" + )); + REQUIRE(utility::containsElement( + client->inheritances, L"C -> B <5:12 5:12>" + )); +} + +TEST_CASE("cxx parser finds method override when virtual") +{ + std::shared_ptr client = parseCode( + "class A {\n" + " virtual void foo();\n" + "};\n" + "class B : public A {\n" + " void foo();\n" + "};" + ); + + REQUIRE(utility::containsElement( + client->overrides, L"void B::foo() -> void A::foo() <5:7 5:9>" + )); +} + +TEST_CASE("cxx parser finds multi layer method overrides") +{ + std::shared_ptr client = parseCode( + "class A {\n" + " virtual void foo();\n" + "};\n" + "class B : public A {\n" + " void foo();\n" + "};\n" + "class C : public B {\n" + " void foo();\n" + "};" + ); + + REQUIRE(utility::containsElement( + client->overrides, L"void B::foo() -> void A::foo() <5:7 5:9>" + )); + REQUIRE(utility::containsElement( + client->overrides, L"void C::foo() -> void B::foo() <8:7 8:9>" + )); +} + +TEST_CASE("cxx parser finds method overrides on different return types") +{ + std::shared_ptr client = parseCode( + "class A {\n" + " virtual void foo();\n" + "};\n" + "class B : public A {\n" + " int foo();\n" + "};\n" + ); + + REQUIRE(client->errors.size() == 1); + REQUIRE(utility::containsElement( + client->overrides, L"int B::foo() -> void A::foo() <5:6 5:8>" + )); +} + +TEST_CASE("cxx parser finds no method override when not virtual") +{ + std::shared_ptr client = parseCode( + "class A {\n" + " void foo();\n" + "};\n" + "class B : public A {\n" + " void foo();\n" + "};" + ); + + REQUIRE(client->overrides.size() == 0); +} + +TEST_CASE("cxx parser finds no method overrides on different signatures") +{ + std::shared_ptr client = parseCode( + "class A {\n" + " virtual void foo(int a);\n" + "};\n" + "class B : public A {\n" + " int foo(int a, int b);\n" + "};\n" + ); + + REQUIRE(client->overrides.size() == 0); +} + +TEST_CASE("cxx parser finds using directive decl in function context") +{ + std::shared_ptr client = parseCode( + "void foo()\n" + "{\n" + " using namespace std;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"void foo() -> std <3:18 3:20>" + )); +} + +TEST_CASE("cxx parser finds using directive decl in file context") +{ + std::shared_ptr client = parseCode( + "using namespace std;\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"input.cc -> std <1:17 1:19>" + )); +} + +TEST_CASE("cxx parser finds using decl in function context") +{ + std::shared_ptr client = parseCode( + "namespace foo\n" + "{\n" + " int a;\n" + "}\n" + "void bar()\n" + "{\n" + " using foo::a;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"void bar() -> foo::a <7:13 7:13>" + )); +} + +TEST_CASE("cxx parser finds using decl in file context") +{ + std::shared_ptr client = parseCode( + "namespace foo\n" + "{\n" + " int a;\n" + "}\n" + "using foo::a;\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"input.cc -> foo::a <5:12 5:12>" + )); +} + +TEST_CASE("cxx parser finds call in function") +{ + std::shared_ptr client = parseCode( + "int sum(int a, int b)\n" + "{\n" + " return a + b;\n" + "}\n" + "int main()\n" + "{\n" + " sum(1, 2);\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"int main() -> int sum(int, int) <7:2 7:4>" + )); +} + +TEST_CASE("cxx parser finds call in function with correct signature") +{ + std::shared_ptr client = parseCode( + "int sum(int a, int b)\n" + "{\n" + " return a + b;\n" + "}\n" + "void func()\n" + "{\n" + "}\n" + "void func(bool right)\n" + "{\n" + " sum(1, 2);\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"void func(bool) -> int sum(int, int) <10:2 10:4>" + )); +} + +TEST_CASE("cxx parser finds call to function with right signature") +{ + std::shared_ptr client = parseCode( + "int sum(int a, int b)\n" + "{\n" + " return a + b;\n" + "}\n" + "float sum(float a, float b)\n" + "{\n" + " return a + b;\n" + "}\n" + "int main()\n" + "{\n" + " sum(1, 2);\n" + " sum(1.0f, 0.5f);\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"int main() -> int sum(int, int) <11:2 11:4>" + )); + REQUIRE(utility::containsElement( + client->calls, L"int main() -> float sum(float, float) <12:2 12:4>" + )); +} + +TEST_CASE("cxx parser finds function call in function parameter list") +{ + std::shared_ptr client = parseCode( + "int sum(int a, int b)\n" + "{\n" + " return a + b;\n" + "}\n" + "int main()\n" + "{\n" + " return sum(1, sum(2, 3));\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"int main() -> int sum(int, int) <7:16 7:18>" + )); +} + +TEST_CASE("cxx parser finds function call in method") +{ + std::shared_ptr client = parseCode( + "int sum(int a, int b)\n" + "{\n" + " return a + b;\n" + "}\n" + "class App\n" + "{\n" + " int main()\n" + " {\n" + " return sum(1, 2);\n" + " }\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"int App::main() -> int sum(int, int) <9:10 9:12>" + )); +} + +TEST_CASE("cxx parser finds implicit constructor without definition call") +{ + std::shared_ptr client = parseCode( + "class App\n" + "{\n" + "};\n" + "int main()\n" + "{\n" + " App app;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"int main() -> void App::App() <6:6 6:8>" + )); +} + +TEST_CASE("cxx parser finds explicit constructor call") +{ + std::shared_ptr client = parseCode( + "class App\n" + "{\n" + "public:\n" + " App() {}\n" + "};\n" + "int main()\n" + "{\n" + " App();\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"int main() -> void App::App() <8:2 8:4>" + )); +} + +TEST_CASE("cxx parser finds explicit constructor call of field") +{ + std::shared_ptr client = parseCode( + "class Item\n" + "{\n" + "};\n" + "class App\n" + "{\n" + "public:\n" + " App() : item() {}\n" + " Item item;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"void App::App() -> void Item::Item() <7:10 7:13>" + )); +} + +TEST_CASE("cxx parser finds function call in member initialization") +{ + std::shared_ptr client = parseCode( + "int one() { return 1; }\n" + "class Item\n" + "{\n" + "public:\n" + " Item(int n) {}\n" + "};\n" + "class App\n" + "{\n" + " App()\n" + " : item(one())" + " {}\n" + " Item item;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"void App::App() -> int one() <10:10 10:12>" + )); +} + +TEST_CASE("cxx parser finds copy constructor call") +{ + std::shared_ptr client = parseCode( + "class App\n" + "{\n" + "public:\n" + " App() {}\n" + " App(const App& other) {}\n" + "};\n" + "int main()\n" + "{\n" + " App app;\n" + " App app2(app);\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"int main() -> void App::App(const App &) <10:6 10:9>" + )); +} + +TEST_CASE("cxx parser finds global variable constructor call") +{ + std::shared_ptr client = parseCode( + "class App\n" + "{\n" + "public:\n" + " App() {}\n" + "};\n" + "App app;\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"App app -> void App::App() <6:5 6:7>" + )); +} + +TEST_CASE("cxx parser finds global variable function call") +{ + std::shared_ptr client = parseCode( + "int one() { return 1; }\n" + "int a = one();\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"int a -> int one() <2:9 2:11>" + )); +} + +TEST_CASE("cxx parser finds operator call") +{ + std::shared_ptr client = parseCode( + "class App\n" + "{\n" + "public:\n" + " void operator+(int a)\n" + " {\n" + " }\n" + "};\n" + "int main()\n" + "{\n" + " App app;\n" + " app + 2;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"int main() -> void App::operator+(int) <11:6 11:6>" + )); +} + +TEST_CASE("cxx parser finds usage of function pointer") +{ + std::shared_ptr client = parseCode( + "void my_int_func(int x)\n" + "{\n" + "}\n" + "\n" + "void test()\n" + "{\n" + " void (*foo)(int);\n" + " foo = &my_int_func;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"void test() -> void my_int_func(int) <8:9 8:19>" + )); +} + +TEST_CASE("cxx parser finds usage of global variable in function") +{ + std::shared_ptr client = parseCode( + "int bar;\n" + "\n" + "int main()\n" + "{\n" + " bar = 1;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"int main() -> int bar <5:2 5:4>" + )); +} + +TEST_CASE("cxx parser finds usage of global variable in global variable initialization") +{ + std::shared_ptr client = parseCode( + "int a = 0;\n" + "int b[] = {a};\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"int [] b -> int a <2:12 2:12>" + )); +} + +TEST_CASE("cxx parser finds usage of global variable in method") +{ + std::shared_ptr client = parseCode( + "int bar;\n" + "\n" + "class App\n" + "{\n" + " void foo()\n" + " {\n" + " bar = 1;\n" + " }\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"void App::foo() -> int bar <7:3 7:5>" + )); +} + +TEST_CASE("cxx parser finds usage of field in method") +{ + std::shared_ptr client = parseCode( + "class App\n" + "{\n" + " void foo()\n" + " {\n" + " bar = 1;\n" + " this->bar = 2;\n" + " }\n" + " int bar;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"void App::foo() -> int App::bar <5:3 5:5>" + )); + REQUIRE(utility::containsElement( + client->usages, L"void App::foo() -> int App::bar <6:9 6:11>" + )); +} + +TEST_CASE("cxx parser finds usage of field in function call arguments") +{ + std::shared_ptr client = parseCode( + "class A\n" + "{\n" + "public:\n" + " void foo(int i)\n" + " {\n" + " foo(bar);\n" + " }\n" + " int bar;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"void A::foo(int) -> int A::bar <6:7 6:9>" + )); +} + +TEST_CASE("cxx parser finds usage of field in function call context") +{ + std::shared_ptr client = parseCode( + "class A\n" + "{\n" + "public:\n" + " void foo(int i)\n" + " {\n" + " a->foo(6);\n" + " }\n" + " A* a;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"void A::foo(int) -> A * A::a <6:3 6:3>" + )); +} + +TEST_CASE("cxx parser finds usage of field in initialization list") +{ + std::shared_ptr client = parseCode( + "class App\n" + "{\n" + " App()\n" + " : bar(42)\n" + " {}\n" + " int bar;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"void App::App() -> int App::bar <4:5 4:7>" + )); +} + +TEST_CASE("cxx parser finds usage of member in call expression to unresolved member expression") +{ + std::shared_ptr client = parseCode( + "class A {\n" + " template \n" + " T run() { return 5; }\n" + "};\n" + "class B {\n" + " template \n" + " T run() {\n" + " return a.run();\n" + " }\n" + " A a;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"T B::run() -> A B::a <8:10 8:10>" + )); +} + +TEST_CASE("cxx parser finds usage of member in temporary object expression") +{ + std::shared_ptr client = parseCode( + "class Foo\n" + "{\n" + "public:\n" + " Foo() { }\n" + " Foo(const Foo& i, int d) { }\n" + "};\n" + "\n" + "class Bar\n" + "{\n" + "public:\n" + " Bar(): m_i() {}\n" + "\n" + " void baba()\n" + " {\n" + " Foo(m_i, 4);\n" + " }\n" + "\n" + " const Foo m_i;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"void Bar::baba() -> const Foo Bar::m_i <15:7 15:9>" + )); +} + +TEST_CASE("cxx parser finds usage of member in dependent scope member expression") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + " T m_t;\n" + "\n" + " void foo()\n" + " {\n" + " m_t.run();\n" + " }\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"void A::foo() -> T A::m_t <8:3 8:5>" + )); +} + +TEST_CASE("cxx parser finds return type use in function") +{ + std::shared_ptr client = parseCode( + "double PI()\n" + "{\n" + " return 3.14159265359;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"double PI() -> double <1:1 1:6>" + )); +} + +TEST_CASE("cxx parser finds parameter type uses in function") +{ + std::shared_ptr client = parseCode( + "void ceil(float a)\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"void ceil(float) -> float <1:11 1:15>" + )); +} + +TEST_CASE("cxx parser finds use of decayed parameter type in function") +{ + std::shared_ptr client = parseCode( + "template\n" + "class VectorBase\n" + "{\n" + "public:\n" + " VectorBase(T values[N]);\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:16> <5:13 5:13>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:32> <5:22 5:22>" + )); +} + +TEST_CASE("cxx parser usage of injected type in method declaration") +{ + std::shared_ptr client = parseCode( + "template \n" + "class Foo\n" + "{\n" + " Foo& operator=(const Foo&) = delete;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"Foo & Foo::operator=(const Foo &) -> Foo <4:2 4:4>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"Foo & Foo::operator=(const Foo &) -> Foo <4:23 4:25>" + )); +} + +TEST_CASE("cxx parser finds use of qualified type in function") +{ + std::shared_ptr client = parseCode( + "void test(const int t)\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"void test(const int) -> int <1:17 1:19>" + )); +} + +TEST_CASE("cxx parser finds parameter type uses in constructor") +{ + std::shared_ptr client = parseCode( + "class A\n" + "{\n" + " A(int a);\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"void A::A(int) -> int <3:4 3:6>" + )); +} + +TEST_CASE("cxx parser finds type uses in function body") +{ + std::shared_ptr client = parseCode( + "int main()\n" + "{\n" + " int a = 42;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> int <3:2 3:4>" + )); +} + +TEST_CASE("cxx parser finds type uses in method body") +{ + std::shared_ptr client = parseCode( + "class A\n" + "{\n" + " int main()\n" + " {\n" + " int a = 42;\n" + " return a;\n" + " }\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"int A::main() -> int <5:3 5:5>" + )); +} + +TEST_CASE("cxx parser finds type uses in loops and conditions") +{ + std::shared_ptr client = parseCode( + "int main()\n" + "{\n" + " if (true)\n" + " {\n" + " int a = 42;\n" + " }\n" + " for (int i = 0; i < 10; i++)\n" + " {\n" + " int b = i * 2;\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> int <5:3 5:5>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> int <7:7 7:9>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> int <9:3 9:5>" + )); +} + +TEST_CASE("cxx parser finds type uses of base class in derived constructor") +{ + std::shared_ptr client = parseCode( + "class A\n" + "{\n" + "public:\n" + " A(int n) {}\n" + "};\n" + "class B : public A\n" + "{\n" + "public:\n" + " B() : A(42) {}\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"void B::B() -> A <9:8 9:8>" + )); +} + +TEST_CASE("cxx parser finds enum uses in global space") +{ + std::shared_ptr client = parseCode( + "enum A\n" + "{\n" + " B,\n" + " C\n" + "};\n" + "A a = B;\n" + "A* aPtr = new A;\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"A a -> A::B <6:7 6:7>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"A a -> A <6:1 6:1>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"A * aPtr -> A <7:1 7:1>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"A * aPtr -> A <7:15 7:15>" + )); +} + +TEST_CASE("cxx parser finds enum uses in function body") +{ + std::shared_ptr client = parseCode( + "enum A\n" + "{\n" + " B,\n" + " C\n" + "};\n" + "int main()\n" + "{\n" + " A a = B;\n" + " A* aPtr = new A;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"int main() -> A::B <8:8 8:8>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> A <8:2 8:2>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> A <9:2 9:2>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> A <9:16 9:16>" + )); +} + +TEST_CASE("cxx parser finds usage of template parameter of template member variable declaration") +{ + std::shared_ptr client = parseCode( + "template \n" + "struct IsBaseType {\n" + " static const bool value = true;\n" + "};\n" + "template \n" + "const bool IsBaseType::value;\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:20> <1:20 1:20>" + )); + REQUIRE(utility::containsElement( // TODO: fix FAIL because usage in name qualifier is not recorded + client->localSymbols, L"input.cc<5:20> <5:20 5:20>" + )); +} + +TEST_CASE("cxx parser finds usage of template parameters with different depth of template function") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + " template \n" + " void foo(Q q)\n" + " {\n" + " T t;\n" + " t.run(q);\n" + " }\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:20> <7:3 7:3>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<4:21> <5:11 5:11>" + )); +} + +TEST_CASE("cxx parser finds usage of template parameters with different depth of partial class template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + " template \n" + " class B\n" + " {\n" + " T foo(Q q, R r);\n" + " };\n" + "\n" + " template \n" + " class B\n" + " {\n" + " T foo(R r);\n" + " };\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:20> <13:3 13:3>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<10:21> <13:9 13:9>" + )); +} + +TEST_CASE("cxx parser finds usage of template template parameter of template class explicitly instantiated with concrete type argument") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{};\n" + "template class T>\n" + "class B\n" + "{\n" + " void foo(T parameter)\n" + " {}\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<4:36> <7:11 7:11>" + )); +} + +TEST_CASE("cxx parser finds usage of template template parameter of template class explicitly instantiated with template type") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{};\n" + "template class T>\n" + "class B\n" + "{\n" + " template \n" + " void foo(T parameter)\n" + " {}\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<4:36> <8:11 8:11>" + )); +} + +TEST_CASE("cxx parser finds typedef in other class that depends on own template parameter") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "public:\n" + " typedef T type;\n" + "};\n" + "template \n" + "class B\n" + "{\n" + "public:\n" + " typedef typename A::type type;\n" + "};\n" + "B::type f = 0;\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"A::type -> int <5:10 5:10>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"B::type -> A::type <11:25 11:28>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"B::type -> A::type <11:25 11:28>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"B::type f -> B::type <13:9 13:12>" + )); +} + +TEST_CASE("cxx parser finds use of dependent template specialization type") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "public:\n" + " template \n" + " using type = T;\n" + "};\n" + "template \n" + "class B\n" + "{\n" + "public:\n" + " typedef typename A::template type type;\n" + "};\n" + "B::type f = 0.0f;\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"B::type -> A::type <12:10 12:17>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"B::type f -> B::type <14:10 14:13>" + )); +} + +TEST_CASE("cxx parser creates single node for all possible parameter pack expansions of template function") +{ + std::shared_ptr client = parseCode( + "template\n" + "T adder(T v) { return v; }\n" + "\n" + "template\n" + "T adder(T first, Args... args) { return first + adder(args...); }\n" + "\n" + "void foo() { long sum = adder(1, 2, 3, 8, 7); }\n" + ); + + REQUIRE(utility::containsElement( + client->functions, L"int adder>(int, ...) <5:1 <5:1 <5:3 5:7> 5:30> 5:65>" + )); + REQUIRE(utility::containsElement( + client->calls, L"int adder>(int, ...) -> int adder>(int, ...) <5:49 5:53>" + )); +} + +TEST_CASE("cxx parser finds type template argument of explicit template instantiation") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "};\n" + "int main()\n" + "{\n" + " A a;\n" + " return 0;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"A -> int <7:4 7:6>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> int <7:4 7:6>" + )); +} + +TEST_CASE("cxx parser finds type template argument of explicit template instantiated with function prototype") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "};\n" + "void foo()\n" + "{\n" + " A a;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"A -> int <7:4 7:6>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"void foo() -> int <7:4 7:6>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"void foo() -> int <7:4 7:6>" + )); +} + +TEST_CASE("cxx parser finds type template argument for parameter pack of explicit template instantiation") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "};\n" + "int main()\n" + "{\n" + " A();\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"A<<...>> -> int <7:6 7:8>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"A<<...>> -> float <7:11 7:15>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> int <7:6 7:8>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> float <7:11 7:15>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> int <7:6 7:8>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> float <7:11 7:15>" + )); +} + +TEST_CASE("cxx parser finds type template argument in non default constructor of explicit template instaitiation") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "public:\n" + " A(int data){}\n" + "};\n" + "int main()\n" + "{\n" + " A(5);\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"A -> int <9:4 9:6>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> int <9:4 9:6>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> int <9:4 9:6>" + )); +} + +TEST_CASE("cxx parser finds type template argument in default constructor of explicit template instaitiation") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "public:\n" + " A(){}\n" + "};\n" + "int main()\n" + "{\n" + " A();\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"A -> int <9:4 9:6>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> int <9:4 9:6>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> int <9:4 9:6>" + )); +} + +TEST_CASE("cxx parser finds type template argument in new expression of explicit template instaitiation") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "public:\n" + " A(){}\n" + "};\n" + "int main()\n" + "{\n" + " new A();\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"A -> int <9:8 9:10>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> int <9:8 9:10>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> int <9:8 9:10>" + )); +} + +TEST_CASE("cxx parser finds no template argument for builtin non type int template parameter of explicit template instantiation") +{ + std::shared_ptr client = parseCode( + "template \n" // use of "int" + "class A\n" + "{\n" + "};\n" + "int main()\n" // use of "int" + "{\n" + " A<1> a;\n" // use of "A" + " return 0;\n" + "}\n" + ); + + REQUIRE(client->typeUses.size() == 3); +} + +TEST_CASE("cxx parser finds no template argument for builtin non type bool template parameter of explicit template instantiation") +{ + std::shared_ptr client = parseCode( + "template \n" // use of "bool" + "class A\n" + "{\n" + "};\n" + "int main()\n" // use of "int" + "{\n" + " A a;\n" // use of "A" + " return 0;\n" + "}\n" + ); + + REQUIRE(client->typeUses.size() == 3); +} + +TEST_CASE("cxx parser finds non type custom pointer template argument of implicit template instantiation") +{ + std::shared_ptr client = parseCode( + "class P\n" + "{};\n" + "template \n" + "class A\n" + "{};\n" + "P g_p;\n" + "int main()\n" + "{\n" + " A<&g_p> a;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"A<&g_p> -> P g_p <9:5 9:7>" + )); +} + +TEST_CASE("cxx parser finds non type custom reference template argument of implicit template instantiation") +{ + std::shared_ptr client = parseCode( + "class P\n" + "{};\n" + "template \n" + "class A\n" + "{};\n" + "P g_p;\n" + "int main()\n" + "{\n" + " A a;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"A<&g_p> -> P g_p <9:4 9:6>" + )); +} + +TEST_CASE("cxx parser finds no template argument for builtin non type int template parameter pack of explicit template instantiation") +{ + std::shared_ptr client = parseCode( + "template \n" // use of "int" + "class A\n" + "{\n" + "};\n" + "int main()\n" // use of "int" + "{\n" + " A<1, 2, 33>();\n" // use of "A" + "}\n" + ); + + REQUIRE(client->typeUses.size() == 3); +} + +TEST_CASE("cxx parser finds template template argument of explicit template instantiation") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{};\n" + "template class T>\n" + "class B\n" + "{};\n" + "int main()\n" + "{\n" + " B ba;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"B -> A <9:4 9:4>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> A <9:4 9:4>" + )); +} + +TEST_CASE("cxx parser finds template template argument for parameter pack of explicit template instantiation") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "};\n" + "template typename... T>\n" + "class B\n" + "{\n" + "};\n" + "int main()\n" + "{\n" + " B();\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"B<<...>> -> A <11:4 11:4>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"B<<...>> -> A <11:7 11:7>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> A <11:4 11:4>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> A <11:7 11:7>" + )); +} + +TEST_CASE("cxx parser finds template argument for implicit specialization of global template variable") +{ + std::shared_ptr client = parseCode( + "template \n" + "T v;\n" + "void test()\n" + "{\n" + " v = 9;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"int v -> int <5:4 5:6>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"void test() -> int <5:4 5:6>" + )); +} + +TEST_CASE("cxx parser finds template member specialization for method of implicit template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "public:\n" + " T foo() {}\n" + "};\n" + "int main()\n" + "{\n" + " A a;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->templateSpecializations, L"int A::foo() -> T A::foo() <5:4 5:6>" + )); +} + +TEST_CASE("cxx parser finds template member specialization for static variable of implicit template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "public:\n" + " static T foo;\n" + "};\n" + "int main()\n" + "{\n" + " A a;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->templateSpecializations, L"static int A::foo -> static T A::foo <5:11 5:13>" + )); +} + +TEST_CASE("cxx parser finds template member specialization for field of implicit template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "public:\n" + " T foo;\n" + "};\n" + "int main()\n" + "{\n" + " A a;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->templateSpecializations, L"int A::foo -> T A::foo <5:4 5:6>" + )); +} + +TEST_CASE("cxx parser finds template member specialization for field of member class of implicit template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "public:\n" + " class B {\n" + " public:\n" + " T foo;\n" + " };\n" + "};\n" + "int main()\n" + "{\n" + " A::B b;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->templateSpecializations, L"int A::B::foo -> T A::B::foo <7:5 7:7>" + )); +} + +TEST_CASE("cxx parser finds template member specialization for member class of implicit template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "public:\n" + " class B {};\n" + "};\n" + "int main()\n" + "{\n" + " A a;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->templateSpecializations, L"A::B -> A::B <5:8 5:8>" + )); +} + +TEST_CASE("cxx parser finds type template argument of explicit template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "};\n" + "template <>\n" + "class A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"A -> int <6:9 6:11>" + )); +} + +TEST_CASE("cxx parser finds no template argument for builtin non type int template parameter of explicit template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" // use of "int" + "class A\n" + "{\n" + "};\n" + "template <>\n" + "class A<1>\n" + "{\n" + "};\n" + ); + + REQUIRE(client->typeUses.size() == 1); +} + +TEST_CASE("cxx parser finds no template argument for builtin non type bool template parameter of explicit template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" // use of "bool" + "class A\n" + "{\n" + "};\n" + "template <>\n" + "class A\n" + "{\n" + "};\n" + ); + + REQUIRE(client->typeUses.size() == 1); +} + +TEST_CASE("cxx parser finds non type custom pointer template argument of explicit template specialization") +{ + std::shared_ptr client = parseCode( + "class P\n" + "{};\n" + "template \n" + "class A\n" + "{};\n" + "P g_p;\n" + "template <>\n" + "class A<&g_p>\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"A<&g_p> -> P g_p <8:10 8:12>" + )); +} + +TEST_CASE("cxx parser finds non type custom reference template argument of explicit template specialization") +{ + std::shared_ptr client = parseCode( + "class P\n" + "{};\n" + "template \n" + "class A\n" + "{};\n" + "P g_p;\n" + "template <>\n" + "class A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"A<&g_p> -> P g_p <8:9 8:11>" + )); +} + +TEST_CASE("cxx parser finds template template argument of explicit template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{};\n" + "template class T>\n" + "class B\n" + "{};\n" + "template <>\n" + "class B\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"B -> A <8:9 8:9>" + )); +} + +TEST_CASE("cxx parser finds type template arguments of explicit partial class template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "};\n" + "template \n" + "class A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<5:20> <6:9 6:9>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"A -> int <6:12 6:14>" + )); +} + +TEST_CASE("cxx parser finds no template argument for builtin non type int template parameter of explicit partial class template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "};\n" + "template \n" + "class A<3, U>\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<5:15> <6:12 6:12>" + )); +} + +TEST_CASE("cxx parser finds no template argument for builtin non type bool template parameter of explicit partial class template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "};\n" + "template \n" + "class A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<5:16> <6:15 6:15>" + )); +} + +TEST_CASE("cxx parser finds template argument for non type custom pointer template parameter of explicit partial class template specialization") +{ + std::shared_ptr client = parseCode( + "class P\n" + "{};\n" + "template \n" + "class A\n" + "{};\n" + "P g_p;\n" + "template \n" + "class A<&g_p, q>\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"A<&g_p, q> -> P g_p <8:10 8:12>" // TODO: this is completely wrong? should be a normal usage + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<7:14> <8:15 8:15>" + )); +} + +TEST_CASE("cxx parser finds template argument for non type custom reference template parameter of explicit partial class template specialization") +{ + std::shared_ptr client = parseCode( + "class P\n" + "{};\n" + "template \n" + "class A\n" + "{};\n" + "P g_p;\n" + "template \n" + "class A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"A<&g_p, q> -> P g_p <8:9 8:11>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<7:14> <8:14 8:14>" + )); +} + +TEST_CASE("cxx parser finds template argument for template template parameter of explicit partial class template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{};\n" + "template class T, template class U>\n" + "class B\n" + "{};\n" + "template class U>\n" + "class B\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"B typename U> -> A <8:9 8:9>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<7:36> <8:12 8:12>" + )); +} + +TEST_CASE("cxx parser finds non type template argument that depends on type template parameter of explicit partial class template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "};\n" + "template \n" + "class A<3, T2, T3>\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<5:27> <6:16 6:17>" + )); +} + +TEST_CASE("cxx parser finds non type template argument that depends on template template parameter of explicit partial class template specialization") +{ + std::shared_ptr client = parseCode( + "template class T2, T2 T3>\n" + "class A\n" + "{\n" + "};\n" + "template class T2, T2 T3>\n" + "class A<3, T2, T3>\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<5:36> <6:12 6:13>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<5:48> <6:16 6:17>" + )); +} + +TEST_CASE("cxx parser finds implicit template class specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + " T foo;\n" + "};\n" + "\n" + "A a;\n" + ); + + REQUIRE(utility::containsElement( + client->templateSpecializations, L"A -> A <2:7 2:7>" + )); +} + +TEST_CASE("cxx parser finds class inheritance from implicit template class specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + " T foo;\n" + "};\n" + "\n" + "class B: public A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->inheritances, L"B -> A <7:17 7:17>" + )); +} + +TEST_CASE("record base class of implicit template class specialization") +{ + std::shared_ptr client = parseCode( + "template\n" + "class VectorBase {}; \n" + "\n" + "template\n" + "class Vector2 : public VectorBase { void foo(); }; \n" + "\n" + "typedef Vector2 Vec2f; \n" + "\n" + "Vec2f v; \n" + ); + + REQUIRE(utility::containsElement( + client->inheritances, L"Vector2 -> VectorBase <5:24 5:33>" + )); +} + +TEST_CASE("cxx parser finds template class specialization with template argument") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + " T foo;\n" + "};\n" + "\n" + "template \n" + "class B: public A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<7:20> <8:19 8:19>" + )); + REQUIRE(client->inheritances.size() == 1); + REQUIRE(client->classes.size() == 2); + REQUIRE(client->fields.size() == 1); +} + +TEST_CASE("cxx parser finds correct order of template arguments for explicit class template specialization") +{ + std::shared_ptr client = parseCode( + "template \n" + "class vector { };\n" + "template\n" + "class vector { };\n" + ); + + REQUIRE(utility::containsElement( + client->classes, L"vector <3:1 <4:7 4:12> 4:33>" + )); +} + +TEST_CASE("cxx parser replaces dependent template arguments of explicit template specialization with name of base template") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A {};\n" + "template \n" + "class vector { };\n" + "template\n" + "class vector> { };\n" + ); + + REQUIRE(utility::containsElement( + client->classes, L"vector> <5:1 <6:7 6:12> 6:31>" + )); +} + +TEST_CASE("cxx parser replaces unknown template arguments of explicit template specialization with depth and position index") +{ + std::shared_ptr client = parseCode( + "template \n" + "class foo {\n" + " template \n" + " class vector { };\n" + " template\n" + " class vector { };\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->classes, L"foo::vector <5:2 <6:8 6:13> 6:25>" + )); +} + +TEST_CASE("cxx parser finds template class constructor usage of field") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + " A(): foo() {}\n" + " T foo;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"void A::A() -> T A::foo <4:7 4:9>" + )); +} + +TEST_CASE("cxx parser finds correct method return type of template class in declaration") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + " T foo();\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<1:20> <4:2 4:2>" + )); + + REQUIRE(utility::containsElement( + client->methods, L"private T A::foo() <4:2 <4:4 4:6> 4:8>" + )); +} + +TEST_CASE("cxx parser finds type template default argument type of template class") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"A -> int <1:24 1:26>" + )); +} + +TEST_CASE("cxx parser finds no default argument type for non type bool template parameter of template class") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{\n" + "};\n" + ); + + REQUIRE(client->typeUses.size() == 1);; // only the "bool" type is recorded and nothing for the default arg +} + +TEST_CASE("cxx parser finds template template default argument type of template class") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{};\n" + "template class T = A>\n" + "class B\n" + "{};\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"B typename T> -> A <4:40 4:40>" + )); +} + +TEST_CASE("cxx parser finds implicit instantiation of template function") +{ + std::shared_ptr client = parseCode( + "template \n" + "T test(T a)\n" + "{\n" + " return a;\n" + "};\n" + "\n" + "int main()\n" + "{\n" + " return test(1);\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->templateSpecializations, L"int test(int) -> T test(T) <2:3 2:6>" + )); +} + +TEST_CASE("cxx parser finds explicit specialization of template function") +{ + std::shared_ptr client = parseCode( + "template \n" + "T test(T a)\n" + "{\n" + " return a;\n" + "};\n" + "\n" + "template <>\n" + "int test(int a)\n" + "{\n" + " return a + a;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->templateSpecializations, L"int test(int) -> T test(T) <8:5 8:8>" + )); +} + +TEST_CASE("cxx parser finds explicit type template argument of explicit instantiation of template function") +{ + std::shared_ptr client = parseCode( + "template \n" + "void test()\n" + "{\n" + "};\n" + "\n" + "template <>\n" + "void test()\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"void test() -> int <7:11 7:13>" + )); +} + +TEST_CASE("cxx parser finds explicit type template argument of function call in function") +{ + std::shared_ptr client = parseCode( + "template \n" + "void test(){}\n" + "\n" + "int main()\n" + "{\n" + " test();\n" + " return 1;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"void test() -> int <6:7 6:9>" + )); +} + +TEST_CASE("cxx parser finds no explicit non type int template argument of function call in function") +{ + std::shared_ptr client = parseCode( + "template \n" // use of "int" + "void test(){}\n" // 2x use of "void" + "\n" + "int main()\n" // use of "int" + "{\n" + " test<33>();\n" + " return 1;\n" + "};\n" + ); + + REQUIRE(client->typeUses.size() == 4); +} + +TEST_CASE("cxx parser finds explicit template template argument of function call in function") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A {};\n" + "template class T>\n" + "void test(){};\n" + "int main()\n" + "{\n" + " test();\n" + " return 1;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"void test() -> A <7:7 7:7>" + )); +} + +TEST_CASE("cxx parser finds no implicit type template argument of function call in function") +{ + std::shared_ptr client = parseCode( + "template \n" + "void test(T data){}\n" // 2x use of "void" + 1x use of "int" + "\n" + "int main()\n" // use of "int" + "{\n" + " test(1);\n" + " return 1;\n" + "};\n" + ); + + REQUIRE(client->typeUses.size() == 4); +} + +TEST_CASE("cxx parser finds explicit type template argument of function call in var decl") +{ + std::shared_ptr client = parseCode( + "template \n" + "T test(){ return 1; }\n" + "\n" + "class A\n" + "{\n" + " int foo = test();\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"int test() -> int <6:17 6:19>" + )); +} + +TEST_CASE("cxx parser finds no implicit type template argument of function call in var decl") +{ + std::shared_ptr client = parseCode( + "template \n" + "T test(T i){ return i; }\n" // 2x use of "int" + "\n" + "class A\n" + "{\n" + " int foo = test(1);\n" // usage of "int" + "};\n" + ); + + REQUIRE(client->typeUses.size() == 3); +} + +TEST_CASE("cxx parser finds type template default argument type of template function") +{ + std::shared_ptr client = parseCode( + "template \n" + "void test()\n" + "{\n" + "};\n" + "\n" + "int main()\n" + "{\n" + " test();\n" + " return 1;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"void test() -> int <1:24 1:26>" + )); +} + +TEST_CASE("cxx parser does not find default argument type for non type bool template parameter of template function") +{ + std::shared_ptr client = parseCode( + "template \n" + "void test()\n" + "{\n" + "};\n" + ); + + REQUIRE(client->typeUses.size() == 2);; // only "bool" and "void" is recorded +} + +TEST_CASE("cxx parser finds template template default argument type of template function") +{ + std::shared_ptr client = parseCode( + "template \n" + "class A\n" + "{};\n" + "template class T = A>\n" + "void test()\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"void test typename T>() -> A <4:40 4:40>" + )); +} + +TEST_CASE("cxx parser finds lambda calling a function") +{ + std::shared_ptr client = parseCode( + "void func() {}\n" + "void lambdaCaller()\n" + "{\n" + " []()\n" + " {\n" + " func();\n" + " }();\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"void lambdaCaller::lambda at 4:2() const -> void func() <6:3 6:6>" + )); +} + +TEST_CASE("cxx parser finds local variable in lambda capture") +{ + std::shared_ptr client = parseCode( + "void lambdaWrapper()\n" + "{\n" + " int x = 2;\n" + " [x]() { return 1; }();\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<3:6> <4:3 4:3>" + )); +} + +TEST_CASE("cxx parser finds usage of local variable in microsoft inline assembly statement") +{ + std::shared_ptr client = parseCode( + "void foo()\n" + "{\n" + " int x = 2;\n" + "__asm\n" + "{\n" + " mov eax, x\n" + " mov x, eax\n" + "}\n" + "}\n", + { L"--target=i686-pc-windows-msvc" } + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<3:6> <6:11 6:11>" + )); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<3:6> <7:6 7:6>" + )); +} + +TEST_CASE("cxx parser finds template argument of unresolved lookup expression") +{ + std::shared_ptr client = parseCode( + "template \n" + "void a()\n" + "{\n" + "}\n" + "\n" + "template \n" + "void dispatch()\n" + "{\n" + " a();\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<6:20> <9:4 9:14>" + )); +} + +/////////////////////////////////////////////////////////////////////////////// +// test finding symbol locations + +TEST_CASE("cxx parser finds correct location of explicit constructor defined in namespace") +{ + std::shared_ptr client = parseCode( + "namespace n\n" + "{\n" + " class App\n" + " {\n" + " public:\n" + " App(int i) {}\n" + " };\n" + "}\n" + "int main()\n" + "{\n" + " n::App a = n::App(2);\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"int main() -> void n::App::App(int) <11:16 11:18>" + )); +} + +TEST_CASE("cxx parser finds macro argument location for field definition with name passed as argument to macro") +{ + std::shared_ptr client = parseCode( + "#define DEF_INT_FIELD(name) int name;\n" + "class A {\n" + " DEF_INT_FIELD(m_value)\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->fields, L"private int A::m_value <3:16 3:22>" + )); +} + +TEST_CASE("cxx parser finds macro usage location for field definition with name partially passed as argument to macro") +{ + std::shared_ptr client = parseCode( + "#define DEF_INT_FIELD(name) int m_##name;\n" + "class A {\n" + " DEF_INT_FIELD(value)\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->fields, L"private int A::m_value <3:2 3:14>" + )); +} + + +TEST_CASE("cxx parser finds macro argument location for function call in code passed as argument to macro") +{ + std::shared_ptr client = parseCode( + "#define DEF_INT_FIELD(name, init) int name = init;\n" + "int foo() { return 5; }\n" + "class A {\n" + " DEF_INT_FIELD(m_value, foo())\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"int A::m_value -> int foo() <4:25 4:27>" + )); +} + + +TEST_CASE("cxx parser finds macro usage location for function call in code of macro body") +{ + std::shared_ptr client = parseCode( + "int foo() { return 5; }\n" + "#define DEF_INT_FIELD(name) int name = foo();\n" + "class A {\n" + " DEF_INT_FIELD(m_value)\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"int A::m_value -> int foo() <4:2 4:14>" + )); +} + + +TEST_CASE("cxx parser finds type template argument of static cast expression") +{ + std::shared_ptr client = parseCode( + "int main()\n" + "{\n" + " return static_cast(4.0f);" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"int main() -> int <3:21 3:23>" + )); +} + +TEST_CASE("cxx parser finds implicit constructor call in initialization") +{ + std::shared_ptr client = parseCode( + "class A\n" + "{\n" + "};\n" + "class B\n" + "{\n" + " B(){}\n" + " A m_a;\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"void B::B() -> void A::A() <6:2 6:2>" + )); +} + +TEST_CASE("cxx parser parses multiple files") +{ + const std::set indexedPaths = { FilePath(L"data/CxxParserTestSuite/") }; + const std::set excludeFilters; + const std::set includeFilters; + const FilePath workingDirectory(L"."); + const FilePath sourceFilePath(L"data/CxxParserTestSuite/code.cpp"); + + std::shared_ptr indexerCommand = std::make_shared( + sourceFilePath, + indexedPaths, + excludeFilters, + includeFilters, + workingDirectory, + std::vector { L"--target=x86_64-pc-windows-msvc", L"-std=c++1z", sourceFilePath.wstr() } + ); + + TestIntermediateStorage storage; + CxxParser parser( + std::make_shared(&storage), + std::make_shared(), + std::make_shared() + ); + + parser.buildIndex(indexerCommand); + + storage.generateStringLists(); + + REQUIRE(storage.errors.size() == 0); + + REQUIRE(storage.typedefs.size() == 1); + REQUIRE(storage.classes.size() == 4); + REQUIRE(storage.enums.size() == 1); + REQUIRE(storage.enumConstants.size() == 2); + REQUIRE(storage.functions.size() == 2); + REQUIRE(storage.fields.size() == 4); + REQUIRE(storage.globalVariables.size() == 2); + REQUIRE(storage.methods.size() == 15); + REQUIRE(storage.namespaces.size() == 2); + REQUIRE(storage.structs.size() == 1); + + REQUIRE(storage.inheritances.size() == 1); + REQUIRE(storage.calls.size() == 3); + REQUIRE(storage.usages.size() == 3); + REQUIRE(storage.typeUses.size() == 16); + + REQUIRE(storage.files.size() == 2); + REQUIRE(storage.includes.size() == 1); +} + + +TEST_CASE("cxx parser finds braces of class decl") +{ + std::shared_ptr client = parseCode( + "class App\n" + "{\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<2:1> <2:1 2:1>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<2:1> <3:1 3:1>" + )); +} + +TEST_CASE("cxx parser finds braces of namespace decl") +{ + std::shared_ptr client = parseCode( + "namespace n\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<2:1> <2:1 2:1>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<2:1> <3:1 3:1>" + )); +} + +TEST_CASE("cxx parser finds braces of function decl") +{ + std::shared_ptr client = parseCode( + "int main()\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<2:1> <2:1 2:1>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<2:1> <3:1 3:1>" + )); +} + +TEST_CASE("cxx parser finds braces of method decl") +{ + std::shared_ptr client = parseCode( + "class App\n" + "{\n" + "public:\n" + " App(int i) {}\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<4:13> <4:13 4:13>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<4:13> <4:14 4:14>" + )); +} + +TEST_CASE("cxx parser finds braces of init list") +{ + std::shared_ptr client = parseCode( + "int a = 0;\n" + "int b[] = {a};\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<2:11> <2:11 2:11>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<2:11> <2:13 2:13>" + )); +} + +TEST_CASE("cxx parser finds braces of lambda") +{ + std::shared_ptr client = parseCode( + "void lambdaCaller()\n" + "{\n" + " [](){}();\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<3:6> <3:6 3:6>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<3:6> <3:7 3:7>" + )); +} + +TEST_CASE("cxx parser finds braces of asm stmt") +{ + std::shared_ptr client = parseCode( + "void foo()\n" + "{\n" + " __asm\n" + " {\n" + " mov eax, eax\n" + " }\n" + "}\n", + { L"--target=i686-pc-windows-msvc" } + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<4:2> <4:2 4:2>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<4:2> <6:2 6:2>" + )); +} + +TEST_CASE("cxx parser finds no duplicate braces of template class and method decl") +{ + std::shared_ptr client = parseCode( + "template \n" + "class App\n" + "{\n" + "public:\n" + " App(int i) {}\n" + "};\n" + "int main()\n" + "{\n" + " App a;\n" + " return 0;\n" + "}\n" + ); + + REQUIRE(client->localSymbols.size() == 9);; // 8 braces + 1 template parameter +} + +TEST_CASE("cxx parser finds braces with closing bracket in macro") +{ + std::shared_ptr client = parseCode( + "\n" + "namespace constants\n" + "{\n" + "\n" + "#define CONSTANT(name, x) \\\n" + " int name = x; \\\n" + " } namespace constants {\n" + "\n" + "CONSTANT(half, 5)\n" + "CONSTANT(third, 3)\n" + "}\n" + ); + + REQUIRE(utility::containsElement(client->localSymbols, L"input.cc<3:1> <3:1 3:1>")); + REQUIRE(utility::containsElement(client->localSymbols, L"input.cc<3:1> <7:2 7:2>")); + // TS_ASSERT(utility::containsElement(client->localSymbols, L"<0:0> <11:1 11:1>")); // unwanted sideeffect + + client = parseCode( + "\n" + "#define CONSTANT(name, x)\\\n" + " int name = x;\\\n" + " } namespace constants {\n" + "\n" + "namespace constants\n" + "{\n" + "CONSTANT(half, 5)\n" + "CONSTANT(third, 3)\n" + "}\n" + ); + + REQUIRE(utility::containsElement(client->localSymbols, L"input.cc<7:1> <7:1 7:1>")); + // TS_ASSERT(utility::containsElement(client->localSymbols, L"input.cc<7:1> <10:1 10:1>")); // missing + // TS_ASSERT(utility::containsElement(client->localSymbols, L"<0:0> <10:1 10:1>")); // unwanted sideeffect +} + +TEST_CASE("cxx parser finds correct signature location of constructor with initializer list") +{ + std::shared_ptr client = parseCode( + "class A\n" + "{\n" + " A(const int& foo) : m_foo(foo)\n" + " {\n" + " }\n" + " const int m_foo\n" + "}\n" + );; + + REQUIRE(utility::containsElement( + client->methods, L"private void A::A(const int &) <3:2 <3:2 <3:2 3:2> 3:18> 5:2>" + )); +} + +TEST_CASE("cxx parser catches error") +{ + std::shared_ptr client = parseCode( + "int a = b;\n" + ); + + REQUIRE(utility::containsElement( + client->errors, L"use of undeclared identifier \'b\' <1:9 1:9>" + )); +} + +TEST_CASE("cxx parser catches error in force include") +{ + std::shared_ptr client = parseCode( + "void foo() {} \n", { L"-include nothing" } + ); + + REQUIRE(utility::containsElement( + client->errors, L"' nothing' file not found <1:1 1:1>" + )); +} + +TEST_CASE("cxx parser finds correct error location after line directive") +{ + std::shared_ptr client = parseCode( + "#line 55 \"foo.hpp\"\n" + "void foo()\n" + ); + + REQUIRE(utility::containsElement( + client->errors, L"expected function body after function declarator <2:11 2:11>" + )); +} + +TEST_CASE("cxx parser catches error in macro expansion") +{ + std::shared_ptr client = parseCode( + "#define MACRO_WITH_NONEXISTING_PATH \"this_path_does_not_exist.txt\"\n" + "#include MACRO_WITH_NONEXISTING_PATH\n" + ); + + REQUIRE(utility::containsElement( + client->errors, L"'this_path_does_not_exist.txt' file not found <2:10 2:10>" + )); +} + +TEST_CASE("cxx parser finds location of line comment") +{ + std::shared_ptr client = parseCode( + "// this is a line comment\n" + ); + + REQUIRE(utility::containsElement( + client->comments, L"comment <1:1 1:26>" + )); +} + +TEST_CASE("cxx parser finds location of block comment") +{ + std::shared_ptr client = parseCode( + "/* this is a\n" + "block comment */\n" + ); + + REQUIRE(utility::containsElement( + client->comments, L"comment <1:1 2:17>" + )); +} + +void _test_TEST() +{ + std::shared_ptr client = parseCode( + "template class> class T>\n" + "class A {\n" + "T<>\n" + "};\n" + "template class T>\n" + "class B {};\n" + "template \n" + "class C {};\n" + "A a;\n" + ); + int ofo = 0; +} diff --git a/src/test/CxxParserTestSuite.h b/src/test/CxxParserTestSuite.h deleted file mode 100644 index 58579749..00000000 --- a/src/test/CxxParserTestSuite.h +++ /dev/null @@ -1,4732 +0,0 @@ -#include - -#include "TextAccess.h" -#include "utility.h" -#include "utilityString.h" - -#include "IndexerCommandCxx.h" -#include "IndexerStateInfo.h" -#include "CxxParser.h" -#include "ParserClientImpl.h" - -#include "TestFileRegister.h" -#include "TestIntermediateStorage.h" - -class CxxParserTestSuite: public CxxTest::TestSuite -{ -public: -/////////////////////////////////////////////////////////////////////////////// -// test finding symbol definitions and declarations - - void test_cxx_parser_finds_global_variable_declaration() - { - std::shared_ptr client = parseCode( - "int x;\n" - ); - - TS_ASSERT(utility::containsElement( - client->globalVariables, L"int x <1:5 1:5>" - )); - } - - void test_cxx_parser_finds_static_global_variable_declaration() - { - std::shared_ptr client = parseCode( - "static int x;\n" - ); - - TS_ASSERT(utility::containsElement( - client->globalVariables, L"int x (input.cc) <1:12 1:12>" - )); - } - - void test_cxx_parser_finds_static_const_global_variable_declaration() - { - std::shared_ptr client = parseCode( - "static const int x;\n" - ); - - TS_ASSERT(utility::containsElement( - client->globalVariables, L"const int x (input.cc) <1:18 1:18>" - )); - } - - void test_cxx_parser_finds_global_class_definition() - { - std::shared_ptr client = parseCode( - "class A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->classes, L"A <1:1 <1:7 1:7> 3:1>" - )); - } - - void test_cxx_parser_finds_global_class_declaration() - { - std::shared_ptr client = parseCode( - "class A;\n" - ); - - TS_ASSERT(utility::containsElement( - client->classes, L"A <1:7 1:7>" - )); - } - - void test_cxx_parser_finds_global_struct_definition() - { - std::shared_ptr client = parseCode( - "struct A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->structs, L"A <1:1 <1:8 1:8> 3:1>" - )); - } - - void test_cxx_parser_finds_global_struct_declaration() - { - std::shared_ptr client = parseCode( - "struct A;\n" - ); - - TS_ASSERT(utility::containsElement( - client->structs, L"A <1:8 1:8>" - )); - } - - void test_cxx_parser_finds_variable_definitions_in_global_scope() - { - std::shared_ptr client = parseCode( - "int x;\n" - ); - - TS_ASSERT(utility::containsElement( - client->globalVariables, L"int x <1:5 1:5>" - )); - } - - void test_cxx_parser_finds_fields_in_class_with_access_type() - { - std::shared_ptr client = parseCode( - "class A\n" - "{\n" - " int a;\n" - "public:\n" - " A() : d(0) {};\n" - " int b;\n" - "protected:\n" - " static int c;\n" - "private:\n" - " const int d;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->fields, L"private int A::a <3:6 3:6>" - )); - TS_ASSERT(utility::containsElement( - client->fields, L"public int A::b <6:6 6:6>" - )); - TS_ASSERT(utility::containsElement( - client->fields, L"protected static int A::c <8:13 8:13>" - )); - TS_ASSERT(utility::containsElement( - client->fields, L"private const int A::d <10:12 10:12>" - )); - } - - void test_cxx_parser_finds_function_declaration() - { - std::shared_ptr client = parseCode( - "int ceil(float a)\n" - "{\n" - " return 1;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->functions, L"int ceil(float) <1:1 <1:1 <1:5 1:8> 1:17> 4:1>" - )); - } - - void test_cxx_parser_finds_static_function_declaration() - { - std::shared_ptr client = parseCode( - "static int ceil(float a)\n" - "{\n" - " return static_cast(a) + 1;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->functions, L"static int ceil(float) (input.cc) <1:1 <1:1 <1:12 1:15> 1:24> 4:1>" - )); - } - - void test_cxx_parser_finds_method_declaration() - { - std::shared_ptr client = parseCode( - "class B\n" - "{\n" - "public:\n" - " B();\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->methods, L"public void B::B() <4:2 <4:2 4:2> 4:4>" - )); - } - - void test_cxx_parser_finds_overloaded_operator_declaration() - { - std::shared_ptr client = parseCode( - "class B\n" - "{\n" - "public:\n" - " B& operator=(const B& other);\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->methods, L"public B & B::operator=(const B &) <4:2 <4:5 4:13> 4:29>" - )); - } - - void test_cxx_parser_finds_method_declaration_and_definition() - { - std::shared_ptr client = parseCode( - "class B\n" - "{\n" - "public:\n" - " B();\n" - "};\n" - "B::B()\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->methods, L"public void B::B() <6:1 <6:4 6:4> 8:1>" - )); - } - - void test_cxx_parser_finds_virtual_method_declaration() - { - std::shared_ptr client = parseCode( - "class B\n" - "{\n" - "public:\n" - " virtual void process();\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->methods, L"public void B::process() <4:2 <4:15 4:21> 4:23>" - )); - } - - void test_cxx_parser_finds_pure_virtual_method_declaration() - { - std::shared_ptr client = parseCode( - "class B\n" - "{\n" - "protected:\n" - " virtual void process() = 0;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->methods, L"protected void B::process() <4:2 <4:15 4:21> 4:27>" - )); - } - - void test_cxx_parser_finds_named_namespace_declaration() - { - std::shared_ptr client = parseCode( - "namespace A\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->namespaces, L"A <1:1 <1:11 1:11> 3:1>" - )); - } - - void test_cxx_parser_finds_anonymous_namespace_declaration() - { - std::shared_ptr client = parseCode( - "namespace\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->namespaces, L"anonymous namespace (input.cc<1:1>) <1:1 <2:1 2:1> 3:1>" - )); - } - - void test_cxx_parser_finds_multiple_anonymous_namespace_declarations_as_same_symbol() - { - std::shared_ptr client = parseCode( - "namespace\n" - "{\n" - "}\n" - "namespace\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->namespaces, L"anonymous namespace (input.cc<1:1>) <1:1 <2:1 2:1> 3:1>" - )); - - TS_ASSERT(utility::containsElement( - client->namespaces, L"anonymous namespace (input.cc<1:1>) <4:1 <5:1 5:1> 6:1>" - )); - } - - void test_cxx_parser_finds_multiple_nested_anonymous_namespace_declarations_as_different_symbol() - { - std::shared_ptr client = parseCode( - "namespace\n" - "{\n" - " namespace\n" - " {\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->namespaces, L"anonymous namespace (input.cc<1:1>) <1:1 <2:1 2:1> 6:1>" - )); - - TS_ASSERT(utility::containsElement( - client->namespaces, L"anonymous namespace (input.cc<1:1>)::anonymous namespace (input.cc<3:2>) <3:2 <4:2 4:2> 5:2>" - )); - } - - void test_cxx_parser_finds_anonymous_namespace_declarations_nested_inside_namespaces_with_different_name_as_different_symbol() - { - std::shared_ptr client = parseCode( - "namespace a\n" - "{\n" - " namespace\n" - " {\n" - " }\n" - "}\n" - "namespace b\n" - "{\n" - " namespace\n" - " {\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->namespaces, L"a <1:1 <1:11 1:11> 6:1>" - )); - - TS_ASSERT(utility::containsElement( - client->namespaces, L"a::anonymous namespace (input.cc<3:2>) <3:2 <4:2 4:2> 5:2>" - )); - - TS_ASSERT(utility::containsElement( - client->namespaces, L"b <7:1 <7:11 7:11> 12:1>" - )); - - TS_ASSERT(utility::containsElement( - client->namespaces, L"b::anonymous namespace (input.cc<9:2>) <9:2 <10:2 10:2> 11:2>" - )); - } - - void test_cxx_parser_finds_anonymous_namespace_declarations_nested_inside_namespaces_with_same_name_as_same_symbol() - { - std::shared_ptr client = parseCode( - "namespace a\n" - "{\n" - " namespace\n" - " {\n" - " }\n" - "}\n" - "namespace a\n" - "{\n" - " namespace\n" - " {\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->namespaces, L"a <1:1 <1:11 1:11> 6:1>" - )); - - TS_ASSERT(utility::containsElement( - client->namespaces, L"a::anonymous namespace (input.cc<3:2>) <3:2 <4:2 4:2> 5:2>" - )); - - TS_ASSERT(utility::containsElement( - client->namespaces, L"a <7:1 <7:11 7:11> 12:1>" - )); - - TS_ASSERT(utility::containsElement( - client->namespaces, L"a::anonymous namespace (input.cc<3:2>) <9:2 <10:2 10:2> 11:2>" - )); - } - - void test_cxx_parser_finds_anonymous_struct_declaration() - { - std::shared_ptr client = parseCode( - "typedef struct\n" - "{\n" - " int x;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->structs, L"anonymous struct (input.cc<1:9>) <1:9 <1:9 1:14> 4:1>" - )); - } - - void test_cxx_parser_finds_multiple_anonymous_struct_declarations_as_distinct_elements() - { - std::shared_ptr client = parseCode( - "typedef struct\n" - "{\n" - " int x;\n" - "};\n" - "typedef struct\n" - "{\n" - " float x;\n" - "};\n" - ); - - TS_ASSERT_EQUALS(client->structs.size(), 2); - TS_ASSERT_EQUALS(client->fields.size(), 2); - TS_ASSERT_DIFFERS(utility::substrBeforeLast(client->fields[0], '<'), utility::substrBeforeLast(client->fields[1], '<')); - } - - void test_cxx_parser_finds_anonymous_union_declaration() - { - std::shared_ptr client = parseCode( - "typedef union\n" - "{\n" - " int i;\n" - " float f;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->unions, L"anonymous union (input.cc<1:9>) <1:9 <1:9 1:13> 5:1>" - )); - } - - void test_cxx_parser_finds_name_of_anonymous_struct_declared_inside_typedef() - { - std::shared_ptr client = parseCode( - "typedef struct\n" - "{\n" - " int x;\n" - "} Foo;\n" - ); - - TS_ASSERT(utility::containsElement( - client->structs, L"Foo <1:9 <1:9 1:14> 4:1>" - )); - TS_ASSERT(utility::containsElement( - client->structs, L"Foo <4:3 4:5>" - )); - } - - void test_cxx_parser_finds_name_of_anonymous_class_declared_inside_typedef() - { - std::shared_ptr client = parseCode( - "typedef class\n" - "{\n" - " int x;\n" - "} Foo;\n" - ); - - TS_ASSERT(utility::containsElement( - client->classes, L"Foo <1:9 <1:9 1:13> 4:1>" - )); - TS_ASSERT(utility::containsElement( - client->classes, L"Foo <4:3 4:5>" - )); - } - - void test_cxx_parser_finds_name_of_anonymous_enum_declared_inside_typedef() - { - std::shared_ptr client = parseCode( - "typedef enum\n" - "{\n" - " CONSTANT_1;\n" - "} Foo;\n" - ); - - TS_ASSERT(utility::containsElement( - client->enums, L"Foo <1:9 <1:9 1:12> 4:1>" - )); - TS_ASSERT(utility::containsElement( - client->enums, L"Foo <4:3 4:5>" - )); - } - - void test_cxx_parser_finds_name_of_anonymous_union_declared_inside_typedef() - { - std::shared_ptr client = parseCode( - "typedef union\n" - "{\n" - " int x;\n" - " float y;\n" - "} Foo;\n" - ); - - TS_ASSERT(utility::containsElement( - client->unions, L"Foo <1:9 <1:9 1:13> 5:1>" - )); - TS_ASSERT(utility::containsElement( - client->unions, L"Foo <5:3 5:5>" - )); - } - - void test_cxx_parser_finds_name_of_anonymous_struct_declared_inside_type_alias() - { - std::shared_ptr client = parseCode( - "using Foo = struct\n" - "{\n" - " int x;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->structs, L"Foo <1:13 <1:13 1:18> 4:1>" - )); - TS_ASSERT(utility::containsElement( - client->structs, L"Foo <1:7 1:9>" - )); - } - - void test_cxx_parser_finds_name_of_anonymous_class_declared_inside_type_alias() - { - std::shared_ptr client = parseCode( - "using Foo = class\n" - "{\n" - " int x;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->classes, L"Foo <1:13 <1:13 1:17> 4:1>" - )); - TS_ASSERT(utility::containsElement( - client->classes, L"Foo <1:7 1:9>" - )); - } - - void test_cxx_parser_finds_name_of_anonymous_enum_declared_inside_type_alias() - { - std::shared_ptr client = parseCode( - "using Foo = enum\n" - "{\n" - " CONSTANT_1;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->enums, L"Foo <1:13 <1:13 1:16> 4:1>" - )); - TS_ASSERT(utility::containsElement( - client->enums, L"Foo <1:7 1:9>" - )); - } - - void test_cxx_parser_finds_name_of_anonymous_union_declared_inside_type_alias() - { - std::shared_ptr client = parseCode( - "using Foo = union\n" - "{\n" - " int x;\n" - " float y;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->unions, L"Foo <1:13 <1:13 1:17> 5:1>" - )); - TS_ASSERT(utility::containsElement( - client->unions, L"Foo <1:7 1:9>" - )); - } - - void test_cxx_parser_finds_enum_defined_in_global_namespace() - { - std::shared_ptr client = parseCode( - "enum E\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->enums, L"E <1:1 <1:6 1:6> 3:1>" - )); - } - - void test_cxx_parser_finds_enum_constant_in_global_enum() - { - std::shared_ptr client = parseCode( - "enum E\n" - "{\n" - " P\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->enumConstants, L"E::P <3:2 3:2>" - )); - } - - void test_cxx_parser_finds_typedef_in_global_namespace() - { - std::shared_ptr client = parseCode( - "typedef unsigned int uint;\n" - ); - - TS_ASSERT(utility::containsElement( - client->typedefs, L"uint <1:22 1:25>" - )); - } - - void test_cxx_parser_finds_typedef_in_named_namespace() - { - std::shared_ptr client = parseCode( - "namespace test\n" - "{\n" - " typedef unsigned int uint;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typedefs, L"test::uint <3:23 3:26>" - )); - } - - void test_cxx_parser_finds_typedef_in_anonymous_namespace() - { - std::shared_ptr client = parseCode( - "namespace\n" - "{\n" - " typedef unsigned int uint;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typedefs, L"anonymous namespace (input.cc<1:1>)::uint <3:23 3:26>" - )); - } - - void test_cxx_parser_finds_type_alias_in_class() - { - std::shared_ptr client = parseCode( - "class Foo\n" - "{\n" - " using Bar = Foo;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typedefs, L"private Foo::Bar <3:8 3:10>" - )); - } - - void test_cxx_parser_finds_macro_define() - { - std::shared_ptr client = parseCode( - "#define PI\n" - "void test()\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->macros, L"PI <1:9 1:10>" - )); - } - - void test_cxx_parser_finds_macro_undefine() - { - std::shared_ptr client = parseCode( - "#undef PI\n" - "void test()\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->macroUses, L"input.cc -> PI <1:8 1:9>" - )); - } - - void test_cxx_parser_finds_macro_in_ifdef() - { - std::shared_ptr client = parseCode( - "#define PI\n" - "#ifdef PI\n" - "void test()\n" - "{\n" - "};\n" - "#endif\n" - ); - - TS_ASSERT(utility::containsElement( - client->macroUses, L"input.cc -> PI <2:8 2:9>" - )); - } - - void test_cxx_parser_finds_macro_in_ifndef() - { - std::shared_ptr client = parseCode( - "#define PI\n" - "#ifndef PI\n" - "void test()\n" - "{\n" - "};\n" - "#endif\n" - ); - - TS_ASSERT(utility::containsElement( - client->macroUses, L"input.cc -> PI <2:9 2:10>" - )); - } - - void test_cxx_parser_finds_macro_in_ifdefined() - { - std::shared_ptr client = parseCode( - "#define PI\n" - "#if defined(PI)\n" - "void test()\n" - "{\n" - "};\n" - "#endif\n" - ); - - TS_ASSERT(utility::containsElement( - client->macroUses, L"input.cc -> PI <2:13 2:14>" - )); - } - - void test_cxx_parser_finds_macro_expand() - { - std::shared_ptr client = parseCode( - "#define PI 3.14159265359\n" - "void test()\n" - "{\n" - "double i = PI;" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->macroUses, L"input.cc -> PI <4:12 4:13>" - )); - } - - void test_cxx_parser_finds_macro_expand_within_macro() - { - std::shared_ptr client = parseCode( - "#define PI 3.14159265359\n" - "#define TAU (2 * PI)\n" - "void test()\n" - "{\n" - "double i = TAU;" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->macroUses, L"input.cc -> PI <2:18 2:19>" - )); - } - - void test_cxx_parser_finds_macro_define_scope() - { - std::shared_ptr client = parseCode( - "#define MAX(a,b) \\\n" - " ((a)>(b)?(a):(b))" - ); - - TS_ASSERT(utility::containsElement( - client->macros, L"MAX <1:9 <1:9 1:11> 2:17>" - )); - } - - void test_cxx_parser_finds_type_template_parameter_definition_of_template_type_alias() - { - std::shared_ptr client = parseCode( - "template\n" - "using MyType = int;\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:16> <1:16 1:16>" - )); - } - - void test_cxx_parser_finds_type_template_parameter_definition_of_class_template() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:20> <1:20 1:20>" - )); - } - - void test_cxx_parser_finds_type_template_parameter_definition_of_explicit_partial_class_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "};\n" - "template \n" - "class A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<5:20> <5:20 5:20>" - )); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<5:20> <6:9 6:9>" - )); - } - - void test_cxx_parser_finds_type_template_parameter_definition_of_variable_template() - { - std::shared_ptr client = parseCode( - "template \n" - "T v;\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:20> <1:20 1:20>" - )); - } - - void test_cxx_parser_finds_type_template_parameter_definition_of_explicit_partial_variable_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "T t = Q(5);\n" - "\n" - "template \n" - "int t = 9;\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<4:20> <4:20 4:20>" - )); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<4:20> <5:12 5:12>" - )); - } - - void test_cxx_parser_finds_type_template_parameter_defined_with_class_keyword() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:17> <1:17 1:17>" - )); - } - - void test_cxx_parser_finds_non_type_int_template_parameter_definition_of_template_class() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:15> <1:15 1:15>" - )); - } - - void test_cxx_parser_finds_non_type_bool_template_parameter_definition_of_template_class() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:16> <1:16 1:16>" - )); - } - - void test_cxx_parser_finds_non_type_custom_pointer_template_parameter_definition_of_template_class() - { - std::shared_ptr client = parseCode( - "class P\n" - "{};\n" - "template \n" - "class A\n" - "{};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<3:14> <3:14 3:14>" - )); - } - - void test_cxx_parser_finds_non_type_custom_reference_template_parameter_definition_of_template_class() - { - std::shared_ptr client = parseCode( - "class P\n" - "{};\n" - "template \n" - "class A\n" - "{};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<3:14> <3:14 3:14>" - )); - } - - void test_cxx_parser_finds_non_type_template_parameter_definition_that_depends_on_type_template_parameter_of_template_class() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:28> <1:28 1:29>" - )); - - // and usage - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:20> <1:24 1:25>" - )); - } - - void test_cxx_parser_finds_non_type_template_parameter_definition_that_depends_on_template_template_parameter_of_template_class() - { - std::shared_ptr client = parseCode( - "template class T1, T1& T2>\n" - "class A\n" - "{};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:49> <1:49 1:50>" - )); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:36> <1:40 1:41>" - )); - } - - void test_cxx_parser_finds_non_type_template_parameter_definition_that_depends_on_type_template_parameter_of_template_template_parameter() - { - std::shared_ptr client = parseCode( - "template typename S>\n" - "class A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:45> <1:45 1:45>" - )); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:29> <1:32 1:32>" - )); - } - - void test_cxx_parser_finds_template_argument_of_dependent_non_type_template_parameter() - { - std::shared_ptr client = parseCode( - "template class T1, T1& T2>\n" - "class A\n" - "{};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A typename T1, T1 & T2> -> int <1:43 1:45>" - )); - } - - //void _test_foofoo() - //{ - // std::shared_ptr client = parseCode( - // "template \n" - // "class vector { };\n" - // "\n" - // "template\n" - // "struct Alloc { };\n" - // "\n" - // "template\n" - // "using Vec = vector>;\n" - // "\n" - // "Vec v;\n" - // ); - - // TS_ASSERT(utility::containsElement( - // client->typeUses, // TODO: record edge between vector> and Alloc (this is an issue because we dont have any typeloc for this edge -.- - // )); - //} - - void test_cxx_parser_finds_template_template_parameter_of_template_class() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{};\n" - "template class T>\n" - "class B\n" - "{};\n" - "int main()\n" - "{\n" - " B ba;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<4:36> <4:36 4:36>" - )); - } - - void test_cxx_parser_finds_type_template_parameter_pack_type_of_template_class() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:23> <1:23 1:23>" - )); - } - - void test_cxx_parser_finds_non_type_int_template_parameter_pack_type_of_template_class() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:18> <1:18 1:18>" - )); - } - - void test_cxx_parser_finds_template_template_parameter_pack_type_of_template_class() - { - std::shared_ptr client = parseCode( - "template typename... T>\n" - "class A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:42> <1:42 1:42>" - )); - } - - void test_cxx_parser_finds_type_template_parameters_of_template_class_with_multiple_parameters() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:20> <1:20 1:20>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:32> <1:32 1:32>" - )); - } - - void test_cxx_parser_skips_creating_node_for_template_parameter_without_a_name() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" // local symbol for brace - "};\n" // local symbol for brace - ); - - TS_ASSERT_EQUALS(client->localSymbols.size(), 2); - TS_ASSERT(utility::containsElement( - client->classes, L"A <1:1 <2:7 2:7> 4:1>" - )); - } - - void test_cxx_parser_finds_type_template_parameter_of_template_method_definition_outside_template_class() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - " template \n" - " U foo();\n" - "};\n" - "template \n" - "template \n" - "U A::foo()\n" - "{}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<8:20> <8:20 8:20>" - )); - } - - void test_cxx_parser_finds_explicit_class_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "};\n" - "template <>\n" - "class A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->classes, L"A <5:1 <6:7 6:7> 8:1>" - )); - } - - void test_cxx_parser_finds_explicit_variable_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "T t = T(5);\n" - "\n" - "template <>\n" - "int t = 99;\n" - ); - - TS_ASSERT(utility::containsElement( - client->globalVariables, L"int t <5:5 5:5>" - )); - } - - void test_cxx_parser_finds_explicit_partial_class_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "};\n" - "template \n" - "class A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->classes, L"A <5:1 <6:7 6:7> 8:1>" - )); - } - - void test_cxx_parser_finds_explicit_partial_variable_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "T t = Q(5);\n" - "\n" - "template \n" - "int t = 9;\n" - ); - - TS_ASSERT(utility::containsElement( - client->globalVariables, L"int t <5:5 5:5>" - )); - } - - void test_cxx_parser_finds_correct_field_member_name_of_template_class_in_declaration() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - " int foo;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->fields, L"private int A::foo <4:6 4:8>" - )); - } - - void test_cxx_parser_finds_correct_type_of_field_member_of_template_class_in_declaration() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - " T foo;\n" - "};\n" - "A a; \n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"int A::foo -> int <4:2 4:2>" - )); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A a -> A <6:1 6:1>" - )); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A a -> int <6:3 6:5>" - )); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:20> <4:2 4:2>" - )); - } - - void test_cxx_parser_finds_correct_method_member_name_of_template_class_in_declaration() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - " int foo();\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->methods, L"private int A::foo() <4:2 <4:6 4:8> 4:10>" - )); - } - - void test_cxx_parser_finds_type_template_parameter_definition_of_template_function() - { - std::shared_ptr client = parseCode( - "template \n" - "T test(T a)\n" - "{\n" - " return a;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:20> <1:20 1:20>" - )); - } - - void test_cxx_parser_finds_non_type_int_template_parameter_definition_of_template_function() - { - std::shared_ptr client = parseCode( - "template \n" - "int test(int a)\n" - "{\n" - " return a + T;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:15> <1:15 1:15>" - )); - } - - void test_cxx_parser_finds_non_type_bool_template_parameter_definition_of_template_function() - { - std::shared_ptr client = parseCode( - "template \n" - "int test(int a)\n" - "{\n" - " return T ? a : 0;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:16> <1:16 1:16>" - )); - } - - void test_cxx_parser_finds_non_type_custom_pointer_template_parameter_definition_of_template_function() - { - std::shared_ptr client = parseCode( - "class P\n" - "{};\n" - "template \n" - "int test(int a)\n" - "{\n" - " return a;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<3:14> <3:14 3:14>" - )); - } - - void test_cxx_parser_finds_non_type_custom_reference_template_parameter_definition_of_template_function() - { - std::shared_ptr client = parseCode( - "class P\n" - "{};\n" - "template \n" - "int test(int a)\n" - "{\n" - " return a;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<3:14> <3:14 3:14>" - )); - } - - void test_cxx_parser_finds_template_template_parameter_definition_of_template_function() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{};\n" - "template class T>\n" - "int test(int a)\n" - "{\n" - " return a;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<4:36> <4:36 4:36>" - )); - } - - void test_cxx_parser_finds_function_for_implicit_instantiation_of_template_function() - { - std::shared_ptr client = parseCode( - "template \n" - "T test(T a)\n" - "{\n" - " return a;\n" - "};\n" - "\n" - "int main()\n" - "{\n" - " return test(1);\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->functions, L"int test(int) <2:1 <2:1 <2:3 2:6> 2:11> 5:1>" - )); - } - - void test_cxx_parser_skips_implicit_template_method_definition_of_implicit_template_class_instantiation() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "public:\n" - " template \n" - " void foo() {}\n" - "};\n" - "\n" - "int main()\n" - "{\n" - " A().foo();\n" - " return 0;\n" - "}\n" - ); - - TS_ASSERT( /*NOT!*/ !utility::containsElement( - client->methods, L"public void A::foo() <6:2 <6:7 6:9> 6:14>" - )); - TS_ASSERT(utility::containsElement( - client->templateSpecializations, L"void A::foo() -> void A::foo() <6:7 6:9>" - )); - } - - void test_cxx_parser_finds_lambda_definition_and_call_in_function() - { - std::shared_ptr client = parseCode( - "void lambdaCaller()\n" - "{\n" - " [](){}();\n" - "}\n" - ); - - // TODO: fix - // TS_ASSERT(utility::containsElement( - // client->functions, L"void lambdaCaller::lambda at 3:2() const <3:5 <3:2 3:2> 3:7>" - // )); - TS_ASSERT(utility::containsElement( - client->calls, L"void lambdaCaller() -> void lambdaCaller::lambda at 3:2() const <3:8 3:8>" - )); - } - - void test_cxx_parser_finds_mutable_lambda_definition() - { - std::shared_ptr client = parseCode( - "void lambdaWrapper()\n" - "{\n" - " [](int foo) mutable { return foo; };\n" - "}\n" - ); - - // TODO: fix - // TS_ASSERT(utility::containsElement( - // client->functions, L"int lambdaWrapper::lambda at 3:2(int) <3:14 <3:2 3:2> 3:36>" - // )); - } - - void test_cxx_parser_finds_local_variable_declared_in_lambda_capture() - { - std::shared_ptr client = parseCode( - "void lambdaWrapper()\n" - "{\n" - " [x(42)]() { return x; };\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<3:3> <3:3 3:3>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<3:3> <3:21 3:21>" - )); - } - - void test_cxx_parser_finds_definition_of_local_symbol_in_function_parameter_list() - { - std::shared_ptr client = parseCode( - "void test(int a)\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:15> <1:15 1:15>" - )); - } - - void test_cxx_parser_finds_definition_of_local_symbol_in_function_scope() - { - std::shared_ptr client = parseCode( - "void test()\n" - "{\n" - " int a;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<3:6> <3:6 3:6>" - )); - } - -/////////////////////////////////////////////////////////////////////////////// -// test finding nested symbol definitions and declarations - - void test_cxx_parser_finds_class_definition_in_class() - { - std::shared_ptr client = parseCode( - "class A\n" - "{\n" - "public:\n" - " class B;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->classes, L"public A::B <4:8 4:8>" - )); - } - - void test_cxx_parser_finds_class_definition_in_namespace() - { - std::shared_ptr client = parseCode( - "namespace a\n" - "{\n" - " class B;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->classes, L"a::B <3:8 3:8>" - )); - } - - void test_cxx_parser_finds_struct_definition_in_class() - { - std::shared_ptr client = parseCode( - "class A\n" - "{\n" - " struct B\n" - " {\n" - " };\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->structs, L"private A::B <3:2 <3:9 3:9> 5:2>" - )); - } - - void test_cxx_parser_finds_struct_definition_in_namespace() - { - std::shared_ptr client = parseCode( - "namespace A\n" - "{\n" - " struct B\n" - " {\n" - " };\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->structs, L"A::B <3:2 <3:9 3:9> 5:2>" - )); - } - - void test_cxx_parser_finds_struct_definition_in_function() - { - std::shared_ptr client = parseCode( - "void foo(int)\n" - "{\n" - " struct B\n" - " {\n" - " };\n" - "};\n" - "void foo(float)\n" - "{\n" - " struct B\n" - " {\n" - " };\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->structs, L"foo::B <3:2 <3:9 3:9> 5:2>" - )); - TS_ASSERT(utility::containsElement( - client->structs, L"foo::B <9:2 <9:9 9:9> 11:2>" - )); - } - - void test_cxx_parser_finds_variable_definitions_in_namespace_scope() - { - std::shared_ptr client = parseCode( - "namespace n" - "{\n" - " int x;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->globalVariables, L"int n::x <2:6 2:6>" - )); - } - - void test_cxx_parser_finds_field_in_nested_class() - { - std::shared_ptr client = parseCode( - "class B\n" - "{\n" - "public:\n" - " class C\n" - " {\n" - " private:\n" - " static const int amount;\n" - " };\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->fields, L"private static const int B::C::amount <7:20 7:25>" - )); - } - - void test_cxx_parser_finds_function_in_anonymous_namespace() - { - std::shared_ptr client = parseCode( - "namespace\n" - "{\n" - " int sum(int a, int b);\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->functions, L"int anonymous namespace (input.cc<1:1>)::sum(int, int) <3:2 <3:6 3:8> 3:22>" - )); - } - - void test_cxx_parser_finds_method_declared_in_nested_class() - { - std::shared_ptr client = parseCode( - "class B\n" - "{\n" - " class C\n" - " {\n" - " bool isGreat() const;\n" - " };\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->methods, L"private bool B::C::isGreat() const <5:3 <5:8 5:14> 5:22>" - )); - } - - void test_cxx_parser_finds_nested_named_namespace() - { - std::shared_ptr client = parseCode( - "namespace A\n" - "{\n" - " namespace B\n" - " {\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->namespaces, L"A::B <3:2 <3:12 3:12> 5:2>" - )); - } - - void test_cxx_parser_finds_enum_defined_in_class() - { - std::shared_ptr client = parseCode( - "class B\n" - "{\n" - "public:\n" - " enum Z\n" - " {\n" - " };\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->enums, L"public B::Z <4:2 <4:7 4:7> 6:2>" - )); - } - - void test_cxx_parser_finds_enum_defined_in_namespace() - { - std::shared_ptr client = parseCode( - "namespace n\n" - "{\n" - " enum Z\n" - " {\n" - " };\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->enums, L"n::Z <3:2 <3:7 3:7> 5:2>" - )); - } - - void test_cxx_parser_finds_enum_definition_in_template_class() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - " enum TestType\n" - " {\n" - " TEST_ONE,\n" - " TEST_TWO\n" - " };\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->enums, L"private A::TestType <4:2 <4:7 4:14> 8:2>" - )); - } - - void test_cxx_parser_finds_enum_constants_in_template_class() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - " enum TestType\n" - " {\n" - " TEST_ONE,\n" - " TEST_TWO\n" - " };\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->enumConstants, L"A::TestType::TEST_ONE <6:3 6:10>" - )); - } - -/////////////////////////////////////////////////////////////////////////////// -// test qualifier locations - - void test_cxx_parser_finds_qualifier_of_access_to_global_variable_defined_in_namespace() - { - std::shared_ptr client = parseCode( - "namespace foo {\n" - " namespace bar {\n" - " int x;\n" - " }\n" - "}\n" - "void f() {\n" - " foo::bar::x = 9;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement(client->qualifiers, L"foo <7:2 7:4>")); - TS_ASSERT(utility::containsElement(client->qualifiers, L"foo::bar <7:7 7:9>")); - } - - void test_cxx_parser_finds_qualifier_of_access_to_static_field() - { - std::shared_ptr client = parseCode( - "class Foo {\n" - "public:\n" - " struct Bar {\n" - " public:\n" - " static int x;\n" - " };\n" - "};\n" - "void f() {\n" - " Foo::Bar::x = 9;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement(client->qualifiers, L"Foo <9:2 9:4>")); - TS_ASSERT(utility::containsElement(client->qualifiers, L"Foo::Bar <9:7 9:9>")); - } - - void test_cxx_parser_finds_qualifier_of_access_to_enum_constant() - { - std::shared_ptr client = parseCode( - "enum Foo {\n" - " FOO_V\n" - "};\n" - "void f() {\n" - " Foo v = Foo::FOO_V;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement(client->qualifiers, L"Foo <5:10 5:12>")); - } - - void test_cxx_parser_finds_qualifier_of_reference_to_method() - { - std::shared_ptr client = parseCode( - "class Foo {\n" - "public:\n" - " static void my_int_func(int x) {\n" - " }\n" - "};\n" - "\n" - "void test() {\n" - " void(*foo)(int);\n" - " foo = &Foo::my_int_func;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement(client->qualifiers, L"Foo <9:9 9:11>")); - } - - void test_cxx_parser_finds_qualifier_of_constructor_call() - { - std::shared_ptr client = parseCode( - "class Foo {\n" - "public:\n" - " Foo(int i) {}\n" - "};\n" - "\n" - "class Bar : public Foo {\n" - "public:\n" - " Bar() : Foo::Foo(4) {}\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement(client->qualifiers, L"Foo <8:10 8:12>")); - } - -/////////////////////////////////////////////////////////////////////////////// -// test implicit symbols - - void test_cxx_parser_finds_builtin_types() - { - std::shared_ptr client = parseCode( - "void t1(int v) {}\n" - "void t2(float v) {}\n" - "void t3(double v) {}\n" - "void t4(bool v) {}\n" - ); - - TS_ASSERT(utility::containsElement(client->builtinTypes, L"void")); - TS_ASSERT(utility::containsElement(client->builtinTypes, L"int")); - TS_ASSERT(utility::containsElement(client->builtinTypes, L"float")); - TS_ASSERT(utility::containsElement(client->builtinTypes, L"double")); - TS_ASSERT(utility::containsElement(client->builtinTypes, L"bool")); - } - - void test_cxx_parser_finds_implicit_copy_constructor() - { - std::shared_ptr client = parseCode( - "class TestClass {}\n" - "void foo()\n" - "{\n" - " TestClass a;\n" - " TestClass b(a);\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement(client->methods, L"public void TestClass::TestClass() <1:7 <1:7 1:15> 1:15>")); - TS_ASSERT(utility::containsElement(client->methods, L"public void TestClass::TestClass(const TestClass &) <1:7 <1:7 1:15> 1:15>")); - TS_ASSERT(utility::containsElement(client->methods, L"public void TestClass::TestClass(TestClass &&) <1:7 <1:7 1:15> 1:15>")); - } - -/////////////////////////////////////////////////////////////////////////////// -// test finding usages of symbols - - void test_cxx_parser_finds_enum_usage_in_template_class() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - " enum TestType\n" - " {\n" - " TEST_ONE,\n" - " TEST_TWO\n" - " };\n" - " TestType foo;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A::TestType A::foo -> A::TestType <9:2 9:9>" - )); - } - - void test_cxx_parser_finds_correct_field_member_type_of_nested_template_class_in_declaration() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "public:\n" - " class B\n" - " {\n" - " T foo;\n" - " };\n" - "};\n" - "A a;\n" - "A::B b;\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:20> <7:3 7:3>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"int A::B::foo -> int <7:3 7:3>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"A a -> A <10:1 10:1>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"A -> int <10:3 10:5>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"A a -> int <10:3 10:5>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"A::B b -> A::B <11:9 11:9>" - )); - } - - void test_cxx_parser_finds_type_usage_of_global_variable() - { - std::shared_ptr client = parseCode( - "int x;\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"int x -> int <1:1 1:3>" - )); - } - - void test_cxx_parser_finds_typedefs_type_use() - { - std::shared_ptr client = parseCode( - "typedef unsigned int uint;\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"uint -> unsigned int <1:9 1:16>" - )); - } - - void test_cxx_parser_finds_typedef_that_uses_type_defined_in_named_namespace() - { - std::shared_ptr client = parseCode( - "namespace test\n" - "{\n" - " struct TestStruct{};\n" - "}\n" - "typedef test::TestStruct globalTestStruct;\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"globalTestStruct -> test::TestStruct <5:15 5:24>" - )); - } - - void test_cxx_parser_finds_type_use_of_typedef() - { - std::shared_ptr client = parseCode( - "typedef unsigned int uint;\n" - "uint number;\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"uint number -> uint <2:1 2:4>" - )); - } - - void test_cxx_parser_finds_class_default_private_inheritance() - { - std::shared_ptr client = parseCode( - "class A {};\n" - "class B : A {};\n" - ); - - TS_ASSERT(utility::containsElement( - client->inheritances, L"B -> A <2:11 2:11>" - )); - } - - void test_cxx_parser_finds_class_public_inheritance() - { - std::shared_ptr client = parseCode( - "class A {};\n" - "class B : public A {};\n" - ); - - TS_ASSERT(utility::containsElement( - client->inheritances, L"B -> A <2:18 2:18>" - )); - } - - void test_cxx_parser_finds_class_protected_inheritance() - { - std::shared_ptr client = parseCode( - "class A {};\n" - "class B : protected A {};\n" - ); - - TS_ASSERT(utility::containsElement( - client->inheritances, L"B -> A <2:21 2:21>" - )); - } - - void test_cxx_parser_finds_class_private_inheritance() - { - std::shared_ptr client = parseCode( - "class A {};\n" - "class B : private A {};\n" - ); - - TS_ASSERT(utility::containsElement( - client->inheritances, L"B -> A <2:19 2:19>" - )); - } - - void test_cxx_parser_finds_class_multiple_inheritance() - { - std::shared_ptr client = parseCode( - "class A {};\n" - "class B {};\n" - "class C\n" - " : public A\n" - " , private B\n" - "{};\n" - ); - - TS_ASSERT(utility::containsElement( - client->inheritances, L"C -> A <4:11 4:11>" - )); - TS_ASSERT(utility::containsElement( - client->inheritances, L"C -> B <5:12 5:12>" - )); - } - - void test_cxx_parser_finds_struct_default_public_inheritance() - { - std::shared_ptr client = parseCode( - "struct A {};\n" - "struct B : A {};\n" - ); - - TS_ASSERT(utility::containsElement( - client->inheritances, L"B -> A <2:12 2:12>" - )); - } - - void test_cxx_parser_finds_struct_public_inheritance() - { - std::shared_ptr client = parseCode( - "struct A {};\n" - "struct B : public A {};\n" - ); - - TS_ASSERT(utility::containsElement( - client->inheritances, L"B -> A <2:19 2:19>" - )); - } - - void test_cxx_parser_finds_struct_protected_inheritance() - { - std::shared_ptr client = parseCode( - "struct A {};\n" - "struct B : protected A {};\n" - ); - - TS_ASSERT(utility::containsElement( - client->inheritances, L"B -> A <2:22 2:22>" - )); - } - - void test_cxx_parser_finds_struct_private_inheritance() - { - std::shared_ptr client = parseCode( - "struct A {};\n" - "struct B : private A {};\n" - ); - - TS_ASSERT(utility::containsElement( - client->inheritances, L"B -> A <2:20 2:20>" - )); - } - - void test_cxx_parser_finds_struct_multiple_inheritance() - { - std::shared_ptr client = parseCode( - "struct A {};\n" - "struct B {};\n" - "struct C\n" - " : public A\n" - " , private B\n" - "{};\n" - ); - - TS_ASSERT(utility::containsElement( - client->inheritances, L"C -> A <4:11 4:11>" - )); - TS_ASSERT(utility::containsElement( - client->inheritances, L"C -> B <5:12 5:12>" - )); - } - - void test_cxx_parser_finds_method_override_when_virtual() - { - std::shared_ptr client = parseCode( - "class A {\n" - " virtual void foo();\n" - "};\n" - "class B : public A {\n" - " void foo();\n" - "};" - ); - - TS_ASSERT(utility::containsElement( - client->overrides, L"void B::foo() -> void A::foo() <5:7 5:9>" - )); - } - - void test_cxx_parser_finds_multi_layer_method_overrides() - { - std::shared_ptr client = parseCode( - "class A {\n" - " virtual void foo();\n" - "};\n" - "class B : public A {\n" - " void foo();\n" - "};\n" - "class C : public B {\n" - " void foo();\n" - "};" - ); - - TS_ASSERT(utility::containsElement( - client->overrides, L"void B::foo() -> void A::foo() <5:7 5:9>" - )); - TS_ASSERT(utility::containsElement( - client->overrides, L"void C::foo() -> void B::foo() <8:7 8:9>" - )); - } - - void test_cxx_parser_finds_method_overrides_on_different_return_types() - { - std::shared_ptr client = parseCode( - "class A {\n" - " virtual void foo();\n" - "};\n" - "class B : public A {\n" - " int foo();\n" - "};\n" - ); - - TS_ASSERT_EQUALS(client->errors.size(), 1); - TS_ASSERT(utility::containsElement( - client->overrides, L"int B::foo() -> void A::foo() <5:6 5:8>" - )); - } - - void test_cxx_parser_finds_no_method_override_when_not_virtual() - { - std::shared_ptr client = parseCode( - "class A {\n" - " void foo();\n" - "};\n" - "class B : public A {\n" - " void foo();\n" - "};" - ); - - TS_ASSERT_EQUALS(client->overrides.size(), 0); - } - - void test_cxx_parser_finds_no_method_overrides_on_different_signatures() - { - std::shared_ptr client = parseCode( - "class A {\n" - " virtual void foo(int a);\n" - "};\n" - "class B : public A {\n" - " int foo(int a, int b);\n" - "};\n" - ); - - TS_ASSERT_EQUALS(client->overrides.size(), 0); - } - - void test_cxx_parser_finds_using_directive_decl_in_function_context() - { - std::shared_ptr client = parseCode( - "void foo()\n" - "{\n" - " using namespace std;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"void foo() -> std <3:18 3:20>" - )); - } - - void test_cxx_parser_finds_using_directive_decl_in_file_context() - { - std::shared_ptr client = parseCode( - "using namespace std;\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"input.cc -> std <1:17 1:19>" - )); - } - - void test_cxx_parser_finds_using_decl_in_function_context() - { - std::shared_ptr client = parseCode( - "namespace foo\n" - "{\n" - " int a;\n" - "}\n" - "void bar()\n" - "{\n" - " using foo::a;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"void bar() -> foo::a <7:13 7:13>" - )); - } - - void test_cxx_parser_finds_using_decl_in_file_context() - { - std::shared_ptr client = parseCode( - "namespace foo\n" - "{\n" - " int a;\n" - "}\n" - "using foo::a;\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"input.cc -> foo::a <5:12 5:12>" - )); - } - - void test_cxx_parser_finds_call_in_function() - { - std::shared_ptr client = parseCode( - "int sum(int a, int b)\n" - "{\n" - " return a + b;\n" - "}\n" - "int main()\n" - "{\n" - " sum(1, 2);\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"int main() -> int sum(int, int) <7:2 7:4>" - )); - } - - void test_cxx_parser_finds_call_in_function_with_correct_signature() - { - std::shared_ptr client = parseCode( - "int sum(int a, int b)\n" - "{\n" - " return a + b;\n" - "}\n" - "void func()\n" - "{\n" - "}\n" - "void func(bool right)\n" - "{\n" - " sum(1, 2);\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"void func(bool) -> int sum(int, int) <10:2 10:4>" - )); - } - - void test_cxx_parser_finds_call_to_function_with_right_signature() - { - std::shared_ptr client = parseCode( - "int sum(int a, int b)\n" - "{\n" - " return a + b;\n" - "}\n" - "float sum(float a, float b)\n" - "{\n" - " return a + b;\n" - "}\n" - "int main()\n" - "{\n" - " sum(1, 2);\n" - " sum(1.0f, 0.5f);\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"int main() -> int sum(int, int) <11:2 11:4>" - )); - TS_ASSERT(utility::containsElement( - client->calls, L"int main() -> float sum(float, float) <12:2 12:4>" - )); - } - - void test_cxx_parser_finds_function_call_in_function_parameter_list() - { - std::shared_ptr client = parseCode( - "int sum(int a, int b)\n" - "{\n" - " return a + b;\n" - "}\n" - "int main()\n" - "{\n" - " return sum(1, sum(2, 3));\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"int main() -> int sum(int, int) <7:16 7:18>" - )); - } - - void test_cxx_parser_finds_function_call_in_method() - { - std::shared_ptr client = parseCode( - "int sum(int a, int b)\n" - "{\n" - " return a + b;\n" - "}\n" - "class App\n" - "{\n" - " int main()\n" - " {\n" - " return sum(1, 2);\n" - " }\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"int App::main() -> int sum(int, int) <9:10 9:12>" - )); - } - - void test_cxx_parser_finds_implicit_constructor_without_definition_call() - { - std::shared_ptr client = parseCode( - "class App\n" - "{\n" - "};\n" - "int main()\n" - "{\n" - " App app;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"int main() -> void App::App() <6:6 6:8>" - )); - } - - void test_cxx_parser_finds_explicit_constructor_call() - { - std::shared_ptr client = parseCode( - "class App\n" - "{\n" - "public:\n" - " App() {}\n" - "};\n" - "int main()\n" - "{\n" - " App();\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"int main() -> void App::App() <8:2 8:4>" - )); - } - - void test_cxx_parser_finds_explicit_constructor_call_of_field() - { - std::shared_ptr client = parseCode( - "class Item\n" - "{\n" - "};\n" - "class App\n" - "{\n" - "public:\n" - " App() : item() {}\n" - " Item item;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"void App::App() -> void Item::Item() <7:10 7:13>" - )); - } - - void test_cxx_parser_finds_function_call_in_member_initialization() - { - std::shared_ptr client = parseCode( - "int one() { return 1; }\n" - "class Item\n" - "{\n" - "public:\n" - " Item(int n) {}\n" - "};\n" - "class App\n" - "{\n" - " App()\n" - " : item(one())" - " {}\n" - " Item item;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"void App::App() -> int one() <10:10 10:12>" - )); - } - - void test_cxx_parser_finds_copy_constructor_call() - { - std::shared_ptr client = parseCode( - "class App\n" - "{\n" - "public:\n" - " App() {}\n" - " App(const App& other) {}\n" - "};\n" - "int main()\n" - "{\n" - " App app;\n" - " App app2(app);\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"int main() -> void App::App(const App &) <10:6 10:9>" - )); - } - - void test_cxx_parser_finds_global_variable_constructor_call() - { - std::shared_ptr client = parseCode( - "class App\n" - "{\n" - "public:\n" - " App() {}\n" - "};\n" - "App app;\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"App app -> void App::App() <6:5 6:7>" - )); - } - - void test_cxx_parser_finds_global_variable_function_call() - { - std::shared_ptr client = parseCode( - "int one() { return 1; }\n" - "int a = one();\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"int a -> int one() <2:9 2:11>" - )); - } - - void test_cxx_parser_finds_operator_call() - { - std::shared_ptr client = parseCode( - "class App\n" - "{\n" - "public:\n" - " void operator+(int a)\n" - " {\n" - " }\n" - "};\n" - "int main()\n" - "{\n" - " App app;\n" - " app + 2;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"int main() -> void App::operator+(int) <11:6 11:6>" - )); - } - - void test_cxx_parser_finds_usage_of_function_pointer() - { - std::shared_ptr client = parseCode( - "void my_int_func(int x)\n" - "{\n" - "}\n" - "\n" - "void test()\n" - "{\n" - " void (*foo)(int);\n" - " foo = &my_int_func;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"void test() -> void my_int_func(int) <8:9 8:19>" - )); - } - - void test_cxx_parser_finds_usage_of_global_variable_in_function() - { - std::shared_ptr client = parseCode( - "int bar;\n" - "\n" - "int main()\n" - "{\n" - " bar = 1;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"int main() -> int bar <5:2 5:4>" - )); - } - - void test_cxx_parser_finds_usage_of_global_variable_in_global_variable_initialization() - { - std::shared_ptr client = parseCode( - "int a = 0;\n" - "int b[] = {a};\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"int [] b -> int a <2:12 2:12>" - )); - } - - void test_cxx_parser_finds_usage_of_global_variable_in_method() - { - std::shared_ptr client = parseCode( - "int bar;\n" - "\n" - "class App\n" - "{\n" - " void foo()\n" - " {\n" - " bar = 1;\n" - " }\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"void App::foo() -> int bar <7:3 7:5>" - )); - } - - void test_cxx_parser_finds_usage_of_field_in_method() - { - std::shared_ptr client = parseCode( - "class App\n" - "{\n" - " void foo()\n" - " {\n" - " bar = 1;\n" - " this->bar = 2;\n" - " }\n" - " int bar;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"void App::foo() -> int App::bar <5:3 5:5>" - )); - TS_ASSERT(utility::containsElement( - client->usages, L"void App::foo() -> int App::bar <6:9 6:11>" - )); - } - - void test_cxx_parser_finds_usage_of_field_in_function_call_arguments() - { - std::shared_ptr client = parseCode( - "class A\n" - "{\n" - "public:\n" - " void foo(int i)\n" - " {\n" - " foo(bar);\n" - " }\n" - " int bar;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"void A::foo(int) -> int A::bar <6:7 6:9>" - )); - } - - void test_cxx_parser_finds_usage_of_field_in_function_call_context() - { - std::shared_ptr client = parseCode( - "class A\n" - "{\n" - "public:\n" - " void foo(int i)\n" - " {\n" - " a->foo(6);\n" - " }\n" - " A* a;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"void A::foo(int) -> A * A::a <6:3 6:3>" - )); - } - - void test_cxx_parser_finds_usage_of_field_in_initialization_list() - { - std::shared_ptr client = parseCode( - "class App\n" - "{\n" - " App()\n" - " : bar(42)\n" - " {}\n" - " int bar;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"void App::App() -> int App::bar <4:5 4:7>" - )); - } - - void test_cxx_parser_finds_usage_of_member_in_call_expression_to_unresolved_member_expression() - { - std::shared_ptr client = parseCode( - "class A {\n" - " template \n" - " T run() { return 5; }\n" - "};\n" - "class B {\n" - " template \n" - " T run() {\n" - " return a.run();\n" - " }\n" - " A a;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"T B::run() -> A B::a <8:10 8:10>" - )); - } - - void test_cxx_parser_finds_usage_of_member_in_temporary_object_expression() - { - std::shared_ptr client = parseCode( - "class Foo\n" - "{\n" - "public:\n" - " Foo() { }\n" - " Foo(const Foo& i, int d) { }\n" - "};\n" - "\n" - "class Bar\n" - "{\n" - "public:\n" - " Bar(): m_i() {}\n" - "\n" - " void baba()\n" - " {\n" - " Foo(m_i, 4);\n" - " }\n" - "\n" - " const Foo m_i;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"void Bar::baba() -> const Foo Bar::m_i <15:7 15:9>" - )); - } - - void test_cxx_parser_finds_usage_of_member_in_dependent_scope_member_expression() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - " T m_t;\n" - "\n" - " void foo()\n" - " {\n" - " m_t.run();\n" - " }\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"void A::foo() -> T A::m_t <8:3 8:5>" - )); - } - - void test_cxx_parser_finds_return_type_use_in_function() - { - std::shared_ptr client = parseCode( - "double PI()\n" - "{\n" - " return 3.14159265359;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"double PI() -> double <1:1 1:6>" - )); - } - - void test_cxx_parser_finds_parameter_type_uses_in_function() - { - std::shared_ptr client = parseCode( - "void ceil(float a)\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"void ceil(float) -> float <1:11 1:15>" - )); - } - - void test_cxx_parser_finds_use_of_decayed_parameter_type_in_function() - { - std::shared_ptr client = parseCode( - "template\n" - "class VectorBase\n" - "{\n" - "public:\n" - " VectorBase(T values[N]);\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:16> <5:13 5:13>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:32> <5:22 5:22>" - )); - } - - void test_cxx_parser_usage_of_injected_type_in_method_declaration() - { - std::shared_ptr client = parseCode( - "template \n" - "class Foo\n" - "{\n" - " Foo& operator=(const Foo&) = delete;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"Foo & Foo::operator=(const Foo &) -> Foo <4:2 4:4>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"Foo & Foo::operator=(const Foo &) -> Foo <4:23 4:25>" - )); - } - - void test_cxx_parser_finds_use_of_qualified_type_in_function() - { - std::shared_ptr client = parseCode( - "void test(const int t)\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"void test(const int) -> int <1:17 1:19>" - )); - } - - void test_cxx_parser_finds_parameter_type_uses_in_constructor() - { - std::shared_ptr client = parseCode( - "class A\n" - "{\n" - " A(int a);\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"void A::A(int) -> int <3:4 3:6>" - )); - } - - void test_cxx_parser_finds_type_uses_in_function_body() - { - std::shared_ptr client = parseCode( - "int main()\n" - "{\n" - " int a = 42;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> int <3:2 3:4>" - )); - } - - void test_cxx_parser_finds_type_uses_in_method_body() - { - std::shared_ptr client = parseCode( - "class A\n" - "{\n" - " int main()\n" - " {\n" - " int a = 42;\n" - " return a;\n" - " }\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"int A::main() -> int <5:3 5:5>" - )); - } - - void test_cxx_parser_finds_type_uses_in_loops_and_conditions() - { - std::shared_ptr client = parseCode( - "int main()\n" - "{\n" - " if (true)\n" - " {\n" - " int a = 42;\n" - " }\n" - " for (int i = 0; i < 10; i++)\n" - " {\n" - " int b = i * 2;\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> int <5:3 5:5>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> int <7:7 7:9>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> int <9:3 9:5>" - )); - } - - void test_cxx_parser_finds_type_uses_of_base_class_in_derived_constructor() - { - std::shared_ptr client = parseCode( - "class A\n" - "{\n" - "public:\n" - " A(int n) {}\n" - "};\n" - "class B : public A\n" - "{\n" - "public:\n" - " B() : A(42) {}\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"void B::B() -> A <9:8 9:8>" - )); - } - - void test_cxx_parser_finds_enum_uses_in_global_space() - { - std::shared_ptr client = parseCode( - "enum A\n" - "{\n" - " B,\n" - " C\n" - "};\n" - "A a = B;\n" - "A* aPtr = new A;\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"A a -> A::B <6:7 6:7>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"A a -> A <6:1 6:1>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"A * aPtr -> A <7:1 7:1>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"A * aPtr -> A <7:15 7:15>" - )); - } - - void test_cxx_parser_finds_enum_uses_in_function_body() - { - std::shared_ptr client = parseCode( - "enum A\n" - "{\n" - " B,\n" - " C\n" - "};\n" - "int main()\n" - "{\n" - " A a = B;\n" - " A* aPtr = new A;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"int main() -> A::B <8:8 8:8>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> A <8:2 8:2>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> A <9:2 9:2>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> A <9:16 9:16>" - )); - } - - void test_cxx_parser_finds_usage_of_template_parameter_of_template_member_variable_declaration() - { - std::shared_ptr client = parseCode( - "template \n" - "struct IsBaseType {\n" - " static const bool value = true;\n" - "};\n" - "template \n" - "const bool IsBaseType::value;\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:20> <1:20 1:20>" - )); - TS_ASSERT(utility::containsElement( // TODO: fix FAIL because usage in name qualifier is not recorded - client->localSymbols, L"input.cc<5:20> <5:20 5:20>" - )); - } - - void test_cxx_parser_finds_usage_of_template_parameters_with_different_depth_of_template_function() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - " template \n" - " void foo(Q q)\n" - " {\n" - " T t;\n" - " t.run(q);\n" - " }\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:20> <7:3 7:3>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<4:21> <5:11 5:11>" - )); - } - - void test_cxx_parser_finds_usage_of_template_parameters_with_different_depth_of_partial_class_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - " template \n" - " class B\n" - " {\n" - " T foo(Q q, R r);\n" - " };\n" - "\n" - " template \n" - " class B\n" - " {\n" - " T foo(R r);\n" - " };\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:20> <13:3 13:3>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<10:21> <13:9 13:9>" - )); - } - - void test_cxx_parser_finds_usage_of_template_template_parameter_of_template_class_explicitly_instantiated_with_concrete_type_argument() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{};\n" - "template class T>\n" - "class B\n" - "{\n" - " void foo(T parameter)\n" - " {}\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<4:36> <7:11 7:11>" - )); - } - - void test_cxx_parser_finds_usage_of_template_template_parameter_of_template_class_explicitly_instantiated_with_template_type() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{};\n" - "template class T>\n" - "class B\n" - "{\n" - " template \n" - " void foo(T parameter)\n" - " {}\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<4:36> <8:11 8:11>" - )); - } - - void test_cxx_parser_finds_typedef_in_other_class_that_depends_on_own_template_parameter() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "public:\n" - " typedef T type;\n" - "};\n" - "template \n" - "class B\n" - "{\n" - "public:\n" - " typedef typename A::type type;\n" - "};\n" - "B::type f = 0;\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A::type -> int <5:10 5:10>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"B::type -> A::type <11:25 11:28>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"B::type -> A::type <11:25 11:28>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"B::type f -> B::type <13:9 13:12>" - )); - } - - void test_cxx_parser_finds_use_of_dependent_template_specialization_type() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "public:\n" - " template \n" - " using type = T;\n" - "};\n" - "template \n" - "class B\n" - "{\n" - "public:\n" - " typedef typename A::template type type;\n" - "};\n" - "B::type f = 0.0f;\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"B::type -> A::type <12:10 12:17>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"B::type f -> B::type <14:10 14:13>" - )); - } - - void test_cxx_parser_creates_single_node_for_all_possible_parameter_pack_expansions_of_template_function() - { - std::shared_ptr client = parseCode( - "template\n" - "T adder(T v) { return v; }\n" - "\n" - "template\n" - "T adder(T first, Args... args) { return first + adder(args...); }\n" - "\n" - "void foo() { long sum = adder(1, 2, 3, 8, 7); }\n" - ); - - TS_ASSERT(utility::containsElement( - client->functions, L"int adder>(int, ...) <5:1 <5:1 <5:3 5:7> 5:30> 5:65>" - )); - TS_ASSERT(utility::containsElement( - client->calls, L"int adder>(int, ...) -> int adder>(int, ...) <5:49 5:53>" - )); - } - - void test_cxx_parser_finds_type_template_argument_of_explicit_template_instantiation() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "};\n" - "int main()\n" - "{\n" - " A a;\n" - " return 0;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A -> int <7:4 7:6>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> int <7:4 7:6>" - )); - } - - void test_cxx_parser_finds_type_template_argument_of_explicit_template_instantiated_with_function_prototype() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "};\n" - "void foo()\n" - "{\n" - " A a;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A -> int <7:4 7:6>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"void foo() -> int <7:4 7:6>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"void foo() -> int <7:4 7:6>" - )); - } - - void test_cxx_parser_finds_type_template_argument_for_parameter_pack_of_explicit_template_instantiation() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "};\n" - "int main()\n" - "{\n" - " A();\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A<<...>> -> int <7:6 7:8>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"A<<...>> -> float <7:11 7:15>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> int <7:6 7:8>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> float <7:11 7:15>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> int <7:6 7:8>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> float <7:11 7:15>" - )); - } - - void test_cxx_parser_finds_type_template_argument_in_non_default_constructor_of_explicit_template_instaitiation() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "public:\n" - " A(int data){}\n" - "};\n" - "int main()\n" - "{\n" - " A(5);\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A -> int <9:4 9:6>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> int <9:4 9:6>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> int <9:4 9:6>" - )); - } - - void test_cxx_parser_finds_type_template_argument_in_default_constructor_of_explicit_template_instaitiation() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "public:\n" - " A(){}\n" - "};\n" - "int main()\n" - "{\n" - " A();\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A -> int <9:4 9:6>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> int <9:4 9:6>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> int <9:4 9:6>" - )); - } - - void test_cxx_parser_finds_type_template_argument_in_new_expression_of_explicit_template_instaitiation() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "public:\n" - " A(){}\n" - "};\n" - "int main()\n" - "{\n" - " new A();\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A -> int <9:8 9:10>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> int <9:8 9:10>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> int <9:8 9:10>" - )); - } - - void test_cxx_parser_finds_no_template_argument_for_builtin_non_type_int_template_parameter_of_explicit_template_instantiation() - { - std::shared_ptr client = parseCode( - "template \n" // use of "int" - "class A\n" - "{\n" - "};\n" - "int main()\n" // use of "int" - "{\n" - " A<1> a;\n" // use of "A" - " return 0;\n" - "}\n" - ); - - TS_ASSERT_EQUALS(client->typeUses.size(), 3); - } - - void test_cxx_parser_finds_no_template_argument_for_builtin_non_type_bool_template_parameter_of_explicit_template_instantiation() - { - std::shared_ptr client = parseCode( - "template \n" // use of "bool" - "class A\n" - "{\n" - "};\n" - "int main()\n" // use of "int" - "{\n" - " A a;\n" // use of "A" - " return 0;\n" - "}\n" - ); - - TS_ASSERT_EQUALS(client->typeUses.size(), 3); - } - - void test_cxx_parser_finds_non_type_custom_pointer_template_argument_of_implicit_template_instantiation() - { - std::shared_ptr client = parseCode( - "class P\n" - "{};\n" - "template \n" - "class A\n" - "{};\n" - "P g_p;\n" - "int main()\n" - "{\n" - " A<&g_p> a;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A<&g_p> -> P g_p <9:5 9:7>" - )); - } - - void test_cxx_parser_finds_non_type_custom_reference_template_argument_of_implicit_template_instantiation() - { - std::shared_ptr client = parseCode( - "class P\n" - "{};\n" - "template \n" - "class A\n" - "{};\n" - "P g_p;\n" - "int main()\n" - "{\n" - " A a;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A<&g_p> -> P g_p <9:4 9:6>" - )); - } - - void test_cxx_parser_finds_no_template_argument_for_builtin_non_type_int_template_parameter_pack_of_explicit_template_instantiation() - { - std::shared_ptr client = parseCode( - "template \n" // use of "int" - "class A\n" - "{\n" - "};\n" - "int main()\n" // use of "int" - "{\n" - " A<1, 2, 33>();\n" // use of "A" - "}\n" - ); - - TS_ASSERT_EQUALS(client->typeUses.size(), 3); - } - - void test_cxx_parser_finds_template_template_argument_of_explicit_template_instantiation() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{};\n" - "template class T>\n" - "class B\n" - "{};\n" - "int main()\n" - "{\n" - " B ba;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"B -> A <9:4 9:4>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> A <9:4 9:4>" - )); - } - - void test_cxx_parser_finds_template_template_argument_for_parameter_pack_of_explicit_template_instantiation() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "};\n" - "template typename... T>\n" - "class B\n" - "{\n" - "};\n" - "int main()\n" - "{\n" - " B();\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"B<<...>> -> A <11:4 11:4>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"B<<...>> -> A <11:7 11:7>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> A <11:4 11:4>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> A <11:7 11:7>" - )); - } - - void test_cxx_parser_finds_template_argument_for_implicit_specialization_of_global_template_variable() - { - std::shared_ptr client = parseCode( - "template \n" - "T v;\n" - "void test()\n" - "{\n" - " v = 9;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"int v -> int <5:4 5:6>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"void test() -> int <5:4 5:6>" - )); - } - - void test_cxx_parser_finds_template_member_specialization_for_method_of_implicit_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "public:\n" - " T foo() {}\n" - "};\n" - "int main()\n" - "{\n" - " A a;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->templateSpecializations, L"int A::foo() -> T A::foo() <5:4 5:6>" - )); - } - - void test_cxx_parser_finds_template_member_specialization_for_static_variable_of_implicit_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "public:\n" - " static T foo;\n" - "};\n" - "int main()\n" - "{\n" - " A a;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->templateSpecializations, L"static int A::foo -> static T A::foo <5:11 5:13>" - )); - } - - void test_cxx_parser_finds_template_member_specialization_for_field_of_implicit_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "public:\n" - " T foo;\n" - "};\n" - "int main()\n" - "{\n" - " A a;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->templateSpecializations, L"int A::foo -> T A::foo <5:4 5:6>" - )); - } - - void test_cxx_parser_finds_template_member_specialization_for_field_of_member_class_of_implicit_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "public:\n" - " class B {\n" - " public:\n" - " T foo;\n" - " };\n" - "};\n" - "int main()\n" - "{\n" - " A::B b;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->templateSpecializations, L"int A::B::foo -> T A::B::foo <7:5 7:7>" - )); - } - - void test_cxx_parser_finds_template_member_specialization_for_member_class_of_implicit_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "public:\n" - " class B {};\n" - "};\n" - "int main()\n" - "{\n" - " A a;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->templateSpecializations, L"A::B -> A::B <5:8 5:8>" - )); - } - - void test_cxx_parser_finds_type_template_argument_of_explicit_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "};\n" - "template <>\n" - "class A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A -> int <6:9 6:11>" - )); - } - - void test_cxx_parser_finds_no_template_argument_for_builtin_non_type_int_template_parameter_of_explicit_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" // use of "int" - "class A\n" - "{\n" - "};\n" - "template <>\n" - "class A<1>\n" - "{\n" - "};\n" - ); - - TS_ASSERT_EQUALS(client->typeUses.size(), 1); - } - - void test_cxx_parser_finds_no_template_argument_for_builtin_non_type_bool_template_parameter_of_explicit_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" // use of "bool" - "class A\n" - "{\n" - "};\n" - "template <>\n" - "class A\n" - "{\n" - "};\n" - ); - - TS_ASSERT_EQUALS(client->typeUses.size(), 1); - } - - void test_cxx_parser_finds_non_type_custom_pointer_template_argument_of_explicit_template_specialization() - { - std::shared_ptr client = parseCode( - "class P\n" - "{};\n" - "template \n" - "class A\n" - "{};\n" - "P g_p;\n" - "template <>\n" - "class A<&g_p>\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A<&g_p> -> P g_p <8:10 8:12>" - )); - } - - void test_cxx_parser_finds_non_type_custom_reference_template_argument_of_explicit_template_specialization() - { - std::shared_ptr client = parseCode( - "class P\n" - "{};\n" - "template \n" - "class A\n" - "{};\n" - "P g_p;\n" - "template <>\n" - "class A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A<&g_p> -> P g_p <8:9 8:11>" - )); - } - - void test_cxx_parser_finds_template_template_argument_of_explicit_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{};\n" - "template class T>\n" - "class B\n" - "{};\n" - "template <>\n" - "class B\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"B -> A <8:9 8:9>" - )); - } - - void test_cxx_parser_finds_type_template_arguments_of_explicit_partial_class_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "};\n" - "template \n" - "class A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<5:20> <6:9 6:9>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"A -> int <6:12 6:14>" - )); - } - - void test_cxx_parser_finds_no_template_argument_for_builtin_non_type_int_template_parameter_of_explicit_partial_class_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "};\n" - "template \n" - "class A<3, U>\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<5:15> <6:12 6:12>" - )); - } - - void test_cxx_parser_finds_no_template_argument_for_builtin_non_type_bool_template_parameter_of_explicit_partial_class_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "};\n" - "template \n" - "class A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<5:16> <6:15 6:15>" - )); - } - - void test_cxx_parser_finds_template_argument_for_non_type_custom_pointer_template_parameter_of_explicit_partial_class_template_specialization() - { - std::shared_ptr client = parseCode( - "class P\n" - "{};\n" - "template \n" - "class A\n" - "{};\n" - "P g_p;\n" - "template \n" - "class A<&g_p, q>\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A<&g_p, q> -> P g_p <8:10 8:12>" // TODO: this is completely wrong? should be a normal usage - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<7:14> <8:15 8:15>" - )); - } - - void test_cxx_parser_finds_template_argument_for_non_type_custom_reference_template_parameter_of_explicit_partial_class_template_specialization() - { - std::shared_ptr client = parseCode( - "class P\n" - "{};\n" - "template \n" - "class A\n" - "{};\n" - "P g_p;\n" - "template \n" - "class A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A<&g_p, q> -> P g_p <8:9 8:11>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<7:14> <8:14 8:14>" - )); - } - - void test_cxx_parser_finds_template_argument_for_template_template_parameter_of_explicit_partial_class_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{};\n" - "template class T, template class U>\n" - "class B\n" - "{};\n" - "template class U>\n" - "class B\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"B typename U> -> A <8:9 8:9>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<7:36> <8:12 8:12>" - )); - } - - void test_cxx_parser_finds_non_type_template_argument_that_depends_on_type_template_parameter_of_explicit_partial_class_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "};\n" - "template \n" - "class A<3, T2, T3>\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<5:27> <6:16 6:17>" - )); - } - - void test_cxx_parser_finds_non_type_template_argument_that_depends_on_template_template_parameter_of_explicit_partial_class_template_specialization() - { - std::shared_ptr client = parseCode( - "template class T2, T2 T3>\n" - "class A\n" - "{\n" - "};\n" - "template class T2, T2 T3>\n" - "class A<3, T2, T3>\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<5:36> <6:12 6:13>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<5:48> <6:16 6:17>" - )); - } - - void test_cxx_parser_finds_implicit_template_class_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - " T foo;\n" - "};\n" - "\n" - "A a;\n" - ); - - TS_ASSERT(utility::containsElement( - client->templateSpecializations, L"A -> A <2:7 2:7>" - )); - } - - void test_cxx_parser_finds_class_inheritance_from_implicit_template_class_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - " T foo;\n" - "};\n" - "\n" - "class B: public A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->inheritances, L"B -> A <7:17 7:17>" - )); - } - - void test_record_base_class_of_implicit_template_class_specialization() - { - std::shared_ptr client = parseCode( - "template\n" - "class VectorBase {}; \n" - "\n" - "template\n" - "class Vector2 : public VectorBase { void foo(); }; \n" - "\n" - "typedef Vector2 Vec2f; \n" - "\n" - "Vec2f v; \n" - ); - - TS_ASSERT(utility::containsElement( - client->inheritances, L"Vector2 -> VectorBase <5:24 5:33>" - )); - } - - void test_cxx_parser_finds_template_class_specialization_with_template_argument() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - " T foo;\n" - "};\n" - "\n" - "template \n" - "class B: public A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<7:20> <8:19 8:19>" - )); - TS_ASSERT(client->inheritances.size() == 1); - TS_ASSERT(client->classes.size() == 2); - TS_ASSERT(client->fields.size() == 1); - } - - void test_cxx_parser_finds_correct_order_of_template_arguments_for_explicit_class_template_specialization() - { - std::shared_ptr client = parseCode( - "template \n" - "class vector { };\n" - "template\n" - "class vector { };\n" - ); - - TS_ASSERT(utility::containsElement( - client->classes, L"vector <3:1 <4:7 4:12> 4:33>" - )); - } - - void test_cxx_parser_replaces_dependent_template_arguments_of_explicit_template_specialization_with_name_of_base_template() - { - std::shared_ptr client = parseCode( - "template \n" - "class A {};\n" - "template \n" - "class vector { };\n" - "template\n" - "class vector> { };\n" - ); - - TS_ASSERT(utility::containsElement( - client->classes, L"vector> <5:1 <6:7 6:12> 6:31>" - )); - } - - void test_cxx_parser_replaces_unknown_template_arguments_of_explicit_template_specialization_with_depth_and_position_index() - { - std::shared_ptr client = parseCode( - "template \n" - "class foo {\n" - " template \n" - " class vector { };\n" - " template\n" - " class vector { };\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->classes, L"foo::vector <5:2 <6:8 6:13> 6:25>" - )); - } - - void test_cxx_parser_finds_template_class_constructor_usage_of_field() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - " A(): foo() {}\n" - " T foo;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"void A::A() -> T A::foo <4:7 4:9>" - )); - } - - void test_cxx_parser_finds_correct_method_return_type_of_template_class_in_declaration() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - " T foo();\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<1:20> <4:2 4:2>" - )); - - TS_ASSERT(utility::containsElement( - client->methods, L"private T A::foo() <4:2 <4:4 4:6> 4:8>" - )); - } - - void test_cxx_parser_finds_type_template_default_argument_type_of_template_class() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A -> int <1:24 1:26>" - )); - } - - void test_cxx_parser_finds_no_default_argument_type_for_non_type_bool_template_parameter_of_template_class() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{\n" - "};\n" - ); - - TS_ASSERT_EQUALS(client->typeUses.size(), 1); // only the "bool" type is recorded and nothing for the default arg - } - - void test_cxx_parser_finds_template_template_default_argument_type_of_template_class() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{};\n" - "template class T = A>\n" - "class B\n" - "{};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"B typename T> -> A <4:40 4:40>" - )); - } - - void test_cxx_parser_finds_implicit_instantiation_of_template_function() - { - std::shared_ptr client = parseCode( - "template \n" - "T test(T a)\n" - "{\n" - " return a;\n" - "};\n" - "\n" - "int main()\n" - "{\n" - " return test(1);\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->templateSpecializations, L"int test(int) -> T test(T) <2:3 2:6>" - )); - } - - void test_cxx_parser_finds_explicit_specialization_of_template_function() - { - std::shared_ptr client = parseCode( - "template \n" - "T test(T a)\n" - "{\n" - " return a;\n" - "};\n" - "\n" - "template <>\n" - "int test(int a)\n" - "{\n" - " return a + a;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->templateSpecializations, L"int test(int) -> T test(T) <8:5 8:8>" - )); - } - - void test_cxx_parser_finds_explicit_type_template_argument_of_explicit_instantiation_of_template_function() - { - std::shared_ptr client = parseCode( - "template \n" - "void test()\n" - "{\n" - "};\n" - "\n" - "template <>\n" - "void test()\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"void test() -> int <7:11 7:13>" - )); - } - - void test_cxx_parser_finds_explicit_type_template_argument_of_function_call_in_function() - { - std::shared_ptr client = parseCode( - "template \n" - "void test(){}\n" - "\n" - "int main()\n" - "{\n" - " test();\n" - " return 1;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"void test() -> int <6:7 6:9>" - )); - } - - void test_cxx_parser_finds_no_explicit_non_type_int_template_argument_of_function_call_in_function() - { - std::shared_ptr client = parseCode( - "template \n" // use of "int" - "void test(){}\n" // 2x use of "void" - "\n" - "int main()\n" // use of "int" - "{\n" - " test<33>();\n" - " return 1;\n" - "};\n" - ); - - TS_ASSERT_EQUALS(client->typeUses.size(), 4); - } - - void test_cxx_parser_finds_explicit_template_template_argument_of_function_call_in_function() - { - std::shared_ptr client = parseCode( - "template \n" - "class A {};\n" - "template class T>\n" - "void test(){};\n" - "int main()\n" - "{\n" - " test();\n" - " return 1;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"void test() -> A <7:7 7:7>" - )); - } - - void test_cxx_parser_finds_no_implicit_type_template_argument_of_function_call_in_function() - { - std::shared_ptr client = parseCode( - "template \n" - "void test(T data){}\n" // 2x use of "void" + 1x use of "int" - "\n" - "int main()\n" // use of "int" - "{\n" - " test(1);\n" - " return 1;\n" - "};\n" - ); - - TS_ASSERT_EQUALS(client->typeUses.size(), 4); - } - - void test_cxx_parser_finds_explicit_type_template_argument_of_function_call_in_var_decl() - { - std::shared_ptr client = parseCode( - "template \n" - "T test(){ return 1; }\n" - "\n" - "class A\n" - "{\n" - " int foo = test();\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"int test() -> int <6:17 6:19>" - )); - } - - void test_cxx_parser_finds_no_implicit_type_template_argument_of_function_call_in_var_decl() - { - std::shared_ptr client = parseCode( - "template \n" - "T test(T i){ return i; }\n" // 2x use of "int" - "\n" - "class A\n" - "{\n" - " int foo = test(1);\n" // usage of "int" - "};\n" - ); - - TS_ASSERT_EQUALS(client->typeUses.size(), 3); - } - - void test_cxx_parser_finds_type_template_default_argument_type_of_template_function() - { - std::shared_ptr client = parseCode( - "template \n" - "void test()\n" - "{\n" - "};\n" - "\n" - "int main()\n" - "{\n" - " test();\n" - " return 1;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"void test() -> int <1:24 1:26>" - )); - } - - void test_cxx_parser_does_not_find_default_argument_type_for_non_type_bool_template_parameter_of_template_function() - { - std::shared_ptr client = parseCode( - "template \n" - "void test()\n" - "{\n" - "};\n" - ); - - TS_ASSERT_EQUALS(client->typeUses.size(), 2); // only "bool" and "void" is recorded - } - - void test_cxx_parser_finds_template_template_default_argument_type_of_template_function() - { - std::shared_ptr client = parseCode( - "template \n" - "class A\n" - "{};\n" - "template class T = A>\n" - "void test()\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"void test typename T>() -> A <4:40 4:40>" - )); - } - - void test_cxx_parser_finds_lambda_calling_a_function() - { - std::shared_ptr client = parseCode( - "void func() {}\n" - "void lambdaCaller()\n" - "{\n" - " []()\n" - " {\n" - " func();\n" - " }();\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"void lambdaCaller::lambda at 4:2() const -> void func() <6:3 6:6>" - )); - } - - void test_cxx_parser_finds_local_variable_in_lambda_capture() - { - std::shared_ptr client = parseCode( - "void lambdaWrapper()\n" - "{\n" - " int x = 2;\n" - " [x]() { return 1; }();\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<3:6> <4:3 4:3>" - )); - } - - void test_cxx_parser_finds_usage_of_local_variable_in_microsoft_inline_assembly_statement() - { - std::shared_ptr client = parseCode( - "void foo()\n" - "{\n" - " int x = 2;\n" - "__asm\n" - "{\n" - " mov eax, x\n" - " mov x, eax\n" - "}\n" - "}\n", - { L"--target=i686-pc-windows-msvc" } - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<3:6> <6:11 6:11>" - )); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<3:6> <7:6 7:6>" - )); - } - - void test_cxx_parser_finds_template_argument_of_unresolved_lookup_expression() - { - std::shared_ptr client = parseCode( - "template \n" - "void a()\n" - "{\n" - "}\n" - "\n" - "template \n" - "void dispatch()\n" - "{\n" - " a();\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<6:20> <9:4 9:14>" - )); - } - - /////////////////////////////////////////////////////////////////////////////// - // test finding symbol locations - - void test_cxx_parser_finds_correct_location_of_explicit_constructor_defined_in_namespace() - { - std::shared_ptr client = parseCode( - "namespace n\n" - "{\n" - " class App\n" - " {\n" - " public:\n" - " App(int i) {}\n" - " };\n" - "}\n" - "int main()\n" - "{\n" - " n::App a = n::App(2);\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"int main() -> void n::App::App(int) <11:16 11:18>" - )); - } - - void test_cxx_parser_finds_macro_argument_location_for_field_definition_with_name_passed_as_argument_to_macro() - { - std::shared_ptr client = parseCode( - "#define DEF_INT_FIELD(name) int name;\n" - "class A {\n" - " DEF_INT_FIELD(m_value)\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->fields, L"private int A::m_value <3:16 3:22>" - )); - } - - void test_cxx_parser_finds_macro_usage_location_for_field_definition_with_name_partially_passed_as_argument_to_macro() - { - std::shared_ptr client = parseCode( - "#define DEF_INT_FIELD(name) int m_##name;\n" - "class A {\n" - " DEF_INT_FIELD(value)\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->fields, L"private int A::m_value <3:2 3:14>" - )); - } - - - void test_cxx_parser_finds_macro_argument_location_for_function_call_in_code_passed_as_argument_to_macro() - { - std::shared_ptr client = parseCode( - "#define DEF_INT_FIELD(name, init) int name = init;\n" - "int foo() { return 5; }\n" - "class A {\n" - " DEF_INT_FIELD(m_value, foo())\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"int A::m_value -> int foo() <4:25 4:27>" - )); - } - - - void test_cxx_parser_finds_macro_usage_location_for_function_call_in_code_of_macro_body() - { - std::shared_ptr client = parseCode( - "int foo() { return 5; }\n" - "#define DEF_INT_FIELD(name) int name = foo();\n" - "class A {\n" - " DEF_INT_FIELD(m_value)\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"int A::m_value -> int foo() <4:2 4:14>" - )); - } - - - void test_cxx_parser_finds_type_template_argument_of_static_cast_expression() - { - std::shared_ptr client = parseCode( - "int main()\n" - "{\n" - " return static_cast(4.0f);" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"int main() -> int <3:21 3:23>" - )); - } - - void test_cxx_parser_finds_implicit_constructor_call_in_initialization() - { - std::shared_ptr client = parseCode( - "class A\n" - "{\n" - "};\n" - "class B\n" - "{\n" - " B(){}\n" - " A m_a;\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"void B::B() -> void A::A() <6:2 6:2>" - )); - } - - void test_cxx_parser_parses_multiple_files() - { - const std::set indexedPaths = { FilePath(L"data/CxxParserTestSuite/") }; - const std::set excludeFilters; - const std::set includeFilters; - const FilePath workingDirectory(L"."); - const FilePath sourceFilePath(L"data/CxxParserTestSuite/code.cpp"); - - std::shared_ptr indexerCommand = std::make_shared( - sourceFilePath, - indexedPaths, - excludeFilters, - includeFilters, - workingDirectory, - std::vector { L"--target=x86_64-pc-windows-msvc", L"-std=c++1z", sourceFilePath.wstr() } - ); - - TestIntermediateStorage storage; - CxxParser parser( - std::make_shared(&storage), - std::make_shared(), - std::make_shared() - ); - - parser.buildIndex(indexerCommand); - - storage.generateStringLists(); - - TS_ASSERT_EQUALS(storage.errors.size(), 0); - - TS_ASSERT_EQUALS(storage.typedefs.size(), 1); - TS_ASSERT_EQUALS(storage.classes.size(), 4); - TS_ASSERT_EQUALS(storage.enums.size(), 1); - TS_ASSERT_EQUALS(storage.enumConstants.size(), 2); - TS_ASSERT_EQUALS(storage.functions.size(), 2); - TS_ASSERT_EQUALS(storage.fields.size(), 4); - TS_ASSERT_EQUALS(storage.globalVariables.size(), 2); - TS_ASSERT_EQUALS(storage.methods.size(), 15); - TS_ASSERT_EQUALS(storage.namespaces.size(), 2); - TS_ASSERT_EQUALS(storage.structs.size(), 1); - - TS_ASSERT_EQUALS(storage.inheritances.size(), 1); - TS_ASSERT_EQUALS(storage.calls.size(), 3); - TS_ASSERT_EQUALS(storage.usages.size(), 3); - TS_ASSERT_EQUALS(storage.typeUses.size(), 16); - - TS_ASSERT_EQUALS(storage.files.size(), 2); - TS_ASSERT_EQUALS(storage.includes.size(), 1); - } - - - void test_cxx_parser_finds_braces_of_class_decl() - { - std::shared_ptr client = parseCode( - "class App\n" - "{\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<2:1> <2:1 2:1>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<2:1> <3:1 3:1>" - )); - } - - void test_cxx_parser_finds_braces_of_namespace_decl() - { - std::shared_ptr client = parseCode( - "namespace n\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<2:1> <2:1 2:1>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<2:1> <3:1 3:1>" - )); - } - - void test_cxx_parser_finds_braces_of_function_decl() - { - std::shared_ptr client = parseCode( - "int main()\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<2:1> <2:1 2:1>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<2:1> <3:1 3:1>" - )); - } - - void test_cxx_parser_finds_braces_of_method_decl() - { - std::shared_ptr client = parseCode( - "class App\n" - "{\n" - "public:\n" - " App(int i) {}\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<4:13> <4:13 4:13>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<4:13> <4:14 4:14>" - )); - } - - void test_cxx_parser_finds_braces_of_init_list() - { - std::shared_ptr client = parseCode( - "int a = 0;\n" - "int b[] = {a};\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<2:11> <2:11 2:11>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<2:11> <2:13 2:13>" - )); - } - - void test_cxx_parser_finds_braces_of_lambda() - { - std::shared_ptr client = parseCode( - "void lambdaCaller()\n" - "{\n" - " [](){}();\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<3:6> <3:6 3:6>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<3:6> <3:7 3:7>" - )); - } - - void test_cxx_parser_finds_braces_of_asm_stmt() - { - std::shared_ptr client = parseCode( - "void foo()\n" - "{\n" - " __asm\n" - " {\n" - " mov eax, eax\n" - " }\n" - "}\n", - { L"--target=i686-pc-windows-msvc" } - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<4:2> <4:2 4:2>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<4:2> <6:2 6:2>" - )); - } - - void test_cxx_parser_finds_no_duplicate_braces_of_template_class_and_method_decl() - { - std::shared_ptr client = parseCode( - "template \n" - "class App\n" - "{\n" - "public:\n" - " App(int i) {}\n" - "};\n" - "int main()\n" - "{\n" - " App a;\n" - " return 0;\n" - "}\n" - ); - - TS_ASSERT_EQUALS(client->localSymbols.size(), 9); // 8 braces + 1 template parameter - } - - void test_cxx_parser_finds_braces_with_closing_bracket_in_macro() - { - std::shared_ptr client = parseCode( -R"( -namespace constants -{ - -#define CONSTANT(name, x)\ - int name = x;\ - } namespace constants { - -CONSTANT(half, 5) -CONSTANT(third, 3) -} -)" - ); - - TS_ASSERT(utility::containsElement(client->localSymbols, L"input.cc<3:1> <3:1 3:1>")); - TS_ASSERT(utility::containsElement(client->localSymbols, L"input.cc<3:1> <7:2 7:2>")); - // TS_ASSERT(utility::containsElement(client->localSymbols, L"<0:0> <11:1 11:1>")); // unwanted sideeffect - - client = parseCode( -R"( -#define CONSTANT(name, x)\ - int name = x;\ - } namespace constants { - -namespace constants -{ -CONSTANT(half, 5) -CONSTANT(third, 3) -} -)" - ); - - TS_ASSERT(utility::containsElement(client->localSymbols, L"input.cc<7:1> <7:1 7:1>")); - // TS_ASSERT(utility::containsElement(client->localSymbols, L"input.cc<7:1> <10:1 10:1>")); // missing - // TS_ASSERT(utility::containsElement(client->localSymbols, L"<0:0> <10:1 10:1>")); // unwanted sideeffect - } - - void test_cxx_parser_finds_correct_signature_location_of_constructor_with_initializer_list() - { - std::shared_ptr client = parseCode( - "class A\n" - "{\n" - " A(const int& foo) : m_foo(foo)\n" - " {\n" - " }\n" - " const int m_foo\n" - "}\n" - );; - - TS_ASSERT(utility::containsElement( - client->methods, L"private void A::A(const int &) <3:2 <3:2 <3:2 3:2> 3:18> 5:2>" - )); - } - - void test_cxx_parser_catches_error() - { - std::shared_ptr client = parseCode( - "int a = b;\n" - ); - - TS_ASSERT(utility::containsElement( - client->errors, L"use of undeclared identifier \'b\' <1:9 1:9>" - )); - } - - void test_cxx_parser_catches_error_in_force_include() - { - std::shared_ptr client = parseCode( - "void foo() {} \n", { L"-include nothing" } - ); - - TS_ASSERT(utility::containsElement( - client->errors, L"' nothing' file not found <1:1 1:1>" - )); - } - - void test_cxx_parser_finds_correct_error_location_after_line_directive() - { - std::shared_ptr client = parseCode( - "#line 55 \"foo.hpp\"\n" - "void foo()\n" - ); - - TS_ASSERT(utility::containsElement( - client->errors, L"expected function body after function declarator <2:11 2:11>" - )); - } - - void test_cxx_parser_catches_error_in_macro_expansion() - { - std::shared_ptr client = parseCode( - "#define MACRO_WITH_NONEXISTING_PATH \"this_path_does_not_exist.txt\"\n" - "#include MACRO_WITH_NONEXISTING_PATH\n" - ); - - TS_ASSERT(utility::containsElement( - client->errors, L"'this_path_does_not_exist.txt' file not found <2:10 2:10>" - )); - } - - void test_cxx_parser_finds_location_of_line_comment() - { - std::shared_ptr client = parseCode( - "// this is a line comment\n" - ); - - TS_ASSERT(utility::containsElement( - client->comments, L"comment <1:1 1:26>" - )); - } - - void test_cxx_parser_finds_location_of_block_comment() - { - std::shared_ptr client = parseCode( - "/* this is a\n" - "block comment */\n" - ); - - TS_ASSERT(utility::containsElement( - client->comments, L"comment <1:1 2:17>" - )); - } - - void _test_TEST() - { - std::shared_ptr client = parseCode( - "template class> class T>\n" - "class A {\n" - "T<>\n" - "};\n" - "template class T>\n" - "class B {};\n" - "template \n" - "class C {};\n" - "A a;\n" - ); - int ofo = 0; - } - -private: - std::shared_ptr parseCode(std::string code, std::vector compilerFlags = {}) - { - std::shared_ptr storage = std::make_shared(); - CxxParser parser(std::make_shared(storage.get()), std::make_shared(), std::make_shared()); - parser.buildIndex(L"input.cc", TextAccess::createFromString(code), utility::concat(compilerFlags, std::vector(1, L"-std=c++1z"))); - storage->generateStringLists(); - return storage; - } -}; diff --git a/src/test/CxxTypeNameTestSuite.cpp b/src/test/CxxTypeNameTestSuite.cpp new file mode 100644 index 00000000..2d0336db --- /dev/null +++ b/src/test/CxxTypeNameTestSuite.cpp @@ -0,0 +1,39 @@ +#include "catch.hpp" + +#include "CxxTypeName.h" + +TEST_CASE("type name created with name has no qualifiers or modifiers") +{ + CxxTypeName typeName(L"int", std::vector(), std::shared_ptr()); + REQUIRE(L"int" == typeName.toString()); +} + +TEST_CASE("type name created with name and const qualifier has no modifiers") +{ + CxxTypeName typeName(L"int", std::vector(), std::shared_ptr()); + typeName.addQualifier(CxxQualifierFlags::QUALIFIER_CONST); + REQUIRE(L"const int" == typeName.toString()); +} + +TEST_CASE("type name created with name and array modifier has array modifier") +{ + CxxTypeName typeName(L"int", std::vector(), std::shared_ptr()); + typeName.addModifier(CxxTypeName::Modifier(L"[]")); + REQUIRE(L"int []" == typeName.toString()); +} + +TEST_CASE("type name created with name and const pointer modifier has const pointer modifier") +{ + CxxTypeName typeName(L"int", std::vector(), std::shared_ptr()); + typeName.addModifier(CxxTypeName::Modifier(L"*")); + typeName.addQualifier(CxxQualifierFlags::QUALIFIER_CONST); + REQUIRE(L"int * const" == typeName.toString()); +} + +TEST_CASE("type name created with name and pointer pointer modifier has pointer pointer modifier") +{ + CxxTypeName typeName(L"int", std::vector(), std::shared_ptr()); + typeName.addModifier(CxxTypeName::Modifier(L"*")); + typeName.addModifier(CxxTypeName::Modifier(L"*")); + REQUIRE(L"int * *" == typeName.toString()); +} diff --git a/src/test/CxxTypeNameTestSuite.h b/src/test/CxxTypeNameTestSuite.h deleted file mode 100644 index 6ad07bd4..00000000 --- a/src/test/CxxTypeNameTestSuite.h +++ /dev/null @@ -1,43 +0,0 @@ -#include - -#include "CxxTypeName.h" - -class CxxTypeNameTestSuite: public CxxTest::TestSuite -{ -public: - void test_type_name_created_with_name_has_no_qualifiers_or_modifiers() - { - CxxTypeName typeName(L"int", std::vector(), std::shared_ptr()); - TS_ASSERT_EQUALS(L"int", typeName.toString()); - } - - void test_type_name_created_with_name_and_const_qualifier_has_no_modifiers() - { - CxxTypeName typeName(L"int", std::vector(), std::shared_ptr()); - typeName.addQualifier(CxxQualifierFlags::QUALIFIER_CONST); - TS_ASSERT_EQUALS(L"const int", typeName.toString()); - } - - void test_type_name_created_with_name_and_array_modifier_has_array_modifier() - { - CxxTypeName typeName(L"int", std::vector(), std::shared_ptr()); - typeName.addModifier(CxxTypeName::Modifier(L"[]")); - TS_ASSERT_EQUALS(L"int []", typeName.toString()); - } - - void test_type_name_created_with_name_and_const_pointer_modifier_has_const_pointer_modifier() - { - CxxTypeName typeName(L"int", std::vector(), std::shared_ptr()); - typeName.addModifier(CxxTypeName::Modifier(L"*")); - typeName.addQualifier(CxxQualifierFlags::QUALIFIER_CONST); - TS_ASSERT_EQUALS(L"int * const", typeName.toString()); - } - - void test_type_name_created_with_name_and_pointer_pointer_modifier_has_pointer_pointer_modifier() - { - CxxTypeName typeName(L"int", std::vector(), std::shared_ptr()); - typeName.addModifier(CxxTypeName::Modifier(L"*")); - typeName.addModifier(CxxTypeName::Modifier(L"*")); - TS_ASSERT_EQUALS(L"int * *", typeName.toString()); - } -}; diff --git a/src/test/FileManagerTestSuite.cpp b/src/test/FileManagerTestSuite.cpp new file mode 100644 index 00000000..baa59c53 --- /dev/null +++ b/src/test/FileManagerTestSuite.cpp @@ -0,0 +1,42 @@ +#include "catch.hpp" + +#include "FileManager.h" +#include "FilePath.h" +#include "FilePathFilter.h" +#include "FileSystem.h" +#include "utility.h" + +TEST_CASE("file manager has added file paths after first fetch") +{ + std::vector sourcePaths; + sourcePaths.push_back(FilePath(L"./data/FileManagerTestSuite/src/")); + sourcePaths.push_back(FilePath(L"./data/FileManagerTestSuite/include/")); + std::vector headerPaths; + std::vector excludeFilters; + + // catch exceptions thrown on linux build machine + try + { + std::vector filePaths = FileSystem::getFilePathsFromDirectory(FilePath(L"./data/FileManagerTestSuite/src/")); + REQUIRE(filePaths.size() == 3); + + std::vector sourceExtensions; + for (FilePath p : filePaths) + { + sourceExtensions.push_back(p.extension()); + } + REQUIRE(sourceExtensions.size() == 3); + + FileManager fm; + fm.update(sourcePaths, excludeFilters, sourceExtensions); + std::vector foundSourcePaths = utility::toVector(fm.getAllSourceFilePaths()); + + REQUIRE(foundSourcePaths.size() == 3); + REQUIRE(utility::containsElement(foundSourcePaths, filePaths[0])); + REQUIRE(utility::containsElement(foundSourcePaths, filePaths[1])); + REQUIRE(utility::containsElement(foundSourcePaths, filePaths[2])); + } + catch (...) + { + } +} diff --git a/src/test/FileManagerTestSuite.h b/src/test/FileManagerTestSuite.h deleted file mode 100644 index 026d7a34..00000000 --- a/src/test/FileManagerTestSuite.h +++ /dev/null @@ -1,45 +0,0 @@ -#include - -#include "FileManager.h" -#include "FilePath.h" -#include "FileSystem.h" -#include "utility.h" - -class FileManagerTestSuite : public CxxTest::TestSuite -{ -public: - void test_file_manager_has_added_file_paths_after_first_fetch() - { - std::vector sourcePaths; - sourcePaths.push_back(FilePath(L"./data/FileManagerTestSuite/src/")); - sourcePaths.push_back(FilePath(L"./data/FileManagerTestSuite/include/")); - std::vector headerPaths; - std::vector excludeFilters; - - // catch exceptions thrown on linux build machine - try - { - std::vector filePaths = FileSystem::getFilePathsFromDirectory(FilePath(L"./data/FileManagerTestSuite/src/")); - TS_ASSERT_EQUALS(filePaths.size(), 3); - - std::vector sourceExtensions; - for (FilePath p : filePaths) - { - sourceExtensions.push_back(p.extension()); - } - TS_ASSERT_EQUALS(sourceExtensions.size(), 3); - - FileManager fm; - fm.update(sourcePaths, excludeFilters, sourceExtensions); - std::vector foundSourcePaths = utility::toVector(fm.getAllSourceFilePaths()); - - TS_ASSERT_EQUALS(foundSourcePaths.size(), 3); - TS_ASSERT(utility::containsElement(foundSourcePaths, filePaths[0])); - TS_ASSERT(utility::containsElement(foundSourcePaths, filePaths[1])); - TS_ASSERT(utility::containsElement(foundSourcePaths, filePaths[2])); - } - catch (...) - { - } - } -}; diff --git a/src/test/FilePathFilterTestSuite.cpp b/src/test/FilePathFilterTestSuite.cpp new file mode 100644 index 00000000..7c037c2c --- /dev/null +++ b/src/test/FilePathFilterTestSuite.cpp @@ -0,0 +1,123 @@ +#include "catch.hpp" + +#include "FilePathFilter.h" + +TEST_CASE("file path filter finds exact match") +{ + FilePathFilter filter(L"test.h"); + + REQUIRE(filter.isMatching(FilePath(L"test.h"))); +} + +TEST_CASE("file path filter finds match with single asterisk in same level") +{ + FilePathFilter filter(L"*test.*"); + + REQUIRE(filter.isMatching(FilePath(L"this_is_a_test.h"))); +} + +TEST_CASE("file path filter finds match with single asterisk in different level") +{ + FilePathFilter filter(L"*/this_is_a_test.h"); + + REQUIRE(filter.isMatching(FilePath(L"folder/this_is_a_test.h"))); +} + +TEST_CASE("file path filter does not find match with single asterisk in different level") +{ + FilePathFilter filter(L"*/test.h"); + + REQUIRE(!filter.isMatching(FilePath(L"test.h"))); +} + +TEST_CASE("file path filter finds match with multiple asterisk in same level") +{ + FilePathFilter filter(L"**test.h"); + + REQUIRE(filter.isMatching(FilePath(L"folder/this_is_a_test.h"))); +} + +TEST_CASE("file path filter finds match with multiple asterisk in different level") +{ + FilePathFilter filter(L"root/**/test.h"); + + REQUIRE(filter.isMatching(FilePath(L"root/folder1/folder2/test.h"))); +} + +TEST_CASE("file path filter does not find match with multiple asterisk in different level") +{ + FilePathFilter filter(L"**/test.h"); + + REQUIRE(!filter.isMatching(FilePath(L"folder/this_is_a_test.h"))); +} + +TEST_CASE("file path filter escapes dot character") +{ + FilePathFilter filter(L"test.h"); + + REQUIRE(!filter.isMatching(FilePath(L"testyh"))); +} + +TEST_CASE("file path filter escapes plus character") +{ + REQUIRE(FilePathFilter(L"folder/test+.h").isMatching(FilePath(L"folder/test+.h"))); +} + +TEST_CASE("file path filter escapes minus character") +{ + REQUIRE(FilePathFilter(L"folder/test[-].h").isMatching(FilePath(L"folder/test[-].h"))); +} + +TEST_CASE("file path filter escapes dollar character") +{ + REQUIRE(FilePathFilter(L"folder/test$.h").isMatching(FilePath(L"folder/test$.h"))); +} + +TEST_CASE("file path filter escapes circumflex character") +{ + REQUIRE(FilePathFilter(L"folder/test^.h").isMatching(FilePath(L"folder/test^.h"))); +} + +TEST_CASE("file path filter escapes open round brace character") +{ + REQUIRE(FilePathFilter(L"folder/test(.h").isMatching(FilePath(L"folder/test(.h"))); +} + +TEST_CASE("file path filter escapes close round brace character") +{ + REQUIRE(FilePathFilter(L"folder\\test).h").isMatching(FilePath(L"folder/test).h"))); +} + +TEST_CASE("file path filter escapes open curly brace character") +{ + REQUIRE(FilePathFilter(L"folder/test{.h").isMatching(FilePath(L"folder/test{.h"))); +} + +TEST_CASE("file path filter escapes close curly brace character") +{ + REQUIRE(FilePathFilter(L"folder/test}.h").isMatching(FilePath(L"folder/test}.h"))); +} + +TEST_CASE("file path filter escapes open squared brace character") +{ + REQUIRE(FilePathFilter(L"folder/test[.h").isMatching(FilePath(L"folder/test[.h"))); +} + +TEST_CASE("file path filter escapes close squared brace character") +{ + REQUIRE(FilePathFilter(L"folder\\test].h").isMatching(FilePath(L"folder/test].h"))); +} + +TEST_CASE("file path filter finds backslash if slash was provided") +{ + FilePathFilter filter(L"folder/test.h"); + + REQUIRE(filter.isMatching(FilePath(L"folder\\test.h"))); +} + +TEST_CASE("file path filter finds slash if backslash was provided") +{ + FilePathFilter filter(L"folder\\test.h"); + + REQUIRE(filter.isMatching(FilePath(L"folder/test.h"))); +} diff --git a/src/test/FilePathFilterTestSuite.h b/src/test/FilePathFilterTestSuite.h deleted file mode 100644 index 36ff722c..00000000 --- a/src/test/FilePathFilterTestSuite.h +++ /dev/null @@ -1,127 +0,0 @@ -#include - -#include "FilePathFilter.h" - -class FilePathFilterTestSuite: public CxxTest::TestSuite -{ -public: - void test_file_path_filter_finds_exact_match() - { - FilePathFilter filter(L"test.h"); - - TS_ASSERT(filter.isMatching(FilePath(L"test.h"))); - } - - void test_file_path_filter_finds_match_with_single_asterisk_in_same_level() - { - FilePathFilter filter(L"*test.*"); - - TS_ASSERT(filter.isMatching(FilePath(L"this_is_a_test.h"))); - } - - void test_file_path_filter_finds_match_with_single_asterisk_in_different_level() - { - FilePathFilter filter(L"*/this_is_a_test.h"); - - TS_ASSERT(filter.isMatching(FilePath(L"folder/this_is_a_test.h"))); - } - - void test_file_path_filter_does_not_find_match_with_single_asterisk_in_different_level() - { - FilePathFilter filter(L"*/test.h"); - - TS_ASSERT(!filter.isMatching(FilePath(L"test.h"))); - } - - void test_file_path_filter_finds_match_with_multiple_asterisk_in_same_level() - { - FilePathFilter filter(L"**test.h"); - - TS_ASSERT(filter.isMatching(FilePath(L"folder/this_is_a_test.h"))); - } - - void test_file_path_filter_finds_match_with_multiple_asterisk_in_different_level() - { - FilePathFilter filter(L"root/**/test.h"); - - TS_ASSERT(filter.isMatching(FilePath(L"root/folder1/folder2/test.h"))); - } - - void test_file_path_filter_does_not_find_match_with_multiple_asterisk_in_different_level() - { - FilePathFilter filter(L"**/test.h"); - - TS_ASSERT(!filter.isMatching(FilePath(L"folder/this_is_a_test.h"))); - } - - void test_file_path_filter_escapes_dot_character() - { - FilePathFilter filter(L"test.h"); - - TS_ASSERT(!filter.isMatching(FilePath(L"testyh"))); - } - - void test_file_path_filter_escapes_plus_character() - { - TS_ASSERT(FilePathFilter(L"folder/test+.h").isMatching(FilePath(L"folder/test+.h"))); - } - - void test_file_path_filter_escapes_minus_character() - { - TS_ASSERT(FilePathFilter(L"folder/test[-].h").isMatching(FilePath(L"folder/test[-].h"))); - } - - void test_file_path_filter_escapes_dollar_character() - { - TS_ASSERT(FilePathFilter(L"folder/test$.h").isMatching(FilePath(L"folder/test$.h"))); - } - - void test_file_path_filter_escapes_circumflex_character() - { - TS_ASSERT(FilePathFilter(L"folder/test^.h").isMatching(FilePath(L"folder/test^.h"))); - } - - void test_file_path_filter_escapes_open_round_brace_character() - { - TS_ASSERT(FilePathFilter(L"folder/test(.h").isMatching(FilePath(L"folder/test(.h"))); - } - - void test_file_path_filter_escapes_close_round_brace_character() - { - TS_ASSERT(FilePathFilter(L"folder\\test).h").isMatching(FilePath(L"folder/test).h"))); - } - - void test_file_path_filter_escapes_open_curly_brace_character() - { - TS_ASSERT(FilePathFilter(L"folder/test{.h").isMatching(FilePath(L"folder/test{.h"))); - } - - void test_file_path_filter_escapes_close_curly_brace_character() - { - TS_ASSERT(FilePathFilter(L"folder/test}.h").isMatching(FilePath(L"folder/test}.h"))); - } - - void test_file_path_filter_escapes_open_squared_brace_character() - { - TS_ASSERT(FilePathFilter(L"folder/test[.h").isMatching(FilePath(L"folder/test[.h"))); - } - - void test_file_path_filter_escapes_close_squared_brace_character() - { - TS_ASSERT(FilePathFilter(L"folder\\test].h").isMatching(FilePath(L"folder/test].h"))); - } - - void test_file_path_filter_finds_backslash_if_slash_was_provided() - { - FilePathFilter filter(L"folder/test.h"); - - TS_ASSERT(filter.isMatching(FilePath(L"folder\\test.h"))); - } - - void test_file_path_filter_finds_slash_if_backslash_was_provided() - { - FilePathFilter filter(L"folder\\test.h"); - - TS_ASSERT(filter.isMatching(FilePath(L"folder/test.h"))); - } -}; diff --git a/src/test/FilePathTestSuite.cpp b/src/test/FilePathTestSuite.cpp new file mode 100644 index 00000000..8602a8b7 --- /dev/null +++ b/src/test/FilePathTestSuite.cpp @@ -0,0 +1,212 @@ +#include "catch.hpp" + +#include "FilePath.h" + +TEST_CASE("file_path_gets_created_empty") +{ + const FilePath path; + + REQUIRE(path.wstr() == L""); +} + +TEST_CASE("file_path_gets_created_with_string") +{ + const std::wstring str(L"data/FilePathTestSuite/main.cpp"); + const FilePath path(str); + + REQUIRE(path.wstr() == str); +} + +TEST_CASE("file_path_gets_created_other_file_path") +{ + const FilePath path(L"data/FilePathTestSuite/main.cpp"); + const FilePath path2(path); + + REQUIRE(path == path2); +} + +TEST_CASE("file_path_empty") +{ + const FilePath path1(L"data/FilePathTestSuite/a.cpp"); + const FilePath path2; + + REQUIRE(!path1.empty()); + REQUIRE(path2.empty()); +} + +TEST_CASE("file_path_exists") +{ + const FilePath path(L"data/FilePathTestSuite/a.cpp"); + + REQUIRE(path.exists()); +} + +TEST_CASE("file_path_not_exists") +{ + const FilePath path(L"data/FilePathTestSuite/a.h"); + + REQUIRE(!path.exists()); +} + +TEST_CASE("file_path_is_directory") +{ + const FilePath path(L"data/FilePathTestSuite/a.cpp"); + + REQUIRE(!path.isDirectory()); + REQUIRE(path.getParentDirectory().isDirectory()); +} + +TEST_CASE("empty_file_path_has_empty_parent_directory") +{ + const FilePath path; + + REQUIRE(path.empty()); + REQUIRE(path.getParentDirectory().empty()); +} + +TEST_CASE("file_path_without_parent_has_empty_parent_directory") +{ + const FilePath path(L"a.cpp"); + + REQUIRE(path.getParentDirectory().empty()); +} + +TEST_CASE("file_path_is_absolute") +{ + const FilePath path(L"data/FilePathTestSuite/a.cpp"); + + REQUIRE(!path.isAbsolute()); + REQUIRE(path.getAbsolute().isAbsolute()); +} + +TEST_CASE("file_path_parent_directory") +{ + const FilePath path(L"data/FilePathTestSuite/a.cpp"); + + REQUIRE(path.getParentDirectory().wstr() == L"data/FilePathTestSuite"); + REQUIRE(path.getParentDirectory().getParentDirectory().wstr() == L"data"); +} + +TEST_CASE("file_path_relative_to_other_path") +{ + const FilePath pathA(L"data/FilePathTestSuite/a.cpp"); + const FilePath pathB(L"data/FilePathTestSuite/test/c.h"); + + REQUIRE(pathA.getRelativeTo(pathB).wstr() == L"../a.cpp"); + REQUIRE(pathB.getRelativeTo(pathA).wstr() == L"test/c.h"); +} + +TEST_CASE("file_path_relative_to_other_directory") +{ + const FilePath pathA(L"data/FilePathTestSuite/a.cpp"); + const FilePath pathB(L"data/FilePathTestSuite/test"); + + REQUIRE(pathA.getRelativeTo(pathB).wstr() == L"../a.cpp"); +} + +TEST_CASE("file_path_relative_to_same_directory") +{ + const FilePath pathA(L"data/FilePathTestSuite/test"); + + REQUIRE(pathA.getRelativeTo(pathA).wstr() == L"./"); +} + +TEST_CASE("file_path_file_name") +{ + const FilePath path(L"data/FilePathTestSuite/abc.h"); + + REQUIRE(path.fileName() == L"abc.h"); +} + +TEST_CASE("file_path_extension") +{ + const FilePath path(L"data/FilePathTestSuite/a.h"); + + REQUIRE(path.extension() == L".h"); +} + +TEST_CASE("file_path_without_extension") +{ + const FilePath path(L"data/FilePathTestSuite/a.h"); + + REQUIRE(path.withoutExtension() == FilePath(L"data/FilePathTestSuite/a")); +} + +TEST_CASE("file_path_has_extension") +{ + std::vector extensions; + extensions.push_back(L".h"); + extensions.push_back(L".cpp"); + extensions.push_back(L".cc"); + + REQUIRE(FilePath(L"data/FilePathTestSuite/a.h").hasExtension(extensions)); + REQUIRE(FilePath(L"data/FilePathTestSuite/b.cpp").hasExtension(extensions)); + REQUIRE(!FilePath(L"data/FilePathTestSuite/a.m").hasExtension(extensions)); +} + +TEST_CASE("file_path_equals_file_with_different_relative_paths") +{ + const FilePath path1(L"data/FilePathTestSuite/a.cpp"); + const FilePath path2(L"data/../data/FilePathTestSuite/./a.cpp"); + + REQUIRE(path1 == path2); +} + +TEST_CASE("file_path_equals_relative_and_absolute_paths") +{ + const FilePath path1(L"data/FilePathTestSuite/a.cpp"); + const FilePath path2 = path1.getAbsolute(); + + REQUIRE(path1 == path2); +} + +TEST_CASE("file_path_equals_absolute_and_canonical_paths") +{ + const FilePath path(L"data/../data/FilePathTestSuite/./a.cpp"); + + REQUIRE(path.getAbsolute() == path.getCanonical()); +} + +TEST_CASE("file_path_canonical_removes_symlinks") +{ +#ifndef _WIN32 + const FilePath pathA(L"data/FilePathTestSuite/parent/target/d.cpp"); + const FilePath pathB(L"data/FilePathTestSuite/target/d.cpp"); + + REQUIRE(pathB.getAbsolute() == pathA.getCanonical()); +#endif +} + +TEST_CASE("file_path_compares_paths_with_posix_and_windows_format") +{ +#ifdef _WIN32 + const FilePath pathB(L"data/FilePathTestSuite/b.cc"); + const FilePath pathB2(L"data\\FilePathTestSuite\\b.cc"); + + REQUIRE(pathB == pathB2); +#endif +} + +TEST_CASE("file_path_differs_for_different_existing_files") +{ + const FilePath pathA(L"data/FilePathTestSuite/a.cpp"); + const FilePath pathB(L"data/FilePathTestSuite/b.cc"); + + REQUIRE(pathA != pathB); +} + +TEST_CASE("file_path_differs_for_different_nonexisting_files") +{ + const FilePath pathA(L"data/FilePathTestSuite/a.h"); + const FilePath pathB(L"data/FilePathTestSuite/b.c"); + + REQUIRE(pathA != pathB); +} + +TEST_CASE("file_path_differs_for_existing_and_nonexisting_files") +{ + const FilePath pathA(L"data/FilePathTestSuite/a.h"); + const FilePath pathB(L"data/FilePathTestSuite/b.cc"); + + REQUIRE(pathA != pathB); +} diff --git a/src/test/FilePathTestSuite.h b/src/test/FilePathTestSuite.h deleted file mode 100644 index 27c767ee..00000000 --- a/src/test/FilePathTestSuite.h +++ /dev/null @@ -1,216 +0,0 @@ -#include - -#include "FilePath.h" - -class FilePathTestSuite : public CxxTest::TestSuite -{ -public: - void test_file_path_gets_created_empty() - { - const FilePath path; - - TS_ASSERT_EQUALS(path.wstr(), L""); - } - - void test_file_path_gets_created_with_string() - { - const std::wstring str(L"data/FilePathTestSuite/main.cpp"); - const FilePath path(str); - - TS_ASSERT_EQUALS(path.wstr(), str); - } - - void test_file_path_gets_created_other_file_path() - { - const FilePath path(L"data/FilePathTestSuite/main.cpp"); - const FilePath path2(path); - - TS_ASSERT_EQUALS(path, path2); - } - - void test_file_path_empty() - { - const FilePath path1(L"data/FilePathTestSuite/a.cpp"); - const FilePath path2; - - TS_ASSERT(!path1.empty()); - TS_ASSERT(path2.empty()); - } - - void test_file_path_exists() - { - const FilePath path(L"data/FilePathTestSuite/a.cpp"); - - TS_ASSERT(path.exists()); - } - - void test_file_path_not_exists() - { - const FilePath path(L"data/FilePathTestSuite/a.h"); - - TS_ASSERT(!path.exists()); - } - - void test_file_path_is_directory() - { - const FilePath path(L"data/FilePathTestSuite/a.cpp"); - - TS_ASSERT(!path.isDirectory()); - TS_ASSERT(path.getParentDirectory().isDirectory()); - } - - void test_empty_file_path_has_empty_parent_directory() - { - const FilePath path; - - TS_ASSERT(path.empty()); - TS_ASSERT(path.getParentDirectory().empty()); - } - - void test_file_path_without_parent_has_empty_parent_directory() - { - const FilePath path(L"a.cpp"); - - TS_ASSERT(path.getParentDirectory().empty()); - } - - void test_file_path_is_absolute() - { - const FilePath path(L"data/FilePathTestSuite/a.cpp"); - - TS_ASSERT(!path.isAbsolute()); - TS_ASSERT(path.getAbsolute().isAbsolute()); - } - - void test_file_path_parent_directory() - { - const FilePath path(L"data/FilePathTestSuite/a.cpp"); - - TS_ASSERT(path.getParentDirectory().wstr() == L"data/FilePathTestSuite"); - TS_ASSERT(path.getParentDirectory().getParentDirectory().wstr() == L"data"); - } - - void test_file_path_relative_to_other_path() - { - const FilePath pathA(L"data/FilePathTestSuite/a.cpp"); - const FilePath pathB(L"data/FilePathTestSuite/test/c.h"); - - TS_ASSERT_EQUALS(pathA.getRelativeTo(pathB).wstr(), L"../a.cpp"); - TS_ASSERT_EQUALS(pathB.getRelativeTo(pathA).wstr(), L"test/c.h"); - } - - void test_file_path_relative_to_other_directory() - { - const FilePath pathA(L"data/FilePathTestSuite/a.cpp"); - const FilePath pathB(L"data/FilePathTestSuite/test"); - - TS_ASSERT_EQUALS(pathA.getRelativeTo(pathB).wstr(), L"../a.cpp"); - } - - void test_file_path_relative_to_same_directory() - { - const FilePath pathA(L"data/FilePathTestSuite/test"); - - TS_ASSERT_EQUALS(pathA.getRelativeTo(pathA).wstr(), L"./"); - } - - void test_file_path_file_name() - { - const FilePath path(L"data/FilePathTestSuite/abc.h"); - - TS_ASSERT_EQUALS(path.fileName(), L"abc.h"); - } - - void test_file_path_extension() - { - const FilePath path(L"data/FilePathTestSuite/a.h"); - - TS_ASSERT_EQUALS(path.extension(), L".h"); - } - - void test_file_path_without_extension() - { - const FilePath path(L"data/FilePathTestSuite/a.h"); - - TS_ASSERT_EQUALS(path.withoutExtension(), FilePath(L"data/FilePathTestSuite/a")); - } - - void test_file_path_has_extension() - { - std::vector extensions; - extensions.push_back(L".h"); - extensions.push_back(L".cpp"); - extensions.push_back(L".cc"); - - TS_ASSERT(FilePath(L"data/FilePathTestSuite/a.h").hasExtension(extensions)); - TS_ASSERT(FilePath(L"data/FilePathTestSuite/b.cpp").hasExtension(extensions)); - TS_ASSERT(!FilePath(L"data/FilePathTestSuite/a.m").hasExtension(extensions)); - } - - void test_file_path_equals_file_with_different_relative_paths() - { - const FilePath path1(L"data/FilePathTestSuite/a.cpp"); - const FilePath path2(L"data/../data/FilePathTestSuite/./a.cpp"); - - TS_ASSERT_EQUALS(path1, path2); - } - - void test_file_path_equals_relative_and_absolute_paths() - { - const FilePath path1(L"data/FilePathTestSuite/a.cpp"); - const FilePath path2 = path1.getAbsolute(); - - TS_ASSERT_EQUALS(path1, path2); - } - - void test_file_path_equals_absolute_and_canonical_paths() - { - const FilePath path(L"data/../data/FilePathTestSuite/./a.cpp"); - - TS_ASSERT_EQUALS(path.getAbsolute(), path.getCanonical()); - } - - void test_file_path_canonical_removes_symlinks() - { -#ifndef _WIN32 - const FilePath pathA(L"data/FilePathTestSuite/parent/target/d.cpp"); - const FilePath pathB(L"data/FilePathTestSuite/target/d.cpp"); - - TS_ASSERT_EQUALS(pathB.getAbsolute(), pathA.getCanonical()); -#endif - } - - void test_file_path_compares_paths_with_posix_and_windows_format() - { -#ifdef _WIN32 - const FilePath pathB(L"data/FilePathTestSuite/b.cc"); - const FilePath pathB2(L"data\\FilePathTestSuite\\b.cc"); - - TS_ASSERT_EQUALS(pathB, pathB2); -#endif - } - - void test_file_path_differs_for_different_existing_files() - { - const FilePath pathA(L"data/FilePathTestSuite/a.cpp"); - const FilePath pathB(L"data/FilePathTestSuite/b.cc"); - - TS_ASSERT_DIFFERS(pathA, pathB); - } - - void test_file_path_differs_for_different_nonexisting_files() - { - const FilePath pathA(L"data/FilePathTestSuite/a.h"); - const FilePath pathB(L"data/FilePathTestSuite/b.c"); - - TS_ASSERT_DIFFERS(pathA, pathB); - } - - void test_file_path_differs_for_existing_and_nonexisting_files() - { - const FilePath pathA(L"data/FilePathTestSuite/a.h"); - const FilePath pathB(L"data/FilePathTestSuite/b.cc"); - - TS_ASSERT_DIFFERS(pathA, pathB); - } -}; diff --git a/src/test/FileSystemTestSuite.cpp b/src/test/FileSystemTestSuite.cpp new file mode 100644 index 00000000..fdee32ac --- /dev/null +++ b/src/test/FileSystemTestSuite.cpp @@ -0,0 +1,133 @@ +#include "catch.hpp" + +#include +#include +#include +#include + +#include "FileSystem.h" +#include "utility.h" + +namespace +{ + bool isInFiles(const std::set& files, const FilePath& filename) + { + return std::end(files) != files.find(filename); + } + + bool isInFileInfos(const std::vector& infos, const std::wstring& filename) + { + for (const FileInfo& info : infos) + { + if (info.path.wstr() == filename) + { + return true; + } + } + + return false; + } + + bool isInFileInfos(const std::vector& infos, const std::wstring& filename, const std::wstring& filename2) + { + for (const FileInfo& info : infos) + { + if (info.path.wstr() == filename || info.path.wstr() == filename2) + { + return true; + } + } + + return false; + } +} + +TEST_CASE("find cpp files") +{ + std::vector cppFiles = utility::convert( + FileSystem::getFilePathsFromDirectory(FilePath(L"data/FileSystemTestSuite"), { L".cpp" }), + [](const FilePath& filePath){ return filePath.wstr(); } + ); + + REQUIRE(cppFiles.size() == 4); + REQUIRE(utility::containsElement(cppFiles, L"data/FileSystemTestSuite/main.cpp")); + REQUIRE(utility::containsElement(cppFiles, L"data/FileSystemTestSuite/Settings/sample.cpp")); + REQUIRE(utility::containsElement(cppFiles, L"data/FileSystemTestSuite/src/main.cpp")); + REQUIRE(utility::containsElement(cppFiles, L"data/FileSystemTestSuite/src/test.cpp")); +} + +TEST_CASE("find h files") +{ + std::vector headerFiles = utility::convert( + FileSystem::getFilePathsFromDirectory(FilePath(L"data/FileSystemTestSuite"), { L".h" }), + [](const FilePath& filePath){ return filePath.wstr(); } + ); + + REQUIRE(headerFiles.size() == 3); + REQUIRE(utility::containsElement(headerFiles, L"data/FileSystemTestSuite/tictactoe.h")); + REQUIRE(utility::containsElement(headerFiles, L"data/FileSystemTestSuite/Settings/player.h")); + REQUIRE(utility::containsElement(headerFiles, L"data/FileSystemTestSuite/src/test.h")); +} + +TEST_CASE("find all source files") +{ + std::vector sourceFiles = utility::convert( + FileSystem::getFilePathsFromDirectory(FilePath(L"data/FileSystemTestSuite"), { L".h", L".hpp", L".cpp" }), + [](const FilePath& filePath){ return filePath.wstr(); } + ); + + REQUIRE(sourceFiles.size() == 8); +} + +TEST_CASE("find file infos") +{ +#ifndef _WIN32 + std::vector directoryPaths; + directoryPaths.push_back(FilePath(L"./data/FileSystemTestSuite/src")); + + std::vector files = FileSystem::getFileInfosFromPaths(directoryPaths, { L".h", L".hpp", L".cpp" }, false); + + REQUIRE(files.size() == 2); + REQUIRE(isInFileInfos(files, L"./data/FileSystemTestSuite/src/test.cpp")); + REQUIRE(isInFileInfos(files, L"./data/FileSystemTestSuite/src/test.h")); +#endif +} + +TEST_CASE("find file infos with symlinks") +{ +#ifndef _WIN32 + std::vector directoryPaths; + directoryPaths.push_back(FilePath(L"./data/FileSystemTestSuite/src")); + + std::vector files = FileSystem::getFileInfosFromPaths(directoryPaths, { L".h", L".hpp", L".cpp" }, true); + + REQUIRE(files.size() == 5); + REQUIRE(isInFileInfos(files, L"./data/FileSystemTestSuite/src/Settings/player.h", + L"./data/FileSystemTestSuite/player.h" + )); + REQUIRE(isInFileInfos(files, L"./data/FileSystemTestSuite/src/Settings/sample.cpp", + L"./data/FileSystemTestSuite/sample.cpp" + )); + REQUIRE(isInFileInfos(files, L"./data/FileSystemTestSuite/src/main.cpp", + L"./data/FileSystemTestSuite/src/Settings/src/main.cpp" + )); + REQUIRE(isInFileInfos(files, L"./data/FileSystemTestSuite/src/test.cpp", + L"./data/FileSystemTestSuite/src/Settings/src/test.cpp" + )); + REQUIRE(isInFileInfos(files, L"./data/FileSystemTestSuite/src/test.h", + L"./data/FileSystemTestSuite/src/Settings/src/test.h" + )); +#endif +} + +TEST_CASE("find symlinked directories") +{ +#ifndef _WIN32 + std::vector directoryPaths; + directoryPaths.push_back(FilePath("./data/FileSystemTestSuite/src")); + + std::set dirs = FileSystem::getSymLinkedDirectories(directoryPaths); + + REQUIRE(dirs.size() == 2); +#endif +} diff --git a/src/test/FileSystemTestSuite.h b/src/test/FileSystemTestSuite.h deleted file mode 100644 index 456f7fd8..00000000 --- a/src/test/FileSystemTestSuite.h +++ /dev/null @@ -1,135 +0,0 @@ -#include - -#include -#include -#include -#include - -#include "FileSystem.h" -#include "utility.h" - -class FileSystemTestSuite: public CxxTest::TestSuite -{ -public: - void test_find_cpp_files() - { - std::vector cppFiles = utility::convert( - FileSystem::getFilePathsFromDirectory(FilePath(L"data/FileSystemTestSuite"), { L".cpp" }), - [](const FilePath& filePath){ return filePath.wstr(); } - ); - - TS_ASSERT_EQUALS(cppFiles.size(), 4); - TS_ASSERT(utility::containsElement(cppFiles, L"data/FileSystemTestSuite/main.cpp")); - TS_ASSERT(utility::containsElement(cppFiles, L"data/FileSystemTestSuite/Settings/sample.cpp")); - TS_ASSERT(utility::containsElement(cppFiles, L"data/FileSystemTestSuite/src/main.cpp")); - TS_ASSERT(utility::containsElement(cppFiles, L"data/FileSystemTestSuite/src/test.cpp")); - } - - void test_find_h_files() - { - std::vector headerFiles = utility::convert( - FileSystem::getFilePathsFromDirectory(FilePath(L"data/FileSystemTestSuite"), { L".h" }), - [](const FilePath& filePath){ return filePath.wstr(); } - ); - - TS_ASSERT_EQUALS(headerFiles.size(), 3); - TS_ASSERT(utility::containsElement(headerFiles, L"data/FileSystemTestSuite/tictactoe.h")); - TS_ASSERT(utility::containsElement(headerFiles, L"data/FileSystemTestSuite/Settings/player.h")); - TS_ASSERT(utility::containsElement(headerFiles, L"data/FileSystemTestSuite/src/test.h")); - } - - void test_find_all_source_files() - { - std::vector sourceFiles = utility::convert( - FileSystem::getFilePathsFromDirectory(FilePath(L"data/FileSystemTestSuite"), { L".h", L".hpp", L".cpp" }), - [](const FilePath& filePath){ return filePath.wstr(); } - ); - - TS_ASSERT_EQUALS(sourceFiles.size(), 8); - } - - void test_find_file_infos() - { -#ifndef _WIN32 - std::vector directoryPaths; - directoryPaths.push_back(FilePath(L"./data/FileSystemTestSuite/src")); - - std::vector files = FileSystem::getFileInfosFromPaths(directoryPaths, { L".h", L".hpp", L".cpp" }, false); - - TS_ASSERT_EQUALS(files.size(), 2); - TS_ASSERT(isInFileInfos(files, L"./data/FileSystemTestSuite/src/test.cpp")); - TS_ASSERT(isInFileInfos(files, L"./data/FileSystemTestSuite/src/test.h")); -#endif - } - - void test_find_file_infos_with_symlinks() - { -#ifndef _WIN32 - std::vector directoryPaths; - directoryPaths.push_back(FilePath(L"./data/FileSystemTestSuite/src")); - - std::vector files = FileSystem::getFileInfosFromPaths(directoryPaths, { L".h", L".hpp", L".cpp" }, true); - - TS_ASSERT_EQUALS(files.size(), 5); - TS_ASSERT(isInFileInfos(files, L"./data/FileSystemTestSuite/src/Settings/player.h", - L"./data/FileSystemTestSuite/player.h" - )); - TS_ASSERT(isInFileInfos(files, L"./data/FileSystemTestSuite/src/Settings/sample.cpp", - L"./data/FileSystemTestSuite/sample.cpp" - )); - TS_ASSERT(isInFileInfos(files, L"./data/FileSystemTestSuite/src/main.cpp", - L"./data/FileSystemTestSuite/src/Settings/src/main.cpp" - )); - TS_ASSERT(isInFileInfos(files, L"./data/FileSystemTestSuite/src/test.cpp", - L"./data/FileSystemTestSuite/src/Settings/src/test.cpp" - )); - TS_ASSERT(isInFileInfos(files, L"./data/FileSystemTestSuite/src/test.h", - L"./data/FileSystemTestSuite/src/Settings/src/test.h" - )); -#endif - } - - void test_find_symlinked_directories() - { -#ifndef _WIN32 - std::vector directoryPaths; - directoryPaths.push_back(FilePath("./data/FileSystemTestSuite/src")); - - std::set dirs = FileSystem::getSymLinkedDirectories(directoryPaths); - - TS_ASSERT_EQUALS(dirs.size(), 2); -#endif - } - -private: - bool isInFiles(const std::set& files, const FilePath& filename) - { - return std::end(files) != files.find(filename); - } - - bool isInFileInfos(const std::vector& infos, const std::wstring& filename) - { - for (const FileInfo& info : infos) - { - if (info.path.wstr() == filename) - { - return true; - } - } - - return false; - } - - bool isInFileInfos(const std::vector& infos, const std::wstring& filename, const std::wstring& filename2) - { - for (const FileInfo& info : infos) - { - if (info.path.wstr() == filename || info.path.wstr() == filename2) - { - return true; - } - } - - return false; - } -}; diff --git a/src/test/GraphTestSuite.cpp b/src/test/GraphTestSuite.cpp new file mode 100644 index 00000000..daa33751 --- /dev/null +++ b/src/test/GraphTestSuite.cpp @@ -0,0 +1,341 @@ +#include "catch.hpp" + +#include "Graph.h" + +namespace +{ + class TestToken : public Token + { + public: + TestToken() + :Token(0) + { + } + + TestToken(const TestToken& other) + : Token(other) + { + } + + virtual bool isNode() const + { + return false; + } + + virtual bool isEdge() const + { + return false; + } + + void addComponent(std::shared_ptr component) + { + Token::addComponent(component); + } + + template + std::shared_ptr removeComponent() + { + return Token::removeComponent(); + } + + virtual std::wstring getReadableTypeString() const + { + return L""; + } + }; + + class TestComponent : public TokenComponent + { + public: + virtual std::shared_ptr copy() const + { + return std::make_shared(*this); + } + }; + + class Test2Component : public TokenComponent + { + public: + virtual std::shared_ptr copy() const + { + return std::make_shared(*this); + } + }; +} + +TEST_CASE("tokens save location ids") +{ + TestToken a; + a.addLocationId(23); + a.addLocationId(5); + + REQUIRE(a.getLocationIds().size() == 2); + REQUIRE(a.getLocationIds()[0] == 23); + REQUIRE(a.getLocationIds()[1] == 5); +} + +TEST_CASE("tokens remove location ids") +{ + TestToken a; + a.addLocationId(23); + a.addLocationId(5); + a.removeLocationId(42); + a.removeLocationId(5); + + REQUIRE(a.getLocationIds().size() == 1); + REQUIRE(a.getLocationIds()[0] == 23); +} + +TEST_CASE("token saves component") +{ + TestToken a; + std::shared_ptr component = std::make_shared(); + a.addComponent(component); + + REQUIRE(a.getComponent()); + REQUIRE(!a.getComponent()); + + REQUIRE(a.getComponent() == component.get()); +} + +TEST_CASE("token saves multiple components") +{ + TestToken a; + std::shared_ptr component = std::make_shared(); + std::shared_ptr component2 = std::make_shared(); + a.addComponent(component2); + a.addComponent(component); + + REQUIRE(a.getComponent()); + REQUIRE(a.getComponent()); + + REQUIRE(a.getComponent() == component.get()); + REQUIRE(a.getComponent() == component2.get()); +} + +TEST_CASE("token removes component") +{ + TestToken a; + std::shared_ptr component = std::make_shared(); + std::shared_ptr component2 = std::make_shared(); + a.addComponent(component2); + a.addComponent(component); + std::shared_ptr component3 = a.removeComponent(); + + REQUIRE(!a.getComponent()); + REQUIRE(a.getComponent()); + + REQUIRE(component3.get() == component.get()); + REQUIRE(a.getComponent() == component2.get()); +} + +TEST_CASE("token copies components when token is copied") +{ + TestToken a; + std::shared_ptr component = std::make_shared(); + std::shared_ptr component2 = std::make_shared(); + a.addComponent(component2); + a.addComponent(component); + + TestToken b(a); + + REQUIRE(b.getComponent()); + REQUIRE(b.getComponent()); + + REQUIRE(b.getComponent() != component.get()); + REQUIRE(b.getComponent() != component2.get()); +} + +TEST_CASE("nodes are nodes") +{ + Node a(1, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + + REQUIRE(a.isNode()); + REQUIRE(!a.isEdge()); +} + +TEST_CASE("edges are edges") +{ + Node a(1, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + Node b(2, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"B", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + Edge e(3, Edge::EDGE_USAGE, &a, &b); + + REQUIRE(!e.isNode()); + REQUIRE(e.isEdge()); +} + +TEST_CASE("set type of node from constructor") +{ + Node n(1, NodeType(NodeType::NODE_FUNCTION), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + REQUIRE(NodeType(NodeType::NODE_FUNCTION) == n.getType()); +} + +TEST_CASE("set type of node from non indexed") +{ + Node n(2, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + n.setType(NodeType(NodeType::NODE_CLASS)); + REQUIRE(NodeType(NodeType::NODE_CLASS) == n.getType()); +} + +TEST_CASE("can not change type of node after it was set") +{ + Node n(3, NodeType(NodeType::NODE_NAMESPACE), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + n.setType(NodeType(NodeType::NODE_CLASS)); + REQUIRE(NodeType(NodeType::NODE_CLASS) != n.getType()); +} + +TEST_CASE("node can be copied and keeps same id") +{ + Node n(4, NodeType(NodeType::NODE_NAMESPACE), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + Node n2(n); + + REQUIRE(&n != &n2); + REQUIRE(n.getId() == n2.getId()); + REQUIRE(n.getName() == n2.getName()); + REQUIRE(n.getType() == n2.getType()); +} + +TEST_CASE("node type bit masking") +{ + Node n(1, NodeType(NodeType::NODE_NAMESPACE), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + REQUIRE(n.isType(NodeType::NODE_FUNCTION | NodeType::NODE_NAMESPACE | NodeType::NODE_CLASS)); + REQUIRE(!n.isType(NodeType::NODE_FUNCTION | NodeType::NODE_METHOD | NodeType::NODE_CLASS)); +} + +TEST_CASE("get type of edges") +{ + Node a(1, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + Node b(2, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"B", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + Edge e(3, Edge::EDGE_USAGE, &a, &b); + + REQUIRE(Edge::EDGE_USAGE == e.getType()); +} + +TEST_CASE("edge can be copied and keeps same id") +{ + Node a(1, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + Node b(2, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"B", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + Edge e(3, Edge::EDGE_USAGE, &a, &b); + Edge e2(e, &a, &b); + + REQUIRE(&e != &e2); + REQUIRE(e.getId() == e2.getId()); + REQUIRE(e.getType() == e2.getType()); +} + +TEST_CASE("edge type bit masking") +{ + Node a(1, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + Node b(2, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"B", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + Edge e(3, Edge::EDGE_USAGE, &a, &b); + + REQUIRE(e.isType(Edge::EDGE_MEMBER | Edge::EDGE_CALL | Edge::EDGE_USAGE)); + REQUIRE(!e.isType(Edge::EDGE_MEMBER | Edge::EDGE_CALL)); +} + +TEST_CASE("node finds child node") +{ + Node a(1, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + Node b(2, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"B", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + Node c(3, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"C", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + Edge e(4, Edge::EDGE_MEMBER, &a, &b); + Edge e2(5, Edge::EDGE_MEMBER, &a, &c); + + Node* x = a.findChildNode( + [](Node* n) + { + return n->getName() == L"C"; + } + ); + + REQUIRE(x == &c); + REQUIRE(x != &b); +} + +TEST_CASE("node can not find child node") +{ + Node a(1, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + Node b(2, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"B", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + Node c(3, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"C", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + Edge e(4, Edge::EDGE_MEMBER, &a, &b); + Edge e2(5, Edge::EDGE_MEMBER, &a, &c); + + Node* x = a.findChildNode( + [](Node* n) + { + return n->getName() == L"D"; + } + ); + + REQUIRE(!x); +} + +TEST_CASE("node visits child nodes") +{ + Node a(1, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + Node b(2, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"B", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + Node c(3, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"C", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + Edge e(4, Edge::EDGE_MEMBER, &a, &b); + Edge e2(5, Edge::EDGE_MEMBER, &a, &c); + + std::vector children; + a.forEachChildNode( + [&children](Node* n) + { + return children.push_back(n); + } + ); + + REQUIRE(children.size() == 2); + REQUIRE(children[0] == &b); + REQUIRE(children[1] == &c); +} + +TEST_CASE("graph saves nodes") +{ + Graph graph; + Node* a = graph.createNode(1, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + Node* b = graph.createNode(2, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"B", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + + REQUIRE(2 == graph.getNodeCount()); + REQUIRE(0 == graph.getEdgeCount()); + + REQUIRE(graph.getNodeById(a->getId())); + REQUIRE(L"A" == graph.getNodeById(a->getId())->getName()); + + REQUIRE(graph.getNodeById(b->getId())); + REQUIRE(L"B" == graph.getNodeById(b->getId())->getName()); + + REQUIRE(!graph.getNodeById(0)); +} + +TEST_CASE("graph saves edges") +{ + Graph graph; + + Node* a = graph.createNode(1, NodeType(NodeType::NODE_FUNCTION), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + Node* b = graph.createNode(2, NodeType(NodeType::NODE_FUNCTION), NameHierarchy(L"B", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + + Edge* e = graph.createEdge(3, Edge::EDGE_CALL, a, b); + + REQUIRE(2 == graph.getNodeCount()); + REQUIRE(1 == graph.getEdgeCount()); + + REQUIRE(graph.getEdgeById(e->getId())); + REQUIRE(Edge::EDGE_CALL == graph.getEdgeById(e->getId())->getType()); +} + +TEST_CASE("graph removes nodes") +{ + Graph graph; + + Node* a = graph.createNode(1, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + graph.createNode(2, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"B", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); + + REQUIRE(2 == graph.getNodeCount()); + REQUIRE(0 == graph.getEdgeCount()); + + graph.removeNode(graph.getNodeById(a->getId())); + + REQUIRE(1 == graph.getNodeCount()); +} diff --git a/src/test/GraphTestSuite.h b/src/test/GraphTestSuite.h deleted file mode 100644 index 9c06fac7..00000000 --- a/src/test/GraphTestSuite.h +++ /dev/null @@ -1,343 +0,0 @@ -#include - -#include "Graph.h" - -class GraphTestSuite : public CxxTest::TestSuite -{ -public: - void test_tokens_save_location_ids() - { - TestToken a; - a.addLocationId(23); - a.addLocationId(5); - - TS_ASSERT_EQUALS(a.getLocationIds().size(), 2); - TS_ASSERT_EQUALS(a.getLocationIds()[0], 23); - TS_ASSERT_EQUALS(a.getLocationIds()[1], 5); - } - - void test_tokens_remove_location_ids() - { - TestToken a; - a.addLocationId(23); - a.addLocationId(5); - a.removeLocationId(42); - a.removeLocationId(5); - - TS_ASSERT_EQUALS(a.getLocationIds().size(), 1); - TS_ASSERT_EQUALS(a.getLocationIds()[0], 23); - } - - void test_token_saves_component() - { - TestToken a; - std::shared_ptr component = std::make_shared(); - a.addComponent(component); - - TS_ASSERT(a.getComponent()); - TS_ASSERT(!a.getComponent()); - - TS_ASSERT_EQUALS(a.getComponent(), component.get()); - } - - void test_token_saves_multiple_components() - { - TestToken a; - std::shared_ptr component = std::make_shared(); - std::shared_ptr component2 = std::make_shared(); - a.addComponent(component2); - a.addComponent(component); - - TS_ASSERT(a.getComponent()); - TS_ASSERT(a.getComponent()); - - TS_ASSERT_EQUALS(a.getComponent(), component.get()); - TS_ASSERT_EQUALS(a.getComponent(), component2.get()); - } - - void test_token_removes_component() - { - TestToken a; - std::shared_ptr component = std::make_shared(); - std::shared_ptr component2 = std::make_shared(); - a.addComponent(component2); - a.addComponent(component); - std::shared_ptr component3 = a.removeComponent(); - - TS_ASSERT(!a.getComponent()); - TS_ASSERT(a.getComponent()); - - TS_ASSERT_EQUALS(component3.get(), component.get()); - TS_ASSERT_EQUALS(a.getComponent(), component2.get()); - } - - void test_token_copies_components_when_token_is_copied() - { - TestToken a; - std::shared_ptr component = std::make_shared(); - std::shared_ptr component2 = std::make_shared(); - a.addComponent(component2); - a.addComponent(component); - - TestToken b(a); - - TS_ASSERT(b.getComponent()); - TS_ASSERT(b.getComponent()); - - TS_ASSERT_DIFFERS(b.getComponent(), component.get()); - TS_ASSERT_DIFFERS(b.getComponent(), component2.get()); - } - - void test_nodes_are_nodes() - { - Node a(1, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - - TS_ASSERT(a.isNode()); - TS_ASSERT(!a.isEdge()); - } - - void test_edges_are_edges() - { - Node a(1, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - Node b(2, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"B", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - Edge e(3, Edge::EDGE_USAGE, &a, &b); - - TS_ASSERT(!e.isNode()); - TS_ASSERT(e.isEdge()); - } - - void test_set_type_of_node_from_constructor() - { - Node n(1, NodeType(NodeType::NODE_FUNCTION), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - TS_ASSERT_EQUALS(NodeType(NodeType::NODE_FUNCTION), n.getType()); - } - - void test_set_type_of_node_from_non_indexed() - { - Node n(2, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - n.setType(NodeType(NodeType::NODE_CLASS)); - TS_ASSERT_EQUALS(NodeType(NodeType::NODE_CLASS), n.getType()); - } - - void test_can_not_change_type_of_node_after_it_was_set() - { - Node n(3, NodeType(NodeType::NODE_NAMESPACE), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - n.setType(NodeType(NodeType::NODE_CLASS)); - TS_ASSERT_DIFFERS(NodeType(NodeType::NODE_CLASS), n.getType()); - } - - void test_node_can_be_copied_and_keeps_same_id() - { - Node n(4, NodeType(NodeType::NODE_NAMESPACE), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - Node n2(n); - - TS_ASSERT_DIFFERS(&n, &n2); - TS_ASSERT_EQUALS(n.getId(), n2.getId()); - TS_ASSERT_EQUALS(n.getName(), n2.getName()); - TS_ASSERT_EQUALS(n.getType(), n2.getType()); - } - - void test_node_type_bit_masking() - { - Node n(1, NodeType(NodeType::NODE_NAMESPACE), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - TS_ASSERT(n.isType(NodeType::NODE_FUNCTION | NodeType::NODE_NAMESPACE | NodeType::NODE_CLASS)); - TS_ASSERT(!n.isType(NodeType::NODE_FUNCTION | NodeType::NODE_METHOD | NodeType::NODE_CLASS)); - } - - void test_get_type_of_edges() - { - Node a(1, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - Node b(2, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"B", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - Edge e(3, Edge::EDGE_USAGE, &a, &b); - - TS_ASSERT_EQUALS(Edge::EDGE_USAGE, e.getType()); - } - - void test_edge_can_be_copied_and_keeps_same_id() - { - Node a(1, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - Node b(2, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"B", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - Edge e(3, Edge::EDGE_USAGE, &a, &b); - Edge e2(e, &a, &b); - - TS_ASSERT_DIFFERS(&e, &e2); - TS_ASSERT_EQUALS(e.getId(), e2.getId()); - TS_ASSERT_EQUALS(e.getType(), e2.getType()); - } - - void test_edge_type_bit_masking() - { - Node a(1, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - Node b(2, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"B", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - Edge e(3, Edge::EDGE_USAGE, &a, &b); - - TS_ASSERT(e.isType(Edge::EDGE_MEMBER | Edge::EDGE_CALL | Edge::EDGE_USAGE)); - TS_ASSERT(!e.isType(Edge::EDGE_MEMBER | Edge::EDGE_CALL)); - } - - void test_node_finds_child_node() - { - Node a(1, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - Node b(2, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"B", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - Node c(3, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"C", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - Edge e(4, Edge::EDGE_MEMBER, &a, &b); - Edge e2(5, Edge::EDGE_MEMBER, &a, &c); - - Node* x = a.findChildNode( - [](Node* n) - { - return n->getName() == L"C"; - } - ); - - TS_ASSERT_EQUALS(x, &c); - TS_ASSERT_DIFFERS(x, &b); - } - - void test_node_can_not_find_child_node() - { - Node a(1, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - Node b(2, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"B", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - Node c(3, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"C", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - Edge e(4, Edge::EDGE_MEMBER, &a, &b); - Edge e2(5, Edge::EDGE_MEMBER, &a, &c); - - Node* x = a.findChildNode( - [](Node* n) - { - return n->getName() == L"D"; - } - ); - - TS_ASSERT(!x); - } - - void test_node_visits_child_nodes() - { - Node a(1, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - Node b(2, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"B", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - Node c(3, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"C", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - Edge e(4, Edge::EDGE_MEMBER, &a, &b); - Edge e2(5, Edge::EDGE_MEMBER, &a, &c); - - std::vector children; - a.forEachChildNode( - [&children](Node* n) - { - return children.push_back(n); - } - ); - - TS_ASSERT_EQUALS(children.size(), 2); - TS_ASSERT_EQUALS(children[0], &b); - TS_ASSERT_EQUALS(children[1], &c); - } - - void test_graph_saves_nodes() - { - Graph graph; - Node* a = graph.createNode(1, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - Node* b = graph.createNode(2, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"B", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - - TS_ASSERT_EQUALS(2, graph.getNodeCount()); - TS_ASSERT_EQUALS(0, graph.getEdgeCount()); - - TS_ASSERT(graph.getNodeById(a->getId())); - TS_ASSERT_EQUALS(L"A", graph.getNodeById(a->getId())->getName()); - - TS_ASSERT(graph.getNodeById(b->getId())); - TS_ASSERT_EQUALS(L"B", graph.getNodeById(b->getId())->getName()); - - TS_ASSERT(!graph.getNodeById(0)); - } - - void test_graph_saves_edges() - { - Graph graph; - - Node* a = graph.createNode(1, NodeType(NodeType::NODE_FUNCTION), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - Node* b = graph.createNode(2, NodeType(NodeType::NODE_FUNCTION), NameHierarchy(L"B", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - - Edge* e = graph.createEdge(3, Edge::EDGE_CALL, a, b); - - TS_ASSERT_EQUALS(2, graph.getNodeCount()); - TS_ASSERT_EQUALS(1, graph.getEdgeCount()); - - TS_ASSERT(graph.getEdgeById(e->getId())); - TS_ASSERT_EQUALS(Edge::EDGE_CALL, graph.getEdgeById(e->getId())->getType()); - } - - void test_graph_removes_nodes() - { - Graph graph; - - Node* a = graph.createNode(1, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"A", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - graph.createNode(2, NodeType(NodeType::NODE_SYMBOL), NameHierarchy(L"B", NAME_DELIMITER_CXX), DEFINITION_EXPLICIT); - - TS_ASSERT_EQUALS(2, graph.getNodeCount()); - TS_ASSERT_EQUALS(0, graph.getEdgeCount()); - - graph.removeNode(graph.getNodeById(a->getId())); - - TS_ASSERT_EQUALS(1, graph.getNodeCount()); - } - -private: - class TestToken: public Token - { - public: - TestToken() - :Token(0) - { - } - - TestToken(const TestToken& other) - : Token(other) - { - } - - virtual bool isNode() const - { - return false; - } - - virtual bool isEdge() const - { - return false; - } - - void addComponent(std::shared_ptr component) - { - Token::addComponent(component); - } - - template - std::shared_ptr removeComponent() - { - return Token::removeComponent(); - } - - virtual std::wstring getReadableTypeString() const - { - return L""; - } - }; - - class TestComponent: public TokenComponent - { - public: - virtual std::shared_ptr copy() const - { - return std::make_shared(*this); - } - }; - - class Test2Component: public TokenComponent - { - public: - virtual std::shared_ptr copy() const - { - return std::make_shared(*this); - } - }; -}; diff --git a/src/test/JavaIndexSampleProjectsTestSuite.cpp b/src/test/JavaIndexSampleProjectsTestSuite.cpp new file mode 100644 index 00000000..f22ee847 --- /dev/null +++ b/src/test/JavaIndexSampleProjectsTestSuite.cpp @@ -0,0 +1,289 @@ +#include "catch.hpp" + +#include +#include + +#include "ApplicationSettings.h" +#include "FileRegister.h" +#include "IndexerCommandJava.h" +#include "JavaEnvironmentFactory.h" +#include "JavaParser.h" +#include "ParserClientImpl.h" +#include "TestIntermediateStorage.h" +#include "TextAccess.h" +#include "TimeStamp.h" +#include "utility.h" +#include "utilityJava.h" +#include "utilityPathDetection.h" +#include "utilityString.h" + +#define REQUIRE_MESSAGE(msg, cond) do { INFO(msg); REQUIRE(cond); } while((void)0, 0) + +namespace +{ + const bool updateExpectedOutput = false; + const bool trackTime = true; + size_t duration; + + void setupJavaEnvironmentFactory() + { + if (!JavaEnvironmentFactory::getInstance()) + { + std::string errorString; +#ifdef _WIN32 + const std::string separator = ";"; +#else + const std::string separator = ":"; +#endif + std::string classPath = ""; + { + const std::vector jarNames = utility::getRequiredJarNames(); + for (size_t i = 0; i < jarNames.size(); i++) + { + if (i != 0) + { + classPath += separator; + } + classPath += FilePath(L"../app/data/java/lib/").concatenate(jarNames[i]).str(); + } + } + + JavaEnvironmentFactory::createInstance( + classPath, + errorString + ); + } + } + + std::shared_ptr parseCode(const FilePath& sourceFilePath, const FilePath& projectDataSrcRoot, const std::vector& classpath) + { + TestIntermediateStorage storage; + JavaParser parser(std::make_shared(&storage), std::make_shared()); + std::shared_ptr command = std::make_shared(sourceFilePath, L"8", classpath); + + TimeStamp startTime = TimeStamp::now(); + parser.buildIndex(command); + duration += TimeStamp::now().deltaMS(startTime); + + storage.generateStringLists(); + + return TextAccess::createFromLines(storage.m_lines); + } + + void processSourceFile(const std::string& projectName, const FilePath& sourceFilePath, const std::vector& classpath) + { + const FilePath projectDataRoot = FilePath("data/JavaIndexSampleProjectsTestSuite/" + projectName); + const FilePath projectDataSrcRoot = projectDataRoot.getConcatenated(L"src"); + const FilePath projectDataExpectedOutputRoot = projectDataRoot.getConcatenated(L"expected_output"); + + std::shared_ptr output = parseCode(projectDataSrcRoot.getConcatenated(sourceFilePath), projectDataSrcRoot, classpath); + + const FilePath expectedOutputFilePath = projectDataExpectedOutputRoot.getConcatenated(utility::replace(sourceFilePath.withoutExtension().wstr() + L".txt", L"/", L"_")); + if (updateExpectedOutput || !expectedOutputFilePath.exists()) + { + std::ofstream expectedOutputFile; + expectedOutputFile.open(expectedOutputFilePath.str()); + expectedOutputFile << output->getText(); + expectedOutputFile.close(); + } + else + { + std::shared_ptr expectedOutput = TextAccess::createFromFile(expectedOutputFilePath); + REQUIRE_MESSAGE(("Output does not match the expected line count for file " + sourceFilePath.str() + " in project " + projectName).c_str(), expectedOutput->getLineCount() == output->getLineCount()); + if (expectedOutput->getLineCount() == output->getLineCount()) + { + for (size_t i = 1; i <= expectedOutput->getLineCount(); i++) + { + REQUIRE(expectedOutput->getLine(i) == output->getLine(i)); + } + } + } + } + + void processSourceFiles(const std::string& projectName, const std::vector& sourceFilePaths, const std::vector& classpath) + { + duration = 0; + for (const FilePath& filePath : sourceFilePaths) + { + processSourceFile(projectName, filePath, classpath); + } + if (trackTime) + { + const FilePath projectDataRoot = FilePath("data/JavaIndexSampleProjectsTestSuite/" + projectName).makeAbsolute(); + + std::ofstream outfile; + outfile.open(FilePath(projectDataRoot.str() + "/" + projectName + ".timing").str(), std::ios_base::app); + outfile << TimeStamp::now().toString() << " - " << duration << " ms\n"; + outfile.close(); + } + } +} + +TEST_CASE("java sample parser can setup environment factory") +{ + std::vector javaPaths = utility::getJavaRuntimePathDetector()->getPaths(); + if (!javaPaths.empty()) + { + ApplicationSettings::getInstance()->setJavaPath(javaPaths[0]); + } + + setupJavaEnvironmentFactory(); + + // if this one fails, maybe your java_path in the test settings is wrong. + REQUIRE(JavaEnvironmentFactory::getInstance().use_count() >= 1); +} + +TEST_CASE("index javasymbolsolver 0 6 0 project") +{ + const std::vector& classpath = { + FilePath(L"data/JavaIndexSampleProjectsTestSuite/JavaSymbolSolver060/lib/guava-21.0.jar").makeAbsolute(), + FilePath(L"data/JavaIndexSampleProjectsTestSuite/JavaSymbolSolver060/lib/javaparser-core-3.3.0.jar").makeAbsolute(), + FilePath(L"data/JavaIndexSampleProjectsTestSuite/JavaSymbolSolver060/lib/javaslang-2.0.3.jar").makeAbsolute(), + FilePath(L"data/JavaIndexSampleProjectsTestSuite/JavaSymbolSolver060/lib/javassist-3.19.0-GA.jar").makeAbsolute(), + FilePath(L"data/JavaIndexSampleProjectsTestSuite/JavaSymbolSolver060/src/java-symbol-solver-core").makeAbsolute(), + FilePath(L"data/JavaIndexSampleProjectsTestSuite/JavaSymbolSolver060/src/java-symbol-solver-logic").makeAbsolute(), + FilePath(L"data/JavaIndexSampleProjectsTestSuite/JavaSymbolSolver060/src/java-symbol-solver-model").makeAbsolute() + }; + + processSourceFiles( + "JavaSymbolSolver060", + { + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/SourceFileInfoExtractor.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/core/resolution/Context.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/core/resolution/ContextHelper.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/declarations/common/MethodDeclarationCommonLogic.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparser/Navigator.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparser/package-info.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/DefaultVisitorAdapter.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFacade.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFactory.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/LambdaArgumentTypePlaceholder.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/package-info.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/TypeExtractor.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/UnsolvedSymbolException.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AbstractJavaParserContext.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AbstractMethodLikeDeclarationContext.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AnonymousClassDeclarationContext.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/CatchClauseContext.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ClassOrInterfaceDeclarationContext.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/CompilationUnitContext.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ConstructorContext.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ContextHelper.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/EnumDeclarationContext.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/FieldAccessContext.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ForechStatementContext.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ForStatementContext.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/JavaParserTypeDeclarationAdapter.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/LambdaExprContext.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/MethodCallExprContext.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/MethodContext.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/StatementContext.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/SwitchEntryContext.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/TryWithResourceContext.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/DefaultConstructorDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/Helper.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserAnnotationDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserAnonymousClassDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserClassDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserConstructorDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserEnumConstantDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserEnumDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserFieldDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserInterfaceDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserMethodDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserParameterDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserSymbolDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeAdapter.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeParameter.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeVariableDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarators/AbstractSymbolDeclarator.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarators/FieldSymbolDeclarator.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarators/NoSymbolDeclarator.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarators/ParameterSymbolDeclarator.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarators/VariableSymbolDeclarator.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistClassDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistConstructorDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistEnumDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistFactory.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistFieldDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistInterfaceDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistMethodDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistParameterDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistTypeDeclarationAdapter.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistTypeParameter.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistUtils.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/package-info.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/model/typesystem/LazyType.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/model/typesystem/ReferenceTypeImpl.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/MyObjectProvider.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/package-info.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionClassAdapter.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionClassDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionConstructorDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionEnumDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionFactory.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionFieldDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionInterfaceDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionMethodDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionMethodResolutionLogic.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionParameterDeclaration.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionTypeParameter.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/comparators/ClassComparator.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/comparators/MethodComparator.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/comparators/ParameterComparator.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/resolution/ConstructorResolutionLogic.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/resolution/MethodResolutionLogic.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/resolution/SymbolDeclarator.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/resolution/SymbolSolver.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/resolution/typesolvers/CombinedTypeSolver.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/resolution/typesolvers/JarTypeSolver.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/resolution/typesolvers/JavaParserTypeSolver.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/resolution/typesolvers/MemoryTypeSolver.java"), + FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/resolution/typesolvers/ReflectionTypeSolver.java"), + FilePath(L"java-symbol-solver-logic/com/github/javaparser/symbolsolver/logic/AbstractClassDeclaration.java"), + FilePath(L"java-symbol-solver-logic/com/github/javaparser/symbolsolver/logic/AbstractTypeDeclaration.java"), + FilePath(L"java-symbol-solver-logic/com/github/javaparser/symbolsolver/logic/ConfilictingGenericTypesException.java"), + FilePath(L"java-symbol-solver-logic/com/github/javaparser/symbolsolver/logic/FunctionalInterfaceLogic.java"), + FilePath(L"java-symbol-solver-logic/com/github/javaparser/symbolsolver/logic/InferenceContext.java"), + FilePath(L"java-symbol-solver-logic/com/github/javaparser/symbolsolver/logic/InferenceVariableType.java"), + FilePath(L"java-symbol-solver-logic/com/github/javaparser/symbolsolver/logic/ObjectProvider.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/AccessLevel.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/AnnotationDeclaration.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/ClassDeclaration.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/ConstructorDeclaration.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/Declaration.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/EnumDeclaration.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/FieldDeclaration.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/HasAccessLevel.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/InterfaceDeclaration.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/MethodAmbiguityException.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/MethodDeclaration.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/MethodLikeDeclaration.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/ParameterDeclaration.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/ReferenceTypeDeclaration.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/TypeDeclaration.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/TypeParameterDeclaration.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/TypeParametrizable.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/ValueDeclaration.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/methods/MethodUsage.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/resolution/SymbolReference.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/resolution/TypeSolver.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/resolution/UnsolvedSymbolException.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/resolution/Value.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/ArrayType.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/LambdaConstraintType.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/NullType.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/PrimitiveType.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/ReferenceType.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/Type.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/TypeTransformer.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/TypeVariable.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/VoidType.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/Wildcard.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/parametrization/TypeParametersMap.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/parametrization/TypeParameterValueProvider.java"), + FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/parametrization/TypeParametrized.java") + }, + classpath + ); +} diff --git a/src/test/JavaIndexSampleProjectsTestSuite.h b/src/test/JavaIndexSampleProjectsTestSuite.h deleted file mode 100644 index 679bc5ad..00000000 --- a/src/test/JavaIndexSampleProjectsTestSuite.h +++ /dev/null @@ -1,289 +0,0 @@ -#include - -#include -#include - -#include "ApplicationSettings.h" -#include "FileRegister.h" -#include "IndexerCommandJava.h" -#include "JavaEnvironmentFactory.h" -#include "JavaParser.h" -#include "ParserClientImpl.h" -#include "TestIntermediateStorage.h" -#include "TextAccess.h" -#include "utility.h" -#include "utilityJava.h" -#include "utilityPathDetection.h" -#include "utilityString.h" - -class JavaIndexSampleProjectsTestSuite : public CxxTest::TestSuite -{ -public: - static const bool s_updateExpectedOutput = false; - static const bool s_trackTime = true; - - void test_java_parser_can_setup_environment_factory() - { - std::vector javaPaths = utility::getJavaRuntimePathDetector()->getPaths(); - if (!javaPaths.empty()) - { - ApplicationSettings::getInstance()->setJavaPath(javaPaths[0]); - } - - setupJavaEnvironmentFactory(); - - // if this one fails, maybe your java_path in the test settings is wrong. - TS_ASSERT_LESS_THAN_EQUALS(1, JavaEnvironmentFactory::getInstance().use_count()); - } - - void test_index_javasymbolsolver_0_6_0_project() - { - const std::vector& classpath = { - FilePath(L"data/JavaIndexSampleProjectsTestSuite/JavaSymbolSolver060/lib/guava-21.0.jar").makeAbsolute(), - FilePath(L"data/JavaIndexSampleProjectsTestSuite/JavaSymbolSolver060/lib/javaparser-core-3.3.0.jar").makeAbsolute(), - FilePath(L"data/JavaIndexSampleProjectsTestSuite/JavaSymbolSolver060/lib/javaslang-2.0.3.jar").makeAbsolute(), - FilePath(L"data/JavaIndexSampleProjectsTestSuite/JavaSymbolSolver060/lib/javassist-3.19.0-GA.jar").makeAbsolute(), - FilePath(L"data/JavaIndexSampleProjectsTestSuite/JavaSymbolSolver060/src/java-symbol-solver-core").makeAbsolute(), - FilePath(L"data/JavaIndexSampleProjectsTestSuite/JavaSymbolSolver060/src/java-symbol-solver-logic").makeAbsolute(), - FilePath(L"data/JavaIndexSampleProjectsTestSuite/JavaSymbolSolver060/src/java-symbol-solver-model").makeAbsolute() - }; - - processSourceFiles( - "JavaSymbolSolver060", - { - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/SourceFileInfoExtractor.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/core/resolution/Context.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/core/resolution/ContextHelper.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/declarations/common/MethodDeclarationCommonLogic.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparser/Navigator.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparser/package-info.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/DefaultVisitorAdapter.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFacade.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFactory.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/LambdaArgumentTypePlaceholder.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/package-info.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/TypeExtractor.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/UnsolvedSymbolException.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AbstractJavaParserContext.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AbstractMethodLikeDeclarationContext.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AnonymousClassDeclarationContext.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/CatchClauseContext.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ClassOrInterfaceDeclarationContext.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/CompilationUnitContext.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ConstructorContext.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ContextHelper.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/EnumDeclarationContext.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/FieldAccessContext.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ForechStatementContext.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ForStatementContext.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/JavaParserTypeDeclarationAdapter.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/LambdaExprContext.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/MethodCallExprContext.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/MethodContext.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/StatementContext.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/SwitchEntryContext.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/contexts/TryWithResourceContext.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/DefaultConstructorDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/Helper.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserAnnotationDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserAnonymousClassDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserClassDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserConstructorDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserEnumConstantDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserEnumDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserFieldDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserInterfaceDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserMethodDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserParameterDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserSymbolDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeAdapter.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeParameter.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeVariableDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarators/AbstractSymbolDeclarator.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarators/FieldSymbolDeclarator.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarators/NoSymbolDeclarator.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarators/ParameterSymbolDeclarator.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javaparsermodel/declarators/VariableSymbolDeclarator.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistClassDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistConstructorDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistEnumDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistFactory.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistFieldDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistInterfaceDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistMethodDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistParameterDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistTypeDeclarationAdapter.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistTypeParameter.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/JavassistUtils.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/javassistmodel/package-info.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/model/typesystem/LazyType.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/model/typesystem/ReferenceTypeImpl.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/MyObjectProvider.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/package-info.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionClassAdapter.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionClassDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionConstructorDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionEnumDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionFactory.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionFieldDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionInterfaceDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionMethodDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionMethodResolutionLogic.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionParameterDeclaration.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionTypeParameter.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/comparators/ClassComparator.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/comparators/MethodComparator.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/reflectionmodel/comparators/ParameterComparator.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/resolution/ConstructorResolutionLogic.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/resolution/MethodResolutionLogic.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/resolution/SymbolDeclarator.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/resolution/SymbolSolver.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/resolution/typesolvers/CombinedTypeSolver.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/resolution/typesolvers/JarTypeSolver.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/resolution/typesolvers/JavaParserTypeSolver.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/resolution/typesolvers/MemoryTypeSolver.java"), - FilePath(L"java-symbol-solver-core/com/github/javaparser/symbolsolver/resolution/typesolvers/ReflectionTypeSolver.java"), - FilePath(L"java-symbol-solver-logic/com/github/javaparser/symbolsolver/logic/AbstractClassDeclaration.java"), - FilePath(L"java-symbol-solver-logic/com/github/javaparser/symbolsolver/logic/AbstractTypeDeclaration.java"), - FilePath(L"java-symbol-solver-logic/com/github/javaparser/symbolsolver/logic/ConfilictingGenericTypesException.java"), - FilePath(L"java-symbol-solver-logic/com/github/javaparser/symbolsolver/logic/FunctionalInterfaceLogic.java"), - FilePath(L"java-symbol-solver-logic/com/github/javaparser/symbolsolver/logic/InferenceContext.java"), - FilePath(L"java-symbol-solver-logic/com/github/javaparser/symbolsolver/logic/InferenceVariableType.java"), - FilePath(L"java-symbol-solver-logic/com/github/javaparser/symbolsolver/logic/ObjectProvider.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/AccessLevel.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/AnnotationDeclaration.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/ClassDeclaration.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/ConstructorDeclaration.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/Declaration.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/EnumDeclaration.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/FieldDeclaration.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/HasAccessLevel.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/InterfaceDeclaration.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/MethodAmbiguityException.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/MethodDeclaration.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/MethodLikeDeclaration.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/ParameterDeclaration.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/ReferenceTypeDeclaration.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/TypeDeclaration.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/TypeParameterDeclaration.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/TypeParametrizable.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/declarations/ValueDeclaration.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/methods/MethodUsage.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/resolution/SymbolReference.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/resolution/TypeSolver.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/resolution/UnsolvedSymbolException.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/resolution/Value.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/ArrayType.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/LambdaConstraintType.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/NullType.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/PrimitiveType.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/ReferenceType.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/Type.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/TypeTransformer.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/TypeVariable.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/VoidType.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/Wildcard.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/parametrization/TypeParametersMap.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/parametrization/TypeParameterValueProvider.java"), - FilePath(L"java-symbol-solver-model/com/github/javaparser/symbolsolver/model/typesystem/parametrization/TypeParametrized.java") - }, - classpath - ); - } - -private: - void setupJavaEnvironmentFactory() - { - if (!JavaEnvironmentFactory::getInstance()) - { - std::string errorString; -#ifdef _WIN32 - const std::string separator = ";"; -#else - const std::string separator = ":"; -#endif - std::string classPath = ""; - { - const std::vector jarNames = utility::getRequiredJarNames(); - for (size_t i = 0; i < jarNames.size(); i++) - { - if (i != 0) - { - classPath += separator; - } - classPath += FilePath(L"../app/data/java/lib/").concatenate(jarNames[i]).str(); - } - } - - JavaEnvironmentFactory::createInstance( - classPath, - errorString - ); - } - } - - void processSourceFiles(const std::string& projectName, const std::vector& sourceFilePaths, const std::vector& classpath) - { - m_duration = 0; - for (const FilePath& filePath : sourceFilePaths) - { - processSourceFile(projectName, filePath, classpath); - } - if (s_trackTime) - { - const FilePath projectDataRoot = FilePath("data/JavaIndexSampleProjectsTestSuite/" + projectName).makeAbsolute(); - - std::ofstream outfile; - outfile.open(FilePath(projectDataRoot.str() + "/" + projectName + ".timing").str(), std::ios_base::app); - outfile << TimeStamp::now().toString() << " - " << m_duration << " ms\n"; - outfile.close(); - } - } - - void processSourceFile(const std::string& projectName, const FilePath& sourceFilePath, const std::vector& classpath) - { - const FilePath projectDataRoot = FilePath("data/JavaIndexSampleProjectsTestSuite/" + projectName); - const FilePath projectDataSrcRoot = projectDataRoot.getConcatenated(L"src"); - const FilePath projectDataExpectedOutputRoot = projectDataRoot.getConcatenated(L"expected_output"); - - std::shared_ptr output = parseCode(projectDataSrcRoot.getConcatenated(sourceFilePath), projectDataSrcRoot, classpath); - - const FilePath expectedOutputFilePath = projectDataExpectedOutputRoot.getConcatenated(utility::replace(sourceFilePath.withoutExtension().wstr() + L".txt", L"/", L"_")); - if (s_updateExpectedOutput || !expectedOutputFilePath.exists()) - { - std::ofstream expectedOutputFile; - expectedOutputFile.open(expectedOutputFilePath.str()); - expectedOutputFile << output->getText(); - expectedOutputFile.close(); - } - else - { - std::shared_ptr expectedOutput = TextAccess::createFromFile(expectedOutputFilePath); - TSM_ASSERT_EQUALS("Output does not match the expected line count for file " + sourceFilePath.str() + " in project " + projectName, expectedOutput->getLineCount(), output->getLineCount()); - if (expectedOutput->getLineCount() == output->getLineCount()) - { - for (size_t i = 1; i <= expectedOutput->getLineCount(); i++) - { - TS_ASSERT_EQUALS(expectedOutput->getLine(i), output->getLine(i)); - } - } - } - } - - std::shared_ptr parseCode(const FilePath& sourceFilePath, const FilePath& projectDataSrcRoot, const std::vector& classpath) - { - TestIntermediateStorage storage; - JavaParser parser(std::make_shared(&storage), std::make_shared()); - std::shared_ptr command = std::make_shared(sourceFilePath, L"8", classpath); - - TimeStamp startTime = TimeStamp::now(); - parser.buildIndex(command); - m_duration += TimeStamp::now().deltaMS(startTime); - - storage.generateStringLists(); - - return TextAccess::createFromLines(storage.m_lines); - } - - size_t m_duration; -}; diff --git a/src/test/JavaParserTestSuite.cpp b/src/test/JavaParserTestSuite.cpp new file mode 100644 index 00000000..e2d0f7b2 --- /dev/null +++ b/src/test/JavaParserTestSuite.cpp @@ -0,0 +1,1883 @@ +#include "catch.hpp" + +#include "ApplicationSettings.h" +#include "JavaEnvironmentFactory.h" +#include "JavaParser.h" +#include "ParserClientImpl.h" +#include "TextAccess.h" +#include "utility.h" +#include "utilityJava.h" +#include "utilityPathDetection.h" + +#include "TestIntermediateStorage.h" + +#define REQUIRE_MESSAGE(msg, cond) do { INFO(msg); REQUIRE(cond); } while((void)0, 0) + +namespace +{ + std::string setupJavaEnvironmentFactory() + { + if (!JavaEnvironmentFactory::getInstance()) + { + std::string errorString; +#ifdef _WIN32 + const std::string separator = ";"; +#else + const std::string separator = ":"; +#endif + std::string classPath = ""; + { + const std::vector jarNames = utility::getRequiredJarNames(); + for (size_t i = 0; i < jarNames.size(); i++) + { + if (i != 0) + { + classPath += separator; + } + classPath += FilePath(L"../app/data/java/lib/").concatenate(jarNames[i]).str(); + } + } + + JavaEnvironmentFactory::createInstance( + classPath, + errorString + ); + + return errorString; + } + + return ""; + } + + std::shared_ptr parseCode(std::string code, bool logErrors = true) + { + setupJavaEnvironmentFactory(); + + std::shared_ptr storage = std::make_shared(); + JavaParser parser(std::make_shared(storage.get()), std::make_shared()); + parser.buildIndex(FilePath(L"input.cc"), TextAccess::createFromString(code)); + + storage->generateStringLists(); + + return storage; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// test finding symbol definitions and declarations + +TEST_CASE("java parser finds all jar dependencies") +{ + for (const std::wstring& jarName : utility::getRequiredJarNames()) + { + FilePath jarPath = FilePath(L"../app/data/java/lib/").concatenate(jarName); + REQUIRE_MESSAGE("Jar dependency path does not exist: " + jarPath.str(), jarPath.exists()); + } +} + +TEST_CASE("java parser can setup environment factory") +{ + std::vector javaPaths = utility::getJavaRuntimePathDetector()->getPaths(); + if (!javaPaths.empty()) + { + ApplicationSettings::getInstance()->setJavaPath(javaPaths[0]); + } + + const std::string errorString = setupJavaEnvironmentFactory(); + + REQUIRE("" == errorString); + + // if this one fails, maybe your java_path in the test settings is wrong. + REQUIRE(JavaEnvironmentFactory::getInstance().use_count() >= 1); +} + +TEST_CASE("java parser finds package declaration") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + ); + + REQUIRE(utility::containsElement( + client->packages, L"foo <1:9 1:11>" + )); +} + +TEST_CASE("java parser finds anotation declaration in defaut package") +{ + std::shared_ptr client = parseCode( + "public @interface SampleAnnotation\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->annotations, L"public SampleAnnotation <1:1 <1:19 1:34> 3:1>" + )); +} + +TEST_CASE("java parser finds anotation member declaration") +{ + std::shared_ptr client = parseCode( + "public @interface SampleAnnotation\n" + "{\n" + " public int value() default 0;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->fields, L"public int SampleAnnotation.value <3:13 3:17>" + )); +} + +TEST_CASE("java parser finds class declaration in defaut package") +{ + std::shared_ptr client = parseCode( + "public class A\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->classes, L"public A <1:1 <1:14 1:14> 3:1>" + )); +} + +TEST_CASE("java parser finds interface declaration in defaut package") +{ + std::shared_ptr client = parseCode( + "public interface A\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->interfaces, L"public A <1:1 <1:18 1:18> 3:1>" + )); +} + +TEST_CASE("java parser finds class declaration in named package") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class A\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->classes, L"public foo.A <2:1 <2:14 2:14> 4:1>" + )); +} + +TEST_CASE("java parser finds class declaration in nested named package") +{ + std::shared_ptr client = parseCode( + "package foo.bar;\n" + "public class A\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->classes, L"public foo.bar.A <2:1 <2:14 2:14> 4:1>" + )); +} + +TEST_CASE("java parser finds enum declaration in named package") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public enum A\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->enums, L"public foo.A <2:1 <2:13 2:13> 4:1>" + )); +} + +TEST_CASE("java parser finds enum constant declaration") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public enum A\n" + "{\n" + " A_TEST(0)\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->enumConstants, L"foo.A.A_TEST <4:2 4:7>" + )); +} + +TEST_CASE("java parser finds constructor declaration without parameters") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class A\n" + "{\n" + " public A()\n" + " {\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->methods, L"public foo.A.A() <4:2 <4:2 <4:9 4:9> 4:11> 6:2>" + )); +} + +TEST_CASE("java parser finds method declaration with custom type in signature") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class A\n" + "{\n" + " public void bar(A a)\n" + " {\n" + " };\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->methods, L"public void foo.A.bar(foo.A) <4:2 <4:2 <4:14 4:16> 4:21> 6:2>" + )); +} + +TEST_CASE("java parser finds anonymous class declaration") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class A\n" + "{\n" + " interface I { }\n" + " public void bar()\n" + " {\n" + " I i = new I() { };\n" + " };\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->classes, L"foo.A.bar.anonymous class (input.cc<7:17>) <7:17 <7:17 7:17> 7:19>" + )); +} + +TEST_CASE("java parser finds method declaration in anonymous class") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class A\n" + "{\n" + " interface I {\n" + " public void foo();\n" + " }\n" + " public void bar()\n" + " {\n" + " I i = new I()\n" + " {\n" + " public void foo() {}\n" + " };\n" + " };\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->methods, L"public void foo.A.bar.anonymous class (input.cc<10:3>).foo() <11:4 <11:4 <11:16 11:18> 11:20> 11:23>" + )); +} + +TEST_CASE("java parser finds method declaration with static keyword in signature") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class A\n" + "{\n" + " static public void bar()\n" + " {\n" + " };\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->methods, L"public static void foo.A.bar() <4:2 <4:2 <4:21 4:23> 4:25> 6:2>" + )); +} + +TEST_CASE("java parser finds field declaration with initial assignment") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class A\n" + "{\n" + " int bar = 0;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->fields, L"default int foo.A.bar <4:6 4:8>" + )); +} + +TEST_CASE("java parser finds public access specifier in field declaration") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class A\n" + "{\n" + " public int bar;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->fields, L"public int foo.A.bar <4:13 4:15>" + )); +} + +TEST_CASE("java parser finds protected access specifier in field declaration") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class A\n" + "{\n" + " protected int bar;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->fields, L"protected int foo.A.bar <4:16 4:18>" + )); +} + +TEST_CASE("java parser finds private access specifier in field declaration") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class A\n" + "{\n" + " private int bar;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->fields, L"private int foo.A.bar <4:14 4:16>" + )); +} + +TEST_CASE("java parser finds static keyword in field declaration") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class A\n" + "{\n" + " static int bar;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->fields, L"default static int foo.A.bar <4:13 4:15>" + )); +} + +TEST_CASE("java parser finds declaration of method parameter") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class A\n" + "{\n" + " void bar(int i)\n" + " {\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"foo.A.bar<0> <4:15 4:15>" + )); +} + +TEST_CASE("java parser finds declaration of local variable") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class A\n" + "{\n" + " void bar()\n" + " {\n" + " int i;\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"foo.A.bar<0> <6:7 6:7>" + )); +} + +TEST_CASE("java parser finds declaration of type parameter of class") +{ + std::shared_ptr client = parseCode( + "public class A \n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeParameters, L"A.T <1:17 1:17>" + )); +} + +TEST_CASE("java parser finds declaration of type parameter of method") +{ + std::shared_ptr client = parseCode( + "public class A\n" + "{\n" + " public void foo()\n" + " {\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeParameters, L"A.foo.T <3:10 3:10>" + )); +} + +TEST_CASE("java parser finds field of interface to be implicitly static") +{ + std::shared_ptr client = parseCode( + "public interface A\n" + "{\n" + " int b = 5;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->fields, L"default static int A.b <3:6 3:6>" + )); +} + +TEST_CASE("java parser finds line comment") +{ + std::shared_ptr client = parseCode( + "// this is a line comment\n" + "package foo;\n" + ); + + REQUIRE(utility::containsElement( + client->comments, L"comment <1:1 1:25>" + )); +} + +TEST_CASE("java parser finds block comment") +{ + std::shared_ptr client = parseCode( + "/* this is a line comment*/\n" + "package foo;\n" + ); + + REQUIRE(utility::containsElement( + client->comments, L"comment <1:1 1:27>" + )); +} + +TEST_CASE("java parser finds missing semicolon as parse error") +{ + std::shared_ptr client = parseCode( + "package foo\n" + ); + + REQUIRE(utility::containsElement( + client->errors, L"Syntax error on token \"foo\", ; expected after this token <1:9 1:9>" + )); +} + +TEST_CASE("java parser finds missing import as error") +{ + std::shared_ptr client = parseCode( + "import foo;\n" + ); + + REQUIRE(utility::containsElement( + client->errors, L"The import foo cannot be resolved <1:8 1:8>" + )); +} + + + + + +/////////////////////////////////////////////////////////////////////////////// +// test finding nested symbol definitions and declarations + +TEST_CASE("java parser finds class declaration nested in class") +{ + std::shared_ptr client = parseCode( + "package foo.bar;\n" + "public class A\n" + "{\n" + " public class B\n" + " {\n" + " }\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->classes, L"public foo.bar.A.B <4:2 <4:15 4:15> 6:2>" + )); +} + +TEST_CASE("java parser finds class declaration nested in method") +{ + std::shared_ptr client = parseCode( + "package foo.bar;\n" + "public class A\n" + "{\n" + " public void bar(int i)\n" + " {\n" + " class B\n" + " {\n" + " }\n" + " };\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->classes, L"default foo.bar.A.bar.B <6:3 <6:9 6:9> 8:3>" + )); +} + + +/////////////////////////////////////////////////////////////////////////////// +// test finding qualifier locations + +TEST_CASE("java parser finds no qualifier location of standalone this expression") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class X\n" + "{\n" + " public void bar()\n" + " {\n" + " X x = this;\n" + " }\n" + "}\n" + ); + + REQUIRE(client->qualifiers.size() == 0); +} + +TEST_CASE("java parser finds qualifier location of import declaration") +{ + std::shared_ptr client = parseCode( + "import foo.bar;\n" + ); + + REQUIRE(utility::containsElement( + client->qualifiers, L"foo <1:8 1:10>" + )); +} + +TEST_CASE("java parser finds qualifier location of simple type") +{ + std::shared_ptr client = parseCode( + "package foo.bar;\n" + "public class A\n" + "{\n" + " public void bar(int i)\n" + " {\n" + " foo.bar.A a;\n" + " };\n" + "};\n" + ); + + REQUIRE(utility::containsElement( + client->qualifiers, L"foo <6:3 6:5>" + )); + REQUIRE(utility::containsElement( + client->qualifiers, L"foo.bar <6:7 6:9>" + )); +} + +TEST_CASE("java parser finds qualifier location of field access") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class X\n" + "{\n" + " public int i;\n" + " \n" + " public void bar()\n" + " {\n" + " this.i = 9;\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->qualifiers, L"foo.X <8:3 8:6>" + )); +} + +TEST_CASE("java parser finds qualifier location of super field access") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "class A\n" + "{\n" + " int a;\n" + "}\n" + "\n" + "class B extends A\n" + "{\n" + " void foo()\n" + " {\n" + " B.super.a = 0;\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->qualifiers, L"foo.B <11:3 11:3>" + )); + + REQUIRE(utility::containsElement( + client->qualifiers, L"foo.A <11:5 11:9>" + )); +} + +TEST_CASE("java parser finds qualifier location of this expression") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "class A\n" + "{\n" + " int a;\n" + " \n" + " void foo()\n" + " {\n" + " A a = A.this;" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->qualifiers, L"foo.A <8:9 8:9>" + )); +} + +TEST_CASE("java parser finds qualifier location of method invocation") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class X\n" + "{\n" + " public static void bar()\n" + " {\n" + " foo.X.bar();\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->qualifiers, L"foo <6:3 6:5>" + )); + + REQUIRE(utility::containsElement( + client->qualifiers, L"foo.X <6:7 6:7>" + )); +} + +TEST_CASE("java parser finds qualifier location of method invocation on this") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class X\n" + "{\n" + " public void bar()\n" + " {\n" + " this.bar();\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->qualifiers, L"foo.X <6:3 6:6>" + )); +} + +TEST_CASE("java parser finds qualifier location of super method invocation") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class X\n" + "{\n" + " public class A\n" + " {\n" + " void bar()\n" + " {\n" + " }\n" + " }\n" + " \n" + " public class B extends A\n" + " {\n" + " void bar()\n" + " {\n" + " foo.X.B.super.bar();\n" + " }\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->qualifiers, L"foo <15:4 15:6>" + )); + + REQUIRE(utility::containsElement( + client->qualifiers, L"foo.X <15:8 15:8>" + )); + + REQUIRE(utility::containsElement( + client->qualifiers, L"foo.X.B <15:10 15:10>" + )); + + REQUIRE(utility::containsElement( + client->qualifiers, L"foo.X.A <15:12 15:16>" + )); +} + +TEST_CASE("java parser finds qualifier location of creation reference") +{ + std::shared_ptr client = parseCode( + "public class A\n" + "{\n" + " public interface Functor\n" + " {\n" + " public void doSomething();\n" + " }\n" + " \n" + " public class Bar\n" + " {\n" + " }\n" + " \n" + " void foo()\n" + " {\n" + " Functor method = A.Bar::new;\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->qualifiers, L"A <14:20 14:20>" + )); + + REQUIRE(utility::containsElement( + client->qualifiers, L"A.Bar <14:22 14:24>" + )); +} + +TEST_CASE("java parser finds qualifier location of expression method reference") +{ + std::shared_ptr client = parseCode( + "public class A\n" + "{\n" + " public interface Functor\n" + " {\n" + " public void doSomething();\n" + " }\n" + "\n" + " public class B\n" + " {\n" + " void bar()\n" + " {\n" + " }\n" + " }\n" + "\n" + " void foo()\n" + " {\n" + " Functor method = B::bar;\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->qualifiers, L"A.B <17:20 17:20>" + )); +} + +TEST_CASE("java parser finds qualifier location of super method reference") +{ + std::shared_ptr client = parseCode( + "public class A {\n" + " public interface Functor {\n" + " public void doSomething();\n" + " }\n" + "\n" + " public class B {\n" + " void bar() {\n" + " }\n" + " }\n" + "\n" + " public class C extends B {\n" + " void foo() {\n" + " Functor method = super::bar;\n" + " }\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->qualifiers, L"A.B <13:21 13:25>" + )); +} + +TEST_CASE("java parser finds qualifier location of class instance creation") +{ + std::shared_ptr client = parseCode( + "public class A {\n" + " public interface Functor {\n" + " public void doSomething();\n" + " }\n" + "\n" + " public class B {\n" + " void bar() {\n" + " B b = new A.B();\n" + " }\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->qualifiers, L"A <8:14 8:14>" + )); +} + + +/////////////////////////////////////////////////////////////////////////////// +// test finding usages of symbols + +TEST_CASE("java parser finds usage of marker annotation") +{ + std::shared_ptr client = parseCode( + "public @interface SampleAnnotation\n" + "{\n" + "}\n" + "\n" + "@SampleAnnotation\n" + "class Foo\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->annotationUses, L"Foo -> SampleAnnotation <5:2 5:17>" + )); +} + +TEST_CASE("java parser finds usage of single member annotation") +{ + std::shared_ptr client = parseCode( + "public @interface SampleAnnotation\n" + "{\n" + " public int value() default 0;\n" + "}\n" + "\n" + "@SampleAnnotation(33)\n" + "class Foo\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->annotationUses, L"Foo -> SampleAnnotation <6:2 6:17>" + )); +} + +TEST_CASE("java parser finds usage of normal annotation") +{ + std::shared_ptr client = parseCode( + "public @interface SampleAnnotation\n" + "{\n" + " public int a() default 0;\n" + " public int b() default 0;\n" + "}\n" + "\n" + "@SampleAnnotation()\n" + "class Foo\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->annotationUses, L"Foo -> SampleAnnotation <7:2 7:17>" + )); +} + +TEST_CASE("java parser finds usage of normal annotation member in initialization") +{ + std::shared_ptr client = parseCode( + "public @interface SampleAnnotation\n" + "{\n" + " public int a() default 0;\n" + " public int b() default 0;\n" + "}\n" + "\n" + "@SampleAnnotation(a = 9)\n" + "class Foo\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"Foo -> int SampleAnnotation.a <7:19 7:19>" + )); +} + +TEST_CASE("java parser finds inheritance using extends keyword") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class A\n" + "{\n" + "}\n" + "\n" + "public class B extends A\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->inheritances, L"foo.B -> foo.A <6:24 6:24>" + )); +} + +TEST_CASE("java parser finds inheritance using implements keyword") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class A\n" + "{\n" + "}\n" + "\n" + "public class B implements A\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->inheritances, L"foo.B -> foo.A <6:27 6:27>" + )); +} + +TEST_CASE("java parser finds inheritance of anonymous class declaration") +{ + std::shared_ptr client = parseCode( + "public class A\n" + "{\n" + " public interface Base\n" + " {\n" + " }\n" + "\n" + " void foo()\n" + " {\n" + " Base b = new Base()\n" + " {\n" + " }\n" + " };\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->inheritances, L"A.foo.anonymous class (input.cc<10:3>) -> A.Base <9:16 9:19>" + )); +} + +TEST_CASE("java parser finds usage of string for var type") +{ + std::shared_ptr client = parseCode( + "public class A\n" + "{\n" + " public void foo(){\n" + " var a = \"test\";\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"void A.foo() -> java.lang.String <4:3 4:5>" + )); +} + +TEST_CASE("java parser finds type parameter in signature of method") +{ + std::shared_ptr client = parseCode( + "public class A \n" + "{\n" + " public A foo(A a){\n" + " return a;\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"A A.foo(A) -> A <3:9 3:9>" + )); + REQUIRE(utility::containsElement( + client->typeUses, L"A A.foo(A) -> A <3:21 3:21>" + )); +} + +TEST_CASE("parser finds usage of type defined in base class") +{ + std::shared_ptr client = parseCode( + "public class Foo {\n" + " public class Base {\n" + " public class X {\n" + " }\n" + " }\n" + " public class Derived extends Base {\n" + " public X x = null;\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"Foo.Base.X Foo.Derived.x -> Foo.Base.X <7:10 7:10>" + )); +} + +TEST_CASE("java parser finds correct location of qualified type usage") +{ + std::shared_ptr client = parseCode( + "public class A\n" + "{\n" + " public class B\n" + " {\n" + " }\n" + " void bar()\n" + " {\n" + " A.B b = new A.B();\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"void A.bar() -> A.B <8:5 8:5>" + )); +} + +TEST_CASE("java parser finds type argument of parameterized type") +{ + std::shared_ptr client = parseCode( + "public class A {\n" + " T t;\n" + "}\n" + "public class B {\n" + " void foo() {\n" + " A a = null;\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeArguments, L"A -> java.lang.Void <6:5 6:8>" + )); + + REQUIRE(utility::containsElement( + client->typeUses, L"void B.foo() -> java.lang.Void <6:5 6:8>" + )); +} + +TEST_CASE("java parser finds type argument of method invocation") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class X\n" + "{\n" + " public static void foo() {\n" + " }\n" + "\n" + " public static void bar() {\n" + " foo.X.foo();\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeArguments, L"static void foo.X.foo() -> java.lang.Void <8:10 8:13>" + )); + + REQUIRE(utility::containsElement( + client->typeUses, L"static void foo.X.bar() -> java.lang.Void <8:10 8:13>" + )); +} + +TEST_CASE("java parser finds type argument of super method invocation") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class X\n" + "{\n" + " public class A\n" + " {\n" + " void bar()\n" + " {\n" + " }\n" + " }\n" + " \n" + " public class B extends A\n" + " {\n" + " void bar()\n" + " {\n" + " super.bar();\n" + " }\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeArguments, L"void foo.X.A.bar() -> java.lang.Void <15:11 15:14>" + )); + + REQUIRE(utility::containsElement( + client->typeUses, L"void foo.X.B.bar() -> java.lang.Void <15:11 15:14>" + )); +} + +TEST_CASE("java parser finds type argument of constructor invocation") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class Bar\n" + "{\n" + " public Bar()\n" + " {\n" + " }\n" + " \n" + " public Bar(int i)\n" + " {\n" + " this();\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeArguments, L"foo.Bar.Bar() -> java.lang.Void <10:4 10:7>" + )); + + REQUIRE(utility::containsElement( + client->typeUses, L"foo.Bar.Bar(int) -> java.lang.Void <10:4 10:7>" + )); +} + +TEST_CASE("java parser finds type argument of super constructor invocation") +{ + std::shared_ptr client = parseCode( + "public class A\n" + "{\n" + " public class Base\n" + " {\n" + " }\n" + "\n" + " public class Derived extends Base\n" + " {\n" + " public Derived()\n" + " {\n" + " super();\n" + " }\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeArguments, L"A.Base.Base() -> java.lang.Void <11:5 11:8>" + )); + + REQUIRE(utility::containsElement( + client->typeUses, L"A.Derived.Derived() -> java.lang.Void <11:5 11:8>" + )); +} + +TEST_CASE("java parser finds type argument of creation reference") +{ + std::shared_ptr client = parseCode( + "public class A\n" + "{\n" + " public interface Functor\n" + " {\n" + " public void doSomething();\n" + " }\n" + "\n" + " public class B\n" + " {\n" + " }\n" + "\n" + " void foo()\n" + " {\n" + " Functor method = B::new;\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeArguments, L"A.B.B() -> java.lang.Void <14:24 14:27>" + )); + + REQUIRE(utility::containsElement( + client->typeUses, L"void A.foo() -> java.lang.Void <14:24 14:27>" + )); +} + +TEST_CASE("java parser finds type argument of expression method reference") +{ + std::shared_ptr client = parseCode( + "public class A\n" + "{\n" + " public interface Functor\n" + " {\n" + " public void doSomething();\n" + " }\n" + "\n" + " static void bar()\n" + " {\n" + " }\n" + "\n" + " void foo()\n" + " {\n" + " Functor method = A::bar;\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeArguments, L"static void A.bar() -> java.lang.Void <14:24 14:27>" + )); + + REQUIRE(utility::containsElement( + client->typeUses, L"void A.foo() -> java.lang.Void <14:24 14:27>" + )); +} + +TEST_CASE("java parser finds type argument of super method reference") +{ + std::shared_ptr client = parseCode( + "public class A {\n" + " public interface Functor {\n" + " public void doSomething();\n" + " }\n" + "\n" + " public class B {\n" + " void bar() {\n" + " }\n" + " }\n" + "\n" + " public class C extends B {\n" + " void foo() {\n" + " Functor method = super::bar;\n" + " }\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeArguments, L"void A.B.bar() -> java.lang.Void <13:29 13:32>" + )); + + REQUIRE(utility::containsElement( + client->typeUses, L"void A.C.foo() -> java.lang.Void <13:29 13:32>" + )); +} + +TEST_CASE("java parser finds type argument of type method reference") +{ + std::shared_ptr client = parseCode( + "public class A {\n" + " \n" + " void foo() {\n" + " Functor method = int []::clone;\n" + " }\n" + "}\n" + ); + + // currently we cannot record the typeArguments of array type methods + + REQUIRE(utility::containsElement( + client->typeUses, L"void A.foo() -> java.lang.Void <4:29 4:32>" + )); +} + +TEST_CASE("java parser finds type argument of class instance creation") +{ + std::shared_ptr client = parseCode( + "public class A {\n" + " public interface Functor {\n" + " public void doSomething();\n" + " }\n" + "\n" + " public class B {\n" + " B() {\n" + " }\n" + " void bar() {\n" + " B b = new A.B();\n" + " }\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeArguments, L"A.B.B() -> java.lang.Void <10:15 10:18>" + )); + + REQUIRE(utility::containsElement( + client->typeUses, L"void A.B.bar() -> java.lang.Void <10:15 10:18>" + )); +} + +TEST_CASE("java parser finds super method invocation") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class X\n" + "{\n" + " public class A\n" + " {\n" + " void bar()\n" + " {\n" + " }\n" + " }\n" + " \n" + " public class B extends A\n" + " {\n" + " void bar()\n" + " {\n" + " super.bar();\n" + " }\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"void foo.X.B.bar() -> void foo.X.A.bar() <15:10 15:12>" + )); +} + +TEST_CASE("java parser finds constructor invocation") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class Bar\n" + "{\n" + " public Bar()\n" + " {\n" + " }\n" + " \n" + " public Bar(int i)\n" + " {\n" + " this();\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"foo.Bar.Bar(int) -> foo.Bar.Bar() <10:3 10:6>" + )); +} + +TEST_CASE("java parser finds super constructor invocation") +{ + std::shared_ptr client = parseCode( + "public class A\n" + "{\n" + " public class Base\n" + " {\n" + " }\n" + "\n" + " public class Derived extends Base\n" + " {\n" + " public Derived()\n" + " {\n" + " super();\n" + " }\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"A.Derived.Derived() -> A.Base.Base() <11:4 11:8>" + )); +} + +TEST_CASE("java parser finds invocation of method of anonymous class") +{ + std::shared_ptr client = parseCode( + "class Main {\n" + " public interface Interfaze {\n" + " public void foo();\n" + " }\n" + "\n" + " private Interfaze i = new Interfaze() {\n" + " public void foo() {\n" + " bar();\n" + " }\n" + "\n" + " private void bar() {\n" + " }\n" + " };\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->calls, L"void Main.anonymous class (input.cc<6:40>).foo() -> void Main.anonymous class (input.cc<6:40>).bar() <8:4 8:6>" + )); +} + +TEST_CASE("java parser finds overridden method with same signature") +{ + std::shared_ptr client = parseCode( + "class Main {\n" + " public interface Interfaze {\n" + " public void foo(int t);\n" + " }\n" + "\n" + " public class C implements Interfaze {\n" + " public void foo(int t) {\n" + " }\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->overrides, L"void Main.C.foo(int) -> void Main.Interfaze.foo(int) <7:15 7:17>" + )); +} + +TEST_CASE("java parser finds overridden method with generic signature") +{ + std::shared_ptr client = parseCode( + "class Main {\n" + " public interface Interfaze {\n" + " public void foo(T t);\n" + " }\n" + "\n" + " public class C implements Interfaze {\n" + " public void foo(X t) {\n" + " }\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->overrides, L"void Main.C.foo(Main.X) -> void Main.Interfaze.foo(Main.Interfaze.T) <7:15 7:17>" + )); +} + +TEST_CASE("java parser finds method usage for creation reference") +{ + std::shared_ptr client = parseCode( + "public class A\n" + "{\n" + " public interface Functor\n" + " {\n" + " public void doSomething();\n" + " }\n" + "\n" + " public class B\n" + " {\n" + " }\n" + "\n" + " void foo()\n" + " {\n" + " Functor method = B::new;\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"void A.foo() -> A.B.B() <14:23 14:25>" + )); +} + +TEST_CASE("java parser finds method usage for expression method reference") +{ + std::shared_ptr client = parseCode( + "public class A\n" + "{\n" + " public interface Functor\n" + " {\n" + " public void doSomething();\n" + " }\n" + "\n" + " public class B\n" + " {\n" + " void bar()\n" + " {\n" + " }\n" + " }\n" + "\n" + " void foo()\n" + " {\n" + " Functor method = B::bar;\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"void A.foo() -> void A.B.bar() <17:23 17:25>" + )); +} + +TEST_CASE("java parser finds method usage for super method reference") +{ + std::shared_ptr client = parseCode( + "public class A {\n" + " public interface Functor {\n" + " public void doSomething();\n" + " }\n" + "\n" + " public class B {\n" + " void bar() {\n" + " }\n" + " }\n" + "\n" + " public class C extends B {\n" + " void foo() {\n" + " Functor method = super::bar;\n" + " }\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"void A.C.foo() -> void A.B.bar() <13:28 13:30>" + )); +} + +TEST_CASE("java parser finds no method usage for type method reference") +{ + std::shared_ptr client = parseCode( + "public class A {\n" + " void foo() {\n" + " Functor method = int []::clone;\n" + " }\n" + "}\n" + ); + + // finding method usage here may be implemented in the future. + REQUIRE(client->usages.size() == 0); +} + +TEST_CASE("java parser finds no usage of field within that fields declaration") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class X\n" + "{\n" + " private int t;\n" + "}\n" + ); + + REQUIRE(client->usages.size() == 0); +} + +TEST_CASE("java parser finds no usage of enum constant within that enum constants declaration") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public enum X\n" + "{\n" + " OPTION_0;\n" + "}\n" + ); + + REQUIRE(client->usages.size() == 0); +} + +TEST_CASE("java parser finds usage of field with same name as method parameter") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class X\n" + "{\n" + " private int t;\n" + " public X(int t)\n" + " {\n" + " this.t = t;\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"foo.X.X(int) -> int foo.X.t <7:8 7:8>" + )); +} + +TEST_CASE("java parser does not confuse method name with field name") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class X\n" + "{\n" + " private int foo;\n" + " public void foo()\n" + " {\n" + " this.foo = 5;\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->usages, L"void foo.X.foo() -> int foo.X.foo <7:8 7:10>" + )); +} + +TEST_CASE("java parser finds assignment of method parameter") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class A\n" + "{\n" + " void bar(int i)\n" + " {\n" + " i = 0;\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"foo.A.bar<0> <6:3 6:3>" + )); +} + +TEST_CASE("java parser finds assignment of local variable") +{ + std::shared_ptr client = parseCode( + "package foo;\n" + "public class A\n" + "{\n" + " void bar()\n" + " {\n" + " int i;\n" + " i = 0;\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"foo.A.bar<0> <7:3 7:3>" + )); +} + +TEST_CASE("java parser finds scope of class declaration") +{ + std::shared_ptr client = parseCode( + "public class A\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<2:1> <2:1 2:1>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<2:1> <3:1 3:1>" + )); +} + +TEST_CASE("java parser finds scope of enum declaration") +{ + std::shared_ptr client = parseCode( + "public enum A\n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<2:1> <2:1 2:1>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<2:1> <3:1 3:1>" + )); +} + +TEST_CASE("java parser finds scope of constructor declaration") +{ + std::shared_ptr client = parseCode( + "public class A\n" + "{\n" + " public A()\n" + " {\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<4:2> <4:2 4:2>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<4:2> <5:2 5:2>" + )); +} + +TEST_CASE("java parser finds scope of method declaration") +{ + std::shared_ptr client = parseCode( + "public class A\n" + "{\n" + " public void a()\n" + " {\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<4:2> <4:2 4:2>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<4:2> <5:2 5:2>" + )); +} + +TEST_CASE("java parser finds scope of switch statement") +{ + std::shared_ptr client = parseCode( + "public class A\n" + "{\n" + " public void a()\n" + " {\n" + " switch(2)\n" + " {\n" + " case 1:\n" + " break;\n" + " }\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<6:3> <6:3 6:3>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<6:3> <9:3 9:3>" + )); +} + +TEST_CASE("java parser finds scope of block statement") +{ + std::shared_ptr client = parseCode( + "public class A\n" + "{\n" + " public void a()\n" + " {\n" + " {\n" + " }\n" + " }\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<5:3> <5:3 5:3>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<5:3> <6:3 6:3>" + )); +} + +TEST_CASE("java parser finds scope of array initialization list") +{ + std::shared_ptr client = parseCode( + "public class A\n" + "{\n" + " private int[] array = {1, 2};\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<3:24> <3:24 3:24>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<3:24> <3:29 3:29>" + )); +} + +TEST_CASE("java parser finds scope of anonymous class declaration") +{ + std::shared_ptr client = parseCode( + "public class A\n" + "{\n" + " public interface Base\n" + " {\n" + " }\n" + "\n" + " void foo()\n" + " {\n" + " Base b = new Base()\n" + " {\n" + " }\n" + " };\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<10:3> <10:3 10:3>" + )); + REQUIRE(utility::containsElement( + client->localSymbols, L"input.cc<10:3> <11:3 11:3>" + )); +} + +TEST_CASE("java parser finds usage of type parameter of class") +{ + std::shared_ptr client = parseCode( + "public class A \n" + "{\n" + " T t;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"A.T A.t -> A.T <3:2 3:2>" + )); +} + +TEST_CASE("java parser finds usage of type parameter of method") +{ + std::shared_ptr client = parseCode( + "public class A\n" + "{\n" + " public void foo(T t){};\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"void A.foo(T) -> A.foo.T <3:22 3:22>" + )); +} + +TEST_CASE("java parser finds correct location of generic type usage") +{ + std::shared_ptr client = parseCode( + "public class A \n" + "{\n" + " A t;\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"A A.t -> A <3:2 3:2>" + )); +} + +TEST_CASE("java parser finds bound type of type parameter") +{ + std::shared_ptr client = parseCode( + "public class A \n" + "{\n" + "}\n" + ); + + REQUIRE(utility::containsElement( + client->typeUses, L"A.T -> java.lang.Void <1:27 1:30>" + )); +} diff --git a/src/test/JavaParserTestSuite.h b/src/test/JavaParserTestSuite.h deleted file mode 100644 index b44c7f5c..00000000 --- a/src/test/JavaParserTestSuite.h +++ /dev/null @@ -1,1917 +0,0 @@ -#include - -#include "ApplicationSettings.h" -#include "JavaEnvironmentFactory.h" -#include "JavaParser.h" -#include "ParserClientImpl.h" -#include "TextAccess.h" -#include "utility.h" -#include "utilityJava.h" -#include "utilityPathDetection.h" - -#include "TestIntermediateStorage.h" - -class JavaParserTestSuite: public CxxTest::TestSuite -{ -public: -/////////////////////////////////////////////////////////////////////////////// -// test finding symbol definitions and declarations - - void test_java_parser_finds_all_jar_dependencies() - { - for (const std::wstring& jarName : utility::getRequiredJarNames()) - { - FilePath jarPath = FilePath(L"../app/data/java/lib/").concatenate(jarName); - TSM_ASSERT(L"Jar dependency path does not exist: " + jarPath.wstr(), jarPath.exists()); - } - } - - void test_java_parser_can_setup_environment_factory() - { - std::vector javaPaths = utility::getJavaRuntimePathDetector()->getPaths(); - if (!javaPaths.empty()) - { - ApplicationSettings::getInstance()->setJavaPath(javaPaths[0]); - } - - const std::string errorString = setupJavaEnvironmentFactory(); - - TS_ASSERT_EQUALS("", errorString); - - // if this one fails, maybe your java_path in the test settings is wrong. - TS_ASSERT_LESS_THAN_EQUALS(1, JavaEnvironmentFactory::getInstance().use_count()); - } - - void test_java_parser_finds_package_declaration() - { - std::shared_ptr client = parseCode( - "package foo;\n" - ); - - TS_ASSERT(utility::containsElement( - client->packages, L"foo <1:9 1:11>" - )); - } - - void test_java_parser_finds_anotation_declaration_in_defaut_package() - { - std::shared_ptr client = parseCode( - "public @interface SampleAnnotation\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->annotations, L"public SampleAnnotation <1:1 <1:19 1:34> 3:1>" - )); - } - - void test_java_parser_finds_anotation_member_declaration() - { - std::shared_ptr client = parseCode( - "public @interface SampleAnnotation\n" - "{\n" - " public int value() default 0;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->fields, L"public int SampleAnnotation.value <3:13 3:17>" - )); - } - - void test_java_parser_finds_class_declaration_in_defaut_package() - { - std::shared_ptr client = parseCode( - "public class A\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->classes, L"public A <1:1 <1:14 1:14> 3:1>" - )); - } - - void test_java_parser_finds_interface_declaration_in_defaut_package() - { - std::shared_ptr client = parseCode( - "public interface A\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->interfaces, L"public A <1:1 <1:18 1:18> 3:1>" - )); - } - - void test_java_parser_finds_class_declaration_in_named_package() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class A\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->classes, L"public foo.A <2:1 <2:14 2:14> 4:1>" - )); - } - - void test_java_parser_finds_class_declaration_in_nested_named_package() - { - std::shared_ptr client = parseCode( - "package foo.bar;\n" - "public class A\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->classes, L"public foo.bar.A <2:1 <2:14 2:14> 4:1>" - )); - } - - void test_java_parser_finds_enum_declaration_in_named_package() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public enum A\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->enums, L"public foo.A <2:1 <2:13 2:13> 4:1>" - )); - } - - void test_java_parser_finds_enum_constant_declaration() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public enum A\n" - "{\n" - " A_TEST(0)\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->enumConstants, L"foo.A.A_TEST <4:2 4:7>" - )); - } - - void test_java_parser_finds_constructor_declaration_without_parameters() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class A\n" - "{\n" - " public A()\n" - " {\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->methods, L"public foo.A.A() <4:2 <4:2 <4:9 4:9> 4:11> 6:2>" - )); - } - - void test_java_parser_finds_method_declaration_with_custom_type_in_signature() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class A\n" - "{\n" - " public void bar(A a)\n" - " {\n" - " };\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->methods, L"public void foo.A.bar(foo.A) <4:2 <4:2 <4:14 4:16> 4:21> 6:2>" - )); - } - - void test_java_parser_finds_anonymous_class_declaration() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class A\n" - "{\n" - " interface I { }\n" - " public void bar()\n" - " {\n" - " I i = new I() { };\n" - " };\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->classes, L"foo.A.bar.anonymous class (input.cc<7:17>) <7:17 <7:17 7:17> 7:19>" - )); - } - - void test_java_parser_finds_method_declaration_in_anonymous_class() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class A\n" - "{\n" - " interface I {\n" - " public void foo();\n" - " }\n" - " public void bar()\n" - " {\n" - " I i = new I()\n" - " {\n" - " public void foo() {}\n" - " };\n" - " };\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->methods, L"public void foo.A.bar.anonymous class (input.cc<10:3>).foo() <11:4 <11:4 <11:16 11:18> 11:20> 11:23>" - )); - } - - void test_java_parser_finds_method_declaration_with_static_keyword_in_signature() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class A\n" - "{\n" - " static public void bar()\n" - " {\n" - " };\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->methods, L"public static void foo.A.bar() <4:2 <4:2 <4:21 4:23> 4:25> 6:2>" - )); - } - - void test_java_parser_finds_field_declaration_with_initial_assignment() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class A\n" - "{\n" - " int bar = 0;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->fields, L"default int foo.A.bar <4:6 4:8>" - )); - } - - void test_java_parser_finds_public_access_specifier_in_field_declaration() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class A\n" - "{\n" - " public int bar;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->fields, L"public int foo.A.bar <4:13 4:15>" - )); - } - - void test_java_parser_finds_protected_access_specifier_in_field_declaration() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class A\n" - "{\n" - " protected int bar;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->fields, L"protected int foo.A.bar <4:16 4:18>" - )); - } - - void test_java_parser_finds_private_access_specifier_in_field_declaration() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class A\n" - "{\n" - " private int bar;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->fields, L"private int foo.A.bar <4:14 4:16>" - )); - } - - void test_java_parser_finds_static_keyword_in_field_declaration() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class A\n" - "{\n" - " static int bar;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->fields, L"default static int foo.A.bar <4:13 4:15>" - )); - } - - void test_java_parser_finds_declaration_of_method_parameter() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class A\n" - "{\n" - " void bar(int i)\n" - " {\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"foo.A.bar<0> <4:15 4:15>" - )); - } - - void test_java_parser_finds_declaration_of_local_variable() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class A\n" - "{\n" - " void bar()\n" - " {\n" - " int i;\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"foo.A.bar<0> <6:7 6:7>" - )); - } - - void test_java_parser_finds_declaration_of_type_parameter_of_class() - { - std::shared_ptr client = parseCode( - "public class A \n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeParameters, L"A.T <1:17 1:17>" - )); - } - - void test_java_parser_finds_declaration_of_type_parameter_of_method() - { - std::shared_ptr client = parseCode( - "public class A\n" - "{\n" - " public void foo()\n" - " {\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeParameters, L"A.foo.T <3:10 3:10>" - )); - } - - void test_java_parser_finds_field_of_interface_to_be_implicitly_static() - { - std::shared_ptr client = parseCode( - "public interface A\n" - "{\n" - " int b = 5;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->fields, L"default static int A.b <3:6 3:6>" - )); - } - - void test_java_parser_finds_line_comment() - { - std::shared_ptr client = parseCode( - "// this is a line comment\n" - "package foo;\n" - ); - - TS_ASSERT(utility::containsElement( - client->comments, L"comment <1:1 1:25>" - )); - } - - void test_java_parser_finds_block_comment() - { - std::shared_ptr client = parseCode( - "/* this is a line comment*/\n" - "package foo;\n" - ); - - TS_ASSERT(utility::containsElement( - client->comments, L"comment <1:1 1:27>" - )); - } - - void test_java_parser_finds_missing_semicolon_as_parse_error() - { - std::shared_ptr client = parseCode( - "package foo\n" - ); - - TS_ASSERT(utility::containsElement( - client->errors, L"Syntax error on token \"foo\", ; expected after this token <1:9 1:9>" - )); - } - - void test_java_parser_finds_missing_import_as_error() - { - std::shared_ptr client = parseCode( - "import foo;\n" - ); - - TS_ASSERT(utility::containsElement( - client->errors, L"The import foo cannot be resolved <1:8 1:8>" - )); - } - - - - - -/////////////////////////////////////////////////////////////////////////////// -// test finding nested symbol definitions and declarations - - void test_java_parser_finds_class_declaration_nested_in_class() - { - std::shared_ptr client = parseCode( - "package foo.bar;\n" - "public class A\n" - "{\n" - " public class B\n" - " {\n" - " }\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->classes, L"public foo.bar.A.B <4:2 <4:15 4:15> 6:2>" - )); - } - - void test_java_parser_finds_class_declaration_nested_in_method() - { - std::shared_ptr client = parseCode( - "package foo.bar;\n" - "public class A\n" - "{\n" - " public void bar(int i)\n" - " {\n" - " class B\n" - " {\n" - " }\n" - " };\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->classes, L"default foo.bar.A.bar.B <6:3 <6:9 6:9> 8:3>" - )); - } - - -/////////////////////////////////////////////////////////////////////////////// -// test finding qualifier locations - - void test_java_parser_finds_no_qualifier_location_of_standalone_this_expression() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class X\n" - "{\n" - " public void bar()\n" - " {\n" - " X x = this;\n" - " }\n" - "}\n" - ); - - TS_ASSERT_EQUALS(client->qualifiers.size(), 0); - } - - void test_java_parser_finds_qualifier_location_of_import_declaration() - { - std::shared_ptr client = parseCode( - "import foo.bar;\n" - ); - - TS_ASSERT(utility::containsElement( - client->qualifiers, L"foo <1:8 1:10>" - )); - } - - void test_java_parser_finds_qualifier_location_of_simple_type() - { - std::shared_ptr client = parseCode( - "package foo.bar;\n" - "public class A\n" - "{\n" - " public void bar(int i)\n" - " {\n" - " foo.bar.A a;\n" - " };\n" - "};\n" - ); - - TS_ASSERT(utility::containsElement( - client->qualifiers, L"foo <6:3 6:5>" - )); - TS_ASSERT(utility::containsElement( - client->qualifiers, L"foo.bar <6:7 6:9>" - )); - } - - void test_java_parser_finds_qualifier_location_of_field_access() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class X\n" - "{\n" - " public int i;\n" - " \n" - " public void bar()\n" - " {\n" - " this.i = 9;\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->qualifiers, L"foo.X <8:3 8:6>" - )); - } - - void test_java_parser_finds_qualifier_location_of_super_field_access() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "class A\n" - "{\n" - " int a;\n" - "}\n" - "\n" - "class B extends A\n" - "{\n" - " void foo()\n" - " {\n" - " B.super.a = 0;\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->qualifiers, L"foo.B <11:3 11:3>" - )); - - TS_ASSERT(utility::containsElement( - client->qualifiers, L"foo.A <11:5 11:9>" - )); - } - - void test_java_parser_finds_qualifier_location_of_this_expression() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "class A\n" - "{\n" - " int a;\n" - " \n" - " void foo()\n" - " {\n" - " A a = A.this;" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->qualifiers, L"foo.A <8:9 8:9>" - )); - } - - void test_java_parser_finds_qualifier_location_of_method_invocation() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class X\n" - "{\n" - " public static void bar()\n" - " {\n" - " foo.X.bar();\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->qualifiers, L"foo <6:3 6:5>" - )); - - TS_ASSERT(utility::containsElement( - client->qualifiers, L"foo.X <6:7 6:7>" - )); - } - - void test_java_parser_finds_qualifier_location_of_method_invocation_on_this() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class X\n" - "{\n" - " public void bar()\n" - " {\n" - " this.bar();\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->qualifiers, L"foo.X <6:3 6:6>" - )); - } - - void test_java_parser_finds_qualifier_location_of_super_method_invocation() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class X\n" - "{\n" - " public class A\n" - " {\n" - " void bar()\n" - " {\n" - " }\n" - " }\n" - " \n" - " public class B extends A\n" - " {\n" - " void bar()\n" - " {\n" - " foo.X.B.super.bar();\n" - " }\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->qualifiers, L"foo <15:4 15:6>" - )); - - TS_ASSERT(utility::containsElement( - client->qualifiers, L"foo.X <15:8 15:8>" - )); - - TS_ASSERT(utility::containsElement( - client->qualifiers, L"foo.X.B <15:10 15:10>" - )); - - TS_ASSERT(utility::containsElement( - client->qualifiers, L"foo.X.A <15:12 15:16>" - )); - } - - void test_java_parser_finds_qualifier_location_of_creation_reference() - { - std::shared_ptr client = parseCode( - "public class A\n" - "{\n" - " public interface Functor\n" - " {\n" - " public void doSomething();\n" - " }\n" - " \n" - " public class Bar\n" - " {\n" - " }\n" - " \n" - " void foo()\n" - " {\n" - " Functor method = A.Bar::new;\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->qualifiers, L"A <14:20 14:20>" - )); - - TS_ASSERT(utility::containsElement( - client->qualifiers, L"A.Bar <14:22 14:24>" - )); - } - - void test_java_parser_finds_qualifier_location_of_expression_method_reference() - { - std::shared_ptr client = parseCode( - "public class A\n" - "{\n" - " public interface Functor\n" - " {\n" - " public void doSomething();\n" - " }\n" - "\n" - " public class B\n" - " {\n" - " void bar()\n" - " {\n" - " }\n" - " }\n" - "\n" - " void foo()\n" - " {\n" - " Functor method = B::bar;\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->qualifiers, L"A.B <17:20 17:20>" - )); - } - - void test_java_parser_finds_qualifier_location_of_super_method_reference() - { - std::shared_ptr client = parseCode( - "public class A {\n" - " public interface Functor {\n" - " public void doSomething();\n" - " }\n" - "\n" - " public class B {\n" - " void bar() {\n" - " }\n" - " }\n" - "\n" - " public class C extends B {\n" - " void foo() {\n" - " Functor method = super::bar;\n" - " }\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->qualifiers, L"A.B <13:21 13:25>" - )); - } - - void test_java_parser_finds_qualifier_location_of_class_instance_creation() - { - std::shared_ptr client = parseCode( - "public class A {\n" - " public interface Functor {\n" - " public void doSomething();\n" - " }\n" - "\n" - " public class B {\n" - " void bar() {\n" - " B b = new A.B();\n" - " }\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->qualifiers, L"A <8:14 8:14>" - )); - } - - -/////////////////////////////////////////////////////////////////////////////// -// test finding usages of symbols - - void test_java_parser_finds_usage_of_marker_annotation() - { - std::shared_ptr client = parseCode( - "public @interface SampleAnnotation\n" - "{\n" - "}\n" - "\n" - "@SampleAnnotation\n" - "class Foo\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->annotationUses, L"Foo -> SampleAnnotation <5:2 5:17>" - )); - } - - void test_java_parser_finds_usage_of_single_member_annotation() - { - std::shared_ptr client = parseCode( - "public @interface SampleAnnotation\n" - "{\n" - " public int value() default 0;\n" - "}\n" - "\n" - "@SampleAnnotation(33)\n" - "class Foo\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->annotationUses, L"Foo -> SampleAnnotation <6:2 6:17>" - )); - } - - void test_java_parser_finds_usage_of_normal_annotation() - { - std::shared_ptr client = parseCode( - "public @interface SampleAnnotation\n" - "{\n" - " public int a() default 0;\n" - " public int b() default 0;\n" - "}\n" - "\n" - "@SampleAnnotation()\n" - "class Foo\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->annotationUses, L"Foo -> SampleAnnotation <7:2 7:17>" - )); - } - - void test_java_parser_finds_usage_of_normal_annotation_member_in_initialization() - { - std::shared_ptr client = parseCode( - "public @interface SampleAnnotation\n" - "{\n" - " public int a() default 0;\n" - " public int b() default 0;\n" - "}\n" - "\n" - "@SampleAnnotation(a = 9)\n" - "class Foo\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"Foo -> int SampleAnnotation.a <7:19 7:19>" - )); - } - - void test_java_parser_finds_inheritance_using_extends_keyword() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class A\n" - "{\n" - "}\n" - "\n" - "public class B extends A\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->inheritances, L"foo.B -> foo.A <6:24 6:24>" - )); - } - - void test_java_parser_finds_inheritance_using_implements_keyword() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class A\n" - "{\n" - "}\n" - "\n" - "public class B implements A\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->inheritances, L"foo.B -> foo.A <6:27 6:27>" - )); - } - - void test_java_parser_finds_inheritance_of_anonymous_class_declaration() - { - std::shared_ptr client = parseCode( - "public class A\n" - "{\n" - " public interface Base\n" - " {\n" - " }\n" - "\n" - " void foo()\n" - " {\n" - " Base b = new Base()\n" - " {\n" - " }\n" - " };\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->inheritances, L"A.foo.anonymous class (input.cc<10:3>) -> A.Base <9:16 9:19>" - )); - } - - void test_java_parser_finds_usage_of_string_for_var_type() - { - std::shared_ptr client = parseCode( - "public class A\n" - "{\n" - " public void foo(){\n" - " var a = \"test\";\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"void A.foo() -> java.lang.String <4:3 4:5>" - )); - } - - void test_java_parser_finds_type_parameter_in_signature_of_method() - { - std::shared_ptr client = parseCode( - "public class A \n" - "{\n" - " public A foo(A a){\n" - " return a;\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A A.foo(A) -> A <3:9 3:9>" - )); - TS_ASSERT(utility::containsElement( - client->typeUses, L"A A.foo(A) -> A <3:21 3:21>" - )); - } - - void test_parser_finds_usage_of_type_defined_in_base_class() - { - std::shared_ptr client = parseCode( - "public class Foo {\n" - " public class Base {\n" - " public class X {\n" - " }\n" - " }\n" - " public class Derived extends Base {\n" - " public X x = null;\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"Foo.Base.X Foo.Derived.x -> Foo.Base.X <7:10 7:10>" - )); - } - - void test_java_parser_finds_correct_location_of_qualified_type_usage() - { - std::shared_ptr client = parseCode( - "public class A\n" - "{\n" - " public class B\n" - " {\n" - " }\n" - " void bar()\n" - " {\n" - " A.B b = new A.B();\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"void A.bar() -> A.B <8:5 8:5>" - )); - } - - void test_java_parser_finds_type_argument_of_parameterized_type() - { - std::shared_ptr client = parseCode( - "public class A {\n" - " T t;\n" - "}\n" - "public class B {\n" - " void foo() {\n" - " A a = null;\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeArguments, L"A -> java.lang.Void <6:5 6:8>" - )); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"void B.foo() -> java.lang.Void <6:5 6:8>" - )); - } - - void test_java_parser_finds_type_argument_of_method_invocation() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class X\n" - "{\n" - " public static void foo() {\n" - " }\n" - "\n" - " public static void bar() {\n" - " foo.X.foo();\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeArguments, L"static void foo.X.foo() -> java.lang.Void <8:10 8:13>" - )); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"static void foo.X.bar() -> java.lang.Void <8:10 8:13>" - )); - } - - void test_java_parser_finds_type_argument_of_super_method_invocation() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class X\n" - "{\n" - " public class A\n" - " {\n" - " void bar()\n" - " {\n" - " }\n" - " }\n" - " \n" - " public class B extends A\n" - " {\n" - " void bar()\n" - " {\n" - " super.bar();\n" - " }\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeArguments, L"void foo.X.A.bar() -> java.lang.Void <15:11 15:14>" - )); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"void foo.X.B.bar() -> java.lang.Void <15:11 15:14>" - )); - } - - void test_java_parser_finds_type_argument_of_constructor_invocation() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class Bar\n" - "{\n" - " public Bar()\n" - " {\n" - " }\n" - " \n" - " public Bar(int i)\n" - " {\n" - " this();\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeArguments, L"foo.Bar.Bar() -> java.lang.Void <10:4 10:7>" - )); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"foo.Bar.Bar(int) -> java.lang.Void <10:4 10:7>" - )); - } - - void test_java_parser_finds_type_argument_of_super_constructor_invocation() - { - std::shared_ptr client = parseCode( - "public class A\n" - "{\n" - " public class Base\n" - " {\n" - " }\n" - "\n" - " public class Derived extends Base\n" - " {\n" - " public Derived()\n" - " {\n" - " super();\n" - " }\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeArguments, L"A.Base.Base() -> java.lang.Void <11:5 11:8>" - )); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A.Derived.Derived() -> java.lang.Void <11:5 11:8>" - )); - } - - void test_java_parser_finds_type_argument_of_creation_reference() - { - std::shared_ptr client = parseCode( - "public class A\n" - "{\n" - " public interface Functor\n" - " {\n" - " public void doSomething();\n" - " }\n" - "\n" - " public class B\n" - " {\n" - " }\n" - "\n" - " void foo()\n" - " {\n" - " Functor method = B::new;\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeArguments, L"A.B.B() -> java.lang.Void <14:24 14:27>" - )); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"void A.foo() -> java.lang.Void <14:24 14:27>" - )); - } - - void test_java_parser_finds_type_argument_of_expression_method_reference() - { - std::shared_ptr client = parseCode( - "public class A\n" - "{\n" - " public interface Functor\n" - " {\n" - " public void doSomething();\n" - " }\n" - "\n" - " static void bar()\n" - " {\n" - " }\n" - "\n" - " void foo()\n" - " {\n" - " Functor method = A::bar;\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeArguments, L"static void A.bar() -> java.lang.Void <14:24 14:27>" - )); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"void A.foo() -> java.lang.Void <14:24 14:27>" - )); - } - - void test_java_parser_finds_type_argument_of_super_method_reference() - { - std::shared_ptr client = parseCode( - "public class A {\n" - " public interface Functor {\n" - " public void doSomething();\n" - " }\n" - "\n" - " public class B {\n" - " void bar() {\n" - " }\n" - " }\n" - "\n" - " public class C extends B {\n" - " void foo() {\n" - " Functor method = super::bar;\n" - " }\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeArguments, L"void A.B.bar() -> java.lang.Void <13:29 13:32>" - )); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"void A.C.foo() -> java.lang.Void <13:29 13:32>" - )); - } - - void test_java_parser_finds_type_argument_of_type_method_reference() - { - std::shared_ptr client = parseCode( - "public class A {\n" - " \n" - " void foo() {\n" - " Functor method = int []::clone;\n" - " }\n" - "}\n" - ); - - // currently we cannot record the typeArguments of array type methods - - TS_ASSERT(utility::containsElement( - client->typeUses, L"void A.foo() -> java.lang.Void <4:29 4:32>" - )); - } - - void test_java_parser_finds_type_argument_of_class_instance_creation() - { - std::shared_ptr client = parseCode( - "public class A {\n" - " public interface Functor {\n" - " public void doSomething();\n" - " }\n" - "\n" - " public class B {\n" - " B() {\n" - " }\n" - " void bar() {\n" - " B b = new A.B();\n" - " }\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeArguments, L"A.B.B() -> java.lang.Void <10:15 10:18>" - )); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"void A.B.bar() -> java.lang.Void <10:15 10:18>" - )); - } - - void test_java_parser_finds_super_method_invocation() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class X\n" - "{\n" - " public class A\n" - " {\n" - " void bar()\n" - " {\n" - " }\n" - " }\n" - " \n" - " public class B extends A\n" - " {\n" - " void bar()\n" - " {\n" - " super.bar();\n" - " }\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"void foo.X.B.bar() -> void foo.X.A.bar() <15:10 15:12>" - )); - } - - void test_java_parser_finds_constructor_invocation() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class Bar\n" - "{\n" - " public Bar()\n" - " {\n" - " }\n" - " \n" - " public Bar(int i)\n" - " {\n" - " this();\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"foo.Bar.Bar(int) -> foo.Bar.Bar() <10:3 10:6>" - )); - } - - void test_java_parser_finds_super_constructor_invocation() - { - std::shared_ptr client = parseCode( - "public class A\n" - "{\n" - " public class Base\n" - " {\n" - " }\n" - "\n" - " public class Derived extends Base\n" - " {\n" - " public Derived()\n" - " {\n" - " super();\n" - " }\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"A.Derived.Derived() -> A.Base.Base() <11:4 11:8>" - )); - } - - void test_java_parser_finds_invocation_of_method_of_anonymous_class() - { - std::shared_ptr client = parseCode( - "class Main {\n" - " public interface Interfaze {\n" - " public void foo();\n" - " }\n" - "\n" - " private Interfaze i = new Interfaze() {\n" - " public void foo() {\n" - " bar();\n" - " }\n" - "\n" - " private void bar() {\n" - " }\n" - " };\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->calls, L"void Main.anonymous class (input.cc<6:40>).foo() -> void Main.anonymous class (input.cc<6:40>).bar() <8:4 8:6>" - )); - } - - void test_java_parser_finds_overridden_method_with_same_signature() - { - std::shared_ptr client = parseCode( - "class Main {\n" - " public interface Interfaze {\n" - " public void foo(int t);\n" - " }\n" - "\n" - " public class C implements Interfaze {\n" - " public void foo(int t) {\n" - " }\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->overrides, L"void Main.C.foo(int) -> void Main.Interfaze.foo(int) <7:15 7:17>" - )); - } - - void test_java_parser_finds_overridden_method_with_generic_signature() - { - std::shared_ptr client = parseCode( - "class Main {\n" - " public interface Interfaze {\n" - " public void foo(T t);\n" - " }\n" - "\n" - " public class C implements Interfaze {\n" - " public void foo(X t) {\n" - " }\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->overrides, L"void Main.C.foo(Main.X) -> void Main.Interfaze.foo(Main.Interfaze.T) <7:15 7:17>" - )); - } - - void test_java_parser_finds_method_usage_for_creation_reference() - { - std::shared_ptr client = parseCode( - "public class A\n" - "{\n" - " public interface Functor\n" - " {\n" - " public void doSomething();\n" - " }\n" - "\n" - " public class B\n" - " {\n" - " }\n" - "\n" - " void foo()\n" - " {\n" - " Functor method = B::new;\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"void A.foo() -> A.B.B() <14:23 14:25>" - )); - } - - void test_java_parser_finds_method_usage_for_expression_method_reference() - { - std::shared_ptr client = parseCode( - "public class A\n" - "{\n" - " public interface Functor\n" - " {\n" - " public void doSomething();\n" - " }\n" - "\n" - " public class B\n" - " {\n" - " void bar()\n" - " {\n" - " }\n" - " }\n" - "\n" - " void foo()\n" - " {\n" - " Functor method = B::bar;\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"void A.foo() -> void A.B.bar() <17:23 17:25>" - )); - } - - void test_java_parser_finds_method_usage_for_super_method_reference() - { - std::shared_ptr client = parseCode( - "public class A {\n" - " public interface Functor {\n" - " public void doSomething();\n" - " }\n" - "\n" - " public class B {\n" - " void bar() {\n" - " }\n" - " }\n" - "\n" - " public class C extends B {\n" - " void foo() {\n" - " Functor method = super::bar;\n" - " }\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"void A.C.foo() -> void A.B.bar() <13:28 13:30>" - )); - } - - void test_java_parser_finds_no_method_usage_for_type_method_reference() - { - std::shared_ptr client = parseCode( - "public class A {\n" - " void foo() {\n" - " Functor method = int []::clone;\n" - " }\n" - "}\n" - ); - - // finding method usage here may be implemented in the future. - TS_ASSERT_EQUALS(client->usages.size(), 0); - } - - void test_java_parser_finds_no_usage_of_field_within_that_fields_declaration() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class X\n" - "{\n" - " private int t;\n" - "}\n" - ); - - TS_ASSERT_EQUALS(client->usages.size(), 0); - } - - void test_java_parser_finds_no_usage_of_enum_constant_within_that_enum_constants_declaration() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public enum X\n" - "{\n" - " OPTION_0;\n" - "}\n" - ); - - TS_ASSERT_EQUALS(client->usages.size(), 0); - } - - void test_java_parser_finds_usage_of_field_with_same_name_as_method_parameter() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class X\n" - "{\n" - " private int t;\n" - " public X(int t)\n" - " {\n" - " this.t = t;\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"foo.X.X(int) -> int foo.X.t <7:8 7:8>" - )); - } - - void test_java_parser_does_not_confuse_method_name_with_field_name() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class X\n" - "{\n" - " private int foo;\n" - " public void foo()\n" - " {\n" - " this.foo = 5;\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->usages, L"void foo.X.foo() -> int foo.X.foo <7:8 7:10>" - )); - } - - void test_java_parser_finds_assignment_of_method_parameter() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class A\n" - "{\n" - " void bar(int i)\n" - " {\n" - " i = 0;\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"foo.A.bar<0> <6:3 6:3>" - )); - } - - void test_java_parser_finds_assignment_of_local_variable() - { - std::shared_ptr client = parseCode( - "package foo;\n" - "public class A\n" - "{\n" - " void bar()\n" - " {\n" - " int i;\n" - " i = 0;\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"foo.A.bar<0> <7:3 7:3>" - )); - } - - void test_java_parser_finds_scope_of_class_declaration() - { - std::shared_ptr client = parseCode( - "public class A\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<2:1> <2:1 2:1>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<2:1> <3:1 3:1>" - )); - } - - void test_java_parser_finds_scope_of_enum_declaration() - { - std::shared_ptr client = parseCode( - "public enum A\n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<2:1> <2:1 2:1>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<2:1> <3:1 3:1>" - )); - } - - void test_java_parser_finds_scope_of_constructor_declaration() - { - std::shared_ptr client = parseCode( - "public class A\n" - "{\n" - " public A()\n" - " {\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<4:2> <4:2 4:2>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<4:2> <5:2 5:2>" - )); - } - - void test_java_parser_finds_scope_of_method_declaration() - { - std::shared_ptr client = parseCode( - "public class A\n" - "{\n" - " public void a()\n" - " {\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<4:2> <4:2 4:2>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<4:2> <5:2 5:2>" - )); - } - - void test_java_parser_finds_scope_of_switch_statement() - { - std::shared_ptr client = parseCode( - "public class A\n" - "{\n" - " public void a()\n" - " {\n" - " switch(2)\n" - " {\n" - " case 1:\n" - " break;\n" - " }\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<6:3> <6:3 6:3>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<6:3> <9:3 9:3>" - )); - } - - void test_java_parser_finds_scope_of_block_statement() - { - std::shared_ptr client = parseCode( - "public class A\n" - "{\n" - " public void a()\n" - " {\n" - " {\n" - " }\n" - " }\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<5:3> <5:3 5:3>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<5:3> <6:3 6:3>" - )); - } - - void test_java_parser_finds_scope_of_array_initialization_list() - { - std::shared_ptr client = parseCode( - "public class A\n" - "{\n" - " private int[] array = {1, 2};\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<3:24> <3:24 3:24>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<3:24> <3:29 3:29>" - )); - } - - void test_java_parser_finds_scope_of_anonymous_class_declaration() - { - std::shared_ptr client = parseCode( - "public class A\n" - "{\n" - " public interface Base\n" - " {\n" - " }\n" - "\n" - " void foo()\n" - " {\n" - " Base b = new Base()\n" - " {\n" - " }\n" - " };\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<10:3> <10:3 10:3>" - )); - TS_ASSERT(utility::containsElement( - client->localSymbols, L"input.cc<10:3> <11:3 11:3>" - )); - } - - void test_java_parser_finds_usage_of_type_parameter_of_class() - { - std::shared_ptr client = parseCode( - "public class A \n" - "{\n" - " T t;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A.T A.t -> A.T <3:2 3:2>" - )); - } - - void test_java_parser_finds_usage_of_type_parameter_of_method() - { - std::shared_ptr client = parseCode( - "public class A\n" - "{\n" - " public void foo(T t){};\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"void A.foo(T) -> A.foo.T <3:22 3:22>" - )); - } - - void test_java_parser_finds_correct_location_of_generic_type_usage() - { - std::shared_ptr client = parseCode( - "public class A \n" - "{\n" - " A t;\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A A.t -> A <3:2 3:2>" - )); - } - - void test_java_parser_finds_bound_type_of_type_parameter() - { - std::shared_ptr client = parseCode( - "public class A \n" - "{\n" - "}\n" - ); - - TS_ASSERT(utility::containsElement( - client->typeUses, L"A.T -> java.lang.Void <1:27 1:30>" - )); - } - - - //void test_cxx_parser_catches_error() - //{ - // std::shared_ptr client = parseCode( - // "int a = b;\n", - // false - // ); - - // TS_ASSERT_EQUALS(client->errors.size(), 1); - // TS_ASSERT_EQUALS(client->errors[0], L"use of undeclared identifier \'b\' <1:9 1:9>"); - //} - - //void test_cxx_parser_finds_location_of_line_comment() - //{ - // std::shared_ptr client = parseCode( - // "// this is a line comment\n" - // ); - - // TS_ASSERT_EQUALS(client->comments.size(), 1); - // TS_ASSERT_EQUALS(client->comments[0], L"comment <1:1 1:26>"); - //} - - //void test_cxx_parser_finds_location_of_block_comment() - //{ - // std::shared_ptr client = parseCode( - // "/* this is a\n" - // "block comment */\n" - // ); - - // TS_ASSERT_EQUALS(client->comments.size(), 1); - // TS_ASSERT_EQUALS(client->comments[0], L"comment <1:1 2:17>"); - //} - - -private: - std::string setupJavaEnvironmentFactory() - { - if (!JavaEnvironmentFactory::getInstance()) - { - std::string errorString; -#ifdef _WIN32 - const std::string separator = ";"; -#else - const std::string separator = ":"; -#endif - std::string classPath = ""; - { - const std::vector jarNames = utility::getRequiredJarNames(); - for (size_t i = 0; i < jarNames.size(); i++) - { - if (i != 0) - { - classPath += separator; - } - classPath += FilePath(L"../app/data/java/lib/").concatenate(jarNames[i]).str(); - } - } - - JavaEnvironmentFactory::createInstance( - classPath, - errorString - ); - - return errorString; - } - - return ""; - } - - std::shared_ptr parseCode(std::string code, bool logErrors = true) - { - setupJavaEnvironmentFactory(); - - std::shared_ptr storage = std::make_shared(); - JavaParser parser(std::make_shared(storage.get()), std::make_shared()); - parser.buildIndex(FilePath(L"input.cc"), TextAccess::createFromString(code)); - - storage->generateStringLists(); - - return storage; - } -}; diff --git a/src/test/LogManagerTestSuite.cpp b/src/test/LogManagerTestSuite.cpp new file mode 100644 index 00000000..144dd56d --- /dev/null +++ b/src/test/LogManagerTestSuite.cpp @@ -0,0 +1,289 @@ +#include "catch.hpp" + +#include + +#include "LogManagerImplementation.h" + +namespace +{ + class TestLogger : public Logger + { + public: + TestLogger(); + + void reset(); + int getMessageCount() const; + int getWarningCount() const; + int getErrorCount() const; + + std::wstring getLastInfo() const; + std::wstring getLastWarning() const; + std::wstring getLastError() const; + + private: + void logInfo(const LogMessage& message) override; + void logWarning(const LogMessage& message) override; + void logError(const LogMessage& message) override; + + int m_logMessageCount; + int m_logWarningCount; + int m_logErrorCount; + + std::wstring m_lastInfo; + std::wstring m_lastWarning; + std::wstring m_lastError; + }; + + TestLogger::TestLogger() + : Logger("TestLogger") + , m_logMessageCount(0) + , m_logWarningCount(0) + , m_logErrorCount(0) + , m_lastInfo(L"") + , m_lastWarning(L"") + , m_lastError(L"") + { + } + + void TestLogger::reset() + { + m_logMessageCount = 0; + m_logWarningCount = 0; + m_logErrorCount = 0; + } + + int TestLogger::getMessageCount() const + { + return m_logMessageCount; + } + + int TestLogger::getWarningCount() const + { + return m_logWarningCount; + } + + int TestLogger::getErrorCount() const + { + return m_logErrorCount; + } + + std::wstring TestLogger::getLastInfo() const + { + return m_lastInfo; + } + + std::wstring TestLogger::getLastWarning() const + { + return m_lastWarning; + } + + std::wstring TestLogger::getLastError() const + { + return m_lastError; + } + + void TestLogger::logInfo(const LogMessage& message) + { + m_lastInfo = message.message; + m_logMessageCount++; + } + + void TestLogger::logWarning(const LogMessage& message) + { + m_lastWarning = message.message; + m_logWarningCount++; + } + + void TestLogger::logError(const LogMessage& message) + { + m_lastError = message.message; + m_logErrorCount++; + } + + + void addTestLogger(LogManagerImplementation* logManagerImplementation, const unsigned int loggerCount) + { + for (unsigned int i = 0; i < loggerCount; i++) + { + std::shared_ptr logger = std::make_shared(); + logManagerImplementation->addLogger(logger); + } + } + + void removeTestLoggers(LogManagerImplementation* logManagerImplementation) + { + std::shared_ptr logger = std::make_shared(); + logManagerImplementation->removeLoggersByType(logger->getType()); + } + + void addAndRemoveTestLogger(LogManagerImplementation* logManagerImplementation, const unsigned int loggerCount) + { + addTestLogger(logManagerImplementation, loggerCount); + removeTestLoggers(logManagerImplementation); + } + + void logSomeMessages( + LogManagerImplementation* logManagerImplementation, + const std::wstring& message, + const unsigned int messageCount + ) + { + for (unsigned int i = 0; i < messageCount; i++) + { + logManagerImplementation->logInfo(message, __FILE__, __FUNCTION__, __LINE__); + logManagerImplementation->logWarning(message, __FILE__, __FUNCTION__, __LINE__); + logManagerImplementation->logError(message, __FILE__, __FUNCTION__, __LINE__); + } + } +} + +TEST_CASE("new logger can be added to manager") +{ + LogManagerImplementation logManagerImplementation; + + std::shared_ptr logger = std::make_shared(); + + int countBeforeAdd = logManagerImplementation.getLoggerCount(); + logManagerImplementation.addLogger(logger); + int countAfterAdd = logManagerImplementation.getLoggerCount(); + logManagerImplementation.removeLogger(logger); + + REQUIRE(1 == countAfterAdd - countBeforeAdd); +} + +TEST_CASE("logger can be removed from manager") +{ + LogManagerImplementation logManagerImplementation; + + std::shared_ptr logger = std::make_shared(); + + int countBeforeAdd = logManagerImplementation.getLoggerCount(); + logManagerImplementation.addLogger(logger); + logManagerImplementation.removeLogger(logger); + int countAfterRemove = logManagerImplementation.getLoggerCount(); + + REQUIRE(countBeforeAdd == countAfterRemove); +} + +TEST_CASE("logger logs message") +{ + LogManagerImplementation logManagerImplementation; + + const std::wstring log = L"test"; + std::shared_ptr logger = std::make_shared(); + + logManagerImplementation.addLogger(logger); + logManagerImplementation.logInfo(log, __FILE__, __FUNCTION__, __LINE__); + const int logCount = logger->getMessageCount(); + const std::wstring lastLog = logger->getLastInfo(); + + REQUIRE(1 == logCount); + REQUIRE(log == lastLog); +} + +TEST_CASE("logger logs warning") +{ + LogManagerImplementation logManagerImplementation; + + const std::wstring log = L"test"; + std::shared_ptr logger = std::make_shared(); + + logManagerImplementation.addLogger(logger); + + logManagerImplementation.logWarning(log, __FILE__, __FUNCTION__, __LINE__); + const int logCount = logger->getWarningCount(); + const std::wstring lastLog = logger->getLastWarning(); + + REQUIRE(1 == logCount); + REQUIRE(log == lastLog); +} + +TEST_CASE("logger logs error") +{ + LogManagerImplementation logManagerImplementation; + + std::wstring log = L"test"; + std::shared_ptr logger = std::make_shared(); + + logManagerImplementation.addLogger(logger); + + logManagerImplementation.logError(log, __FILE__, __FUNCTION__, __LINE__); + const int logCount = logger->getErrorCount(); + const std::wstring lastLog = logger->getLastError(); + + REQUIRE(1 == logCount); + REQUIRE(log == lastLog); +} + +TEST_CASE("logger logs only logs of defined log level") +{ + LogManagerImplementation logManagerImplementation; + + std::wstring info = L"info"; + std::wstring warning = L"warning"; + std::wstring error = L"error"; + + std::shared_ptr logger = std::make_shared(); + + logger->setLogLevel(Logger::LOG_INFOS | Logger::LOG_ERRORS); + + logManagerImplementation.addLogger(logger); + + logManagerImplementation.logInfo(info, __FILE__, __FUNCTION__, __LINE__); + logManagerImplementation.logWarning(warning, __FILE__, __FUNCTION__, __LINE__); + logManagerImplementation.logError(error, __FILE__, __FUNCTION__, __LINE__); + + REQUIRE(1 == logger->getMessageCount()); + REQUIRE(0 == logger->getWarningCount()); + REQUIRE(1 == logger->getErrorCount()); + + REQUIRE(info == logger->getLastInfo()); + REQUIRE(error == logger->getLastError()); +} + +TEST_CASE("new logger can be added to manager threaded") +{ + LogManagerImplementation logManagerImplementation; + unsigned int loggerCount = 100; + + std::thread thread0(addTestLogger, &logManagerImplementation, loggerCount); + std::thread thread1(addTestLogger, &logManagerImplementation, loggerCount); + + thread0.join(); + thread1.join(); + + REQUIRE(loggerCount * 2 == logManagerImplementation.getLoggerCount()); +} + +TEST_CASE("logger can be removed from manager threaded") +{ + LogManagerImplementation logManagerImplementation; + unsigned int loggerCount = 100; + + std::thread thread0(addAndRemoveTestLogger, &logManagerImplementation, loggerCount); + std::thread thread1(addAndRemoveTestLogger, &logManagerImplementation, loggerCount); + + thread0.join(); + thread1.join(); + + REQUIRE(0 == logManagerImplementation.getLoggerCount()); +} + +TEST_CASE("logger logs threaded") +{ + LogManagerImplementation logManagerImplementation; + + std::wstring log = L"foo"; + unsigned int messageCount = 100; + std::shared_ptr logger = std::make_shared(); + logManagerImplementation.addLogger(logger); + + std::thread thread0(logSomeMessages, &logManagerImplementation, log, messageCount); + std::thread thread1(logSomeMessages, &logManagerImplementation, log, messageCount); + + thread0.join(); + thread1.join(); + + REQUIRE(logger->getLastError() == log); + REQUIRE(messageCount * 6 == logger->getErrorCount() + logger->getWarningCount() + logger->getMessageCount()); +} diff --git a/src/test/LogManagerTestSuite.h b/src/test/LogManagerTestSuite.h deleted file mode 100644 index b7a82ba9..00000000 --- a/src/test/LogManagerTestSuite.h +++ /dev/null @@ -1,291 +0,0 @@ -#include - -#include - -#include "LogManagerImplementation.h" - -class LogManagerTestSuite : public CxxTest::TestSuite -{ -public: - void test_new_logger_can_be_added_to_manager() - { - LogManagerImplementation logManagerImplementation; - - std::shared_ptr logger = std::make_shared(); - - int countBeforeAdd = logManagerImplementation.getLoggerCount(); - logManagerImplementation.addLogger(logger); - int countAfterAdd = logManagerImplementation.getLoggerCount(); - logManagerImplementation.removeLogger(logger); - - TS_ASSERT_EQUALS(1, countAfterAdd - countBeforeAdd); - } - - void test_logger_can_be_removed_from_manager() - { - LogManagerImplementation logManagerImplementation; - - std::shared_ptr logger = std::make_shared(); - - int countBeforeAdd = logManagerImplementation.getLoggerCount(); - logManagerImplementation.addLogger(logger); - logManagerImplementation.removeLogger(logger); - int countAfterRemove = logManagerImplementation.getLoggerCount(); - - TS_ASSERT_EQUALS(countBeforeAdd, countAfterRemove); - } - - void test_logger_logs_message() - { - LogManagerImplementation logManagerImplementation; - - const std::wstring log = L"test"; - std::shared_ptr logger = std::make_shared(); - - logManagerImplementation.addLogger(logger); - logManagerImplementation.logInfo(log, __FILE__, __FUNCTION__, __LINE__); - const int logCount = logger->getMessageCount(); - const std::wstring lastLog = logger->getLastInfo(); - - TS_ASSERT_EQUALS(1, logCount); - TS_ASSERT_EQUALS(log, lastLog); - } - - void test_logger_logs_warning() - { - LogManagerImplementation logManagerImplementation; - - const std::wstring log = L"test"; - std::shared_ptr logger = std::make_shared(); - - logManagerImplementation.addLogger(logger); - - logManagerImplementation.logWarning(log, __FILE__, __FUNCTION__, __LINE__); - const int logCount = logger->getWarningCount(); - const std::wstring lastLog = logger->getLastWarning(); - - TS_ASSERT_EQUALS(1, logCount); - TS_ASSERT_EQUALS(log, lastLog); - } - - void test_logger_logs_error() - { - LogManagerImplementation logManagerImplementation; - - std::wstring log = L"test"; - std::shared_ptr logger = std::make_shared(); - - logManagerImplementation.addLogger(logger); - - logManagerImplementation.logError(log, __FILE__, __FUNCTION__, __LINE__); - const int logCount = logger->getErrorCount(); - const std::wstring lastLog = logger->getLastError(); - - TS_ASSERT_EQUALS(1, logCount); - TS_ASSERT_EQUALS(log, lastLog); - } - - void test_logger_logs_only_logs_of_defined_log_level() - { - LogManagerImplementation logManagerImplementation; - - std::wstring info = L"info"; - std::wstring warning = L"warning"; - std::wstring error = L"error"; - - std::shared_ptr logger = std::make_shared(); - - logger->setLogLevel(Logger::LOG_INFOS | Logger::LOG_ERRORS); - - logManagerImplementation.addLogger(logger); - - logManagerImplementation.logInfo(info, __FILE__, __FUNCTION__, __LINE__); - logManagerImplementation.logWarning(warning, __FILE__, __FUNCTION__, __LINE__); - logManagerImplementation.logError(error, __FILE__, __FUNCTION__, __LINE__); - - TS_ASSERT_EQUALS(1, logger->getMessageCount()); - TS_ASSERT_EQUALS(0, logger->getWarningCount()); - TS_ASSERT_EQUALS(1, logger->getErrorCount()); - - TS_ASSERT_EQUALS(info, logger->getLastInfo()); - TS_ASSERT_EQUALS(error, logger->getLastError()); - } - - void test_new_logger_can_be_added_to_manager_threaded() - { - LogManagerImplementation logManagerImplementation; - unsigned int loggerCount = 100; - - std::thread thread0(addTestLogger, &logManagerImplementation, loggerCount); - std::thread thread1(addTestLogger, &logManagerImplementation, loggerCount); - - thread0.join(); - thread1.join(); - - TS_ASSERT_EQUALS(loggerCount * 2, logManagerImplementation.getLoggerCount()); - } - - void test_logger_can_be_removed_from_manager_threaded() - { - LogManagerImplementation logManagerImplementation; - unsigned int loggerCount = 100; - - std::thread thread0(addAndRemoveTestLogger, &logManagerImplementation, loggerCount); - std::thread thread1(addAndRemoveTestLogger, &logManagerImplementation, loggerCount); - - thread0.join(); - thread1.join(); - - TS_ASSERT_EQUALS(0, logManagerImplementation.getLoggerCount()); - } - - void test_logger_logs_threaded() - { - LogManagerImplementation logManagerImplementation; - - std::wstring log = L"foo"; - unsigned int messageCount = 100; - std::shared_ptr logger = std::make_shared(); - logManagerImplementation.addLogger(logger); - - std::thread thread0(logSomeMessages, &logManagerImplementation, log, messageCount); - std::thread thread1(logSomeMessages, &logManagerImplementation, log, messageCount); - - thread0.join(); - thread1.join(); - - TS_ASSERT_EQUALS(logger->getLastError(), log); - TS_ASSERT_EQUALS(messageCount * 6, logger->getErrorCount() + logger->getWarningCount() + logger->getMessageCount()); - } - -private: - static void addTestLogger(LogManagerImplementation* logManagerImplementation, const unsigned int loggerCount) - { - for(unsigned int i = 0; i < loggerCount; i++) - { - std::shared_ptr logger = std::make_shared(); - logManagerImplementation->addLogger(logger); - } - } - - static void removeTestLoggers(LogManagerImplementation* logManagerImplementation) - { - std::shared_ptr logger = std::make_shared(); - logManagerImplementation->removeLoggersByType(logger->getType()); - } - - static void addAndRemoveTestLogger(LogManagerImplementation* logManagerImplementation, const unsigned int loggerCount) - { - addTestLogger(logManagerImplementation, loggerCount); - removeTestLoggers(logManagerImplementation); - } - - static void logSomeMessages( - LogManagerImplementation* logManagerImplementation, - const std::wstring& message, - const unsigned int messageCount - ) - { - for(unsigned int i = 0; i < messageCount; i++) - { - logManagerImplementation->logInfo(message, __FILE__, __FUNCTION__, __LINE__); - logManagerImplementation->logWarning(message, __FILE__, __FUNCTION__, __LINE__); - logManagerImplementation->logError(message, __FILE__, __FUNCTION__, __LINE__); - } - } - - class TestLogger: public Logger - { - public: - TestLogger(); - - void reset(); - int getMessageCount() const; - int getWarningCount() const; - int getErrorCount() const; - - std::wstring getLastInfo() const; - std::wstring getLastWarning() const; - std::wstring getLastError() const; - - private: - void logInfo(const LogMessage& message) override; - void logWarning(const LogMessage& message) override; - void logError(const LogMessage& message) override; - - int m_logMessageCount; - int m_logWarningCount; - int m_logErrorCount; - - std::wstring m_lastInfo; - std::wstring m_lastWarning; - std::wstring m_lastError; - }; -}; - - -LogManagerTestSuite::TestLogger::TestLogger() - : Logger("TestLogger") - , m_logMessageCount(0) - , m_logWarningCount(0) - , m_logErrorCount(0) - , m_lastInfo(L"") - , m_lastWarning(L"") - , m_lastError(L"") -{ -} - -void LogManagerTestSuite::TestLogger::reset() -{ - m_logMessageCount = 0; - m_logWarningCount = 0; - m_logErrorCount = 0; -} - -int LogManagerTestSuite::TestLogger::getMessageCount() const -{ - return m_logMessageCount; -} - -int LogManagerTestSuite::TestLogger::getWarningCount() const -{ - return m_logWarningCount; -} - -int LogManagerTestSuite::TestLogger::getErrorCount() const -{ - return m_logErrorCount; -} - -std::wstring LogManagerTestSuite::TestLogger::getLastInfo() const -{ - return m_lastInfo; -} - -std::wstring LogManagerTestSuite::TestLogger::getLastWarning() const -{ - return m_lastWarning; -} - -std::wstring LogManagerTestSuite::TestLogger::getLastError() const -{ - return m_lastError; -} - -void LogManagerTestSuite::TestLogger::logInfo(const LogMessage& message) -{ - m_lastInfo = message.message; - m_logMessageCount++; -} - -void LogManagerTestSuite::TestLogger::logWarning(const LogMessage& message) -{ - m_lastWarning = message.message; - m_logWarningCount++; -} - -void LogManagerTestSuite::TestLogger::logError(const LogMessage& message) -{ - m_lastError = message.message; - m_logErrorCount++; -} diff --git a/src/test/LowMemoryStringMapTestSuite.cpp b/src/test/LowMemoryStringMapTestSuite.cpp new file mode 100644 index 00000000..f2d7b309 --- /dev/null +++ b/src/test/LowMemoryStringMapTestSuite.cpp @@ -0,0 +1,161 @@ +#include "catch.hpp" + +#include "LowMemoryStringMap.h" +#include "TextAccess.h" +#include "types.h" +#include "utilityString.h" + +TEST_CASE("roughly everything") +{ + LowMemoryStringMap map; + + map.add("abcdefg", 2); + map.add("abcdefgerlitz", 1); + map.add("abcdefghij", 3); + map.add("abc", 4); + + // map.print(std::cout); + // std::cout << std::endl << map.getByteSize() << " : " << map.getUncompressedByteSize() << std::endl; + + REQUIRE(map.find("abcdefgerlitz") == 1); + REQUIRE(map.find("abcdefg") == 2); + REQUIRE(map.find("abcdefghij") == 3); + REQUIRE(map.find("abc") == 4); + REQUIRE(map.find("bc") == 0); + REQUIRE(map.find("") == 0); + REQUIRE(map.find(";asdfl;kjasd;flkasdf") == 0); + + // TS_ASSERT(map.getByteSize() < map.getUncompressedByteSize()); +} + +TEST_CASE("cannot find element after creation") +{ + LowMemoryStringMap map; + + REQUIRE(map.find("a") == 0); +} + +TEST_CASE("find element") +{ + LowMemoryStringMap map; + map.add("a", 1); + + REQUIRE(map.find("a") == 1); + REQUIRE(map.find("b") == 0); +} + +TEST_CASE("find fully different elements") +{ + LowMemoryStringMap map; + map.add("a", 1); + map.add("b", 2); + + REQUIRE(map.find("a") == 1); + REQUIRE(map.find("b") == 2); +} + +TEST_CASE("find similar short elements") +{ + LowMemoryStringMap map; + map.add("ab", 1); + map.add("ac", 2); + + REQUIRE(map.find("ab") == 1); + REQUIRE(map.find("ac") == 2); + REQUIRE(map.find("bc") == 0); +} + +TEST_CASE("find similar long elements") +{ + LowMemoryStringMap map; + map.add("aaaaabbbbb", 1); + map.add("aaaaaccccc", 2); + map.add("aaaaccccc", 3); + map.add("aaaccccc", 4); + + REQUIRE(map.find("aaaaabbbbb") == 1); + REQUIRE(map.find("aaaaaccccc") == 2); + REQUIRE(map.find("aaaaacccccc") == 0); + REQUIRE(map.find("aacc") == 0); +} + +TEST_CASE("add twice") +{ + LowMemoryStringMap map; + map.add("abba", 1); + map.add("abba", 2); + + REQUIRE(map.find("abba") == 1); +} + +TEST_CASE("find parent child elements") +{ + LowMemoryStringMap map; + map.add("a", 1); + map.add("ab", 2); + + REQUIRE(map.find("a") == 1); + REQUIRE(map.find("ab") == 2); + REQUIRE(map.find("b") == 0); +} + +TEST_CASE("find long parent child elements") +{ + LowMemoryStringMap map; + map.add("ababaaa", 1); + map.add("aba", 2); + + REQUIRE(map.find("ababaaa") == 1); + REQUIRE(map.find("aba") == 2); + REQUIRE(map.find("ab") == 0); + REQUIRE(map.find("") == 0); +} + +TEST_CASE("find long similar prefix elements") +{ + LowMemoryStringMap map; + map.add("ababababaab", 1); + map.add("abababababababaccc", 2); + map.add("abababababababaer", 3); + map.add("abababababababber", 4); + map.add("abababababababaaaa", 5); + + // map.print(std::cout); + // std::cout << std::endl << map.getByteSize() << " : " << map.getUncompressedByteSize() << std::endl; + + REQUIRE(map.find("ababababaab") == 1); + REQUIRE(map.find("abababababababaccc") == 2); + REQUIRE(map.find("abababababababaer") == 3); + REQUIRE(map.find("abababababababber") == 4); + REQUIRE(map.find("abababababababaaaa") == 5); + REQUIRE(map.find("abababab") == 0); +} + +TEST_CASE("wstring") +{ + LowMemoryStringMap map; + + FilePath filePath(L"data/LowMemoryStringMapTestSuite/names.txt"); + std::shared_ptr textAccess = TextAccess::createFromFile(filePath); + + std::vector names; + for (std::string line : textAccess->getAllLines()) + { + names.emplace_back(utility::decodeFromUtf8(line.substr(0, line.find("\n")))); + } + + for (size_t i = 0; i < names.size(); i++) + { + map.add(names[i], i); + } + + // map.print(std::wcout); + // std::cout << std::endl << map.getByteSize() << " : " << map.getUncompressedByteSize() << std::endl; + + for (size_t i = 0; i < names.size(); i++) + { + REQUIRE(map.find(names[i]) == i); + } + + // TS_ASSERT(map.getByteSize() < map.getUncompressedByteSize()); +} diff --git a/src/test/LowMemoryStringMapTestSuite.h b/src/test/LowMemoryStringMapTestSuite.h deleted file mode 100644 index 9044d01d..00000000 --- a/src/test/LowMemoryStringMapTestSuite.h +++ /dev/null @@ -1,165 +0,0 @@ -#include - -#include "LowMemoryStringMap.h" -#include "TextAccess.h" -#include "types.h" -#include "utilityString.h" - -class LowMemoryStringMapTestSuite : public CxxTest::TestSuite -{ -public: - void test_roughly_everything() - { - LowMemoryStringMap map; - - map.add("abcdefg", 2); - map.add("abcdefgerlitz", 1); - map.add("abcdefghij", 3); - map.add("abc", 4); - - // map.print(std::cout); - // std::cout << std::endl << map.getByteSize() << " : " << map.getUncompressedByteSize() << std::endl; - - TS_ASSERT(map.find("abcdefgerlitz") == 1); - TS_ASSERT(map.find("abcdefg") == 2); - TS_ASSERT(map.find("abcdefghij") == 3); - TS_ASSERT(map.find("abc") == 4); - TS_ASSERT(map.find("bc") == 0); - TS_ASSERT(map.find("") == 0); - TS_ASSERT(map.find(";asdfl;kjasd;flkasdf") == 0); - - // TS_ASSERT(map.getByteSize() < map.getUncompressedByteSize()); - } - - void test_cannot_find_element_after_creation() - { - LowMemoryStringMap map; - - TS_ASSERT(map.find("a") == 0); - } - - void test_find_element() - { - LowMemoryStringMap map; - map.add("a", 1); - - TS_ASSERT(map.find("a") == 1); - TS_ASSERT(map.find("b") == 0); - } - - void test_find_fully_different_elements() - { - LowMemoryStringMap map; - map.add("a", 1); - map.add("b", 2); - - TS_ASSERT(map.find("a") == 1); - TS_ASSERT(map.find("b") == 2); - } - - void test_find_similar_short_elements() - { - LowMemoryStringMap map; - map.add("ab", 1); - map.add("ac", 2); - - TS_ASSERT(map.find("ab") == 1); - TS_ASSERT(map.find("ac") == 2); - TS_ASSERT(map.find("bc") == 0); - } - - void test_find_similar_long_elements() - { - LowMemoryStringMap map; - map.add("aaaaabbbbb", 1); - map.add("aaaaaccccc", 2); - map.add("aaaaccccc", 3); - map.add("aaaccccc", 4); - - TS_ASSERT(map.find("aaaaabbbbb") == 1); - TS_ASSERT(map.find("aaaaaccccc") == 2); - TS_ASSERT(map.find("aaaaacccccc") == 0); - TS_ASSERT(map.find("aacc") == 0); - } - - void test_add_twice() - { - LowMemoryStringMap map; - map.add("abba", 1); - map.add("abba", 2); - - TS_ASSERT(map.find("abba") == 1); - } - - void test_find_parent_child_elements() - { - LowMemoryStringMap map; - map.add("a", 1); - map.add("ab", 2); - - TS_ASSERT(map.find("a") == 1); - TS_ASSERT(map.find("ab") == 2); - TS_ASSERT(map.find("b") == 0); - } - - void test_find_long_parent_child_elements() - { - LowMemoryStringMap map; - map.add("ababaaa", 1); - map.add("aba", 2); - - TS_ASSERT(map.find("ababaaa") == 1); - TS_ASSERT(map.find("aba") == 2); - TS_ASSERT(map.find("ab") == 0); - TS_ASSERT(map.find("") == 0); - } - - void test_find_long_similar_prefix_elements() - { - LowMemoryStringMap map; - map.add("ababababaab", 1); - map.add("abababababababaccc", 2); - map.add("abababababababaer", 3); - map.add("abababababababber", 4); - map.add("abababababababaaaa", 5); - - // map.print(std::cout); - // std::cout << std::endl << map.getByteSize() << " : " << map.getUncompressedByteSize() << std::endl; - - TS_ASSERT(map.find("ababababaab") == 1); - TS_ASSERT(map.find("abababababababaccc") == 2); - TS_ASSERT(map.find("abababababababaer") == 3); - TS_ASSERT(map.find("abababababababber") == 4); - TS_ASSERT(map.find("abababababababaaaa") == 5); - TS_ASSERT(map.find("abababab") == 0); - } - - void test_wstring() - { - LowMemoryStringMap map; - - FilePath filePath(L"data/LowMemoryStringMapTestSuite/names.txt"); - std::shared_ptr textAccess = TextAccess::createFromFile(filePath); - - std::vector names; - for (std::string line : textAccess->getAllLines()) - { - names.emplace_back(utility::decodeFromUtf8(line.substr(0, line.find("\n")))); - } - - for (size_t i = 0; i < names.size(); i++) - { - map.add(names[i], i); - } - - // map.print(std::wcout); - // std::cout << std::endl << map.getByteSize() << " : " << map.getUncompressedByteSize() << std::endl; - - for (size_t i = 0; i < names.size(); i++) - { - TS_ASSERT(map.find(names[i]) == i); - } - - // TS_ASSERT(map.getByteSize() < map.getUncompressedByteSize()); - } -}; diff --git a/src/test/MatrixBaseTestSuite.cpp b/src/test/MatrixBaseTestSuite.cpp new file mode 100644 index 00000000..3eff0542 --- /dev/null +++ b/src/test/MatrixBaseTestSuite.cpp @@ -0,0 +1,470 @@ +#include "catch.hpp" + +#include "MatrixBase.h" +#include "VectorBase.h" +namespace +{ + /** + * C++ functions can't return statically allocated arrays. + * I don't want to use dynamically allocated arrays, so here's my work around for that... + * + * Update: acutally they can... see MatrixBase [] operator (in MatrixBase.cpp) + */ + template + struct Array3x5 + { + T array[3][5]; + }; + + template + struct Array5x3 + { + T array[5][3]; + }; + + Array3x5 getTestValues3x5() + { + Array3x5 result; + + for (unsigned int i = 0; i < 3; i++) + { + for (unsigned int j = 0; j < 5; j++) + { + result.array[i][j] = i + j; + } + } + + return result; + } + + Array3x5 getTestValues3x5_b() + { + Array3x5 result; + + for (unsigned int i = 0; i < 3; i++) + { + for (unsigned int j = 0; j < 5; j++) + { + result.array[i][j] = -i - j; + } + } + + return result; + } + + Array5x3 getTestValues5x3() + { + Array5x3 result; + + for (unsigned int i = 0; i < 5; i++) + { + for (unsigned int j = 0; j < 3; j++) + { + result.array[i][j] = i + j; + } + } + + return result; + } + + MatrixBase getTestMatrix3x5() + { + Array3x5 testValues = getTestValues3x5(); + + return MatrixBase(testValues.array); + } + + MatrixBase getTestMatrix3x5_b() + { + Array3x5 testValues = getTestValues3x5_b(); + + return MatrixBase(testValues.array); + } + + MatrixBase getTestMatrix5x3() + { + Array5x3 testValues = getTestValues5x3(); + + return MatrixBase(testValues.array); + } +} + +TEST_CASE("matrixBase constructors") +{ + MatrixBase matrix0; + + REQUIRE(4 == matrix0.getColumnsCount()); + REQUIRE(5 == matrix0.getRowsCount()); + + Array3x5 testValues = getTestValues3x5(); + + MatrixBase matrix1(testValues.array); + + REQUIRE(3 == matrix1.getColumnsCount()); + REQUIRE(5 == matrix1.getRowsCount()); + + REQUIRE(0 == matrix1.getValue(0, 0)); + REQUIRE(6 == matrix1.getValue(2, 4)); + + MatrixBase matrix2(matrix1); + + REQUIRE(0 == matrix2.getValue(0, 0)); + REQUIRE(6 == matrix2.getValue(2, 4)); +} + +TEST_CASE("matrixBase getSetValue") +{ + MatrixBase matrix0 = getTestMatrix3x5(); + + int value2_2 = matrix0.getValue(2, 2); + matrix0.setValue(2, 2, value2_2*2); + REQUIRE(value2_2*2 == matrix0.getValue(2, 2)); + REQUIRE(3 == matrix0.getValue(1, 2)); + REQUIRE(0 == matrix0.getValue(0, 0)); +} + +TEST_CASE("matrixBase getRowsColumnsCount") +{ + MatrixBase matrix0 = getTestMatrix3x5(); + MatrixBase matrix1 = getTestMatrix5x3(); + + REQUIRE(3 == matrix0.getColumnsCount()); + REQUIRE(5 == matrix0.getRowsCount()); + + REQUIRE(5 == matrix1.getColumnsCount()); + REQUIRE(3 == matrix1.getRowsCount()); +} + +TEST_CASE("matrixBase transposed") +{ + MatrixBase matrix0 = getTestMatrix3x5(); + REQUIRE(3 == matrix0.getColumnsCount()); + REQUIRE(5 == matrix0.getRowsCount()); + + MatrixBase matrix1 = matrix0.transposed(); + REQUIRE(5 == matrix1.getColumnsCount()); + REQUIRE(3 == matrix1.getRowsCount()); + + REQUIRE(matrix0.getValue(0, 0) == matrix1.getValue(0, 0)); + REQUIRE(matrix0.getValue(0, 1) == matrix1.getValue(1, 0)); + REQUIRE(matrix0.getValue(0, 4) == matrix1.getValue(4, 0)); + REQUIRE(matrix0.getValue(1, 4) == matrix1.getValue(4, 1)); + REQUIRE(matrix0.getValue(2, 4) == matrix1.getValue(4, 2)); + REQUIRE(matrix0.getValue(0, 3) == matrix1.getValue(3, 0)); + REQUIRE(matrix0.getValue(1, 3) == matrix1.getValue(3, 1)); + REQUIRE(matrix0.getValue(2, 3) == matrix1.getValue(3, 2)); +} + +TEST_CASE("matrixBase assign") +{ + MatrixBase matrix0 = getTestMatrix3x5(); + MatrixBase matrix1 = getTestMatrix3x5_b(); + + REQUIRE(0 == matrix0.getValue(0, 0)); + REQUIRE(2 == matrix0.getValue(1, 1)); + REQUIRE(4 == matrix0.getValue(2, 2)); + + REQUIRE(0 == matrix1.getValue(0, 0)); + REQUIRE(-2 == matrix1.getValue(1, 1)); + REQUIRE(-4 == matrix1.getValue(2, 2)); + + matrix0.assign(matrix1); + + REQUIRE(0 == matrix0.getValue(0, 0)); + REQUIRE(-2 == matrix0.getValue(1, 1)); + REQUIRE(-4 == matrix0.getValue(2, 2)); +} + +TEST_CASE("matrixBase add subtract") +{ + MatrixBase matrix0 = getTestMatrix3x5(); + MatrixBase matrix1 = getTestMatrix3x5_b(); + + matrix0.add(matrix1); + + REQUIRE(0 == matrix0.getValue(0, 0)); + REQUIRE(0 == matrix0.getValue(1, 1)); + REQUIRE(0 == matrix0.getValue(2, 2)); + + matrix0.subtract(matrix1); + + REQUIRE(0 == matrix0.getValue(0, 0)); + REQUIRE(2 == matrix0.getValue(1, 1)); + REQUIRE(4 == matrix0.getValue(2, 2)); +} + +TEST_CASE("matrixBase multiplyDivideScalar") +{ + MatrixBase matrix0 = getTestMatrix3x5(); + + matrix0.scalarMultiplication(2.0f); // float is on porpoise (so is porpoise, womp womp) + + REQUIRE(0 == matrix0.getValue(0, 0)); + REQUIRE(4 == matrix0.getValue(1, 1)); + REQUIRE(8 == matrix0.getValue(2, 2)); + + matrix0.scalarMultiplication(0.5f); + + REQUIRE(0 == matrix0.getValue(0, 0)); + REQUIRE(2 == matrix0.getValue(1, 1)); + REQUIRE(4 == matrix0.getValue(2, 2)); + + matrix0.scalarMultiplication(0.5f); + + REQUIRE(0 == matrix0.getValue(0, 0)); + REQUIRE(1 == matrix0.getValue(1, 1)); + REQUIRE(2 == matrix0.getValue(2, 2)); + + matrix0.scalarMultiplication(0.5f); + + REQUIRE(0 == matrix0.getValue(0, 0)); + REQUIRE(0 == matrix0.getValue(1, 1)); + REQUIRE(1 == matrix0.getValue(2, 2)); +} + +TEST_CASE("matrixBase multiplyMatrix") +{ + MatrixBase matrix0 = getTestMatrix3x5(); + MatrixBase matrix1 = getTestMatrix3x5_b(); + MatrixBase matrix1t = matrix1.transposed(); + + MatrixBase matrix2 = matrix0.matrixMultiplication(matrix1t); + MatrixBase matrix3 = matrix1t.matrixMultiplication(matrix0); + + // expected results + // matrix0 * matrix1t + /** + * -5, -8, -11, -14, -17 + * -8, -14, -20, -26, -32 + * -11, -20, -29, -38, -47 + * -14, -26, -38, -50, -62 + * -17, -32, -47, -62, -77 + */ + + // matrix1t * matrix0 + /** + * -30, -40, -50 + * -40, -55, -70 + * -50, -70, -90 + */ + + + REQUIRE(5 == matrix2.getColumnsCount()); + REQUIRE(5 == matrix2.getRowsCount()); + + REQUIRE(3 == matrix3.getColumnsCount()); + REQUIRE(3 == matrix3.getRowsCount()); + + REQUIRE(-5 == matrix2.getValue(0, 0)); + REQUIRE(-77 == matrix2.getValue(4, 4)); + REQUIRE(-29 == matrix2.getValue(2, 2)); + REQUIRE(-11 == matrix2.getValue(2, 0)); + REQUIRE(-11 == matrix2.getValue(0, 2)); + REQUIRE(-38 == matrix2.getValue(3, 2)); + + + REQUIRE(-30 == matrix3.getValue(0, 0)); + REQUIRE(-90 == matrix3.getValue(2, 2)); + REQUIRE(-50 == matrix3.getValue(2, 0)); + REQUIRE(-50 == matrix3.getValue(0, 2)); + REQUIRE(-55 == matrix3.getValue(1, 1)); +} + +TEST_CASE("matrixBase isEqual") +{ + MatrixBase matrix0 = getTestMatrix3x5(); + MatrixBase matrix0_b = getTestMatrix3x5(); + MatrixBase matrix1 = getTestMatrix3x5_b(); + + REQUIRE(true == matrix0.isEqual(matrix0_b)); + REQUIRE(false == matrix0.isEqual(matrix1)); + REQUIRE(true == matrix0.isEqual(matrix0)); +} + +TEST_CASE("matrixBase isSame") +{ + MatrixBase matrix0 = getTestMatrix3x5(); + MatrixBase matrix0_b = getTestMatrix3x5(); + MatrixBase matrix1 = getTestMatrix3x5_b(); + + REQUIRE(false == matrix0.isSame(matrix0_b)); + REQUIRE(false == matrix0.isSame(matrix1)); + REQUIRE(true == matrix0.isSame(matrix0)); +} + +TEST_CASE("matrixBase accessOperator") +{ + MatrixBase matrix0 = getTestMatrix3x5(); + + REQUIRE(0 == matrix0[0][0]); + REQUIRE(4 == matrix0[2][2]); + REQUIRE(6 == matrix0[2][4]); + + matrix0[0][0] = 42; + + REQUIRE(42 == matrix0[0][0]); + REQUIRE(4 == matrix0[2][2]); + REQUIRE(6 == matrix0[2][4]); +} + +TEST_CASE("matrixBase operators") +{ + MatrixBase matrix0 = getTestMatrix3x5(); + MatrixBase matrix0_b = getTestMatrix3x5_b(); + + REQUIRE(0 == matrix0.getValue(0, 0)); + REQUIRE(4 == matrix0.getValue(2, 2)); + REQUIRE(6 == matrix0.getValue(2, 4)); + + REQUIRE(0 == matrix0_b.getValue(0, 0)); + REQUIRE(-4 == matrix0_b.getValue(2, 2)); + REQUIRE(-6 == matrix0_b.getValue(2, 4)); + + MatrixBase matrix1 = matrix0 + matrix0_b; + MatrixBase matrix2 = matrix0 - matrix0_b; + + REQUIRE(0 == matrix1.getValue(0, 0)); + REQUIRE(0 == matrix1.getValue(2, 2)); + REQUIRE(0 == matrix1.getValue(2, 4)); + + REQUIRE(0 == matrix2.getValue(0, 0)); + REQUIRE(8 == matrix2.getValue(2, 2)); + REQUIRE(12 == matrix2.getValue(2, 4)); + + MatrixBase matrix3 = matrix0 * 3; + MatrixBase matrix4 = matrix0 / 2; + MatrixBase matrix5 = matrix0 * 3.3f; // float is on purpose + + REQUIRE(0 == matrix3.getValue(0, 0)); + REQUIRE(12 == matrix3.getValue(2, 2)); + REQUIRE(18 == matrix3.getValue(2, 4)); + + REQUIRE(0 == matrix4.getValue(0, 0)); + REQUIRE(2 == matrix4.getValue(2, 2)); + REQUIRE(3 == matrix4.getValue(2, 4)); + + REQUIRE(0 == matrix5.getValue(0, 0)); + REQUIRE(13 == matrix5.getValue(2, 2)); + REQUIRE(19 == matrix5.getValue(2, 4)); +} + +TEST_CASE("matrixBase assignOperators") +{ + MatrixBase matrix0 = getTestMatrix3x5(); + MatrixBase matrix0_b = getTestMatrix3x5_b(); + + REQUIRE(0 == matrix0.getValue(0, 0)); + REQUIRE(4 == matrix0.getValue(2, 2)); + REQUIRE(6 == matrix0.getValue(2, 4)); + + REQUIRE(0 == matrix0_b.getValue(0, 0)); + REQUIRE(-4 == matrix0_b.getValue(2, 2)); + REQUIRE(-6 == matrix0_b.getValue(2, 4)); + + MatrixBase matrix1 = getTestMatrix3x5(); + matrix1 += matrix0; + + REQUIRE(0 == matrix1.getValue(0, 0)); + REQUIRE(8 == matrix1.getValue(2, 2)); + REQUIRE(12 == matrix1.getValue(2, 4)); + + matrix1 += matrix0_b; + + REQUIRE(0 == matrix1.getValue(0, 0)); + REQUIRE(4 == matrix1.getValue(2, 2)); + REQUIRE(6 == matrix1.getValue(2, 4)); + + matrix1 -= matrix0_b; + + REQUIRE(0 == matrix1.getValue(0, 0)); + REQUIRE(8 == matrix1.getValue(2, 2)); + REQUIRE(12 == matrix1.getValue(2, 4)); + + matrix1 *= 3.3f; + + REQUIRE(0 == matrix1.getValue(0, 0)); + REQUIRE(26 == matrix1.getValue(2, 2)); + REQUIRE(39 == matrix1.getValue(2, 4)); + + matrix1 /= 3; + + REQUIRE(0 == matrix1.getValue(0, 0)); + REQUIRE(8 == matrix1.getValue(2, 2)); + REQUIRE(13 == matrix1.getValue(2, 4)); +} + +TEST_CASE("matrixBase comparisonOperators") +{ + MatrixBase matrix0 = getTestMatrix3x5(); + MatrixBase matrix0_b = getTestMatrix3x5_b(); + MatrixBase matrix1 = getTestMatrix3x5(); + + REQUIRE(0 == matrix0.getValue(0, 0)); + REQUIRE(4 == matrix0.getValue(2, 2)); + REQUIRE(6 == matrix0.getValue(2, 4)); + + REQUIRE(0 == matrix0_b.getValue(0, 0)); + REQUIRE(-4 == matrix0_b.getValue(2, 2)); + REQUIRE(-6 == matrix0_b.getValue(2, 4)); + + REQUIRE(0 == matrix1.getValue(0, 0)); + REQUIRE(4 == matrix1.getValue(2, 2)); + REQUIRE(6 == matrix1.getValue(2, 4)); + + REQUIRE(true == (matrix0 == matrix0)); + REQUIRE(true == (matrix0 == matrix1)); + REQUIRE(true == (matrix0 != matrix0_b)); + + REQUIRE(false == (matrix0 != matrix0)); + REQUIRE(false == (matrix0 != matrix1)); + REQUIRE(false == (matrix0 == matrix0_b)); +} + +TEST_CASE("matrixBase vectorMultiplication") +{ + MatrixBase matrix0 = getTestMatrix3x5(); + VectorBase vector0; + + for(unsigned int i = 0; i < vector0.getDimensions(); i++) + { + vector0.setValue(i, i+1); + } + + VectorBase vector0_r = multiply(matrix0, vector0); + + REQUIRE(8 == vector0_r[0]); + REQUIRE(14 == vector0_r[1]); + REQUIRE(20 == vector0_r[2]); + REQUIRE(26 == vector0_r[3]); + REQUIRE(32 == vector0_r[4]); + + MatrixBase matrix1 = getTestMatrix5x3(); + VectorBase vector1; + + for(unsigned int i = 0; i < vector1.getDimensions(); i++) + { + vector1.setValue(i, i+1); + } + + VectorBase vector1_r = multiply(vector1, matrix1); + + REQUIRE(8 == vector1_r[0]); + REQUIRE(14 == vector1_r[1]); + REQUIRE(20 == vector1_r[2]); + REQUIRE(26 == vector1_r[3]); + REQUIRE(32 == vector1_r[4]); + + MatrixBase matrix2 = getTestMatrix3x5(); + VectorBase vector2; + + for(unsigned int i = 0; i < vector2.getDimensions(); i++) + { + vector2.setValue(i, i+1); + } + + VectorBase vector2_r = multiply(vector2, matrix2); + + REQUIRE(40 == vector2_r[0]); + REQUIRE(55 == vector2_r[1]); + REQUIRE(70 == vector2_r[2]); +} diff --git a/src/test/MatrixBaseTestSuite.h b/src/test/MatrixBaseTestSuite.h deleted file mode 100644 index cd5327f5..00000000 --- a/src/test/MatrixBaseTestSuite.h +++ /dev/null @@ -1,473 +0,0 @@ -#include - -#include "MatrixBase.h" -#include "VectorBase.h" - -class MatrixBaseTestSuite : public CxxTest::TestSuite -{ -public: - void test_matrixBase_constructors() - { - MatrixBase matrix0; - - TS_ASSERT_EQUALS(4, matrix0.getColumnsCount()); - TS_ASSERT_EQUALS(5, matrix0.getRowsCount()); - - Array3x5 testValues = getTestValues3x5(); - - MatrixBase matrix1(testValues.array); - - TS_ASSERT_EQUALS(3, matrix1.getColumnsCount()); - TS_ASSERT_EQUALS(5, matrix1.getRowsCount()); - - TS_ASSERT_EQUALS(0, matrix1.getValue(0, 0)); - TS_ASSERT_EQUALS(6, matrix1.getValue(2, 4)); - - MatrixBase matrix2(matrix1); - - TS_ASSERT_EQUALS(0, matrix2.getValue(0, 0)); - TS_ASSERT_EQUALS(6, matrix2.getValue(2, 4)); - } - - void test_matrixBase_getSetValue() - { - MatrixBase matrix0 = getTestMatrix3x5(); - - int value2_2 = matrix0.getValue(2, 2); - matrix0.setValue(2, 2, value2_2*2); - TS_ASSERT_EQUALS(value2_2*2, matrix0.getValue(2, 2)); - TS_ASSERT_EQUALS(3, matrix0.getValue(1, 2)); - TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0)); - } - - void test_matrixBase_getRowsColumnsCount() - { - MatrixBase matrix0 = getTestMatrix3x5(); - MatrixBase matrix1 = getTestMatrix5x3(); - - TS_ASSERT_EQUALS(3, matrix0.getColumnsCount()); - TS_ASSERT_EQUALS(5, matrix0.getRowsCount()); - - TS_ASSERT_EQUALS(5, matrix1.getColumnsCount()); - TS_ASSERT_EQUALS(3, matrix1.getRowsCount()); - } - - void test_matrixBase_transposed() - { - MatrixBase matrix0 = getTestMatrix3x5(); - TS_ASSERT_EQUALS(3, matrix0.getColumnsCount()); - TS_ASSERT_EQUALS(5, matrix0.getRowsCount()); - - MatrixBase matrix1 = matrix0.transposed(); - TS_ASSERT_EQUALS(5, matrix1.getColumnsCount()); - TS_ASSERT_EQUALS(3, matrix1.getRowsCount()); - - TS_ASSERT_EQUALS(matrix0.getValue(0, 0), matrix1.getValue(0, 0)); - TS_ASSERT_EQUALS(matrix0.getValue(0, 1), matrix1.getValue(1, 0)); - TS_ASSERT_EQUALS(matrix0.getValue(0, 4), matrix1.getValue(4, 0)); - TS_ASSERT_EQUALS(matrix0.getValue(1, 4), matrix1.getValue(4, 1)); - TS_ASSERT_EQUALS(matrix0.getValue(2, 4), matrix1.getValue(4, 2)); - TS_ASSERT_EQUALS(matrix0.getValue(0, 3), matrix1.getValue(3, 0)); - TS_ASSERT_EQUALS(matrix0.getValue(1, 3), matrix1.getValue(3, 1)); - TS_ASSERT_EQUALS(matrix0.getValue(2, 3), matrix1.getValue(3, 2)); - } - - void test_matrixBase_assign() - { - MatrixBase matrix0 = getTestMatrix3x5(); - MatrixBase matrix1 = getTestMatrix3x5_b(); - - TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0)); - TS_ASSERT_EQUALS(2, matrix0.getValue(1, 1)); - TS_ASSERT_EQUALS(4, matrix0.getValue(2, 2)); - - TS_ASSERT_EQUALS(0, matrix1.getValue(0, 0)); - TS_ASSERT_EQUALS(-2, matrix1.getValue(1, 1)); - TS_ASSERT_EQUALS(-4, matrix1.getValue(2, 2)); - - matrix0.assign(matrix1); - - TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0)); - TS_ASSERT_EQUALS(-2, matrix0.getValue(1, 1)); - TS_ASSERT_EQUALS(-4, matrix0.getValue(2, 2)); - } - - void test_matrixBase_add_subtract() - { - MatrixBase matrix0 = getTestMatrix3x5(); - MatrixBase matrix1 = getTestMatrix3x5_b(); - - matrix0.add(matrix1); - - TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0)); - TS_ASSERT_EQUALS(0, matrix0.getValue(1, 1)); - TS_ASSERT_EQUALS(0, matrix0.getValue(2, 2)); - - matrix0.subtract(matrix1); - - TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0)); - TS_ASSERT_EQUALS(2, matrix0.getValue(1, 1)); - TS_ASSERT_EQUALS(4, matrix0.getValue(2, 2)); - } - - void test_matrixBase_multiplyDivideScalar() - { - MatrixBase matrix0 = getTestMatrix3x5(); - - matrix0.scalarMultiplication(2.0f); // float is on porpoise (so is porpoise, womp womp) - - TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0)); - TS_ASSERT_EQUALS(4, matrix0.getValue(1, 1)); - TS_ASSERT_EQUALS(8, matrix0.getValue(2, 2)); - - matrix0.scalarMultiplication(0.5f); - - TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0)); - TS_ASSERT_EQUALS(2, matrix0.getValue(1, 1)); - TS_ASSERT_EQUALS(4, matrix0.getValue(2, 2)); - - matrix0.scalarMultiplication(0.5f); - - TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0)); - TS_ASSERT_EQUALS(1, matrix0.getValue(1, 1)); - TS_ASSERT_EQUALS(2, matrix0.getValue(2, 2)); - - matrix0.scalarMultiplication(0.5f); - - TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0)); - TS_ASSERT_EQUALS(0, matrix0.getValue(1, 1)); - TS_ASSERT_EQUALS(1, matrix0.getValue(2, 2)); - } - - void test_matrixBase_multiplyMatrix() - { - MatrixBase matrix0 = getTestMatrix3x5(); - MatrixBase matrix1 = getTestMatrix3x5_b(); - MatrixBase matrix1t = matrix1.transposed(); - - MatrixBase matrix2 = matrix0.matrixMultiplication(matrix1t); - MatrixBase matrix3 = matrix1t.matrixMultiplication(matrix0); - - // expected results - // matrix0 * matrix1t - /** - * -5, -8, -11, -14, -17 - * -8, -14, -20, -26, -32 - * -11, -20, -29, -38, -47 - * -14, -26, -38, -50, -62 - * -17, -32, -47, -62, -77 - */ - - // matrix1t * matrix0 - /** - * -30, -40, -50 - * -40, -55, -70 - * -50, -70, -90 - */ - - - TS_ASSERT_EQUALS(5, matrix2.getColumnsCount()); - TS_ASSERT_EQUALS(5, matrix2.getRowsCount()); - - TS_ASSERT_EQUALS(3, matrix3.getColumnsCount()); - TS_ASSERT_EQUALS(3, matrix3.getRowsCount()); - - TS_ASSERT_EQUALS(-5, matrix2.getValue(0, 0)); - TS_ASSERT_EQUALS(-77, matrix2.getValue(4, 4)); - TS_ASSERT_EQUALS(-29, matrix2.getValue(2, 2)); - TS_ASSERT_EQUALS(-11, matrix2.getValue(2, 0)); - TS_ASSERT_EQUALS(-11, matrix2.getValue(0, 2)); - TS_ASSERT_EQUALS(-38, matrix2.getValue(3, 2)); - - - TS_ASSERT_EQUALS(-30, matrix3.getValue(0, 0)); - TS_ASSERT_EQUALS(-90, matrix3.getValue(2, 2)); - TS_ASSERT_EQUALS(-50, matrix3.getValue(2, 0)); - TS_ASSERT_EQUALS(-50, matrix3.getValue(0, 2)); - TS_ASSERT_EQUALS(-55, matrix3.getValue(1, 1)); - } - - void test_matrixBase_isEqual() - { - MatrixBase matrix0 = getTestMatrix3x5(); - MatrixBase matrix0_b = getTestMatrix3x5(); - MatrixBase matrix1 = getTestMatrix3x5_b(); - - TS_ASSERT_EQUALS(true, matrix0.isEqual(matrix0_b)); - TS_ASSERT_EQUALS(false, matrix0.isEqual(matrix1)); - TS_ASSERT_EQUALS(true, matrix0.isEqual(matrix0)); - } - - void test_matrixBase_isSame() - { - MatrixBase matrix0 = getTestMatrix3x5(); - MatrixBase matrix0_b = getTestMatrix3x5(); - MatrixBase matrix1 = getTestMatrix3x5_b(); - - TS_ASSERT_EQUALS(false, matrix0.isSame(matrix0_b)); - TS_ASSERT_EQUALS(false, matrix0.isSame(matrix1)); - TS_ASSERT_EQUALS(true, matrix0.isSame(matrix0)); - } - - void test_matrixBase_accessOperator() - { - MatrixBase matrix0 = getTestMatrix3x5(); - - TS_ASSERT_EQUALS(0, matrix0[0][0]); - TS_ASSERT_EQUALS(4, matrix0[2][2]); - TS_ASSERT_EQUALS(6, matrix0[2][4]); - - matrix0[0][0] = 42; - - TS_ASSERT_EQUALS(42, matrix0[0][0]); - TS_ASSERT_EQUALS(4, matrix0[2][2]); - TS_ASSERT_EQUALS(6, matrix0[2][4]); - } - - void test_matrixBase_operators() - { - MatrixBase matrix0 = getTestMatrix3x5(); - MatrixBase matrix0_b = getTestMatrix3x5_b(); - - TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0)); - TS_ASSERT_EQUALS(4, matrix0.getValue(2, 2)); - TS_ASSERT_EQUALS(6, matrix0.getValue(2, 4)); - - TS_ASSERT_EQUALS(0, matrix0_b.getValue(0, 0)); - TS_ASSERT_EQUALS(-4, matrix0_b.getValue(2, 2)); - TS_ASSERT_EQUALS(-6, matrix0_b.getValue(2, 4)); - - MatrixBase matrix1 = matrix0 + matrix0_b; - MatrixBase matrix2 = matrix0 - matrix0_b; - - TS_ASSERT_EQUALS(0, matrix1.getValue(0, 0)); - TS_ASSERT_EQUALS(0, matrix1.getValue(2, 2)); - TS_ASSERT_EQUALS(0, matrix1.getValue(2, 4)); - - TS_ASSERT_EQUALS(0, matrix2.getValue(0, 0)); - TS_ASSERT_EQUALS(8, matrix2.getValue(2, 2)); - TS_ASSERT_EQUALS(12, matrix2.getValue(2, 4)); - - MatrixBase matrix3 = matrix0 * 3; - MatrixBase matrix4 = matrix0 / 2; - MatrixBase matrix5 = matrix0 * 3.3f; // float is on purpose - - TS_ASSERT_EQUALS(0, matrix3.getValue(0, 0)); - TS_ASSERT_EQUALS(12, matrix3.getValue(2, 2)); - TS_ASSERT_EQUALS(18, matrix3.getValue(2, 4)); - - TS_ASSERT_EQUALS(0, matrix4.getValue(0, 0)); - TS_ASSERT_EQUALS(2, matrix4.getValue(2, 2)); - TS_ASSERT_EQUALS(3, matrix4.getValue(2, 4)); - - TS_ASSERT_EQUALS(0, matrix5.getValue(0, 0)); - TS_ASSERT_EQUALS(13, matrix5.getValue(2, 2)); - TS_ASSERT_EQUALS(19, matrix5.getValue(2, 4)); - } - - void test_matrixBase_assignOperators() - { - MatrixBase matrix0 = getTestMatrix3x5(); - MatrixBase matrix0_b = getTestMatrix3x5_b(); - - TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0)); - TS_ASSERT_EQUALS(4, matrix0.getValue(2, 2)); - TS_ASSERT_EQUALS(6, matrix0.getValue(2, 4)); - - TS_ASSERT_EQUALS(0, matrix0_b.getValue(0, 0)); - TS_ASSERT_EQUALS(-4, matrix0_b.getValue(2, 2)); - TS_ASSERT_EQUALS(-6, matrix0_b.getValue(2, 4)); - - MatrixBase matrix1 = getTestMatrix3x5(); - matrix1 += matrix0; - - TS_ASSERT_EQUALS(0, matrix1.getValue(0, 0)); - TS_ASSERT_EQUALS(8, matrix1.getValue(2, 2)); - TS_ASSERT_EQUALS(12, matrix1.getValue(2, 4)); - - matrix1 += matrix0_b; - - TS_ASSERT_EQUALS(0, matrix1.getValue(0, 0)); - TS_ASSERT_EQUALS(4, matrix1.getValue(2, 2)); - TS_ASSERT_EQUALS(6, matrix1.getValue(2, 4)); - - matrix1 -= matrix0_b; - - TS_ASSERT_EQUALS(0, matrix1.getValue(0, 0)); - TS_ASSERT_EQUALS(8, matrix1.getValue(2, 2)); - TS_ASSERT_EQUALS(12, matrix1.getValue(2, 4)); - - matrix1 *= 3.3f; - - TS_ASSERT_EQUALS(0, matrix1.getValue(0, 0)); - TS_ASSERT_EQUALS(26, matrix1.getValue(2, 2)); - TS_ASSERT_EQUALS(39, matrix1.getValue(2, 4)); - - matrix1 /= 3; - - TS_ASSERT_EQUALS(0, matrix1.getValue(0, 0)); - TS_ASSERT_EQUALS(8, matrix1.getValue(2, 2)); - TS_ASSERT_EQUALS(13, matrix1.getValue(2, 4)); - } - - void test_matrixBase_comparisonOperators() - { - MatrixBase matrix0 = getTestMatrix3x5(); - MatrixBase matrix0_b = getTestMatrix3x5_b(); - MatrixBase matrix1 = getTestMatrix3x5(); - - TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0)); - TS_ASSERT_EQUALS(4, matrix0.getValue(2, 2)); - TS_ASSERT_EQUALS(6, matrix0.getValue(2, 4)); - - TS_ASSERT_EQUALS(0, matrix0_b.getValue(0, 0)); - TS_ASSERT_EQUALS(-4, matrix0_b.getValue(2, 2)); - TS_ASSERT_EQUALS(-6, matrix0_b.getValue(2, 4)); - - TS_ASSERT_EQUALS(0, matrix1.getValue(0, 0)); - TS_ASSERT_EQUALS(4, matrix1.getValue(2, 2)); - TS_ASSERT_EQUALS(6, matrix1.getValue(2, 4)); - - TS_ASSERT_EQUALS(true, matrix0 == matrix0); - TS_ASSERT_EQUALS(true, matrix0 == matrix1); - TS_ASSERT_EQUALS(true, matrix0 != matrix0_b); - - TS_ASSERT_EQUALS(false, matrix0 != matrix0); - TS_ASSERT_EQUALS(false, matrix0 != matrix1); - TS_ASSERT_EQUALS(false, matrix0 == matrix0_b); - } - - void test_matrixBase_vectorMultiplication() - { - MatrixBase matrix0 = getTestMatrix3x5(); - VectorBase vector0; - - for(unsigned int i = 0; i < vector0.getDimensions(); i++) - { - vector0.setValue(i, i+1); - } - - VectorBase vector0_r = multiply(matrix0, vector0); - - TS_ASSERT_EQUALS(8, vector0_r[0]); - TS_ASSERT_EQUALS(14, vector0_r[1]); - TS_ASSERT_EQUALS(20, vector0_r[2]); - TS_ASSERT_EQUALS(26, vector0_r[3]); - TS_ASSERT_EQUALS(32, vector0_r[4]); - - MatrixBase matrix1 = getTestMatrix5x3(); - VectorBase vector1; - - for(unsigned int i = 0; i < vector1.getDimensions(); i++) - { - vector1.setValue(i, i+1); - } - - VectorBase vector1_r = multiply(vector1, matrix1); - - TS_ASSERT_EQUALS(8, vector1_r[0]); - TS_ASSERT_EQUALS(14, vector1_r[1]); - TS_ASSERT_EQUALS(20, vector1_r[2]); - TS_ASSERT_EQUALS(26, vector1_r[3]); - TS_ASSERT_EQUALS(32, vector1_r[4]); - - MatrixBase matrix2 = getTestMatrix3x5(); - VectorBase vector2; - - for(unsigned int i = 0; i < vector2.getDimensions(); i++) - { - vector2.setValue(i, i+1); - } - - VectorBase vector2_r = multiply(vector2, matrix2); - - TS_ASSERT_EQUALS(40, vector2_r[0]); - TS_ASSERT_EQUALS(55, vector2_r[1]); - TS_ASSERT_EQUALS(70, vector2_r[2]); - } - -private: - /** - * C++ functions can't return statically allocated arrays. - * I don't want to use dynamically allocated arrays, so here's my work around for that... - * - * Update: acutally they can... see MatrixBase [] operator (in MatrixBase.cpp) - */ - template - struct Array3x5 - { - T array[3][5]; - }; - - template - struct Array5x3 - { - T array[5][3]; - }; - - Array3x5 getTestValues3x5() - { - Array3x5 result; - - for(unsigned int i = 0; i < 3; i++) - { - for(unsigned int j = 0; j < 5; j++) - { - result.array[i][j] = i + j; - } - } - - return result; - } - - Array3x5 getTestValues3x5_b() - { - Array3x5 result; - - for(unsigned int i = 0; i < 3; i++) - { - for(unsigned int j = 0; j < 5; j++) - { - result.array[i][j] = -i - j; - } - } - - return result; - } - - Array5x3 getTestValues5x3() - { - Array5x3 result; - - for(unsigned int i = 0; i < 5; i++) - { - for(unsigned int j = 0; j < 3; j++) - { - result.array[i][j] = i + j; - } - } - - return result; - } - - MatrixBase getTestMatrix3x5() - { - Array3x5 testValues = getTestValues3x5(); - - return MatrixBase(testValues.array); - } - - MatrixBase getTestMatrix3x5_b() - { - Array3x5 testValues = getTestValues3x5_b(); - - return MatrixBase(testValues.array); - } - - MatrixBase getTestMatrix5x3() - { - Array5x3 testValues = getTestValues5x3(); - - return MatrixBase(testValues.array); - } -}; \ No newline at end of file diff --git a/src/test/MatrixDynamicBaseTestSuite.cpp b/src/test/MatrixDynamicBaseTestSuite.cpp new file mode 100644 index 00000000..92ca94da --- /dev/null +++ b/src/test/MatrixDynamicBaseTestSuite.cpp @@ -0,0 +1,60 @@ +#include "catch.hpp" + +#include "logging.h" +#include "MatrixDynamicBase.h" + +namespace +{ + std::vector> getTestValues(const unsigned int numColumns, const unsigned int numRows) + { + std::vector> testValues; + + for (unsigned int x = 0; x < numColumns; x++) + { + std::vector row; + + for (unsigned int y = 0; y < numRows; y++) + { + row.push_back(x + y); + } + + testValues.push_back(row); + } + + return testValues; + } +} + +TEST_CASE("matrixDynamicBase constructors") +{ + MatrixDynamicBase matrix0; + MatrixDynamicBase matrix1(3, 5); + + std::vector> testValues = getTestValues(3, 5); + MatrixDynamicBase matrix2(testValues); + + REQUIRE(0 == matrix0.getColumnsCount()); + REQUIRE(0 == matrix0.getRowsCount()); + REQUIRE(3 == matrix1.getColumnsCount()); + REQUIRE(5 == matrix1.getRowsCount()); + REQUIRE(3 == matrix2.getColumnsCount()); + REQUIRE(5 == matrix2.getRowsCount()); +} + +TEST_CASE("matrixDynamicBase getValue setValue") +{ + std::vector> testValues = getTestValues(3, 5); + MatrixDynamicBase matrix0(testValues); + + REQUIRE(0 == matrix0.getValue(0, 0)); + REQUIRE(4 == matrix0.getValue(2, 2)); + REQUIRE(6 == matrix0.getValue(2, 4)); + + matrix0.setValue(0, 0, 42); + matrix0.setValue(2, 2, 84); + matrix0.setValue(2, 4, 126); + + REQUIRE(42 == matrix0.getValue(0, 0)); + REQUIRE(84 == matrix0.getValue(2, 2)); + REQUIRE(126 == matrix0.getValue(2, 4)); +} diff --git a/src/test/MatrixDynamicBaseTestSuite.h b/src/test/MatrixDynamicBaseTestSuite.h deleted file mode 100644 index 8d3fc371..00000000 --- a/src/test/MatrixDynamicBaseTestSuite.h +++ /dev/null @@ -1,62 +0,0 @@ -#include "TestSuite.h" - -#include "logging.h" -#include "MatrixDynamicBase.h" - -class MatrixDynamicBaseTestSuite : public CxxTest::TestSuite -{ -public: - void test_matrixDynamicBase_constructors() - { - MatrixDynamicBase matrix0; - MatrixDynamicBase matrix1(3, 5); - - std::vector> testValues = getTestValues(3, 5); - MatrixDynamicBase matrix2(testValues); - - TS_ASSERT_EQUALS(0, matrix0.getColumnsCount()); - TS_ASSERT_EQUALS(0, matrix0.getRowsCount()); - TS_ASSERT_EQUALS(3, matrix1.getColumnsCount()); - TS_ASSERT_EQUALS(5, matrix1.getRowsCount()); - TS_ASSERT_EQUALS(3, matrix2.getColumnsCount()); - TS_ASSERT_EQUALS(5, matrix2.getRowsCount()); - } - - void test_matrixDynamicBase_getValue_setValue() - { - std::vector> testValues = getTestValues(3, 5); - MatrixDynamicBase matrix0(testValues); - - TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0)); - TS_ASSERT_EQUALS(4, matrix0.getValue(2, 2)); - TS_ASSERT_EQUALS(6, matrix0.getValue(2, 4)); - - matrix0.setValue(0, 0, 42); - matrix0.setValue(2, 2, 84); - matrix0.setValue(2, 4, 126); - - TS_ASSERT_EQUALS(42, matrix0.getValue(0, 0)); - TS_ASSERT_EQUALS(84, matrix0.getValue(2, 2)); - TS_ASSERT_EQUALS(126, matrix0.getValue(2, 4)); - } - -private: - std::vector> getTestValues(const unsigned int numColumns, const unsigned int numRows) - { - std::vector> testValues; - - for(unsigned int x = 0; x < numColumns; x++) - { - std::vector row; - - for(unsigned int y = 0; y < numRows; y++) - { - row.push_back(x + y); - } - - testValues.push_back(row); - } - - return testValues; - } -}; \ No newline at end of file diff --git a/src/test/MessageQueueTestSuite.cpp b/src/test/MessageQueueTestSuite.cpp new file mode 100644 index 00000000..4890ce11 --- /dev/null +++ b/src/test/MessageQueueTestSuite.cpp @@ -0,0 +1,246 @@ +#include "catch.hpp" + +#include +#include + +#include "Message.h" +#include "MessageListener.h" +#include "MessageQueue.h" + +namespace +{ + class TestMessage : public Message + { + public: + static const std::string getStaticType() + { + return "TestMessage"; + } + }; + + class Test2Message : public Message + { + public: + static const std::string getStaticType() + { + return "TestMessage2"; + } + }; + + class TestMessageListener : public MessageListener + { + public: + TestMessageListener() + : m_messageCount(0) + { + } + + int m_messageCount; + + private: + virtual void handleMessage(TestMessage* message) + { + m_messageCount++; + } + }; + + class Test2MessageListener : public MessageListener + { + public: + Test2MessageListener() + : m_messageCount(0) + { + } + + int m_messageCount; + + private: + virtual void handleMessage(Test2Message* message) + { + m_messageCount++; + TestMessage().dispatch(); + } + }; + + class Test3MessageListener : public MessageListener + { + public: + std::shared_ptr m_listener; + + private: + virtual void handleMessage(Test2Message* message) + { + m_listener = std::make_shared(); + } + }; + + class Test4MessageListener : + public MessageListener, + public MessageListener + { + public: + std::shared_ptr m_listener; + + private: + virtual void handleMessage(TestMessage* message) + { + if (!m_listener) + { + m_listener = std::make_shared(); + } + } + + virtual void handleMessage(Test2Message* message) + { + m_listener.reset(); + } + }; + + class Test5MessageListener : + public MessageListener + { + public: + std::vector> m_listeners; + + private: + virtual void handleMessage(TestMessage* message) + { + if (!m_listeners.size()) + { + for (size_t i = 0; i < 5; i++) + { + m_listeners.push_back(std::make_shared()); + } + } + } + }; + + void waitForThread() + { + static const int THREAD_WAIT_TIME_MS = 20; + do + { + std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_WAIT_TIME_MS)); + } while (MessageQueue::getInstance()->hasMessagesQueued()); + } +} + +TEST_CASE("message loop starts and stops") +{ + REQUIRE(!MessageQueue::getInstance()->loopIsRunning()); + + MessageQueue::getInstance()->startMessageLoopThreaded(); + + waitForThread(); + + REQUIRE(MessageQueue::getInstance()->loopIsRunning()); + + MessageQueue::getInstance()->stopMessageLoop(); + + waitForThread(); + + REQUIRE(!MessageQueue::getInstance()->loopIsRunning()); +} + +TEST_CASE("registered listener receives messages") +{ + MessageQueue::getInstance()->startMessageLoopThreaded(); + + TestMessageListener listener; + Test2MessageListener listener2; + + TestMessage().dispatch(); + TestMessage().dispatch(); + TestMessage().dispatch(); + + waitForThread(); + + MessageQueue::getInstance()->stopMessageLoop(); + + REQUIRE(3 == listener.m_messageCount); + REQUIRE(0 == listener2.m_messageCount); +} + +TEST_CASE("message dispatching within message handling") +{ + MessageQueue::getInstance()->startMessageLoopThreaded(); + + TestMessageListener listener; + Test2MessageListener listener2; + + Test2Message().dispatch(); + + waitForThread(); + + MessageQueue::getInstance()->stopMessageLoop(); + + REQUIRE(1 == listener.m_messageCount); + REQUIRE(1 == listener2.m_messageCount); +} + +TEST_CASE("listener registration within message handling") +{ + MessageQueue::getInstance()->startMessageLoopThreaded(); + + Test3MessageListener listener; + + Test2Message().dispatch(); + TestMessage().dispatch(); + + waitForThread(); + + MessageQueue::getInstance()->stopMessageLoop(); + + REQUIRE(listener.m_listener); + if (listener.m_listener) + { + REQUIRE(1 == listener.m_listener->m_messageCount); + } +} + +TEST_CASE("listener unregistration within message handling") +{ + MessageQueue::getInstance()->startMessageLoopThreaded(); + + Test4MessageListener listener; + + TestMessage().dispatch(); + + Test2Message().dispatch(); + + TestMessage().dispatch(); + TestMessage().dispatch(); + TestMessage().dispatch(); + + waitForThread(); + + MessageQueue::getInstance()->stopMessageLoop(); + + REQUIRE(listener.m_listener); + if (listener.m_listener) + { + REQUIRE(2 == listener.m_listener->m_messageCount); + } +} + +TEST_CASE("listener registration to front and back within message handling") +{ + MessageQueue::getInstance()->startMessageLoopThreaded(); + + Test5MessageListener listener; + + TestMessage().dispatch(); + TestMessage().dispatch(); + TestMessage().dispatch(); + + waitForThread(); + + MessageQueue::getInstance()->stopMessageLoop(); + + REQUIRE(5 == listener.m_listeners.size()); + REQUIRE(2 == listener.m_listeners[0]->m_messageCount); + REQUIRE(2 == listener.m_listeners[1]->m_messageCount); + REQUIRE(2 == listener.m_listeners[2]->m_messageCount); + REQUIRE(2 == listener.m_listeners[3]->m_messageCount); + REQUIRE(2 == listener.m_listeners[4]->m_messageCount); +} diff --git a/src/test/MessageQueueTestSuite.h b/src/test/MessageQueueTestSuite.h deleted file mode 100644 index 7d642ccb..00000000 --- a/src/test/MessageQueueTestSuite.h +++ /dev/null @@ -1,249 +0,0 @@ -#include - -#include -#include - -#include "Message.h" -#include "MessageListener.h" -#include "MessageQueue.h" - -class MessageQueueTestSuite: public CxxTest::TestSuite -{ -public: - void test_message_loop_starts_and_stops(void) - { - TS_ASSERT(!MessageQueue::getInstance()->loopIsRunning()); - - MessageQueue::getInstance()->startMessageLoopThreaded(); - - waitForThread(); - - TS_ASSERT(MessageQueue::getInstance()->loopIsRunning()); - - MessageQueue::getInstance()->stopMessageLoop(); - - waitForThread(); - - TS_ASSERT(!MessageQueue::getInstance()->loopIsRunning()); - } - - void test_registered_listener_receives_messages(void) - { - MessageQueue::getInstance()->startMessageLoopThreaded(); - - TestMessageListener listener; - Test2MessageListener listener2; - - TestMessage().dispatch(); - TestMessage().dispatch(); - TestMessage().dispatch(); - - waitForThread(); - - MessageQueue::getInstance()->stopMessageLoop(); - - TS_ASSERT_EQUALS(3, listener.m_messageCount); - TS_ASSERT_EQUALS(0, listener2.m_messageCount); - } - - void test_message_dispatching_within_message_handling(void) - { - MessageQueue::getInstance()->startMessageLoopThreaded(); - - TestMessageListener listener; - Test2MessageListener listener2; - - Test2Message().dispatch(); - - waitForThread(); - - MessageQueue::getInstance()->stopMessageLoop(); - - TS_ASSERT_EQUALS(1, listener.m_messageCount); - TS_ASSERT_EQUALS(1, listener2.m_messageCount); - } - - void test_listener_registration_within_message_handling(void) - { - MessageQueue::getInstance()->startMessageLoopThreaded(); - - Test3MessageListener listener; - - Test2Message().dispatch(); - TestMessage().dispatch(); - - waitForThread(); - - MessageQueue::getInstance()->stopMessageLoop(); - - TS_ASSERT(listener.m_listener); - if (listener.m_listener) - { - TS_ASSERT_EQUALS(1, listener.m_listener->m_messageCount); - } - } - - void test_listener_unregistration_within_message_handling(void) - { - MessageQueue::getInstance()->startMessageLoopThreaded(); - - Test4MessageListener listener; - - TestMessage().dispatch(); - - Test2Message().dispatch(); - - TestMessage().dispatch(); - TestMessage().dispatch(); - TestMessage().dispatch(); - - waitForThread(); - - MessageQueue::getInstance()->stopMessageLoop(); - - TS_ASSERT(listener.m_listener); - if (listener.m_listener) - { - TS_ASSERT_EQUALS(2, listener.m_listener->m_messageCount); - } - } - - void test_listener_registration_to_front_and_back_within_message_handling(void) - { - MessageQueue::getInstance()->startMessageLoopThreaded(); - - Test5MessageListener listener; - - TestMessage().dispatch(); - TestMessage().dispatch(); - TestMessage().dispatch(); - - waitForThread(); - - MessageQueue::getInstance()->stopMessageLoop(); - - TS_ASSERT_EQUALS(5, listener.m_listeners.size()); - TS_ASSERT_EQUALS(2, listener.m_listeners[0]->m_messageCount); - TS_ASSERT_EQUALS(2, listener.m_listeners[1]->m_messageCount); - TS_ASSERT_EQUALS(2, listener.m_listeners[2]->m_messageCount); - TS_ASSERT_EQUALS(2, listener.m_listeners[3]->m_messageCount); - TS_ASSERT_EQUALS(2, listener.m_listeners[4]->m_messageCount); - } - -private: - class TestMessage: public Message - { - public: - static const std::string getStaticType() - { - return "TestMessage"; - } - }; - - class Test2Message: public Message - { - public: - static const std::string getStaticType() - { - return "TestMessage2"; - } - }; - - class TestMessageListener: public MessageListener - { - public: - TestMessageListener() - : m_messageCount(0) - { - } - - int m_messageCount; - - private: - virtual void handleMessage(TestMessage* message) - { - m_messageCount++; - } - }; - - class Test2MessageListener: public MessageListener - { - public: - Test2MessageListener() - : m_messageCount(0) - { - } - - int m_messageCount; - - private: - virtual void handleMessage(Test2Message* message) - { - m_messageCount++; - TestMessage().dispatch(); - } - }; - - class Test3MessageListener: public MessageListener - { - public: - std::shared_ptr m_listener; - - private: - virtual void handleMessage(Test2Message* message) - { - m_listener = std::make_shared(); - } - }; - - class Test4MessageListener: - public MessageListener, - public MessageListener - { - public: - std::shared_ptr m_listener; - - private: - virtual void handleMessage(TestMessage* message) - { - if (!m_listener) - { - m_listener = std::make_shared(); - } - } - - virtual void handleMessage(Test2Message* message) - { - m_listener.reset(); - } - }; - - class Test5MessageListener: - public MessageListener - { - public: - std::vector> m_listeners; - - private: - virtual void handleMessage(TestMessage* message) - { - if (!m_listeners.size()) - { - for (size_t i = 0; i < 5; i++) - { - m_listeners.push_back(std::make_shared()); - } - } - } - }; - - void waitForThread() const - { - static const int THREAD_WAIT_TIME_MS = 20; - do - { - std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_WAIT_TIME_MS)); - } - while (MessageQueue::getInstance()->hasMessagesQueued()); - } -}; diff --git a/src/test/NetworkProtocolHelperTestSuite.cpp b/src/test/NetworkProtocolHelperTestSuite.cpp new file mode 100644 index 00000000..b37b068a --- /dev/null +++ b/src/test/NetworkProtocolHelperTestSuite.cpp @@ -0,0 +1,66 @@ +#include "catch.hpp" + +#include + +#include "NetworkProtocolHelper.h" + +TEST_CASE("parse message") +{ + std::wstring type = L"setActiveToken"; + std::wstring divider = L">>"; + std::wstring filePath = L"C:/Users/Manuel/imporant/file/location/fileName.cpp"; + std::wstring endOfMessageToken = L""; + int row = 1; + int column = 2; + + // valid message + std::wstringstream message; + message << type << divider << filePath << divider << row << divider << column << endOfMessageToken; + + NetworkProtocolHelper::SetActiveTokenMessage networkMessage = NetworkProtocolHelper::parseSetActiveTokenMessage(message.str()); + + REQUIRE(networkMessage.filePath.wstr() == filePath); + REQUIRE(networkMessage.row == row); + REQUIRE(networkMessage.column == column); + REQUIRE(networkMessage.valid == true); + + // invalid type + message.str(L""); + message << L"foo" << divider << filePath << divider << row << divider << column << endOfMessageToken; + networkMessage = NetworkProtocolHelper::parseSetActiveTokenMessage(message.str()); + + REQUIRE(networkMessage.filePath.wstr() == L""); + REQUIRE(networkMessage.row == 0); + REQUIRE(networkMessage.column == 0); + REQUIRE(networkMessage.valid == false); + + // missing divider + message.str(L""); + message << type << divider << filePath << row << divider << column << endOfMessageToken; + networkMessage = NetworkProtocolHelper::parseSetActiveTokenMessage(message.str()); + + REQUIRE(networkMessage.filePath.wstr() == L""); + REQUIRE(networkMessage.row == 0); + REQUIRE(networkMessage.column == 0); + REQUIRE(networkMessage.valid == false); + + // invalid row + message.str(L""); + message << type << divider << filePath << divider << "potato" << divider << column << endOfMessageToken; + networkMessage = NetworkProtocolHelper::parseSetActiveTokenMessage(message.str()); + + REQUIRE(networkMessage.filePath.wstr() == L""); + REQUIRE(networkMessage.row == 0); + REQUIRE(networkMessage.column == 0); + REQUIRE(networkMessage.valid == false); + + // invalid column + message.str(L""); + message << type << divider << filePath << divider << row << divider << "laz0r" << endOfMessageToken; + networkMessage = NetworkProtocolHelper::parseSetActiveTokenMessage(message.str()); + + REQUIRE(networkMessage.filePath.wstr() == L""); + REQUIRE(networkMessage.row == 0); + REQUIRE(networkMessage.column == 0); + REQUIRE(networkMessage.valid == false); +} diff --git a/src/test/NetworkProtocolHelperTestSuite.h b/src/test/NetworkProtocolHelperTestSuite.h deleted file mode 100644 index 5ad65799..00000000 --- a/src/test/NetworkProtocolHelperTestSuite.h +++ /dev/null @@ -1,68 +0,0 @@ -#include - -#include "NetworkProtocolHelper.h" - -class NetworkProtocolHelperTestSuite : public CxxTest::TestSuite -{ -public: - void test_parse_message(void) - { - std::wstring type = L"setActiveToken"; - std::wstring divider = L">>"; - std::wstring filePath = L"C:/Users/Manuel/imporant/file/location/fileName.cpp"; - std::wstring endOfMessageToken = L""; - int row = 1; - int column = 2; - - // valid message - std::wstringstream message; - message << type << divider << filePath << divider << row << divider << column << endOfMessageToken; - - NetworkProtocolHelper::SetActiveTokenMessage networkMessage = NetworkProtocolHelper::parseSetActiveTokenMessage(message.str()); - - TS_ASSERT_EQUALS(networkMessage.filePath.wstr(), filePath); - TS_ASSERT_EQUALS(networkMessage.row, row); - TS_ASSERT_EQUALS(networkMessage.column, column); - TS_ASSERT_EQUALS(networkMessage.valid, true); - - // invalid type - message.str(L""); - message << L"foo" << divider << filePath << divider << row << divider << column << endOfMessageToken; - networkMessage = NetworkProtocolHelper::parseSetActiveTokenMessage(message.str()); - - TS_ASSERT_EQUALS(networkMessage.filePath.wstr(), L""); - TS_ASSERT_EQUALS(networkMessage.row, 0); - TS_ASSERT_EQUALS(networkMessage.column, 0); - TS_ASSERT_EQUALS(networkMessage.valid, false); - - // missing divider - message.str(L""); - message << type << divider << filePath << row << divider << column << endOfMessageToken; - networkMessage = NetworkProtocolHelper::parseSetActiveTokenMessage(message.str()); - - TS_ASSERT_EQUALS(networkMessage.filePath.wstr(), L""); - TS_ASSERT_EQUALS(networkMessage.row, 0); - TS_ASSERT_EQUALS(networkMessage.column, 0); - TS_ASSERT_EQUALS(networkMessage.valid, false); - - // invalid row - message.str(L""); - message << type << divider << filePath << divider << "potato" << divider << column << endOfMessageToken; - networkMessage = NetworkProtocolHelper::parseSetActiveTokenMessage(message.str()); - - TS_ASSERT_EQUALS(networkMessage.filePath.wstr(), L""); - TS_ASSERT_EQUALS(networkMessage.row, 0); - TS_ASSERT_EQUALS(networkMessage.column, 0); - TS_ASSERT_EQUALS(networkMessage.valid, false); - - // invalid column - message.str(L""); - message << type << divider << filePath << divider << row << divider << "laz0r" << endOfMessageToken; - networkMessage = NetworkProtocolHelper::parseSetActiveTokenMessage(message.str()); - - TS_ASSERT_EQUALS(networkMessage.filePath.wstr(), L""); - TS_ASSERT_EQUALS(networkMessage.row, 0); - TS_ASSERT_EQUALS(networkMessage.column, 0); - TS_ASSERT_EQUALS(networkMessage.valid, false); - } -}; \ No newline at end of file diff --git a/src/test/RefreshInfoGeneratorTestSuite.cpp b/src/test/RefreshInfoGeneratorTestSuite.cpp new file mode 100644 index 00000000..b4b8e138 --- /dev/null +++ b/src/test/RefreshInfoGeneratorTestSuite.cpp @@ -0,0 +1,1055 @@ +#include "catch.hpp" + +#include "PersistentStorage.h" +#include "RefreshInfo.h" +#include "RefreshInfoGenerator.h" +#include "SourceGroup.h" +#include "ProjectSettings.h" +#include "SourceGroupSettings.h" +#include "FileSystem.h" +#include "utility.h" + +namespace +{ + FilePath m_indexDbPath = FilePath(L"data/RefreshInfoGeneratorTestSuite/project.srctrldb"); + FilePath m_bookmarkDbPath = FilePath(L"data/RefreshInfoGeneratorTestSuite/project.srctrlbm"); + FilePath m_sourceFolder = FilePath(L"data/RefreshInfoGeneratorTestSuite/src"); + + class SourceGroupSettingsTest : public SourceGroupSettings + { + public: + SourceGroupSettingsTest(const ProjectSettings* projectSettings) + : SourceGroupSettings(SOURCE_GROUP_UNKNOWN, "TEST_ID", projectSettings) + { + } + + std::shared_ptr createCopy() const override + { + return nullptr; + } + + void loadSettings(const ConfigManager* config) override + { + } + + void saveSettings(ConfigManager* config) override + { + } + + bool equalsSettings(const SourceGroupSettingsBase* other) override + { + return true; + } + }; + + class SourceGroupTest : public SourceGroup + { + public: + + SourceGroupTest(std::set sourceFilePaths) + : m_sourceFilePaths(sourceFilePaths) + , m_allFilePaths(sourceFilePaths) + { + m_sourceGroupSettings = std::make_shared(&m_projectSettings); + } + + SourceGroupTest(std::set sourceFilePaths, std::set allFilePaths) + : m_sourceFilePaths(sourceFilePaths) + , m_allFilePaths(allFilePaths) + { + m_sourceGroupSettings = std::make_shared(&m_projectSettings); + } + + std::set filterToContainedFilePaths(const std::set& filePaths) const override + { + std::set containedFilePaths; + + for (const FilePath& filePath : filePaths) + { + if (m_allFilePaths.find(filePath) != m_allFilePaths.end()) + { + containedFilePaths.insert(filePath); + } + } + + return containedFilePaths; + } + + std::set getAllSourceFilePaths() const override + { + return m_sourceFilePaths; + } + + std::vector> getIndexerCommands(const std::set& filesToIndex) const override + { + return std::vector>(); + } + + void setStatus(SourceGroupStatusType status) + { + m_sourceGroupSettings->setStatus(status); + } + + private: + std::shared_ptr getSourceGroupSettings() override + { + return m_sourceGroupSettings; + } + + std::shared_ptr getSourceGroupSettings() const override + { + return m_sourceGroupSettings; + } + + ProjectSettings m_projectSettings; + std::shared_ptr m_sourceGroupSettings; + const std::set m_sourceFilePaths; + const std::set m_allFilePaths; + }; + + void cleanup() + { + FileSystem::remove(m_indexDbPath); + FileSystem::remove(m_bookmarkDbPath); + + if (m_sourceFolder.recheckExists()) + { + for (const FilePath& path : FileSystem::getFilePathsFromDirectory(m_sourceFolder)) + { + FileSystem::remove(path); + } + FileSystem::remove(m_sourceFolder); + } + } + + void addFileToFileSystem(const FilePath& filePath) + { + FileSystem::createDirectory(filePath.getParentDirectory()); + std::ofstream file; + file.open(filePath.str()); + file << "This is some file content.\n"; + file.close(); + } + + Id addFileToStorage(const FilePath& filePath, const std::string& modificationTime, bool indexed, bool complete, std::shared_ptr storage) + { + const Id id = storage->addNode(StorageNodeData(NodeType::NODE_FILE, NameHierarchy::serialize(NameHierarchy(filePath.wstr(), NAME_DELIMITER_FILE)))).first; + storage->addFile(StorageFile(id, filePath.wstr(), L"someLanguage", modificationTime, indexed, complete)); + return id; + } + + Id addVeryOldFileToStorage(const FilePath& filePath, bool indexed, bool complete, std::shared_ptr storage) + { + return addFileToStorage(filePath, "2000-01-01 10:10:10", indexed, complete, storage); + } + + Id addVeryNewFileToStorage(const FilePath& filePath, bool indexed, bool complete, std::shared_ptr storage) + { + return addFileToStorage(filePath, "2020-01-01 10:10:10", indexed, complete, storage); + } +} + +TEST_CASE("refresh info for all files is empty for empty project") +{ + std::vector> sourceGroups; + sourceGroups.push_back(std::shared_ptr(new SourceGroupTest({}))); + + const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForAllFiles(sourceGroups); + + REQUIRE(REFRESH_ALL_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size()); +} + +TEST_CASE("refresh info for all files clears nothing and indexes previously unknown source file") +{ + cleanup(); + { + const FilePath sourceFilePath = m_sourceFolder.getConcatenated(L"main.cpp"); + + std::vector> sourceGroups; + sourceGroups.push_back(std::shared_ptr(new SourceGroupTest({ sourceFilePath }))); + + addFileToFileSystem(sourceFilePath); + + const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForAllFiles(sourceGroups); + + REQUIRE(REFRESH_ALL_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(1 == refreshInfo.filesToIndex.size()); + + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToIndex), sourceFilePath + )); + } + cleanup(); +} + +TEST_CASE("refresh info for all files is empty for disabled source group") +{ + cleanup(); + { + const FilePath sourceFilePath = m_sourceFolder.getConcatenated(L"main.cpp"); + + std::shared_ptr sourceGroup(new SourceGroupTest({ sourceFilePath })); + sourceGroup->setStatus(SOURCE_GROUP_STATUS_DISABLED); + + std::vector> sourceGroups; + sourceGroups.push_back(sourceGroup); + + addFileToFileSystem(sourceFilePath); + + const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForAllFiles(sourceGroups); + + REQUIRE(REFRESH_ALL_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size()); + } + cleanup(); +} + +TEST_CASE("refresh info for all files is clears indexed files of disabled source group") +{ + cleanup(); + { + const FilePath sourceFilePath = m_sourceFolder.getConcatenated(L"main.cpp"); + + std::shared_ptr sourceGroup(new SourceGroupTest({ sourceFilePath })); + sourceGroup->setStatus(SOURCE_GROUP_STATUS_DISABLED); + + std::vector> sourceGroups; + sourceGroups.push_back(sourceGroup); + + addFileToFileSystem(sourceFilePath); + + std::shared_ptr storage = std::make_shared( + m_indexDbPath, + m_bookmarkDbPath + ); + storage->setup(); + addVeryNewFileToStorage(sourceFilePath, true, true, storage); + storage->buildCaches(); + + const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); + + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(1 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size()); + + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToClear), sourceFilePath + )); + } + cleanup(); +} + +TEST_CASE("refresh info for all files is clears nonindexed files of disabled source group") +{ + cleanup(); + { + const FilePath upToDateSourceFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.cpp"); + const FilePath upToDateHeaderFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.h"); + + std::shared_ptr sourceGroup(new SourceGroupTest( + { + upToDateSourceFilePath + }, + { + upToDateSourceFilePath, + upToDateHeaderFilePath + } + )); + sourceGroup->setStatus(SOURCE_GROUP_STATUS_DISABLED); + + std::vector> sourceGroups; + sourceGroups.push_back(sourceGroup); + + std::shared_ptr storage = std::make_shared( + m_indexDbPath, + m_bookmarkDbPath + ); + storage->setup(); + + const Id upToDateSourceFileId = addVeryNewFileToStorage(upToDateSourceFilePath, true, true, storage); + addFileToFileSystem(upToDateSourceFilePath); + const Id upToDateHeaderFileId = addVeryNewFileToStorage(upToDateHeaderFilePath, false, true, storage); + addFileToFileSystem(upToDateHeaderFilePath); + + storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, upToDateSourceFileId, upToDateHeaderFileId)); + + storage->buildCaches(); + + const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); + + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(1 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(1 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size()); + + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToClear), upToDateSourceFilePath + )); + + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.nonIndexedFilesToClear), upToDateHeaderFilePath + )); + } + cleanup(); +} + +TEST_CASE("refresh info for updated files is empty for empty storage and empty sourcegroup") +{ + cleanup(); + { + std::vector> sourceGroups; + sourceGroups.push_back(std::shared_ptr(new SourceGroupTest({}))); + + std::shared_ptr storage = std::make_shared( + m_indexDbPath, + m_bookmarkDbPath + ); + storage->setup(); + + storage->buildCaches(); + + const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); + + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size()); + } + cleanup(); +} + +// Now we will test how the refresh info generator reacts to different situations when generating refresh info for updated +// files. A file can have different states in the following dimensions: +// file may be known by the storage unknown / nonindexed / indexed +// file may be changed unchanged / changed +// file may be a source file sourcefile / headerfile +// file may now be indexed by the source group nottoindex / toindex +// +// We now wite a TEST_CASE that checks for the expected result for every possible combination. +// Example: test_unknown_unchanged_sourcefile_that_is_nottoindex +namespace +{ + enum KnownState + { + UNKNOWN, + NON_INDEXED, + INDEXED + }; + enum ChangedState + { + UNCHANGED, + CHANGED + }; + enum FileState + { + SOURCE_FILE, + HEADER_FILE + }; + enum IndexingState + { + NOT_TO_INDEX, + TO_INDEX + }; + + RefreshInfo getRefreshInfo(KnownState knownState, ChangedState changedState, FileState fileState, IndexingState indexingState) + { + RefreshInfo refreshInfo; + cleanup(); + { + const FilePath filePath = m_sourceFolder.getConcatenated(L"file.extension"); + + const std::set sourceFilePaths = ((fileState == SOURCE_FILE) ? std::set({ filePath }) : std::set({})); + const std::set allFilePaths = { filePath }; + + std::vector> sourceGroups; + if (indexingState == NOT_TO_INDEX) + { + // a file is "not existing" if the source group does not care about it + sourceGroups.push_back(std::shared_ptr(new SourceGroupTest({}, {}))); + } + else // if (indexingState == TO_INDEX) + { + sourceGroups.push_back(std::shared_ptr(new SourceGroupTest(sourceFilePaths, allFilePaths))); + } + + std::shared_ptr storage = std::make_shared( + m_indexDbPath, + m_bookmarkDbPath + ); + storage->setup(); + + if (knownState == UNKNOWN) + { + // do not add anything to storage + } + else if (knownState == NON_INDEXED) + { + if (changedState == UNCHANGED) + { + addVeryNewFileToStorage(filePath, false, true, storage); + } + else // if (changedState == CHANGED) + { + addVeryOldFileToStorage(filePath, false, true, storage); + } + } + else // if (knownState == INDEXED) + { + if (changedState == UNCHANGED) + { + addVeryNewFileToStorage(filePath, true, true, storage); + } + else // if (changedState == CHANGED) + { + addVeryOldFileToStorage(filePath, true, true, storage); + } + } + + addFileToFileSystem(filePath); + + storage->buildCaches(); + + refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); + } + cleanup(); + + return refreshInfo; + } +} + +TEST_CASE("unknown unchanged sourcefile that is nottoindex") +{ + const RefreshInfo refreshInfo = getRefreshInfo(UNKNOWN, UNCHANGED, SOURCE_FILE, NOT_TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size()); +} + +TEST_CASE("unknown unchanged sourcefile that is toindex") +{ + const RefreshInfo refreshInfo = getRefreshInfo(UNKNOWN, UNCHANGED, SOURCE_FILE, TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(1 == refreshInfo.filesToIndex.size()); +} + +TEST_CASE("unknown unchanged headerfile that is nottoindex") +{ + const RefreshInfo refreshInfo = getRefreshInfo(UNKNOWN, UNCHANGED, HEADER_FILE, NOT_TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size()); +} + +TEST_CASE("unknown unchanged headerfile that is toindex") +{ + const RefreshInfo refreshInfo = getRefreshInfo(UNKNOWN, UNCHANGED, HEADER_FILE, TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size());; // the header file will only be indexed on demand +} + +TEST_CASE("unknown changed sourcefile that is nottoindex") +{ + const RefreshInfo refreshInfo = getRefreshInfo(UNKNOWN, CHANGED, SOURCE_FILE, NOT_TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size()); +} + +TEST_CASE("unknown changed sourcefile that is toindex") +{ + const RefreshInfo refreshInfo = getRefreshInfo(UNKNOWN, CHANGED, SOURCE_FILE, TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(1 == refreshInfo.filesToIndex.size()); +} + +TEST_CASE("unknown changed headerfile that is nottoindex") +{ + const RefreshInfo refreshInfo = getRefreshInfo(UNKNOWN, CHANGED, HEADER_FILE, NOT_TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size()); +} + +TEST_CASE("unknown changed headerfile that is toindex") +{ + const RefreshInfo refreshInfo = getRefreshInfo(UNKNOWN, CHANGED, HEADER_FILE, TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size());; // the header file will only be indexed on demand +} + +TEST_CASE("nonindexed unchanged sourcefile that is nottoindex") // this test does not really make sense +{ + const RefreshInfo refreshInfo = getRefreshInfo(NON_INDEXED, UNCHANGED, SOURCE_FILE, NOT_TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size()); +} + +TEST_CASE("nonindexed unchanged sourcefile that is toindex") +{ + const RefreshInfo refreshInfo = getRefreshInfo(NON_INDEXED, UNCHANGED, SOURCE_FILE, TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(1 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(1 == refreshInfo.filesToIndex.size()); +} + +TEST_CASE("nonindexed unchanged headerfile that is nottoindex") // this test does not make much sense without source files +{ + const RefreshInfo refreshInfo = getRefreshInfo(NON_INDEXED, UNCHANGED, HEADER_FILE, NOT_TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size()); +} + +TEST_CASE("nonindexed unchanged headerfile that is toindex") +{ + const RefreshInfo refreshInfo = getRefreshInfo(NON_INDEXED, UNCHANGED, HEADER_FILE, TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(1 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size());; // the header file will only be indexed on demand +} + +TEST_CASE("nonindexed changed sourcefile that is nottoindex") +{ + const RefreshInfo refreshInfo = getRefreshInfo(NON_INDEXED, CHANGED, SOURCE_FILE, NOT_TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(1 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size()); +} + +TEST_CASE("nonindexed changed sourcefile that is toindex") +{ + const RefreshInfo refreshInfo = getRefreshInfo(NON_INDEXED, CHANGED, SOURCE_FILE, TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(1 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(1 == refreshInfo.filesToIndex.size()); +} + +TEST_CASE("nonindexed changed headerfile that is nottoindex") +{ + const RefreshInfo refreshInfo = getRefreshInfo(NON_INDEXED, CHANGED, HEADER_FILE, NOT_TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(1 == refreshInfo.nonIndexedFilesToClear.size());; // must be cleard here and will be re-indexed on demand + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size()); +} + +TEST_CASE("nonindexed changed headerfile that is toindex") +{ + const RefreshInfo refreshInfo = getRefreshInfo(NON_INDEXED, CHANGED, HEADER_FILE, TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(1 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size());; // the header file will only be indexed on demand +} + +TEST_CASE("indexed unchanged sourcefile that is nottoindex") +{ + const RefreshInfo refreshInfo = getRefreshInfo(INDEXED, UNCHANGED, SOURCE_FILE, NOT_TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(1 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size()); +} + +TEST_CASE("indexed unchanged sourcefile that is toindex") +{ + const RefreshInfo refreshInfo = getRefreshInfo(INDEXED, UNCHANGED, SOURCE_FILE, TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size()); +} + +TEST_CASE("indexed unchanged headerfile that is nottoindex") // TODO: check if depending source file gets reindexed +{ + const RefreshInfo refreshInfo = getRefreshInfo(INDEXED, UNCHANGED, HEADER_FILE, NOT_TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(1 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size()); +} + +TEST_CASE("indexed unchanged headerfile that is toindex") +{ + const RefreshInfo refreshInfo = getRefreshInfo(INDEXED, UNCHANGED, HEADER_FILE, TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size()); +} + +TEST_CASE("indexed changed sourcefile that is nottoindex") +{ + const RefreshInfo refreshInfo = getRefreshInfo(INDEXED, CHANGED, SOURCE_FILE, NOT_TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(1 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size()); +} + +TEST_CASE("indexed changed sourcefile that is toindex") +{ + const RefreshInfo refreshInfo = getRefreshInfo(INDEXED, CHANGED, SOURCE_FILE, TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(1 == refreshInfo.filesToClear.size()); + REQUIRE(1 == refreshInfo.filesToIndex.size()); +} + +TEST_CASE("indexed changed headerfile that is nottoindex") +{ + const RefreshInfo refreshInfo = getRefreshInfo(INDEXED, CHANGED, HEADER_FILE, NOT_TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(1 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size()); +} + +TEST_CASE("indexed changed headerfile that is toindex") +{ + const RefreshInfo refreshInfo = getRefreshInfo(INDEXED, CHANGED, HEADER_FILE, TO_INDEX); + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(1 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size());; // the header file will only be indexed on demand +} + +// Now we test some referencing stuff + +TEST_CASE("refresh info for updated files clears and reindexes known outdated file and referencing source file") +{ + cleanup(); + { + const FilePath upToDateSourceFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.cpp"); + const FilePath outdatedSourceFilePath = m_sourceFolder.getConcatenated(L"outdated_file.cpp"); + + std::vector> sourceGroups; + sourceGroups.push_back(std::shared_ptr(new SourceGroupTest({ + upToDateSourceFilePath, + outdatedSourceFilePath + }))); + + std::shared_ptr storage = std::make_shared( + m_indexDbPath, + m_bookmarkDbPath + ); + storage->setup(); + + const Id upToDateSourceFileId = addVeryNewFileToStorage(upToDateSourceFilePath, true, true, storage); + addFileToFileSystem(upToDateSourceFilePath); + const Id outdatedSourceFileId = addVeryOldFileToStorage(outdatedSourceFilePath, true, true, storage); + addFileToFileSystem(outdatedSourceFilePath); + + storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, upToDateSourceFileId, outdatedSourceFileId)); + + storage->buildCaches(); + + const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); + + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(2 == refreshInfo.filesToClear.size()); + REQUIRE(2 == refreshInfo.filesToIndex.size()); + + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToClear), upToDateSourceFilePath + )); + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToClear), outdatedSourceFilePath + )); + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToIndex), upToDateSourceFilePath + )); + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToIndex), outdatedSourceFilePath + )); + } + cleanup(); +} + +TEST_CASE("refresh info for updated files clears known outdated header file and reindexes referencing source file") +{ + cleanup(); + { + const FilePath upToDateSourceFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.cpp"); + const FilePath outdatedHeaderFilePath = m_sourceFolder.getConcatenated(L"outdated_file.h"); + + std::vector> sourceGroups; + sourceGroups.push_back(std::shared_ptr(new SourceGroupTest( + { + upToDateSourceFilePath + }, + { + upToDateSourceFilePath, + outdatedHeaderFilePath + } + ))); + + std::shared_ptr storage = std::make_shared( + m_indexDbPath, + m_bookmarkDbPath + ); + storage->setup(); + + const Id upToDateSourceFileId = addVeryNewFileToStorage(upToDateSourceFilePath, true, true, storage); + addFileToFileSystem(upToDateSourceFilePath); + const Id outdatedHeaderFileId = addVeryOldFileToStorage(outdatedHeaderFilePath, true, true, storage); + addFileToFileSystem(outdatedHeaderFilePath); + + storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, upToDateSourceFileId, outdatedHeaderFileId)); + + storage->buildCaches(); + + const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); + + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(2 == refreshInfo.filesToClear.size()); + REQUIRE(1 == refreshInfo.filesToIndex.size()); + + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToClear), upToDateSourceFilePath + )); + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToClear), outdatedHeaderFilePath + )); + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToIndex), upToDateSourceFilePath + )); + } + cleanup(); +} + +TEST_CASE("refresh info for updated files clears unknown outdated header file and reindexes referencing source") +{ + cleanup(); + { + const FilePath upToDateSourceFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.cpp"); + const FilePath outdatedHeaderFilePath = m_sourceFolder.getConcatenated(L"outdated_file.h"); + + std::vector> sourceGroups; + sourceGroups.push_back(std::shared_ptr(new SourceGroupTest({ + upToDateSourceFilePath + }))); + + std::shared_ptr storage = std::make_shared( + m_indexDbPath, + m_bookmarkDbPath + ); + storage->setup(); + + const Id upToDateSourceFileId = addVeryNewFileToStorage(upToDateSourceFilePath, true, true, storage); + addFileToFileSystem(upToDateSourceFilePath); + const Id outdatedHeaderFileId = addVeryOldFileToStorage(outdatedHeaderFilePath, false, true, storage); + addFileToFileSystem(outdatedHeaderFilePath); + + storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, upToDateSourceFileId, outdatedHeaderFileId)); + + storage->buildCaches(); + + const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); + + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(1 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(1 == refreshInfo.filesToClear.size()); + REQUIRE(1 == refreshInfo.filesToIndex.size()); + + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToClear), upToDateSourceFilePath + )); + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.nonIndexedFilesToClear), outdatedHeaderFilePath + )); + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToIndex), upToDateSourceFilePath + )); + } + cleanup(); +} + +TEST_CASE("refresh info for updated files does not clear unknown uptodate header file") +{ + cleanup(); + { + const FilePath upToDateSourceFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.cpp"); + const FilePath upToDateHeaderFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.h"); + + std::vector> sourceGroups; + sourceGroups.push_back(std::shared_ptr(new SourceGroupTest( + { + upToDateSourceFilePath + } + ))); + + std::shared_ptr storage = std::make_shared( + m_indexDbPath, + m_bookmarkDbPath + ); + storage->setup(); + + const Id upToDateSourceFileId = addVeryNewFileToStorage(upToDateSourceFilePath, true, true, storage); + addFileToFileSystem(upToDateSourceFilePath); + const Id upToDateHeaderFileId = addVeryNewFileToStorage(upToDateHeaderFilePath, false, true, storage); + addFileToFileSystem(upToDateHeaderFilePath); + + storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, upToDateSourceFileId, upToDateHeaderFileId)); + + storage->buildCaches(); + + const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); + + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(0 == refreshInfo.filesToClear.size()); + REQUIRE(0 == refreshInfo.filesToIndex.size()); + } + cleanup(); +} + +TEST_CASE("refresh info for updated files clears outdated source file and referened uptodate header file") +{ + cleanup(); + { + const FilePath outdatedSourceFilePath = m_sourceFolder.getConcatenated(L"outdated_file.cpp"); + const FilePath upToDateHeaderFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.h"); + + std::vector> sourceGroups; + sourceGroups.push_back(std::shared_ptr(new SourceGroupTest({ + outdatedSourceFilePath + }, + { + outdatedSourceFilePath, + upToDateHeaderFilePath + }))); + + std::shared_ptr storage = std::make_shared( + m_indexDbPath, + m_bookmarkDbPath + ); + storage->setup(); + + const Id outdatedSourceFileId = addVeryOldFileToStorage(outdatedSourceFilePath, true, true, storage); + addFileToFileSystem(outdatedSourceFilePath); + const Id upToDateHeaderFileId = addVeryNewFileToStorage(upToDateHeaderFilePath, true, true, storage); + addFileToFileSystem(upToDateHeaderFilePath); + + storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, outdatedSourceFileId, upToDateHeaderFileId)); + + storage->buildCaches(); + + const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); + + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(2 == refreshInfo.filesToClear.size()); + REQUIRE(1 == refreshInfo.filesToIndex.size()); + + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToClear), outdatedSourceFilePath + )); + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToClear), upToDateHeaderFilePath + )); + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToIndex), outdatedSourceFilePath + )); + } + cleanup(); +} + +TEST_CASE("refresh info for updated files does not clear uptodate header referenced by uptodate source") +{ + cleanup(); + { + const FilePath outdatedSourceFilePath = m_sourceFolder.getConcatenated(L"outdated_file.cpp"); + const FilePath upToDateSourceFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.cpp"); + const FilePath upToDateHeaderFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.h"); + + std::vector> sourceGroups; + sourceGroups.push_back(std::shared_ptr(new SourceGroupTest({ + outdatedSourceFilePath, + upToDateSourceFilePath + }, + { + outdatedSourceFilePath, + upToDateSourceFilePath, + upToDateHeaderFilePath + }))); + + std::shared_ptr storage = std::make_shared( + m_indexDbPath, + m_bookmarkDbPath + ); + storage->setup(); + + const Id outdatedSourceFileId = addVeryOldFileToStorage(outdatedSourceFilePath, true, true, storage); + addFileToFileSystem(outdatedSourceFilePath); + const Id upToDateSourceFileId = addVeryNewFileToStorage(upToDateSourceFilePath, true, true, storage); + addFileToFileSystem(upToDateSourceFilePath); + const Id upToDateHeaderFileId = addVeryNewFileToStorage(upToDateHeaderFilePath, true, true, storage); + addFileToFileSystem(upToDateHeaderFilePath); + + storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, outdatedSourceFileId, upToDateHeaderFileId)); + storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, upToDateSourceFileId, upToDateHeaderFileId)); + + storage->buildCaches(); + + const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); + + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(0 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(1 == refreshInfo.filesToClear.size()); + REQUIRE(1 == refreshInfo.filesToIndex.size()); + + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToClear), outdatedSourceFilePath + )); + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToIndex), outdatedSourceFilePath + )); + } + cleanup(); +} + +TEST_CASE("clears unchanged files referenced by unchanged file that referenced changed indexed file") +{ + cleanup(); + { + const FilePath upToDateSourceFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.cpp"); + const FilePath upToDateHeaderFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.h"); + const FilePath outOfDateHeaderFilePath = m_sourceFolder.getConcatenated(L"out_of_date_file.h"); + + std::vector> sourceGroups; + sourceGroups.push_back(std::shared_ptr(new SourceGroupTest({ + upToDateSourceFilePath + }, + { + upToDateSourceFilePath, + outOfDateHeaderFilePath, + }))); + + std::shared_ptr storage = std::make_shared( + m_indexDbPath, + m_bookmarkDbPath + ); + storage->setup(); + + const Id upToDateSourceFileId = addVeryNewFileToStorage(upToDateSourceFilePath, true, true, storage); + addFileToFileSystem(upToDateSourceFilePath); + const Id upToDateHeaderFileId = addVeryNewFileToStorage(upToDateHeaderFilePath, false, true, storage); + addFileToFileSystem(upToDateHeaderFilePath); + const Id outOfDateHeaderFileId = addVeryOldFileToStorage(outOfDateHeaderFilePath, true, true, storage); + addFileToFileSystem(outOfDateHeaderFilePath); + + storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, upToDateSourceFileId, upToDateHeaderFileId)); + storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, upToDateSourceFileId, outOfDateHeaderFileId)); + + storage->buildCaches(); + + const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); + + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(1 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(2 == refreshInfo.filesToClear.size()); + REQUIRE(1 == refreshInfo.filesToIndex.size()); + + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.nonIndexedFilesToClear), upToDateHeaderFilePath + )); + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToClear), upToDateSourceFilePath + )); + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToClear), outOfDateHeaderFilePath + )); + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToIndex), upToDateSourceFilePath + )); + } + cleanup(); +} + +TEST_CASE("clears unchanged files referenced by unchanged file that referenced changed nonindexed file") +{ + cleanup(); + { + const FilePath upToDateSourceFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.cpp"); + const FilePath upToDateHeaderFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.h"); + const FilePath outOfDateHeaderFilePath = m_sourceFolder.getConcatenated(L"out_of_date_file.h"); + + std::vector> sourceGroups; + sourceGroups.push_back(std::shared_ptr(new SourceGroupTest({ + upToDateSourceFilePath + }, + { + upToDateSourceFilePath, + upToDateHeaderFilePath, + }))); + + std::shared_ptr storage = std::make_shared( + m_indexDbPath, + m_bookmarkDbPath + ); + storage->setup(); + + const Id upToDateSourceFileId = addVeryNewFileToStorage(upToDateSourceFilePath, true, true, storage); + addFileToFileSystem(upToDateSourceFilePath); + const Id upToDateHeaderFileId = addVeryNewFileToStorage(upToDateHeaderFilePath, true, true, storage); + addFileToFileSystem(upToDateHeaderFilePath); + const Id outOfDateHeaderFileId = addVeryOldFileToStorage(outOfDateHeaderFilePath, false, true, storage); + addFileToFileSystem(outOfDateHeaderFilePath); + + storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, upToDateSourceFileId, upToDateHeaderFileId)); + storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, upToDateSourceFileId, outOfDateHeaderFileId)); + + storage->buildCaches(); + + const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); + + REQUIRE(REFRESH_UPDATED_FILES == refreshInfo.mode); + REQUIRE(1 == refreshInfo.nonIndexedFilesToClear.size()); + REQUIRE(2 == refreshInfo.filesToClear.size()); + REQUIRE(1 == refreshInfo.filesToIndex.size()); + + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.nonIndexedFilesToClear), outOfDateHeaderFilePath + )); + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToClear), upToDateSourceFilePath + )); + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToClear), upToDateHeaderFilePath + )); + REQUIRE(utility::containsElement( + utility::toVector(refreshInfo.filesToIndex), upToDateSourceFilePath + )); + } + cleanup(); +} diff --git a/src/test/RefreshInfoGeneratorTestSuite.h b/src/test/RefreshInfoGeneratorTestSuite.h deleted file mode 100644 index 6ab2f427..00000000 --- a/src/test/RefreshInfoGeneratorTestSuite.h +++ /dev/null @@ -1,1056 +0,0 @@ -#include - -#include "PersistentStorage.h" -#include "RefreshInfo.h" -#include "RefreshInfoGenerator.h" -#include "SourceGroup.h" -#include "ProjectSettings.h" -#include "SourceGroupSettings.h" -#include "FileSystem.h" -#include "utility.h" - -class RefreshInfoGeneratorTestSuite: public CxxTest::TestSuite -{ -public: - FilePath m_indexDbPath = FilePath(L"data/RefreshInfoGeneratorTestSuite/project.srctrldb"); - FilePath m_bookmarkDbPath = FilePath(L"data/RefreshInfoGeneratorTestSuite/project.srctrlbm"); - FilePath m_sourceFolder = FilePath(L"data/RefreshInfoGeneratorTestSuite/src"); - - void test_refresh_info_for_all_files_is_empty_for_empty_project() - { - std::vector> sourceGroups; - sourceGroups.push_back(std::shared_ptr(new SourceGroupTest({}))); - - const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForAllFiles(sourceGroups); - - TS_ASSERT_EQUALS(REFRESH_ALL_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); - } - - void test_refresh_info_for_all_files_clears_nothing_and_indexes_previously_unknown_source_file() - { - cleanup(); - { - const FilePath sourceFilePath = m_sourceFolder.getConcatenated(L"main.cpp"); - - std::vector> sourceGroups; - sourceGroups.push_back(std::shared_ptr(new SourceGroupTest({ sourceFilePath }))); - - addFileToFileSystem(sourceFilePath); - - const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForAllFiles(sourceGroups); - - TS_ASSERT_EQUALS(REFRESH_ALL_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToIndex.size()); - - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToIndex), sourceFilePath - )); - } - cleanup(); - } - - void test_refresh_info_for_all_files_is_empty_for_disabled_source_group() - { - cleanup(); - { - const FilePath sourceFilePath = m_sourceFolder.getConcatenated(L"main.cpp"); - - std::shared_ptr sourceGroup(new SourceGroupTest({ sourceFilePath })); - sourceGroup->setStatus(SOURCE_GROUP_STATUS_DISABLED); - - std::vector> sourceGroups; - sourceGroups.push_back(sourceGroup); - - addFileToFileSystem(sourceFilePath); - - const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForAllFiles(sourceGroups); - - TS_ASSERT_EQUALS(REFRESH_ALL_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); - } - cleanup(); - } - - void test_refresh_info_for_all_files_is_clears_indexed_files_of_disabled_source_group() - { - cleanup(); - { - const FilePath sourceFilePath = m_sourceFolder.getConcatenated(L"main.cpp"); - - std::shared_ptr sourceGroup(new SourceGroupTest({ sourceFilePath })); - sourceGroup->setStatus(SOURCE_GROUP_STATUS_DISABLED); - - std::vector> sourceGroups; - sourceGroups.push_back(sourceGroup); - - addFileToFileSystem(sourceFilePath); - - std::shared_ptr storage = std::make_shared( - m_indexDbPath, - m_bookmarkDbPath - ); - storage->setup(); - addVeryNewFileToStorage(sourceFilePath, true, true, storage); - storage->buildCaches(); - - const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); - - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); - - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToClear), sourceFilePath - )); - } - cleanup(); - } - - void test_refresh_info_for_all_files_is_clears_nonindexed_files_of_disabled_source_group() - { - cleanup(); - { - const FilePath upToDateSourceFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.cpp"); - const FilePath upToDateHeaderFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.h"); - - std::shared_ptr sourceGroup(new SourceGroupTest( - { - upToDateSourceFilePath - }, - { - upToDateSourceFilePath, - upToDateHeaderFilePath - } - )); - sourceGroup->setStatus(SOURCE_GROUP_STATUS_DISABLED); - - std::vector> sourceGroups; - sourceGroups.push_back(sourceGroup); - - std::shared_ptr storage = std::make_shared( - m_indexDbPath, - m_bookmarkDbPath - ); - storage->setup(); - - const Id upToDateSourceFileId = addVeryNewFileToStorage(upToDateSourceFilePath, true, true, storage); - addFileToFileSystem(upToDateSourceFilePath); - const Id upToDateHeaderFileId = addVeryNewFileToStorage(upToDateHeaderFilePath, false, true, storage); - addFileToFileSystem(upToDateHeaderFilePath); - - storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, upToDateSourceFileId, upToDateHeaderFileId)); - - storage->buildCaches(); - - const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); - - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(1, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); - - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToClear), upToDateSourceFilePath - )); - - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.nonIndexedFilesToClear), upToDateHeaderFilePath - )); - } - cleanup(); - } - - void test_refresh_info_for_updated_files_is_empty_for_empty_storage_and_empty_sourcegroup() - { - cleanup(); - { - std::vector> sourceGroups; - sourceGroups.push_back(std::shared_ptr(new SourceGroupTest({}))); - - std::shared_ptr storage = std::make_shared( - m_indexDbPath, - m_bookmarkDbPath - ); - storage->setup(); - - storage->buildCaches(); - - const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); - - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); - } - cleanup(); - } - - // Now we will test how the refresh info generator reacts to different situations when generating refresh info for updated - // files. A file can have different states in the following dimensions: - // file may be known by the storage unknown / nonindexed / indexed - // file may be changed unchanged / changed - // file may be a source file sourcefile / headerfile - // file may now be indexed by the source group nottoindex / toindex - // - // We now wite a test case that checks for the expected result for every possible combination. - // Example: test_unknown_unchanged_sourcefile_that_is_nottoindex - - enum KnownState - { - UNKNOWN, - NON_INDEXED, - INDEXED - }; - enum ChangedState - { - UNCHANGED, - CHANGED - }; - enum FileState - { - SOURCE_FILE, - HEADER_FILE - }; - enum IndexingState - { - NOT_TO_INDEX, - TO_INDEX - }; - - RefreshInfo getRefreshInfo(KnownState knownState, ChangedState changedState, FileState fileState, IndexingState indexingState) - { - RefreshInfo refreshInfo; - cleanup(); - { - const FilePath filePath = m_sourceFolder.getConcatenated(L"file.extension"); - - const std::set sourceFilePaths = ((fileState == SOURCE_FILE) ? std::set({ filePath }) : std::set({})); - const std::set allFilePaths = { filePath }; - - std::vector> sourceGroups; - if (indexingState == NOT_TO_INDEX) - { - // a file is "not existing" if the source group does not care about it - sourceGroups.push_back(std::shared_ptr(new SourceGroupTest({}, {}))); - } - else // if (indexingState == TO_INDEX) - { - sourceGroups.push_back(std::shared_ptr(new SourceGroupTest(sourceFilePaths, allFilePaths))); - } - - std::shared_ptr storage = std::make_shared( - m_indexDbPath, - m_bookmarkDbPath - ); - storage->setup(); - - if (knownState == UNKNOWN) - { - // do not add anything to storage - } - else if (knownState == NON_INDEXED) - { - if (changedState == UNCHANGED) - { - addVeryNewFileToStorage(filePath, false, true, storage); - } - else // if (changedState == CHANGED) - { - addVeryOldFileToStorage(filePath, false, true, storage); - } - } - else // if (knownState == INDEXED) - { - if (changedState == UNCHANGED) - { - addVeryNewFileToStorage(filePath, true, true, storage); - } - else // if (changedState == CHANGED) - { - addVeryOldFileToStorage(filePath, true, true, storage); - } - } - - addFileToFileSystem(filePath); - - storage->buildCaches(); - - refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); - } - cleanup(); - - return refreshInfo; - } - - void test_unknown_unchanged_sourcefile_that_is_nottoindex() - { - const RefreshInfo refreshInfo = getRefreshInfo(UNKNOWN, UNCHANGED, SOURCE_FILE, NOT_TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); - } - - void test_unknown_unchanged_sourcefile_that_is_toindex() - { - const RefreshInfo refreshInfo = getRefreshInfo(UNKNOWN, UNCHANGED, SOURCE_FILE, TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToIndex.size()); - } - - void test_unknown_unchanged_headerfile_that_is_nottoindex() - { - const RefreshInfo refreshInfo = getRefreshInfo(UNKNOWN, UNCHANGED, HEADER_FILE, NOT_TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); - } - - void test_unknown_unchanged_headerfile_that_is_toindex() - { - const RefreshInfo refreshInfo = getRefreshInfo(UNKNOWN, UNCHANGED, HEADER_FILE, TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); // the header file will only be indexed on demand - } - - void test_unknown_changed_sourcefile_that_is_nottoindex() - { - const RefreshInfo refreshInfo = getRefreshInfo(UNKNOWN, CHANGED, SOURCE_FILE, NOT_TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); - } - - void test_unknown_changed_sourcefile_that_is_toindex() - { - const RefreshInfo refreshInfo = getRefreshInfo(UNKNOWN, CHANGED, SOURCE_FILE, TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToIndex.size()); - } - - void test_unknown_changed_headerfile_that_is_nottoindex() - { - const RefreshInfo refreshInfo = getRefreshInfo(UNKNOWN, CHANGED, HEADER_FILE, NOT_TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); - } - - void test_unknown_changed_headerfile_that_is_toindex() - { - const RefreshInfo refreshInfo = getRefreshInfo(UNKNOWN, CHANGED, HEADER_FILE, TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); // the header file will only be indexed on demand - } - - void test_nonindexed_unchanged_sourcefile_that_is_nottoindex() // this test does not really make sense - { - const RefreshInfo refreshInfo = getRefreshInfo(NON_INDEXED, UNCHANGED, SOURCE_FILE, NOT_TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); - } - - void test_nonindexed_unchanged_sourcefile_that_is_toindex() - { - const RefreshInfo refreshInfo = getRefreshInfo(NON_INDEXED, UNCHANGED, SOURCE_FILE, TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(1, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToIndex.size()); - } - - void test_nonindexed_unchanged_headerfile_that_is_nottoindex() // this test does not make much sense without source files - { - const RefreshInfo refreshInfo = getRefreshInfo(NON_INDEXED, UNCHANGED, HEADER_FILE, NOT_TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); - } - - void test_nonindexed_unchanged_headerfile_that_is_toindex() - { - const RefreshInfo refreshInfo = getRefreshInfo(NON_INDEXED, UNCHANGED, HEADER_FILE, TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(1, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); // the header file will only be indexed on demand - } - - void test_nonindexed_changed_sourcefile_that_is_nottoindex() - { - const RefreshInfo refreshInfo = getRefreshInfo(NON_INDEXED, CHANGED, SOURCE_FILE, NOT_TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(1, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); - } - - void test_nonindexed_changed_sourcefile_that_is_toindex() - { - const RefreshInfo refreshInfo = getRefreshInfo(NON_INDEXED, CHANGED, SOURCE_FILE, TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(1, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToIndex.size()); - } - - void test_nonindexed_changed_headerfile_that_is_nottoindex() - { - const RefreshInfo refreshInfo = getRefreshInfo(NON_INDEXED, CHANGED, HEADER_FILE, NOT_TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(1, refreshInfo.nonIndexedFilesToClear.size()); // must be cleard here and will be re-indexed on demand - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); - } - - void test_nonindexed_changed_headerfile_that_is_toindex() - { - const RefreshInfo refreshInfo = getRefreshInfo(NON_INDEXED, CHANGED, HEADER_FILE, TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(1, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); // the header file will only be indexed on demand - } - - void test_indexed_unchanged_sourcefile_that_is_nottoindex() - { - const RefreshInfo refreshInfo = getRefreshInfo(INDEXED, UNCHANGED, SOURCE_FILE, NOT_TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); - } - - void test_indexed_unchanged_sourcefile_that_is_toindex() - { - const RefreshInfo refreshInfo = getRefreshInfo(INDEXED, UNCHANGED, SOURCE_FILE, TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); - } - - void test_indexed_unchanged_headerfile_that_is_nottoindex() // TODO: check if depending source file gets reindexed - { - const RefreshInfo refreshInfo = getRefreshInfo(INDEXED, UNCHANGED, HEADER_FILE, NOT_TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); - } - - void test_indexed_unchanged_headerfile_that_is_toindex() - { - const RefreshInfo refreshInfo = getRefreshInfo(INDEXED, UNCHANGED, HEADER_FILE, TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); - } - - void test_indexed_changed_sourcefile_that_is_nottoindex() - { - const RefreshInfo refreshInfo = getRefreshInfo(INDEXED, CHANGED, SOURCE_FILE, NOT_TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); - } - - void test_indexed_changed_sourcefile_that_is_toindex() - { - const RefreshInfo refreshInfo = getRefreshInfo(INDEXED, CHANGED, SOURCE_FILE, TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToIndex.size()); - } - - void test_indexed_changed_headerfile_that_is_nottoindex() - { - const RefreshInfo refreshInfo = getRefreshInfo(INDEXED, CHANGED, HEADER_FILE, NOT_TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); - } - - void test_indexed_changed_headerfile_that_is_toindex() - { - const RefreshInfo refreshInfo = getRefreshInfo(INDEXED, CHANGED, HEADER_FILE, TO_INDEX); - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); // the header file will only be indexed on demand - } - - // Now we test some referencing stuff - - void test_refresh_info_for_updated_files_clears_and_reindexes_known_outdated_file_and_referencing_source_file() - { - cleanup(); - { - const FilePath upToDateSourceFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.cpp"); - const FilePath outdatedSourceFilePath = m_sourceFolder.getConcatenated(L"outdated_file.cpp"); - - std::vector> sourceGroups; - sourceGroups.push_back(std::shared_ptr(new SourceGroupTest({ - upToDateSourceFilePath, - outdatedSourceFilePath - }))); - - std::shared_ptr storage = std::make_shared( - m_indexDbPath, - m_bookmarkDbPath - ); - storage->setup(); - - const Id upToDateSourceFileId = addVeryNewFileToStorage(upToDateSourceFilePath, true, true, storage); - addFileToFileSystem(upToDateSourceFilePath); - const Id outdatedSourceFileId = addVeryOldFileToStorage(outdatedSourceFilePath, true, true, storage); - addFileToFileSystem(outdatedSourceFilePath); - - storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, upToDateSourceFileId, outdatedSourceFileId)); - - storage->buildCaches(); - - const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); - - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(2, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(2, refreshInfo.filesToIndex.size()); - - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToClear), upToDateSourceFilePath - )); - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToClear), outdatedSourceFilePath - )); - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToIndex), upToDateSourceFilePath - )); - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToIndex), outdatedSourceFilePath - )); - } - cleanup(); - } - - void test_refresh_info_for_updated_files_clears_known_outdated_header_file_and_reindexes_referencing_source_file() - { - cleanup(); - { - const FilePath upToDateSourceFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.cpp"); - const FilePath outdatedHeaderFilePath = m_sourceFolder.getConcatenated(L"outdated_file.h"); - - std::vector> sourceGroups; - sourceGroups.push_back(std::shared_ptr(new SourceGroupTest( - { - upToDateSourceFilePath - }, - { - upToDateSourceFilePath, - outdatedHeaderFilePath - } - ))); - - std::shared_ptr storage = std::make_shared( - m_indexDbPath, - m_bookmarkDbPath - ); - storage->setup(); - - const Id upToDateSourceFileId = addVeryNewFileToStorage(upToDateSourceFilePath, true, true, storage); - addFileToFileSystem(upToDateSourceFilePath); - const Id outdatedHeaderFileId = addVeryOldFileToStorage(outdatedHeaderFilePath, true, true, storage); - addFileToFileSystem(outdatedHeaderFilePath); - - storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, upToDateSourceFileId, outdatedHeaderFileId)); - - storage->buildCaches(); - - const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); - - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(2, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToIndex.size()); - - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToClear), upToDateSourceFilePath - )); - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToClear), outdatedHeaderFilePath - )); - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToIndex), upToDateSourceFilePath - )); - } - cleanup(); - } - - void test_refresh_info_for_updated_files_clears_unknown_outdated_header_file_and_reindexes_referencing_source() - { - cleanup(); - { - const FilePath upToDateSourceFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.cpp"); - const FilePath outdatedHeaderFilePath = m_sourceFolder.getConcatenated(L"outdated_file.h"); - - std::vector> sourceGroups; - sourceGroups.push_back(std::shared_ptr(new SourceGroupTest({ - upToDateSourceFilePath - }))); - - std::shared_ptr storage = std::make_shared( - m_indexDbPath, - m_bookmarkDbPath - ); - storage->setup(); - - const Id upToDateSourceFileId = addVeryNewFileToStorage(upToDateSourceFilePath, true, true, storage); - addFileToFileSystem(upToDateSourceFilePath); - const Id outdatedHeaderFileId = addVeryOldFileToStorage(outdatedHeaderFilePath, false, true, storage); - addFileToFileSystem(outdatedHeaderFilePath); - - storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, upToDateSourceFileId, outdatedHeaderFileId)); - - storage->buildCaches(); - - const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); - - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(1, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToIndex.size()); - - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToClear), upToDateSourceFilePath - )); - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.nonIndexedFilesToClear), outdatedHeaderFilePath - )); - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToIndex), upToDateSourceFilePath - )); - } - cleanup(); - } - - void test_refresh_info_for_updated_files_does_not_clear_unknown_uptodate_header_file() - { - cleanup(); - { - const FilePath upToDateSourceFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.cpp"); - const FilePath upToDateHeaderFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.h"); - - std::vector> sourceGroups; - sourceGroups.push_back(std::shared_ptr(new SourceGroupTest( - { - upToDateSourceFilePath - } - ))); - - std::shared_ptr storage = std::make_shared( - m_indexDbPath, - m_bookmarkDbPath - ); - storage->setup(); - - const Id upToDateSourceFileId = addVeryNewFileToStorage(upToDateSourceFilePath, true, true, storage); - addFileToFileSystem(upToDateSourceFilePath); - const Id upToDateHeaderFileId = addVeryNewFileToStorage(upToDateHeaderFilePath, false, true, storage); - addFileToFileSystem(upToDateHeaderFilePath); - - storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, upToDateSourceFileId, upToDateHeaderFileId)); - - storage->buildCaches(); - - const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); - - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(0, refreshInfo.filesToIndex.size()); - } - cleanup(); - } - - void test_refresh_info_for_updated_files_clears_outdated_source_file_and_referened_uptodate_header_file() - { - cleanup(); - { - const FilePath outdatedSourceFilePath = m_sourceFolder.getConcatenated(L"outdated_file.cpp"); - const FilePath upToDateHeaderFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.h"); - - std::vector> sourceGroups; - sourceGroups.push_back(std::shared_ptr(new SourceGroupTest({ - outdatedSourceFilePath - }, - { - outdatedSourceFilePath, - upToDateHeaderFilePath - }))); - - std::shared_ptr storage = std::make_shared( - m_indexDbPath, - m_bookmarkDbPath - ); - storage->setup(); - - const Id outdatedSourceFileId = addVeryOldFileToStorage(outdatedSourceFilePath, true, true, storage); - addFileToFileSystem(outdatedSourceFilePath); - const Id upToDateHeaderFileId = addVeryNewFileToStorage(upToDateHeaderFilePath, true, true, storage); - addFileToFileSystem(upToDateHeaderFilePath); - - storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, outdatedSourceFileId, upToDateHeaderFileId)); - - storage->buildCaches(); - - const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); - - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(2, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToIndex.size()); - - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToClear), outdatedSourceFilePath - )); - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToClear), upToDateHeaderFilePath - )); - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToIndex), outdatedSourceFilePath - )); - } - cleanup(); - } - - void test_refresh_info_for_updated_files_does_not_clear_uptodate_header_referenced_by_uptodate_source() - { - cleanup(); - { - const FilePath outdatedSourceFilePath = m_sourceFolder.getConcatenated(L"outdated_file.cpp"); - const FilePath upToDateSourceFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.cpp"); - const FilePath upToDateHeaderFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.h"); - - std::vector> sourceGroups; - sourceGroups.push_back(std::shared_ptr(new SourceGroupTest({ - outdatedSourceFilePath, - upToDateSourceFilePath - }, - { - outdatedSourceFilePath, - upToDateSourceFilePath, - upToDateHeaderFilePath - }))); - - std::shared_ptr storage = std::make_shared( - m_indexDbPath, - m_bookmarkDbPath - ); - storage->setup(); - - const Id outdatedSourceFileId = addVeryOldFileToStorage(outdatedSourceFilePath, true, true, storage); - addFileToFileSystem(outdatedSourceFilePath); - const Id upToDateSourceFileId = addVeryNewFileToStorage(upToDateSourceFilePath, true, true, storage); - addFileToFileSystem(upToDateSourceFilePath); - const Id upToDateHeaderFileId = addVeryNewFileToStorage(upToDateHeaderFilePath, true, true, storage); - addFileToFileSystem(upToDateHeaderFilePath); - - storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, outdatedSourceFileId, upToDateHeaderFileId)); - storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, upToDateSourceFileId, upToDateHeaderFileId)); - - storage->buildCaches(); - - const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); - - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(0, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToIndex.size()); - - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToClear), outdatedSourceFilePath - )); - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToIndex), outdatedSourceFilePath - )); - } - cleanup(); - } - - void test_clears_unchanged_files_referenced_by_unchanged_file_that_referenced_changed_indexed_file() - { - cleanup(); - { - const FilePath upToDateSourceFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.cpp"); - const FilePath upToDateHeaderFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.h"); - const FilePath outOfDateHeaderFilePath = m_sourceFolder.getConcatenated(L"out_of_date_file.h"); - - std::vector> sourceGroups; - sourceGroups.push_back(std::shared_ptr(new SourceGroupTest({ - upToDateSourceFilePath - }, - { - upToDateSourceFilePath, - outOfDateHeaderFilePath, - }))); - - std::shared_ptr storage = std::make_shared( - m_indexDbPath, - m_bookmarkDbPath - ); - storage->setup(); - - const Id upToDateSourceFileId = addVeryNewFileToStorage(upToDateSourceFilePath, true, true, storage); - addFileToFileSystem(upToDateSourceFilePath); - const Id upToDateHeaderFileId = addVeryNewFileToStorage(upToDateHeaderFilePath, false, true, storage); - addFileToFileSystem(upToDateHeaderFilePath); - const Id outOfDateHeaderFileId = addVeryOldFileToStorage(outOfDateHeaderFilePath, true, true, storage); - addFileToFileSystem(outOfDateHeaderFilePath); - - storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, upToDateSourceFileId, upToDateHeaderFileId)); - storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, upToDateSourceFileId, outOfDateHeaderFileId)); - - storage->buildCaches(); - - const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); - - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(1, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(2, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToIndex.size()); - - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.nonIndexedFilesToClear), upToDateHeaderFilePath - )); - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToClear), upToDateSourceFilePath - )); - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToClear), outOfDateHeaderFilePath - )); - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToIndex), upToDateSourceFilePath - )); - } - cleanup(); - } - - void test_clears_unchanged_files_referenced_by_unchanged_file_that_referenced_changed_nonindexed_file() - { - cleanup(); - { - const FilePath upToDateSourceFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.cpp"); - const FilePath upToDateHeaderFilePath = m_sourceFolder.getConcatenated(L"up_to_date_file.h"); - const FilePath outOfDateHeaderFilePath = m_sourceFolder.getConcatenated(L"out_of_date_file.h"); - - std::vector> sourceGroups; - sourceGroups.push_back(std::shared_ptr(new SourceGroupTest({ - upToDateSourceFilePath - }, - { - upToDateSourceFilePath, - upToDateHeaderFilePath, - }))); - - std::shared_ptr storage = std::make_shared( - m_indexDbPath, - m_bookmarkDbPath - ); - storage->setup(); - - const Id upToDateSourceFileId = addVeryNewFileToStorage(upToDateSourceFilePath, true, true, storage); - addFileToFileSystem(upToDateSourceFilePath); - const Id upToDateHeaderFileId = addVeryNewFileToStorage(upToDateHeaderFilePath, true, true, storage); - addFileToFileSystem(upToDateHeaderFilePath); - const Id outOfDateHeaderFileId = addVeryOldFileToStorage(outOfDateHeaderFilePath, false, true, storage); - addFileToFileSystem(outOfDateHeaderFilePath); - - storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, upToDateSourceFileId, upToDateHeaderFileId)); - storage->addEdge(StorageEdgeData(Edge::EDGE_INCLUDE, upToDateSourceFileId, outOfDateHeaderFileId)); - - storage->buildCaches(); - - const RefreshInfo refreshInfo = RefreshInfoGenerator::getRefreshInfoForUpdatedFiles(sourceGroups, storage); - - TS_ASSERT_EQUALS(REFRESH_UPDATED_FILES, refreshInfo.mode); - TS_ASSERT_EQUALS(1, refreshInfo.nonIndexedFilesToClear.size()); - TS_ASSERT_EQUALS(2, refreshInfo.filesToClear.size()); - TS_ASSERT_EQUALS(1, refreshInfo.filesToIndex.size()); - - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.nonIndexedFilesToClear), outOfDateHeaderFilePath - )); - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToClear), upToDateSourceFilePath - )); - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToClear), upToDateHeaderFilePath - )); - TS_ASSERT(utility::containsElement( - utility::toVector(refreshInfo.filesToIndex), upToDateSourceFilePath - )); - } - cleanup(); - } - -private: - class SourceGroupSettingsTest : public SourceGroupSettings - { - public: - SourceGroupSettingsTest(const ProjectSettings* projectSettings) - : SourceGroupSettings(SOURCE_GROUP_UNKNOWN, "TEST_ID", projectSettings) - { - } - - std::shared_ptr createCopy() const override - { - return nullptr; - } - - void loadSettings(const ConfigManager* config) override - { - } - - void saveSettings(ConfigManager* config) override - { - } - - bool equalsSettings(const SourceGroupSettingsBase* other) override - { - return true; - } - }; - - class SourceGroupTest : public SourceGroup - { - public: - - SourceGroupTest(std::set sourceFilePaths) - : m_sourceFilePaths(sourceFilePaths) - , m_allFilePaths(sourceFilePaths) - { - m_sourceGroupSettings = std::make_shared(&m_projectSettings); - } - - SourceGroupTest(std::set sourceFilePaths, std::set allFilePaths) - : m_sourceFilePaths(sourceFilePaths) - , m_allFilePaths(allFilePaths) - { - m_sourceGroupSettings = std::make_shared(&m_projectSettings); - } - - std::set filterToContainedFilePaths(const std::set& filePaths) const override - { - std::set containedFilePaths; - - for (const FilePath& filePath : filePaths) - { - if (m_allFilePaths.find(filePath) != m_allFilePaths.end()) - { - containedFilePaths.insert(filePath); - } - } - - return containedFilePaths; - } - - std::set getAllSourceFilePaths() const override - { - return m_sourceFilePaths; - } - - std::vector> getIndexerCommands(const std::set& filesToIndex) const override - { - return std::vector>(); - } - - void setStatus(SourceGroupStatusType status) - { - m_sourceGroupSettings->setStatus(status); - } - - private: - std::shared_ptr getSourceGroupSettings() override - { - return m_sourceGroupSettings; - } - - std::shared_ptr getSourceGroupSettings() const override - { - return m_sourceGroupSettings; - } - - ProjectSettings m_projectSettings; - std::shared_ptr m_sourceGroupSettings; - const std::set m_sourceFilePaths; - const std::set m_allFilePaths; - }; - - void cleanup() - { - FileSystem::remove(m_indexDbPath); - FileSystem::remove(m_bookmarkDbPath); - - if (m_sourceFolder.recheckExists()) - { - for (const FilePath& path : FileSystem::getFilePathsFromDirectory(m_sourceFolder)) - { - FileSystem::remove(path); - } - FileSystem::remove(m_sourceFolder); - } - } - - void addFileToFileSystem(const FilePath& filePath) - { - FileSystem::createDirectory(filePath.getParentDirectory()); - std::ofstream file; - file.open(filePath.str()); - file << "This is some file content.\n"; - file.close(); - } - - Id addFileToStorage(const FilePath& filePath, const std::string& modificationTime, bool indexed, bool complete, std::shared_ptr storage) - { - const Id id = storage->addNode(StorageNodeData(NodeType::NODE_FILE, NameHierarchy::serialize(NameHierarchy(filePath.wstr(), NAME_DELIMITER_FILE)))).first; - storage->addFile(StorageFile(id, filePath.wstr(), L"someLanguage", modificationTime, indexed, complete)); - return id; - } - - Id addVeryOldFileToStorage(const FilePath& filePath, bool indexed, bool complete, std::shared_ptr storage) - { - return addFileToStorage(filePath, "2000-01-01 10:10:10", indexed, complete, storage); - } - - Id addVeryNewFileToStorage(const FilePath& filePath, bool indexed, bool complete, std::shared_ptr storage) - { - return addFileToStorage(filePath, "2020-01-01 10:10:10", indexed, complete, storage); - } - -}; diff --git a/src/test/SearchIndexTestSuite.cpp b/src/test/SearchIndexTestSuite.cpp new file mode 100644 index 00000000..7a4e0b5b --- /dev/null +++ b/src/test/SearchIndexTestSuite.cpp @@ -0,0 +1,91 @@ +#include "catch.hpp" + +#include "NameHierarchy.h" +#include "SearchIndex.h" +#include "utility.h" + +TEST_CASE("search index finds id of element added") +{ + SearchIndex index; + index.addNode(1, NameHierarchy::deserialize(L"::\tmfoo\tsvoid\tp() const").getQualifiedName()); + index.finishSetup(); + std::vector results = index.search(L"oo", NodeTypeSet::all(), 0); + + REQUIRE(1 == results.size()); + REQUIRE(1 == results[0].elementIds.size()); + REQUIRE(utility::containsElement(results[0].elementIds, 1)); +} + +TEST_CASE("search index finds correct indices for query") +{ + SearchIndex index; + index.addNode(1, NameHierarchy::deserialize(L"::\tmfoo\tsvoid\tp() const").getQualifiedName()); + index.finishSetup(); + std::vector results = index.search(L"oo", NodeTypeSet::all(), 0); + + REQUIRE(1 == results.size()); + REQUIRE(2 == results[0].indices.size()); + REQUIRE(1 == results[0].indices[0]); + REQUIRE(2 == results[0].indices[1]); +} + +TEST_CASE("search index finds ids for ambiguous query") +{ + SearchIndex index; + index.addNode(1, NameHierarchy::deserialize(L"::\tmfor\tsvoid\tp() const").getQualifiedName()); + index.addNode(2, NameHierarchy::deserialize(L"::\tmfos\tsvoid\tp() const").getQualifiedName()); + index.finishSetup(); + std::vector results = index.search(L"fo", NodeTypeSet::all(), 0); + + REQUIRE(2 == results.size()); + REQUIRE(1 == results[0].elementIds.size()); + REQUIRE(utility::containsElement(results[0].elementIds, 1)); + REQUIRE(1 == results[1].elementIds.size()); + REQUIRE(utility::containsElement(results[1].elementIds, 2)); +} + +TEST_CASE("search index does not find anything after clear") +{ + SearchIndex index; + index.addNode(1, NameHierarchy::deserialize(L"::\tmfoo\tsvoid\tp() const").getQualifiedName()); + index.finishSetup(); + index.clear(); + std::vector results = index.search(L"oo", NodeTypeSet::all(), 0); + + REQUIRE(0 == results.size()); +} + +TEST_CASE("search index does not find all results when max amount is limited") +{ + SearchIndex index; + index.addNode(1, NameHierarchy::deserialize(L"::\tmfoo1\tsvoid\tp() const").getQualifiedName()); + index.addNode(2, NameHierarchy::deserialize(L"::\tmfoo2\tsvoid\tp() const").getQualifiedName()); + index.finishSetup(); + std::vector results = index.search(L"oo", NodeTypeSet::all(), 1); + + REQUIRE(1 == results.size()); +} + +TEST_CASE("search index query is case insensitive") +{ + SearchIndex index; + index.addNode(1, NameHierarchy::deserialize(L"::\tmfoo1\tsvoid\tp() const").getQualifiedName()); + index.addNode(2, NameHierarchy::deserialize(L"::\tmFOO2\tsvoid\tp() const").getQualifiedName()); + index.finishSetup(); + std::vector results = index.search(L"oo", NodeTypeSet::all(), 0); + + REQUIRE(2 == results.size()); +} + +TEST_CASE("search index rates higher on consecutive letters") +{ + SearchIndex index; + index.addNode(1, NameHierarchy::deserialize(L"::\tmoaabbcc\tsvoid\tp() const").getQualifiedName()); + index.addNode(2, NameHierarchy::deserialize(L"::\tmocbcabc\tsvoid\tp() const").getQualifiedName()); + index.finishSetup(); + std::vector results = index.search(L"abc", NodeTypeSet::all(), 0); + + REQUIRE(2 == results.size()); + REQUIRE(L"ocbcabc" == results[0].text); + REQUIRE(L"oaabbcc" == results[1].text); +} diff --git a/src/test/SearchIndexTestSuite.h b/src/test/SearchIndexTestSuite.h deleted file mode 100644 index 3a674cf4..00000000 --- a/src/test/SearchIndexTestSuite.h +++ /dev/null @@ -1,97 +0,0 @@ -#include - -#include "NameHierarchy.h" -#include "SearchIndex.h" -#include "utility.h" - -class SearchIndexTestSuite : public CxxTest::TestSuite -{ -public: - - void test_search_index_finds_id_of_element_added() - { - SearchIndex index; - index.addNode(1, NameHierarchy::deserialize(L"::\tmfoo\tsvoid\tp() const").getQualifiedName()); - index.finishSetup(); - std::vector results = index.search(L"oo", NodeTypeSet::all(), 0); - - TS_ASSERT_EQUALS(1, results.size()); - TS_ASSERT_EQUALS(1, results[0].elementIds.size()); - TS_ASSERT(utility::containsElement(results[0].elementIds, 1)); - } - - void test_search_index_finds_correct_indices_for_query() - { - SearchIndex index; - index.addNode(1, NameHierarchy::deserialize(L"::\tmfoo\tsvoid\tp() const").getQualifiedName()); - index.finishSetup(); - std::vector results = index.search(L"oo", NodeTypeSet::all(), 0); - - TS_ASSERT_EQUALS(1, results.size()); - TS_ASSERT_EQUALS(2, results[0].indices.size()); - TS_ASSERT_EQUALS(1, results[0].indices[0]); - TS_ASSERT_EQUALS(2, results[0].indices[1]); - } - - void test_search_index_finds_ids_for_ambiguous_query() - { - SearchIndex index; - index.addNode(1, NameHierarchy::deserialize(L"::\tmfor\tsvoid\tp() const").getQualifiedName()); - index.addNode(2, NameHierarchy::deserialize(L"::\tmfos\tsvoid\tp() const").getQualifiedName()); - index.finishSetup(); - std::vector results = index.search(L"fo", NodeTypeSet::all(), 0); - - TS_ASSERT_EQUALS(2, results.size()); - TS_ASSERT_EQUALS(1, results[0].elementIds.size()); - TS_ASSERT(utility::containsElement(results[0].elementIds, 1)); - TS_ASSERT_EQUALS(1, results[1].elementIds.size()); - TS_ASSERT(utility::containsElement(results[1].elementIds, 2)); - } - - void test_search_index_does_not_find_anything_after_clear() - { - SearchIndex index; - index.addNode(1, NameHierarchy::deserialize(L"::\tmfoo\tsvoid\tp() const").getQualifiedName()); - index.finishSetup(); - index.clear(); - std::vector results = index.search(L"oo", NodeTypeSet::all(), 0); - - TS_ASSERT_EQUALS(0, results.size()); - } - - void test_search_index_does_not_find_all_results_when_max_amount_is_limited() - { - SearchIndex index; - index.addNode(1, NameHierarchy::deserialize(L"::\tmfoo1\tsvoid\tp() const").getQualifiedName()); - index.addNode(2, NameHierarchy::deserialize(L"::\tmfoo2\tsvoid\tp() const").getQualifiedName()); - index.finishSetup(); - std::vector results = index.search(L"oo", NodeTypeSet::all(), 1); - - TS_ASSERT_EQUALS(1, results.size()); - } - - void test_search_index_query_is_case_insensitive() - { - SearchIndex index; - index.addNode(1, NameHierarchy::deserialize(L"::\tmfoo1\tsvoid\tp() const").getQualifiedName()); - index.addNode(2, NameHierarchy::deserialize(L"::\tmFOO2\tsvoid\tp() const").getQualifiedName()); - index.finishSetup(); - std::vector results = index.search(L"oo", NodeTypeSet::all(), 0); - - TS_ASSERT_EQUALS(2, results.size()); - } - - void test_search_index_rates_higher_on_consecutive_letters() - { - - SearchIndex index; - index.addNode(1, NameHierarchy::deserialize(L"::\tmoaabbcc\tsvoid\tp() const").getQualifiedName()); - index.addNode(2, NameHierarchy::deserialize(L"::\tmocbcabc\tsvoid\tp() const").getQualifiedName()); - index.finishSetup(); - std::vector results = index.search(L"abc", NodeTypeSet::all(), 0); - - TS_ASSERT_EQUALS(2, results.size()); - TS_ASSERT_EQUALS(L"ocbcabc", results[0].text); - TS_ASSERT_EQUALS(L"oaabbcc", results[1].text); - } -}; diff --git a/src/test/SettingsMigratorTestSuite.cpp b/src/test/SettingsMigratorTestSuite.cpp new file mode 100644 index 00000000..84e83b6b --- /dev/null +++ b/src/test/SettingsMigratorTestSuite.cpp @@ -0,0 +1,381 @@ +#include "catch.hpp" + +#include "Settings.h" +#include "SettingsMigrator.h" +#include "SettingsMigrationLambda.h" +#include "SettingsMigrationMoveKey.h" +#include "TextAccess.h" + +namespace +{ + class TestSettings + : public Settings + { + public: + static TestSettings createFromText(const std::shared_ptr textAccess) + { + TestSettings settings; + settings.m_config = ConfigManager::createAndLoad(textAccess); + return settings; + } + + std::string getAsText() const + { + if (m_config) + { + return m_config->toString(); + } + + return ""; + } + }; + + TestSettings createSettings(const std::string& text) + { + return TestSettings::createFromText(TextAccess::createFromString(text)); + } +} + +TEST_CASE("migrator changes nothing without migrations except version") +{ + TestSettings settingsBefore = createSettings( + "\n" + "\n" + " 2\n" + "\n" + ); + + TestSettings settingsAfter = createSettings( + "\n" + "\n" + " 2\n" + " 1\n" + "\n" + ); + + SettingsMigrator migrator; + migrator.migrate(&settingsBefore, 1); + + REQUIRE(settingsBefore.getAsText() == settingsAfter.getAsText()); +} + +TEST_CASE("migrator changes name") +{ + TestSettings settingsBefore = createSettings( + "\n" + "\n" + " 2\n" + "\n" + ); + + TestSettings settingsAfter = createSettings( + "\n" + "\n" + " 2\n" + " 1\n" + "\n" + ); + + SettingsMigrator migrator; + migrator.addMigration(1, std::make_shared("value", "int")); + migrator.migrate(&settingsBefore, 1); + + REQUIRE(settingsBefore.getAsText() == settingsAfter.getAsText()); +} + +TEST_CASE("migrator changes path") +{ + TestSettings settingsBefore = createSettings( + "\n" + "\n" + " 2\n" + "\n" + ); + + TestSettings settingsAfter = createSettings( + "\n" + "\n" + " \n" + " 2\n" + " \n" + " 1\n" + "\n" + ); + + SettingsMigrator migrator; + migrator.addMigration(1, std::make_shared("value", "sub/int")); + migrator.migrate(&settingsBefore, 1); + + REQUIRE(settingsBefore.getAsText() == settingsAfter.getAsText()); +} + +TEST_CASE("migrator changes group name") +{ + TestSettings settingsBefore = createSettings( + "\n" + "\n" + " \n" + " 2\n" + " 3\n" + " 4\n" + " \n" + "\n" + ); + + TestSettings settingsAfter = createSettings( + "\n" + "\n" + " \n" + " 2\n" + " 3\n" + " 4\n" + " \n" + " 1\n" + "\n" + ); + + SettingsMigrator migrator; + migrator.addMigration(1, std::make_shared("values/value", "vals/value")); + migrator.migrate(&settingsBefore, 1); + + REQUIRE(settingsBefore.getAsText() == settingsAfter.getAsText()); +} + +TEST_CASE("migrator changes group element name") +{ + TestSettings settingsBefore = createSettings( + "\n" + "\n" + " \n" + " 2\n" + " 3\n" + " 4\n" + " \n" + "\n" + ); + + TestSettings settingsAfter = createSettings( + "\n" + "\n" + " \n" + " 2\n" + " 3\n" + " 4\n" + " \n" + " 1\n" + "\n" + ); + + SettingsMigrator migrator; + migrator.addMigration(1, std::make_shared("values/value", "values/val")); + migrator.migrate(&settingsBefore, 1); + + REQUIRE(settingsBefore.getAsText() == settingsAfter.getAsText()); +} + +TEST_CASE("migrator changes only up specified version") +{ + TestSettings settingsBefore = createSettings( + "\n" + "\n" + " 2\n" + "\n" + ); + + TestSettings settingsAfter = createSettings( + "\n" + "\n" + " 2\n" + " 1\n" + "\n" + ); + + SettingsMigrator migrator; + migrator.addMigration(1, std::make_shared("value", "int")); + migrator.addMigration(2, std::make_shared("int", "val")); + migrator.migrate(&settingsBefore, 1); + + REQUIRE(settingsBefore.getAsText() == settingsAfter.getAsText()); +} + +TEST_CASE("migrator changes only from specified version") +{ + TestSettings settingsBefore = createSettings( + "\n" + "\n" + " 2\n" + " 1\n" + "\n" + ); + + TestSettings settingsAfter = createSettings( + "\n" + "\n" + " 2\n" + " 2\n" + "\n" + ); + + SettingsMigrator migrator; + migrator.addMigration(1, std::make_shared("value", "int")); + migrator.addMigration(2, std::make_shared("int", "val")); + migrator.migrate(&settingsBefore, 2); + + REQUIRE(settingsBefore.getAsText() == settingsAfter.getAsText()); +} + +TEST_CASE("migrator changes for multiple versions") +{ + TestSettings settingsBefore = createSettings( + "\n" + "\n" + " 2\n" + "\n" + ); + + TestSettings settingsAfter = createSettings( + "\n" + "\n" + " 2\n" + " 2\n" + "\n" + ); + + SettingsMigrator migrator; + migrator.addMigration(1, std::make_shared("value", "int")); + migrator.addMigration(2, std::make_shared("int", "val")); + migrator.migrate(&settingsBefore, 2); + + REQUIRE(settingsBefore.getAsText() == settingsAfter.getAsText()); +} + +TEST_CASE("migrator changes for multiple migrations") +{ + TestSettings settingsBefore = createSettings( + "\n" + "\n" + " 2\n" + " hi there\n" + "\n" + ); + + TestSettings settingsAfter = createSettings( + "\n" + "\n" + " 2\n" + " hi there\n" + " 2\n" + "\n" + ); + + SettingsMigrator migrator; + migrator.addMigration(1, std::make_shared("value", "val")); + migrator.addMigration(1, std::make_shared("element", "ele")); + migrator.migrate(&settingsBefore, 2); + + REQUIRE(settingsBefore.getAsText() == settingsAfter.getAsText()); +} + +TEST_CASE("migrator changes for multiple versions and migrations") +{ + TestSettings settingsBefore = createSettings( + "\n" + "\n" + " 2\n" + " two\n" + " three\n" + " four\n" + "\n" + ); + + TestSettings settingsAfter = createSettings( + "\n" + "\n" + " 2\n" + " \n" + " two\n" + " three\n" + " four\n" + " \n" + " 3\n" + "\n" + ); + + SettingsMigrator migrator; + migrator.addMigration(1, std::make_shared("value", "int/val")); + migrator.addMigration(1, std::make_shared("element", "ele")); + migrator.addMigration(2, std::make_shared("int/val", "int")); + migrator.addMigration(3, std::make_shared("ele", "elements/element")); + migrator.migrate(&settingsBefore, 3); + + REQUIRE(settingsBefore.getAsText() == settingsAfter.getAsText()); +} + +TEST_CASE("migrator with lambda") +{ + TestSettings settingsBefore = createSettings( + "\n" + "\n" + " 2\n" + "\n" + ); + + TestSettings settingsAfter = createSettings( + "\n" + "\n" + " 4\n" + " 1\n" + "\n" + ); + + SettingsMigrator migrator; + migrator.addMigration( + 1, std::make_shared( + [](const SettingsMigration* migration, Settings* settings) + { + migration->setValueInSettings(settings, "value", migration->getValueFromSettings(settings, "value", 0) * 2); + } + )); + migrator.migrate(&settingsBefore, 1); + + REQUIRE(settingsBefore.getAsText() == settingsAfter.getAsText()); +} + +TEST_CASE("migrator with multiple lambdas") +{ + TestSettings settingsBefore = createSettings( + "\n" + "\n" + " 2\n" + "\n" + ); + + TestSettings settingsAfter = createSettings( + "\n" + "\n" + " 3\n" + " 2\n" + "\n" + ); + + SettingsMigrator migrator; + migrator.addMigration( + 1, std::make_shared( + [](const SettingsMigration* migration, Settings* settings) + { + migration->setValueInSettings(settings, "value", migration->getValueFromSettings(settings, "value", 0) * 2); + } + ) + ); + migrator.addMigration( + 2, std::make_shared( + [](const SettingsMigration* migration, Settings* settings) + { + migration->setValueInSettings(settings, "value", migration->getValueFromSettings(settings, "value", 0) - 1); + } + ) + ); + migrator.migrate(&settingsBefore, 2); + + REQUIRE(settingsBefore.getAsText() == settingsAfter.getAsText()); +} diff --git a/src/test/SettingsMigratorTestSuite.h b/src/test/SettingsMigratorTestSuite.h deleted file mode 100644 index 68df8555..00000000 --- a/src/test/SettingsMigratorTestSuite.h +++ /dev/null @@ -1,383 +0,0 @@ -#include - -#include "Settings.h" -#include "SettingsMigrator.h" -#include "SettingsMigrationLambda.h" -#include "SettingsMigrationMoveKey.h" -#include "TextAccess.h" - -class SettingsMigratorTestSuite : public CxxTest::TestSuite -{ -public: - void test_migrator_changes_nothing_without_migrations_except_version() - { - TestSettings settingsBefore = createSettings( - "\n" - "\n" - " 2\n" - "\n" - ); - - TestSettings settingsAfter = createSettings( - "\n" - "\n" - " 2\n" - " 1\n" - "\n" - ); - - SettingsMigrator migrator; - migrator.migrate(&settingsBefore, 1); - - TS_ASSERT_EQUALS(settingsBefore.getAsText(), settingsAfter.getAsText()); - } - - void test_migrator_changes_name() - { - TestSettings settingsBefore = createSettings( - "\n" - "\n" - " 2\n" - "\n" - ); - - TestSettings settingsAfter = createSettings( - "\n" - "\n" - " 2\n" - " 1\n" - "\n" - ); - - SettingsMigrator migrator; - migrator.addMigration(1, std::make_shared("value", "int")); - migrator.migrate(&settingsBefore, 1); - - TS_ASSERT_EQUALS(settingsBefore.getAsText(), settingsAfter.getAsText()); - } - - void test_migrator_changes_path() - { - TestSettings settingsBefore = createSettings( - "\n" - "\n" - " 2\n" - "\n" - ); - - TestSettings settingsAfter = createSettings( - "\n" - "\n" - " \n" - " 2\n" - " \n" - " 1\n" - "\n" - ); - - SettingsMigrator migrator; - migrator.addMigration(1, std::make_shared("value", "sub/int")); - migrator.migrate(&settingsBefore, 1); - - TS_ASSERT_EQUALS(settingsBefore.getAsText(), settingsAfter.getAsText()); - } - - void test_migrator_changes_group_name() - { - TestSettings settingsBefore = createSettings( - "\n" - "\n" - " \n" - " 2\n" - " 3\n" - " 4\n" - " \n" - "\n" - ); - - TestSettings settingsAfter = createSettings( - "\n" - "\n" - " \n" - " 2\n" - " 3\n" - " 4\n" - " \n" - " 1\n" - "\n" - ); - - SettingsMigrator migrator; - migrator.addMigration(1, std::make_shared("values/value", "vals/value")); - migrator.migrate(&settingsBefore, 1); - - TS_ASSERT_EQUALS(settingsBefore.getAsText(), settingsAfter.getAsText()); - } - - void test_migrator_changes_group_element_name() - { - TestSettings settingsBefore = createSettings( - "\n" - "\n" - " \n" - " 2\n" - " 3\n" - " 4\n" - " \n" - "\n" - ); - - TestSettings settingsAfter = createSettings( - "\n" - "\n" - " \n" - " 2\n" - " 3\n" - " 4\n" - " \n" - " 1\n" - "\n" - ); - - SettingsMigrator migrator; - migrator.addMigration(1, std::make_shared("values/value", "values/val")); - migrator.migrate(&settingsBefore, 1); - - TS_ASSERT_EQUALS(settingsBefore.getAsText(), settingsAfter.getAsText()); - } - - void test_migrator_changes_only_up_specified_version() - { - TestSettings settingsBefore = createSettings( - "\n" - "\n" - " 2\n" - "\n" - ); - - TestSettings settingsAfter = createSettings( - "\n" - "\n" - " 2\n" - " 1\n" - "\n" - ); - - SettingsMigrator migrator; - migrator.addMigration(1, std::make_shared("value", "int")); - migrator.addMigration(2, std::make_shared("int", "val")); - migrator.migrate(&settingsBefore, 1); - - TS_ASSERT_EQUALS(settingsBefore.getAsText(), settingsAfter.getAsText()); - } - - void test_migrator_changes_only_from_specified_version() - { - TestSettings settingsBefore = createSettings( - "\n" - "\n" - " 2\n" - " 1\n" - "\n" - ); - - TestSettings settingsAfter = createSettings( - "\n" - "\n" - " 2\n" - " 2\n" - "\n" - ); - - SettingsMigrator migrator; - migrator.addMigration(1, std::make_shared("value", "int")); - migrator.addMigration(2, std::make_shared("int", "val")); - migrator.migrate(&settingsBefore, 2); - - TS_ASSERT_EQUALS(settingsBefore.getAsText(), settingsAfter.getAsText()); - } - - void test_migrator_changes_for_multiple_versions() - { - TestSettings settingsBefore = createSettings( - "\n" - "\n" - " 2\n" - "\n" - ); - - TestSettings settingsAfter = createSettings( - "\n" - "\n" - " 2\n" - " 2\n" - "\n" - ); - - SettingsMigrator migrator; - migrator.addMigration(1, std::make_shared("value", "int")); - migrator.addMigration(2, std::make_shared("int", "val")); - migrator.migrate(&settingsBefore, 2); - - TS_ASSERT_EQUALS(settingsBefore.getAsText(), settingsAfter.getAsText()); - } - - void test_migrator_changes_for_multiple_migrations() - { - TestSettings settingsBefore = createSettings( - "\n" - "\n" - " 2\n" - " hi there\n" - "\n" - ); - - TestSettings settingsAfter = createSettings( - "\n" - "\n" - " 2\n" - " hi there\n" - " 2\n" - "\n" - ); - - SettingsMigrator migrator; - migrator.addMigration(1, std::make_shared("value", "val")); - migrator.addMigration(1, std::make_shared("element", "ele")); - migrator.migrate(&settingsBefore, 2); - - TS_ASSERT_EQUALS(settingsBefore.getAsText(), settingsAfter.getAsText()); - } - - void test_migrator_changes_for_multiple_versions_and_migrations() - { - TestSettings settingsBefore = createSettings( - "\n" - "\n" - " 2\n" - " two\n" - " three\n" - " four\n" - "\n" - ); - - TestSettings settingsAfter = createSettings( - "\n" - "\n" - " 2\n" - " \n" - " two\n" - " three\n" - " four\n" - " \n" - " 3\n" - "\n" - ); - - SettingsMigrator migrator; - migrator.addMigration(1, std::make_shared("value", "int/val")); - migrator.addMigration(1, std::make_shared("element", "ele")); - migrator.addMigration(2, std::make_shared("int/val", "int")); - migrator.addMigration(3, std::make_shared("ele", "elements/element")); - migrator.migrate(&settingsBefore, 3); - - TS_ASSERT_EQUALS(settingsBefore.getAsText(), settingsAfter.getAsText()); - } - - void test_migrator_with_lambda() - { - TestSettings settingsBefore = createSettings( - "\n" - "\n" - " 2\n" - "\n" - ); - - TestSettings settingsAfter = createSettings( - "\n" - "\n" - " 4\n" - " 1\n" - "\n" - ); - - SettingsMigrator migrator; - migrator.addMigration( - 1, std::make_shared( - [](const SettingsMigration* migration, Settings* settings) - { - migration->setValueInSettings(settings, "value", migration->getValueFromSettings(settings, "value", 0) * 2); - } - )); - migrator.migrate(&settingsBefore, 1); - - TS_ASSERT_EQUALS(settingsBefore.getAsText(), settingsAfter.getAsText()); - } - - void test_migrator_with_multiple_lambdas() - { - TestSettings settingsBefore = createSettings( - "\n" - "\n" - " 2\n" - "\n" - ); - - TestSettings settingsAfter = createSettings( - "\n" - "\n" - " 3\n" - " 2\n" - "\n" - ); - - SettingsMigrator migrator; - migrator.addMigration( - 1, std::make_shared( - [](const SettingsMigration* migration, Settings* settings) - { - migration->setValueInSettings(settings, "value", migration->getValueFromSettings(settings, "value", 0) * 2); - } - ) - ); - migrator.addMigration( - 2, std::make_shared( - [](const SettingsMigration* migration, Settings* settings) - { - migration->setValueInSettings(settings, "value", migration->getValueFromSettings(settings, "value", 0) - 1); - } - ) - ); - migrator.migrate(&settingsBefore, 2); - - TS_ASSERT_EQUALS(settingsBefore.getAsText(), settingsAfter.getAsText()); - } - -private: - class TestSettings - : public Settings - { - public: - static TestSettings createFromText(const std::shared_ptr textAccess) - { - TestSettings settings; - settings.m_config = ConfigManager::createAndLoad(textAccess); - return settings; - } - - std::string getAsText() const - { - if (m_config) - { - return m_config->toString(); - } - - return ""; - } - }; - - TestSettings createSettings(const std::string& text) - { - return TestSettings::createFromText(TextAccess::createFromString(text)); - } -}; diff --git a/src/test/SettingsTestSuite.cpp b/src/test/SettingsTestSuite.cpp new file mode 100644 index 00000000..36d199a4 --- /dev/null +++ b/src/test/SettingsTestSuite.cpp @@ -0,0 +1,215 @@ +#include "catch.hpp" + +#include "ProjectSettings.h" +#include "Settings.h" +#include "SourceGroupSettingsWithCxxPathsAndFlags.h" +#include "SourceGroupSettings.h" +#include "SourceGroupSettingsWithSourcePaths.h" + +namespace +{ + class TestSettings : public Settings + { + public: + bool getBool() const + { + return getValue("Bool", false); + } + + bool setBool(bool value) + { + return setValue("Bool", value); + } + + int getInt() const + { + return getValue("Int", -1); + } + + bool setInt(int value) + { + return setValue("Int", value); + } + + float getFloat() const + { + return getValue("Float", 0.01f); + } + + bool setFloat(float value) + { + return setValue("Float", value); + } + + std::string getString() const + { + return getValue("String", ""); + } + + bool setString(const std::string& value) + { + return setValue("String", value); + } + + std::wstring getWString() const + { + return getValue("WString", L""); + } + + bool setWString(const std::wstring& value) + { + return setValue("WString", value); + } + + bool getNewBool() const + { + return getValue("NewBool", false); + } + + bool setNewBool(bool value) + { + return setValue("NewBool", value); + } + }; +} + +TEST_CASE("settings get loaded from file") +{ + TestSettings settings; + REQUIRE(settings.load(FilePath(L"data/SettingsTestSuite/settings.xml"))); +} + +TEST_CASE("settings get not loaded from file") +{ + TestSettings settings; + REQUIRE(!settings.load(FilePath(L"data/SettingsTestSuite/wrong_settings.xml"))); +} + +TEST_CASE("settings get loaded value") +{ + TestSettings settings; + REQUIRE(settings.load(FilePath(L"data/SettingsTestSuite/settings.xml"))); + + REQUIRE(settings.getBool() == true); + REQUIRE(settings.getInt() == 42); + REQUIRE(settings.getFloat() == 3.1416f); + REQUIRE(settings.getString() == "Hello World!"); + REQUIRE(settings.getWString() == L"Hello World!"); +} + +TEST_CASE("settings get default value when not loaded") +{ + TestSettings settings; + REQUIRE(settings.getBool() == false); + REQUIRE(settings.getInt() == -1); + REQUIRE(settings.getFloat() == 0.01f); + REQUIRE(settings.getString() == ""); + REQUIRE(settings.getWString() == L""); +} + +TEST_CASE("settings get default value when wrongly loaded") +{ + TestSettings settings; + REQUIRE(!settings.load(FilePath(L"data/SettingsTestSuite/wrong_settings.xml"))); + + REQUIRE(settings.getBool() == false); + REQUIRE(settings.getInt() == -1); + REQUIRE(settings.getFloat() == 0.01f); + REQUIRE(settings.getString() == ""); + REQUIRE(settings.getWString() == L""); +} + +TEST_CASE("settings get default value after clearing") +{ + TestSettings settings; + REQUIRE(settings.load(FilePath(L"data/SettingsTestSuite/settings.xml"))); + + settings.clear(); + REQUIRE(settings.getBool() == false); + REQUIRE(settings.getInt() == -1); + REQUIRE(settings.getFloat() == 0.01f); + REQUIRE(settings.getString() == ""); + REQUIRE(settings.getWString() == L""); +} + +TEST_CASE("settings can be set when not loaded") +{ + TestSettings settings; + + REQUIRE(settings.setBool(false)); + REQUIRE(settings.getBool() == false); + + REQUIRE(settings.setInt(2)); + REQUIRE(settings.getInt() == 2); + + REQUIRE(settings.setFloat(2.5f)); + REQUIRE(settings.getFloat() == 2.5f); + + REQUIRE(settings.setString("foo")); + REQUIRE(settings.getString() == "foo"); + + REQUIRE(settings.setWString(L"bar")); + REQUIRE(settings.getWString() == L"bar"); +} + +TEST_CASE("settings can be replaced when loaded") +{ + TestSettings settings; + REQUIRE(settings.load(FilePath(L"data/SettingsTestSuite/settings.xml"))); + + REQUIRE(settings.setBool(false)); + REQUIRE(settings.getBool() == false); + + REQUIRE(settings.setInt(2)); + REQUIRE(settings.getInt() == 2); + + REQUIRE(settings.setFloat(2.5f)); + REQUIRE(settings.getFloat() == 2.5f); + + REQUIRE(settings.setString("foo")); + REQUIRE(settings.getString() == "foo"); + + REQUIRE(settings.setWString(L"bar")); + REQUIRE(settings.getWString() == L"bar"); +} + +TEST_CASE("settings can be added when loaded") +{ + TestSettings settings; + REQUIRE(settings.load(FilePath(L"data/SettingsTestSuite/settings.xml"))); + + REQUIRE(settings.getNewBool() == false); + REQUIRE(settings.setNewBool(true)); + REQUIRE(settings.getNewBool() == true); +} + +TEST_CASE("load project settings from file") +{ + ProjectSettings settings; + REQUIRE(settings.load(FilePath(L"data/SettingsTestSuite/settings.xml"))); +} + +TEST_CASE("load source path from file") +{ + ProjectSettings projectSettings; + projectSettings.load(FilePath(L"data/SettingsTestSuite/settings.xml")); + std::shared_ptr sourceGroupSettings = + std::dynamic_pointer_cast(projectSettings.getAllSourceGroupSettings().front()); + std::vector paths = sourceGroupSettings->getSourcePaths(); + + REQUIRE(paths.size() == 1); + REQUIRE(paths[0].wstr() == L"data"); +} + +TEST_CASE("load header search paths from file") +{ + ProjectSettings projectSettings; + projectSettings.load(FilePath(L"data/SettingsTestSuite/settings.xml")); + std::shared_ptr sourceGroupSettings = + std::dynamic_pointer_cast(projectSettings.getAllSourceGroupSettings().front()); + std::vector paths = sourceGroupSettings->getHeaderSearchPaths(); + + REQUIRE(paths.size() == 2); + REQUIRE(paths[0].wstr() == L"data/"); + REQUIRE(paths[1].wstr() == L"src/"); +} diff --git a/src/test/SettingsTestSuite.h b/src/test/SettingsTestSuite.h deleted file mode 100644 index 05fc286f..00000000 --- a/src/test/SettingsTestSuite.h +++ /dev/null @@ -1,217 +0,0 @@ -#include - -#include "ProjectSettings.h" -#include "Settings.h" -#include "SourceGroupSettingsWithCxxPathsAndFlags.h" -#include "SourceGroupSettingsWithSourcePaths.h" - -class SettingsTestSuite : public CxxTest::TestSuite -{ -public: - void test_settings_get_loaded_from_file() - { - TestSettings settings; - TS_ASSERT(settings.load(FilePath(L"data/SettingsTestSuite/settings.xml"))); - } - - void test_settings_get_not_loaded_from_file() - { - TestSettings settings; - TS_ASSERT(!settings.load(FilePath(L"data/SettingsTestSuite/wrong_settings.xml"))); - } - - void test_settings_get_loaded_value() - { - TestSettings settings; - TS_ASSERT(settings.load(FilePath(L"data/SettingsTestSuite/settings.xml"))); - - TS_ASSERT_EQUALS(settings.getBool(), true); - TS_ASSERT_EQUALS(settings.getInt(), 42); - TS_ASSERT_EQUALS(settings.getFloat(), 3.1416f); - TS_ASSERT_EQUALS(settings.getString(), "Hello World!"); - TS_ASSERT_EQUALS(settings.getWString(), L"Hello World!"); - } - - void test_settings_get_default_value_when_not_loaded() - { - TestSettings settings; - TS_ASSERT_EQUALS(settings.getBool(), false); - TS_ASSERT_EQUALS(settings.getInt(), -1); - TS_ASSERT_EQUALS(settings.getFloat(), 0.01f); - TS_ASSERT_EQUALS(settings.getString(), ""); - TS_ASSERT_EQUALS(settings.getWString(), L""); - } - - void test_settings_get_default_value_when_wrongly_loaded() - { - TestSettings settings; - TS_ASSERT(!settings.load(FilePath(L"data/SettingsTestSuite/wrong_settings.xml"))); - - TS_ASSERT_EQUALS(settings.getBool(), false); - TS_ASSERT_EQUALS(settings.getInt(), -1); - TS_ASSERT_EQUALS(settings.getFloat(), 0.01f); - TS_ASSERT_EQUALS(settings.getString(), ""); - TS_ASSERT_EQUALS(settings.getWString(), L""); - } - - void test_settings_get_default_value_after_clearing() - { - TestSettings settings; - TS_ASSERT(settings.load(FilePath(L"data/SettingsTestSuite/settings.xml"))); - - settings.clear(); - TS_ASSERT_EQUALS(settings.getBool(), false); - TS_ASSERT_EQUALS(settings.getInt(), -1); - TS_ASSERT_EQUALS(settings.getFloat(), 0.01f); - TS_ASSERT_EQUALS(settings.getString(), ""); - TS_ASSERT_EQUALS(settings.getWString(), L""); - } - - void test_settings_can_be_set_when_not_loaded() - { - TestSettings settings; - - TS_ASSERT(settings.setBool(false)); - TS_ASSERT_EQUALS(settings.getBool(), false); - - TS_ASSERT(settings.setInt(2)); - TS_ASSERT_EQUALS(settings.getInt(), 2); - - TS_ASSERT(settings.setFloat(2.5f)); - TS_ASSERT_EQUALS(settings.getFloat(), 2.5f); - - TS_ASSERT(settings.setString("foo")); - TS_ASSERT_EQUALS(settings.getString(), "foo"); - - TS_ASSERT(settings.setWString(L"bar")); - TS_ASSERT_EQUALS(settings.getWString(), L"bar"); - } - - void test_settings_can_be_replaced_when_loaded() - { - TestSettings settings; - TS_ASSERT(settings.load(FilePath(L"data/SettingsTestSuite/settings.xml"))); - - TS_ASSERT(settings.setBool(false)); - TS_ASSERT_EQUALS(settings.getBool(), false); - - TS_ASSERT(settings.setInt(2)); - TS_ASSERT_EQUALS(settings.getInt(), 2); - - TS_ASSERT(settings.setFloat(2.5f)); - TS_ASSERT_EQUALS(settings.getFloat(), 2.5f); - - TS_ASSERT(settings.setString("foo")); - TS_ASSERT_EQUALS(settings.getString(), "foo"); - - TS_ASSERT(settings.setWString(L"bar")); - TS_ASSERT_EQUALS(settings.getWString(), L"bar"); - } - - void test_settings_can_be_added_when_loaded() - { - TestSettings settings; - TS_ASSERT(settings.load(FilePath(L"data/SettingsTestSuite/settings.xml"))); - - TS_ASSERT_EQUALS(settings.getNewBool(), false); - TS_ASSERT(settings.setNewBool(true)); - TS_ASSERT_EQUALS(settings.getNewBool(), true); - } - - void test_load_project_settings_from_file() - { - ProjectSettings settings; - TS_ASSERT(settings.load(FilePath(L"data/SettingsTestSuite/settings.xml"))); - } - - void test_load_source_path_from_file() - { - ProjectSettings projectSettings; - projectSettings.load(FilePath(L"data/SettingsTestSuite/settings.xml")); - std::shared_ptr sourceGroupSettings = - std::dynamic_pointer_cast(projectSettings.getAllSourceGroupSettings().front()); - std::vector paths = sourceGroupSettings->getSourcePaths(); - - TS_ASSERT_EQUALS(paths.size(), 1); - TS_ASSERT_EQUALS(paths[0].wstr(), L"data"); - } - - void test_load_header_search_paths_from_file() - { - ProjectSettings projectSettings; - projectSettings.load(FilePath(L"data/SettingsTestSuite/settings.xml")); - std::shared_ptr sourceGroupSettings = - std::dynamic_pointer_cast(projectSettings.getAllSourceGroupSettings().front()); - std::vector paths = sourceGroupSettings->getHeaderSearchPaths(); - - TS_ASSERT_EQUALS(paths.size(), 2); - TS_ASSERT_EQUALS(paths[0].wstr(), L"data/"); - TS_ASSERT_EQUALS(paths[1].wstr(), L"src/"); - } - -private: - class TestSettings - : public Settings - { - public: - bool getBool() const - { - return getValue("Bool", false); - } - - bool setBool(bool value) - { - return setValue("Bool", value); - } - - int getInt() const - { - return getValue("Int", -1); - } - - bool setInt(int value) - { - return setValue("Int", value); - } - - float getFloat() const - { - return getValue("Float", 0.01f); - } - - bool setFloat(float value) - { - return setValue("Float", value); - } - - std::string getString() const - { - return getValue("String", ""); - } - - bool setString(const std::string& value) - { - return setValue("String", value); - } - - std::wstring getWString() const - { - return getValue("WString", L""); - } - - bool setWString(const std::wstring& value) - { - return setValue("WString", value); - } - - bool getNewBool() const - { - return getValue("NewBool", false); - } - - bool setNewBool(bool value) - { - return setValue("NewBool", value); - } - }; -}; diff --git a/src/test/SharedMemoryTestSuite.cpp b/src/test/SharedMemoryTestSuite.cpp new file mode 100644 index 00000000..6570348a --- /dev/null +++ b/src/test/SharedMemoryTestSuite.cpp @@ -0,0 +1,95 @@ +#include "catch.hpp" + +#include +#include + +#include "SharedMemory.h" + +TEST_CASE("shared memory") +{ + SharedMemory memory("memory", 1000, SharedMemory::CREATE_AND_DELETE); + + { + SharedMemory::ScopedAccess access(&memory); + + REQUIRE(access.getMemorySize() == 1000); + *access.accessValue("count") = 0; + } + + std::vector> threads; + for (unsigned int i = 0; i < 4; i++) + { + threads.push_back(std::make_shared( + []() + { + SharedMemory memory("memory", 0, SharedMemory::OPEN_ONLY); + + SharedMemory::ScopedAccess access(&memory); + + if (access.getMemorySize() < 5000) + { + access.growMemory(5000 - access.getMemorySize()); + } + + int* count = access.accessValue("count"); + *count += 1; + + SharedMemory::String* str = access.accessValueWithAllocator("string"); + str->append("hi"); + + SharedMemory::Vector* nums = access.accessValueWithAllocator>("nums"); + nums->push_back(nums->size()); + + SharedMemory::Vector* strings = + access.accessValueWithAllocator>("strings"); + strings->push_back(SharedMemory::String("ho", access.getAllocator())); + + SharedMemory::Map* vals = + access.accessValueWithAllocator>("vals"); + vals->emplace(vals->size(), vals->size() * vals->size()); + } + )); + } + + for (auto& thread : threads) + { + thread->join(); + } + threads.clear(); + + { + SharedMemory::ScopedAccess access(&memory); + + REQUIRE(access.getMemorySize() == 5000); + REQUIRE(*access.accessValue("count") == 4); + const std::string value = access.accessValueWithAllocator("string")->c_str(); + REQUIRE(value == "hihihihi"); + + SharedMemory::Vector* nums = access.accessValueWithAllocator>("nums"); + REQUIRE(nums->size() == 4); + for (int i : *nums) + { + REQUIRE(i == i); + } + + SharedMemory::Vector* strings = + access.accessValueWithAllocator>("strings"); + REQUIRE(strings->size() == 4); + for (SharedMemory::String& str : *strings) + { + REQUIRE(str == "ho"); + } + + SharedMemory::Map* vals = + access.accessValueWithAllocator>("vals"); + REQUIRE(vals->size() == 4); + + size_t i = 0; + for (auto val : *vals) + { + REQUIRE(val.first == i); + REQUIRE(val.second == i * i); + i++; + } + } +} diff --git a/src/test/SharedMemoryTestSuite.h b/src/test/SharedMemoryTestSuite.h deleted file mode 100644 index ebd56905..00000000 --- a/src/test/SharedMemoryTestSuite.h +++ /dev/null @@ -1,98 +0,0 @@ -#include - -#include -#include - -#include "SharedMemory.h" - -class SharedMemoryTestSuite : public CxxTest::TestSuite -{ -public: - void test_shared_memory(void) - { - SharedMemory memory("memory", 1000, SharedMemory::CREATE_AND_DELETE); - - { - SharedMemory::ScopedAccess access(&memory); - - TS_ASSERT_EQUALS(access.getMemorySize(), 1000); - *access.accessValue("count") = 0; - } - - std::vector> threads; - for (unsigned int i = 0; i < 4; i++) - { - threads.push_back(std::make_shared( - []() - { - SharedMemory memory("memory", 0, SharedMemory::OPEN_ONLY); - - SharedMemory::ScopedAccess access(&memory); - - if (access.getMemorySize() < 5000) - { - access.growMemory(5000 - access.getMemorySize()); - } - - int* count = access.accessValue("count"); - *count += 1; - - SharedMemory::String* str = access.accessValueWithAllocator("string"); - str->append("hi"); - - SharedMemory::Vector* nums = access.accessValueWithAllocator>("nums"); - nums->push_back(nums->size()); - - SharedMemory::Vector* strings = - access.accessValueWithAllocator>("strings"); - strings->push_back(SharedMemory::String("ho", access.getAllocator())); - - SharedMemory::Map* vals = - access.accessValueWithAllocator>("vals"); - vals->emplace(vals->size(), vals->size() * vals->size()); - } - )); - } - - for (auto& thread : threads) - { - thread->join(); - } - threads.clear(); - - { - SharedMemory::ScopedAccess access(&memory); - - TS_ASSERT_EQUALS(access.getMemorySize(), 5000); - TS_ASSERT_EQUALS(*access.accessValue("count"), 4); - TS_ASSERT_EQUALS(access.accessValueWithAllocator("string")->c_str(), "hihihihi"); - - SharedMemory::Vector* nums = access.accessValueWithAllocator>("nums"); - TS_ASSERT_EQUALS(nums->size(), 4); - for (int i : *nums) - { - TS_ASSERT_EQUALS(i, i); - } - - SharedMemory::Vector* strings = - access.accessValueWithAllocator>("strings"); - TS_ASSERT_EQUALS(strings->size(), 4); - for (SharedMemory::String& str : *strings) - { - TS_ASSERT_EQUALS(str, "ho"); - } - - SharedMemory::Map* vals = - access.accessValueWithAllocator>("vals"); - TS_ASSERT_EQUALS(vals->size(), 4); - - size_t i = 0; - for (auto val : *vals) - { - TS_ASSERT_EQUALS(val.first, i); - TS_ASSERT_EQUALS(val.second, i * i); - i++; - } - } - } -}; diff --git a/src/test/SourceGroupTestSuite.cpp b/src/test/SourceGroupTestSuite.cpp new file mode 100644 index 00000000..b310d62e --- /dev/null +++ b/src/test/SourceGroupTestSuite.cpp @@ -0,0 +1,619 @@ +#include "catch.hpp" + +#include + +#include "IndexerCommandCustom.h" +#include "IndexerCommandCxx.h" +#include "IndexerCommandJava.h" +#include "JavaEnvironmentFactory.h" +#include "SourceGroupCxxEmpty.h" +#include "SourceGroupCxxCdb.h" +#include "SourceGroupCxxCodeblocks.h" +#include "SourceGroupCxxSonargraph.h" +#include "SourceGroupCustomCommand.h" +#include "SourceGroupJavaEmpty.h" +#include "SourceGroupJavaGradle.h" +#include "SourceGroupJavaMaven.h" +#include "SourceGroupJavaSonargraph.h" +#include "SourceGroupSettingsCEmpty.h" +#include "SourceGroupSettingsCppEmpty.h" +#include "SourceGroupSettingsCxxCdb.h" +#include "SourceGroupSettingsCxxCodeblocks.h" +#include "SourceGroupSettingsCxxSonargraph.h" +#include "SourceGroupSettingsCustomCommand.h" +#include "SourceGroupSettingsJavaEmpty.h" +#include "SourceGroupSettingsJavaGradle.h" +#include "SourceGroupSettingsJavaMaven.h" +#include "SourceGroupSettingsJavaSonargraph.h" +#include "ProjectSettings.h" +#include "ApplicationSettings.h" +#include "FileSystem.h" +#include "TextAccess.h" +#include "AppPath.h" +#include "utilityJava.h" +#include "utilityPathDetection.h" +#include "utilityString.h" +#include "Version.h" +#include "Application.h" + +#define REQUIRE_MESSAGE(msg, cond) do { INFO(msg); REQUIRE(cond); } while((void)0, 0) + +namespace +{ + const bool updateExpectedOutput = false; + + static FilePath getInputDirectoryPath(const std::wstring& projectName) + { + return FilePath(L"data/SourceGroupTestSuite/" + projectName + L"/input").makeAbsolute().makeCanonical(); + } + + static FilePath getOutputDirectoryPath(const std::wstring& projectName) + { + return FilePath(L"data/SourceGroupTestSuite/" + projectName + L"/expected_output").makeAbsolute().makeCanonical(); + } + + std::string setupJavaEnvironmentFactory() + { + if (!JavaEnvironmentFactory::getInstance()) + { + std::string errorString; +#ifdef _WIN32 + const std::string separator = ";"; +#else + const std::string separator = ":"; +#endif + std::string classPath = ""; + { + const std::vector jarNames = utility::getRequiredJarNames(); + for (size_t i = 0; i < jarNames.size(); i++) + { + if (i != 0) + { + classPath += separator; + } + classPath += FilePath(L"../app/data/java/lib/").concatenate(jarNames[i]).str(); + } + } + + JavaEnvironmentFactory::createInstance( + classPath, + errorString + ); + + return errorString; + } + + return ""; + } + + std::wstring indexerCommandCxxToString(std::shared_ptr indexerCommand, const FilePath& baseDirectory) + { + std::wstring result; + result += L"SourceFilePath: \"" + indexerCommand->getSourceFilePath().getRelativeTo(baseDirectory).wstr() + L"\"\n"; + for (const FilePath& indexedPath : indexerCommand->getIndexedPaths()) + { + result += L"\tIndexedPath: \"" + indexedPath.getRelativeTo(baseDirectory).wstr() + L"\"\n"; + } + for (std::wstring compilerFlag : indexerCommand->getCompilerFlags()) + { + FilePath flagAsPath(compilerFlag); + if (flagAsPath.exists()) + { + compilerFlag = flagAsPath.getRelativeTo(baseDirectory).wstr(); + } + result += L"\tCompilerFlag: \"" + compilerFlag + L"\"\n"; + } + for (const FilePathFilter& filter : indexerCommand->getExcludeFilters()) + { + result += L"\tExcludeFilter: \"" + filter.wstr() + L"\"\n"; + } + return result; + } + + std::wstring indexerCommandJavaToString(std::shared_ptr indexerCommand, const FilePath& baseDirectory) + { + std::wstring result; + result += L"SourceFilePath: \"" + indexerCommand->getSourceFilePath().getRelativeTo(baseDirectory).wstr() + L"\"\n"; + result += L"\tLanguageStandard: \"" + indexerCommand->getLanguageStandard() + L"\"\n"; + for (const FilePath& classPathItem : indexerCommand->getClassPath()) + { + result += L"\tClassPathItem: \"" + classPathItem.getRelativeTo(baseDirectory).wstr() + L"\"\n"; + } + return result; + } + + std::wstring indexerCommandCustomToString(std::shared_ptr indexerCommand, const FilePath& baseDirectory) + { + std::wstring result; + result += L"SourceFilePath: \"" + indexerCommand->getSourceFilePath().getRelativeTo(baseDirectory).wstr() + L"\"\n"; + result += L"\tCustom Command: \"" + indexerCommand->getCustomCommand() + L"\"\n"; + return result; + } + + std::wstring indexerCommandToString(std::shared_ptr indexerCommand, const FilePath& baseDirectory) + { + if (indexerCommand) + { + if (std::shared_ptr indexerCommandCxx = std::dynamic_pointer_cast(indexerCommand)) + { + return indexerCommandCxxToString(indexerCommandCxx, baseDirectory); + } + if (std::shared_ptr indexerCommandJava = std::dynamic_pointer_cast(indexerCommand)) + { + return indexerCommandJavaToString(indexerCommandJava, baseDirectory); + } + if (std::shared_ptr indexerCommandCustom = std::dynamic_pointer_cast(indexerCommand)) + { + return indexerCommandCustomToString(indexerCommandCustom, baseDirectory); + } + return L"Unsupported indexer command type: " + utility::decodeFromUtf8(indexerCommandTypeToString(indexerCommand->getIndexerCommandType())); + } + return L"No IndexerCommand provided."; + } + + std::shared_ptr generateExpectedOutput( + std::wstring projectName, + std::shared_ptr sourceGroup) + { + const FilePath projectDataRoot = getInputDirectoryPath(projectName).makeAbsolute(); + + std::vector> indexerCommands = sourceGroup->getIndexerCommands(sourceGroup->getAllSourceFilePaths()); + + std::sort( + indexerCommands.begin(), + indexerCommands.end(), + [](std::shared_ptr a, std::shared_ptr b) + { + return a->getSourceFilePath().wstr() < b->getSourceFilePath().wstr(); + } + ); + + std::wstring outputString; + for (std::shared_ptr indexerCommand : indexerCommands) + { + outputString += indexerCommandToString(indexerCommand, projectDataRoot); + } + + return TextAccess::createFromString(utility::encodeToUtf8(outputString)); + } + + void generateAndCompareExpectedOutput( + std::wstring projectName, + std::shared_ptr sourceGroup) + { + const std::shared_ptr output = generateExpectedOutput(projectName, sourceGroup); +#ifdef WIN32 + const std::wstring expectedOutputFileName = L"output_windows.txt"; +#else + const std::wstring expectedOutputFileName = L"output_unix.txt"; +#endif + const FilePath expectedOutputFilePath = getOutputDirectoryPath(projectName).concatenate(expectedOutputFileName); + if (updateExpectedOutput || !expectedOutputFilePath.exists()) + { + std::ofstream expectedOutputFile; + expectedOutputFile.open(expectedOutputFilePath.str()); + expectedOutputFile << output->getText(); + expectedOutputFile.close(); + } + else + { + const std::shared_ptr expectedOutput = TextAccess::createFromFile(expectedOutputFilePath); + REQUIRE_MESSAGE(("Output does not match the expected line count for project \"" + utility::encodeToUtf8(projectName) + "\".").c_str(), expectedOutput->getLineCount() == output->getLineCount()); + if (expectedOutput->getLineCount() == output->getLineCount()) + { + for (size_t i = 1; i <= expectedOutput->getLineCount(); i++) + { + REQUIRE(expectedOutput->getLine(i) == output->getLine(i)); + } + } + } + } +} + +TEST_CASE("finds all jar dependencies") +{ + for (const std::wstring& jarName : utility::getRequiredJarNames()) + { + FilePath jarPath = FilePath(L"../app/data/java/lib/").concatenate(jarName); + REQUIRE_MESSAGE("Jar dependency path does not exist: " + jarPath.str(), jarPath.exists()); + } +} + +TEST_CASE("can setup environment factory") +{ + std::vector javaPaths = utility::getJavaRuntimePathDetector()->getPaths(); + if (!javaPaths.empty()) + { + ApplicationSettings::getInstance()->setJavaPath(javaPaths[0]); + } + const std::string errorString = setupJavaEnvironmentFactory(); + + REQUIRE("" == errorString); + + // if this one fails, maybe your java_path in the test settings is wrong. + REQUIRE(JavaEnvironmentFactory::getInstance().use_count() >= 1); +} + +TEST_CASE("can create application instance") +{ + // required to query in SourceGroup for dialog view... this is not a very elegant solution. should be refactored to pass dialog view to SourceGroup on creation. + Application::createInstance(Version(), nullptr, nullptr); + REQUIRE(Application::getInstance().use_count() >= 1); +} + +TEST_CASE("source group cxx c empty generates expected output") +{ + const std::wstring projectName = L"cxx_c_empty"; + + ProjectSettings projectSettings; + projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); + + std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); + sourceGroupSettings->setSourcePaths({ getInputDirectoryPath(projectName).concatenate(L"src") }); + sourceGroupSettings->setSourceExtensions({ L".c" }); + sourceGroupSettings->setExcludeFilterStrings({ L"**/excluded/**" }); + sourceGroupSettings->setTargetOptionsEnabled(true); + sourceGroupSettings->setTargetArch(L"test_arch"); + sourceGroupSettings->setTargetVendor(L"test_vendor"); + sourceGroupSettings->setTargetSys(L"test_sys"); + sourceGroupSettings->setTargetAbi(L"test_abi"); + sourceGroupSettings->setCStandard(L"c11"); + sourceGroupSettings->setHeaderSearchPaths({ getInputDirectoryPath(projectName).concatenate(L"header_search/local") }); + sourceGroupSettings->setFrameworkSearchPaths({ getInputDirectoryPath(projectName).concatenate(L"framework_search/local") }); + sourceGroupSettings->setCompilerFlags({ L"-local-flag" }); + + std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); + + std::vector storedHeaderSearchPaths = applicationSettings->getHeaderSearchPaths(); + std::vector storedFrameworkSearchPaths = applicationSettings->getFrameworkSearchPaths(); + + applicationSettings->setHeaderSearchPaths({ FilePath(L"test/header/search/path") }); + applicationSettings->setFrameworkSearchPaths({ FilePath(L"test/framework/search/path") }); + + generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); + + applicationSettings->setHeaderSearchPaths(storedHeaderSearchPaths); + applicationSettings->setFrameworkSearchPaths(storedFrameworkSearchPaths); +} + +TEST_CASE("source group cxx cpp empty generates expected output") +{ + const std::wstring projectName = L"cxx_cpp_empty"; + + ProjectSettings projectSettings; + projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); + + std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); + sourceGroupSettings->setSourcePaths({ getInputDirectoryPath(projectName).concatenate(L"/src") }); + sourceGroupSettings->setSourceExtensions({ L".cpp" }); + sourceGroupSettings->setExcludeFilterStrings({ L"**/excluded/**" }); + sourceGroupSettings->setTargetOptionsEnabled(true); + sourceGroupSettings->setTargetArch(L"test_arch"); + sourceGroupSettings->setTargetVendor(L"test_vendor"); + sourceGroupSettings->setTargetSys(L"test_sys"); + sourceGroupSettings->setTargetAbi(L"test_abi"); + sourceGroupSettings->setCppStandard(L"c++11"); + sourceGroupSettings->setHeaderSearchPaths({ getInputDirectoryPath(projectName).concatenate(L"header_search/local") }); + sourceGroupSettings->setFrameworkSearchPaths({ getInputDirectoryPath(projectName).concatenate(L"framework_search/local") }); + sourceGroupSettings->setCompilerFlags({ L"-local-flag" }); + + std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); + + std::vector storedHeaderSearchPaths = applicationSettings->getHeaderSearchPaths(); + std::vector storedFrameworkSearchPaths = applicationSettings->getFrameworkSearchPaths(); + + applicationSettings->setHeaderSearchPaths({ FilePath(L"test/header/search/path") }); + applicationSettings->setFrameworkSearchPaths({ FilePath(L"test/framework/search/path") }); + + generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); + + applicationSettings->setHeaderSearchPaths(storedHeaderSearchPaths); + applicationSettings->setFrameworkSearchPaths(storedFrameworkSearchPaths); +} + +TEST_CASE("source group cxx codeblocks generates expected output") +{ + const std::wstring projectName = L"cxx_codeblocks"; + const FilePath cbpPath = getInputDirectoryPath(projectName).concatenate(L"project.cbp"); + const FilePath sourceCbpPath = getInputDirectoryPath(projectName).concatenate(L"project.cbp.in"); + + FileSystem::remove(cbpPath); + + { + std::ofstream fileStream; + fileStream.open(cbpPath.str(), std::ios::app); + fileStream << utility::replace( + TextAccess::createFromFile(sourceCbpPath)->getText(), "", getInputDirectoryPath(projectName).concatenate(L"src").getAbsolute().str() + ); + fileStream.close(); + } + + ProjectSettings projectSettings; + projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); + + std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); + sourceGroupSettings->setCodeblocksProjectPath(cbpPath); + sourceGroupSettings->setCppStandard(L"c++11"); + sourceGroupSettings->setCStandard(L"c11"); + sourceGroupSettings->setExcludeFilterStrings({ L"**/excluded/**" }); + sourceGroupSettings->setIndexedHeaderPaths({ FilePath(L"test/indexed/header/path") }); + sourceGroupSettings->setSourceExtensions({ L".cpp", L".c" }); + sourceGroupSettings->setHeaderSearchPaths({ getInputDirectoryPath(projectName).concatenate(L"header_search/local") }); + sourceGroupSettings->setFrameworkSearchPaths({ getInputDirectoryPath(projectName).concatenate(L"framework_search/local") }); + sourceGroupSettings->setCompilerFlags({ L"-local-flag" }); + + std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); + + std::vector storedHeaderSearchPaths = applicationSettings->getHeaderSearchPaths(); + std::vector storedFrameworkSearchPaths = applicationSettings->getFrameworkSearchPaths(); + + applicationSettings->setHeaderSearchPaths({ FilePath(L"test/header/search/path") }); + applicationSettings->setFrameworkSearchPaths({ FilePath(L"test/framework/search/path") }); + + generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); + + applicationSettings->setHeaderSearchPaths(storedHeaderSearchPaths); + applicationSettings->setFrameworkSearchPaths(storedFrameworkSearchPaths); + + FileSystem::remove(cbpPath); +} + +TEST_CASE("source group cxx cdb generates expected output") +{ + const std::wstring projectName = L"cxx_cdb"; + + ProjectSettings projectSettings; + projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); + + std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); + sourceGroupSettings->setIndexedHeaderPaths({ FilePath(L"test/indexed/header/path") }); + sourceGroupSettings->setCompilationDatabasePath(getInputDirectoryPath(projectName).concatenate(L"compile_commands.json")); + sourceGroupSettings->setExcludeFilterStrings({ L"**/excluded/**" }); + sourceGroupSettings->setHeaderSearchPaths({ getInputDirectoryPath(projectName).concatenate(L"header_search/local") }); + sourceGroupSettings->setFrameworkSearchPaths({ getInputDirectoryPath(projectName).concatenate(L"framework_search/local") }); + sourceGroupSettings->setCompilerFlags({ L"-local-flag" }); + + std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); + + std::vector storedHeaderSearchPaths = applicationSettings->getHeaderSearchPaths(); + std::vector storedFrameworkSearchPaths = applicationSettings->getFrameworkSearchPaths(); + + applicationSettings->setHeaderSearchPaths({ FilePath(L"test/header/search/path") }); + applicationSettings->setFrameworkSearchPaths({ FilePath(L"test/framework/search/path") }); + + generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); + + applicationSettings->setHeaderSearchPaths(storedHeaderSearchPaths); + applicationSettings->setFrameworkSearchPaths(storedFrameworkSearchPaths); +} + +TEST_CASE("source group cxx sonargraph with cmake json modules generates expected output") +{ + const std::wstring projectName = L"cxx_sonargraph_cmake_json"; + + ProjectSettings projectSettings; + projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); + + std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); + sourceGroupSettings->setIndexedHeaderPaths({ FilePath(L"test/indexed/header/path") }); + sourceGroupSettings->setSonargraphProjectPath(getInputDirectoryPath(projectName).concatenate(L"sonargraph/system.sonargraph")); + + std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); + + std::vector storedHeaderSearchPaths = applicationSettings->getHeaderSearchPaths(); + std::vector storedFrameworkSearchPaths = applicationSettings->getFrameworkSearchPaths(); + + applicationSettings->setHeaderSearchPaths({ FilePath(L"test/header/search/path") }); + applicationSettings->setFrameworkSearchPaths({ FilePath(L"test/framework/search/path") }); + + generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); + + applicationSettings->setHeaderSearchPaths(storedHeaderSearchPaths); + applicationSettings->setFrameworkSearchPaths(storedFrameworkSearchPaths); +} + +TEST_CASE("source group cxx sonargraph with cpp manual modules generates expected output") +{ + const std::wstring projectName = L"cxx_sonargraph_cpp_manual"; + + ProjectSettings projectSettings; + projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); + + std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); + sourceGroupSettings->setIndexedHeaderPaths({ FilePath(L"test/indexed/header/path") }); + sourceGroupSettings->setSonargraphProjectPath(getInputDirectoryPath(projectName).concatenate(L"/sonargraph/system.sonargraph")); + + std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); + + std::vector storedHeaderSearchPaths = applicationSettings->getHeaderSearchPaths(); + std::vector storedFrameworkSearchPaths = applicationSettings->getFrameworkSearchPaths(); + + applicationSettings->setHeaderSearchPaths({ FilePath(L"test/header/search/path") }); + applicationSettings->setFrameworkSearchPaths({ FilePath(L"test/framework/search/path") }); + + generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); + + applicationSettings->setHeaderSearchPaths(storedHeaderSearchPaths); + applicationSettings->setFrameworkSearchPaths(storedFrameworkSearchPaths); +} + +TEST_CASE("sourcegroup java empty generates expected output") +{ + const std::wstring projectName = L"java_empty"; + + ProjectSettings projectSettings; + projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); + + std::shared_ptr sourceGroupSettings = + std::make_shared("fake_id", &projectSettings); + sourceGroupSettings->setSourceExtensions({ L".java" }); + sourceGroupSettings->setExcludeFilterStrings({ L"**/Foo.java" }); + sourceGroupSettings->setJavaStandard({ L"10" }); + sourceGroupSettings->setSourcePaths({ getInputDirectoryPath(projectName).concatenate(L"src") }); + sourceGroupSettings->setUseJreSystemLibrary(true); + sourceGroupSettings->setClasspath({ + getInputDirectoryPath(projectName).concatenate(L"lib/dependency.jar"), getInputDirectoryPath(projectName).concatenate(L"classpath_dir") + }); + + std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); + + std::vector storedJreSystemLibraryPaths = applicationSettings->getJreSystemLibraryPaths(); + + applicationSettings->setJreSystemLibraryPaths({ FilePath(L"test/jre/system/library/path.jar") }); + + generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); + + applicationSettings->setJreSystemLibraryPaths(storedJreSystemLibraryPaths); +} + +TEST_CASE("sourcegroup java gradle generates expected output") +{ +#ifndef __linux__ + const std::wstring projectName = L"java_gradle"; + + ProjectSettings projectSettings; + projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); + + std::shared_ptr sourceGroupSettings = + std::make_shared("fake_id", &projectSettings); + sourceGroupSettings->setSourceExtensions({ L".java" }); + sourceGroupSettings->setExcludeFilterStrings({ L"**/HelloWorld.java" }); + sourceGroupSettings->setJavaStandard({ L"10" }); + sourceGroupSettings->setGradleProjectFilePath({ getInputDirectoryPath(projectName).concatenate(L"build.gradle") }); + sourceGroupSettings->setShouldIndexGradleTests(true); + + std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); + + const FilePath storedAppPath = AppPath::getAppPath(); + AppPath::setAppPath(storedAppPath.getConcatenated(L"../app").makeAbsolute()); + + std::vector storedJreSystemLibraryPaths = applicationSettings->getJreSystemLibraryPaths(); + applicationSettings->setJreSystemLibraryPaths({ FilePath(L"test/jre/system/library/path.jar") }); + + generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); + + applicationSettings->setJreSystemLibraryPaths(storedJreSystemLibraryPaths); + AppPath::setAppPath(storedAppPath); +#endif +} + +TEST_CASE("sourcegroup java maven generates expected output") +{ + std::vector mavenPaths = utility::getMavenExecutablePathDetector()->getPaths(); + + REQUIRE(!mavenPaths.empty()); + + if (!mavenPaths.empty()) + { + ApplicationSettings::getInstance()->setMavenPath(mavenPaths.front()); + } + + const std::wstring projectName = L"java_maven"; + + ProjectSettings projectSettings; + projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); + + std::shared_ptr sourceGroupSettings = + std::make_shared("fake_id", &projectSettings); + sourceGroupSettings->setSourceExtensions({ L".java" }); + sourceGroupSettings->setExcludeFilterStrings({ L"**/Foo.java" }); + sourceGroupSettings->setJavaStandard({ L"10" }); + sourceGroupSettings->setMavenProjectFilePath({ getInputDirectoryPath(projectName).concatenate(L"my-app/pom.xml") }); + sourceGroupSettings->setShouldIndexMavenTests(true); + + std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); + + std::vector storedJreSystemLibraryPaths = applicationSettings->getJreSystemLibraryPaths(); + + applicationSettings->setJreSystemLibraryPaths({ FilePath(L"test/jre/system/library/path.jar") }); + + generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); + + applicationSettings->setJreSystemLibraryPaths(storedJreSystemLibraryPaths); +} + +TEST_CASE("sourcegroup java sonargraph with java modules generates expected output") +{ + const std::wstring projectName = L"java_sonargraph"; + + ProjectSettings projectSettings; + projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); + + std::shared_ptr sourceGroupSettings = + std::make_shared("fake_id", &projectSettings); + sourceGroupSettings->setUseJreSystemLibrary(true); + sourceGroupSettings->setClasspath({ + FilePath(L"test/classpath/file.jar"), FilePath(L"test/classpath/dir") + }); + sourceGroupSettings->setSonargraphProjectPath(getInputDirectoryPath(projectName).concatenate(L"sonargraph/system.sonargraph")); + + std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); + + std::vector storedJreSystemLibraryPaths = applicationSettings->getJreSystemLibraryPaths(); + + applicationSettings->setJreSystemLibraryPaths({ FilePath(L"test/jre/system/library/path.jar") }); + + generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); + + applicationSettings->setJreSystemLibraryPaths(storedJreSystemLibraryPaths); +} + +TEST_CASE("source group custom command generates expected output") +{ + const std::wstring projectName = L"custom_command"; + + ProjectSettings projectSettings; + projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); + + std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); + sourceGroupSettings->setCustomCommand(L"echo \"Hello World\""); + sourceGroupSettings->setSourcePaths({ getInputDirectoryPath(projectName).concatenate(L"/src") }); + sourceGroupSettings->setSourceExtensions({ L".txt" }); + sourceGroupSettings->setExcludeFilterStrings({ L"**/excluded/**" }); + + generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); +} + +// Special Tests + +TEST_CASE("sourcegroup java sonargraph with cpp modules does not generate output") +{ + const std::wstring projectName = L"cxx_sonargraph_cpp_manual"; + const FilePath sonargraphProjectFilePath(getInputDirectoryPath(projectName).concatenate(L"sonargraph/system.sonargraph")); + + REQUIRE(sonargraphProjectFilePath.exists()); + + ProjectSettings projectSettings; + projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); + + std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); + sourceGroupSettings->setSonargraphProjectPath(sonargraphProjectFilePath); + + std::shared_ptr output = generateExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); + + REQUIRE(0 == output->getLineCount()); +} + +TEST_CASE("sourcegroup cxx sonargraph with java modules does not generate output") +{ + const std::wstring projectName = L"java_sonargraph"; + const FilePath sonargraphProjectFilePath(getInputDirectoryPath(projectName).concatenate(L"sonargraph/system.sonargraph")); + + REQUIRE(sonargraphProjectFilePath.exists()); + + ProjectSettings projectSettings; + projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); + + std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); + sourceGroupSettings->setSonargraphProjectPath(sonargraphProjectFilePath); + + std::shared_ptr output = generateExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); + + REQUIRE(0 == output->getLineCount()); +} + +TEST_CASE("can destroy application instance") +{ + Application::destroyInstance(); + REQUIRE(0 == Application::getInstance().use_count()); +} diff --git a/src/test/SourceGroupTestSuite.h b/src/test/SourceGroupTestSuite.h deleted file mode 100644 index 952bf8ad..00000000 --- a/src/test/SourceGroupTestSuite.h +++ /dev/null @@ -1,622 +0,0 @@ -#include - -#include - -#include "IndexerCommandCustom.h" -#include "IndexerCommandCxx.h" -#include "IndexerCommandJava.h" -#include "JavaEnvironmentFactory.h" -#include "SourceGroupCxxEmpty.h" -#include "SourceGroupCxxCdb.h" -#include "SourceGroupCxxCodeblocks.h" -#include "SourceGroupCxxSonargraph.h" -#include "SourceGroupCustomCommand.h" -#include "SourceGroupJavaEmpty.h" -#include "SourceGroupJavaGradle.h" -#include "SourceGroupJavaMaven.h" -#include "SourceGroupJavaSonargraph.h" -#include "SourceGroupSettingsCEmpty.h" -#include "SourceGroupSettingsCppEmpty.h" -#include "SourceGroupSettingsCxxCdb.h" -#include "SourceGroupSettingsCxxCodeblocks.h" -#include "SourceGroupSettingsCxxSonargraph.h" -#include "SourceGroupSettingsCustomCommand.h" -#include "SourceGroupSettingsJavaEmpty.h" -#include "SourceGroupSettingsJavaGradle.h" -#include "SourceGroupSettingsJavaMaven.h" -#include "SourceGroupSettingsJavaSonargraph.h" -#include "ProjectSettings.h" -#include "ApplicationSettings.h" -#include "FileSystem.h" -#include "TextAccess.h" -#include "AppPath.h" -#include "utilityJava.h" -#include "utilityPathDetection.h" -#include "utilityString.h" -#include "Version.h" -#include "Application.h" - -class SourceGroupTestSuite: public CxxTest::TestSuite -{ -public: - static const bool s_updateExpectedOutput = false; - - void test_finds_all_jar_dependencies() - { - for (const std::wstring& jarName : utility::getRequiredJarNames()) - { - FilePath jarPath = FilePath(L"../app/data/java/lib/").concatenate(jarName); - TSM_ASSERT(L"Jar dependency path does not exist: " + jarPath.wstr(), jarPath.exists()); - } - } - - void test_can_setup_environment_factory() - { - std::vector javaPaths = utility::getJavaRuntimePathDetector()->getPaths(); - if (!javaPaths.empty()) - { - ApplicationSettings::getInstance()->setJavaPath(javaPaths[0]); - } - - const std::string errorString = setupJavaEnvironmentFactory(); - - TS_ASSERT_EQUALS("", errorString); - - // if this one fails, maybe your java_path in the test settings is wrong. - TS_ASSERT_LESS_THAN_EQUALS(1, JavaEnvironmentFactory::getInstance().use_count()); - } - - void test_can_create_application_instance() - { - // required to query in SourceGroup for dialog view... this is not a very elegant solution. should be refactored to pass dialog view to SourceGroup on creation. - Application::createInstance(Version(), nullptr, nullptr); - TS_ASSERT_LESS_THAN_EQUALS(1, Application::getInstance().use_count()); - } - - void test_source_group_cxx_c_empty_generates_expected_output() - { - const std::wstring projectName = L"cxx_c_empty"; - - ProjectSettings projectSettings; - projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); - - std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); - sourceGroupSettings->setSourcePaths({ getInputDirectoryPath(projectName).concatenate(L"src") }); - sourceGroupSettings->setSourceExtensions({ L".c" }); - sourceGroupSettings->setExcludeFilterStrings({ L"**/excluded/**" }); - sourceGroupSettings->setTargetOptionsEnabled(true); - sourceGroupSettings->setTargetArch(L"test_arch"); - sourceGroupSettings->setTargetVendor(L"test_vendor"); - sourceGroupSettings->setTargetSys(L"test_sys"); - sourceGroupSettings->setTargetAbi(L"test_abi"); - sourceGroupSettings->setCStandard(L"c11"); - sourceGroupSettings->setHeaderSearchPaths({ getInputDirectoryPath(projectName).concatenate(L"header_search/local") }); - sourceGroupSettings->setFrameworkSearchPaths({ getInputDirectoryPath(projectName).concatenate(L"framework_search/local") }); - sourceGroupSettings->setCompilerFlags({ L"-local-flag" }); - - std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); - - std::vector storedHeaderSearchPaths = applicationSettings->getHeaderSearchPaths(); - std::vector storedFrameworkSearchPaths = applicationSettings->getFrameworkSearchPaths(); - - applicationSettings->setHeaderSearchPaths({ FilePath(L"test/header/search/path") }); - applicationSettings->setFrameworkSearchPaths({ FilePath(L"test/framework/search/path") }); - - generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); - - applicationSettings->setHeaderSearchPaths(storedHeaderSearchPaths); - applicationSettings->setFrameworkSearchPaths(storedFrameworkSearchPaths); - } - - void test_source_group_cxx_cpp_empty_generates_expected_output() - { - const std::wstring projectName = L"cxx_cpp_empty"; - - ProjectSettings projectSettings; - projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); - - std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); - sourceGroupSettings->setSourcePaths({ getInputDirectoryPath(projectName).concatenate(L"/src") }); - sourceGroupSettings->setSourceExtensions({ L".cpp" }); - sourceGroupSettings->setExcludeFilterStrings({ L"**/excluded/**" }); - sourceGroupSettings->setTargetOptionsEnabled(true); - sourceGroupSettings->setTargetArch(L"test_arch"); - sourceGroupSettings->setTargetVendor(L"test_vendor"); - sourceGroupSettings->setTargetSys(L"test_sys"); - sourceGroupSettings->setTargetAbi(L"test_abi"); - sourceGroupSettings->setCppStandard(L"c++11"); - sourceGroupSettings->setHeaderSearchPaths({ getInputDirectoryPath(projectName).concatenate(L"header_search/local") }); - sourceGroupSettings->setFrameworkSearchPaths({ getInputDirectoryPath(projectName).concatenate(L"framework_search/local") }); - sourceGroupSettings->setCompilerFlags({ L"-local-flag" }); - - std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); - - std::vector storedHeaderSearchPaths = applicationSettings->getHeaderSearchPaths(); - std::vector storedFrameworkSearchPaths = applicationSettings->getFrameworkSearchPaths(); - - applicationSettings->setHeaderSearchPaths({ FilePath(L"test/header/search/path") }); - applicationSettings->setFrameworkSearchPaths({ FilePath(L"test/framework/search/path") }); - - generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); - - applicationSettings->setHeaderSearchPaths(storedHeaderSearchPaths); - applicationSettings->setFrameworkSearchPaths(storedFrameworkSearchPaths); - } - - void test_source_group_cxx_codeblocks_generates_expected_output() - { - const std::wstring projectName = L"cxx_codeblocks"; - const FilePath cbpPath = getInputDirectoryPath(projectName).concatenate(L"project.cbp"); - const FilePath sourceCbpPath = getInputDirectoryPath(projectName).concatenate(L"project.cbp.in"); - - FileSystem::remove(cbpPath); - - { - std::ofstream fileStream; - fileStream.open(cbpPath.str(), std::ios::app); - fileStream << utility::replace( - TextAccess::createFromFile(sourceCbpPath)->getText(), "", getInputDirectoryPath(projectName).concatenate(L"src").getAbsolute().str() - ); - fileStream.close(); - } - - ProjectSettings projectSettings; - projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); - - std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); - sourceGroupSettings->setCodeblocksProjectPath(cbpPath); - sourceGroupSettings->setCppStandard(L"c++11"); - sourceGroupSettings->setCStandard(L"c11"); - sourceGroupSettings->setExcludeFilterStrings({ L"**/excluded/**" }); - sourceGroupSettings->setIndexedHeaderPaths({ FilePath(L"test/indexed/header/path") }); - sourceGroupSettings->setSourceExtensions({ L".cpp", L".c" }); - sourceGroupSettings->setHeaderSearchPaths({ getInputDirectoryPath(projectName).concatenate(L"header_search/local") }); - sourceGroupSettings->setFrameworkSearchPaths({ getInputDirectoryPath(projectName).concatenate(L"framework_search/local") }); - sourceGroupSettings->setCompilerFlags({ L"-local-flag" }); - - std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); - - std::vector storedHeaderSearchPaths = applicationSettings->getHeaderSearchPaths(); - std::vector storedFrameworkSearchPaths = applicationSettings->getFrameworkSearchPaths(); - - applicationSettings->setHeaderSearchPaths({ FilePath(L"test/header/search/path") }); - applicationSettings->setFrameworkSearchPaths({ FilePath(L"test/framework/search/path") }); - - generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); - - applicationSettings->setHeaderSearchPaths(storedHeaderSearchPaths); - applicationSettings->setFrameworkSearchPaths(storedFrameworkSearchPaths); - - FileSystem::remove(cbpPath); - } - - void test_source_group_cxx_cdb_generates_expected_output() - { - const std::wstring projectName = L"cxx_cdb"; - - ProjectSettings projectSettings; - projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); - - std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); - sourceGroupSettings->setIndexedHeaderPaths({ FilePath(L"test/indexed/header/path") }); - sourceGroupSettings->setCompilationDatabasePath(getInputDirectoryPath(projectName).concatenate(L"compile_commands.json")); - sourceGroupSettings->setExcludeFilterStrings({ L"**/excluded/**" }); - sourceGroupSettings->setHeaderSearchPaths({ getInputDirectoryPath(projectName).concatenate(L"header_search/local") }); - sourceGroupSettings->setFrameworkSearchPaths({ getInputDirectoryPath(projectName).concatenate(L"framework_search/local") }); - sourceGroupSettings->setCompilerFlags({ L"-local-flag" }); - - std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); - - std::vector storedHeaderSearchPaths = applicationSettings->getHeaderSearchPaths(); - std::vector storedFrameworkSearchPaths = applicationSettings->getFrameworkSearchPaths(); - - applicationSettings->setHeaderSearchPaths({ FilePath(L"test/header/search/path") }); - applicationSettings->setFrameworkSearchPaths({ FilePath(L"test/framework/search/path") }); - - generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); - - applicationSettings->setHeaderSearchPaths(storedHeaderSearchPaths); - applicationSettings->setFrameworkSearchPaths(storedFrameworkSearchPaths); - } - - void test_source_group_cxx_sonargraph_with_cmake_json_modules_generates_expected_output() - { - const std::wstring projectName = L"cxx_sonargraph_cmake_json"; - - ProjectSettings projectSettings; - projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); - - std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); - sourceGroupSettings->setIndexedHeaderPaths({ FilePath(L"test/indexed/header/path") }); - sourceGroupSettings->setSonargraphProjectPath(getInputDirectoryPath(projectName).concatenate(L"sonargraph/system.sonargraph")); - - std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); - - std::vector storedHeaderSearchPaths = applicationSettings->getHeaderSearchPaths(); - std::vector storedFrameworkSearchPaths = applicationSettings->getFrameworkSearchPaths(); - - applicationSettings->setHeaderSearchPaths({ FilePath(L"test/header/search/path") }); - applicationSettings->setFrameworkSearchPaths({ FilePath(L"test/framework/search/path") }); - - generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); - - applicationSettings->setHeaderSearchPaths(storedHeaderSearchPaths); - applicationSettings->setFrameworkSearchPaths(storedFrameworkSearchPaths); - } - - void test_source_group_cxx_sonargraph_with_cpp_manual_modules_generates_expected_output() - { - const std::wstring projectName = L"cxx_sonargraph_cpp_manual"; - - ProjectSettings projectSettings; - projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); - - std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); - sourceGroupSettings->setIndexedHeaderPaths({ FilePath(L"test/indexed/header/path") }); - sourceGroupSettings->setSonargraphProjectPath(getInputDirectoryPath(projectName).concatenate(L"/sonargraph/system.sonargraph")); - - std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); - - std::vector storedHeaderSearchPaths = applicationSettings->getHeaderSearchPaths(); - std::vector storedFrameworkSearchPaths = applicationSettings->getFrameworkSearchPaths(); - - applicationSettings->setHeaderSearchPaths({ FilePath(L"test/header/search/path") }); - applicationSettings->setFrameworkSearchPaths({ FilePath(L"test/framework/search/path") }); - - generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); - - applicationSettings->setHeaderSearchPaths(storedHeaderSearchPaths); - applicationSettings->setFrameworkSearchPaths(storedFrameworkSearchPaths); - } - - void test_sourcegroup_java_empty_generates_expected_output() - { - const std::wstring projectName = L"java_empty"; - - ProjectSettings projectSettings; - projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); - - std::shared_ptr sourceGroupSettings = - std::make_shared("fake_id", &projectSettings); - sourceGroupSettings->setSourceExtensions({ L".java" }); - sourceGroupSettings->setExcludeFilterStrings({ L"**/Foo.java" }); - sourceGroupSettings->setJavaStandard({ L"10" }); - sourceGroupSettings->setSourcePaths({ getInputDirectoryPath(projectName).concatenate(L"src") }); - sourceGroupSettings->setUseJreSystemLibrary(true); - sourceGroupSettings->setClasspath({ - getInputDirectoryPath(projectName).concatenate(L"lib/dependency.jar"), getInputDirectoryPath(projectName).concatenate(L"classpath_dir") - }); - - std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); - - std::vector storedJreSystemLibraryPaths = applicationSettings->getJreSystemLibraryPaths(); - - applicationSettings->setJreSystemLibraryPaths({ FilePath(L"test/jre/system/library/path.jar") }); - - generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); - - applicationSettings->setJreSystemLibraryPaths(storedJreSystemLibraryPaths); - } - - void test_sourcegroup_java_gradle_generates_expected_output() - { -#ifndef __linux__ - const std::wstring projectName = L"java_gradle"; - - ProjectSettings projectSettings; - projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); - - std::shared_ptr sourceGroupSettings = - std::make_shared("fake_id", &projectSettings); - sourceGroupSettings->setSourceExtensions({ L".java" }); - sourceGroupSettings->setExcludeFilterStrings({ L"**/HelloWorld.java" }); - sourceGroupSettings->setJavaStandard({ L"10" }); - sourceGroupSettings->setGradleProjectFilePath({ getInputDirectoryPath(projectName).concatenate(L"build.gradle") }); - sourceGroupSettings->setShouldIndexGradleTests(true); - - std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); - - const FilePath storedAppPath = AppPath::getAppPath(); - AppPath::setAppPath(storedAppPath.getConcatenated(L"../app").makeAbsolute()); - - std::vector storedJreSystemLibraryPaths = applicationSettings->getJreSystemLibraryPaths(); - applicationSettings->setJreSystemLibraryPaths({ FilePath(L"test/jre/system/library/path.jar") }); - - generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); - - applicationSettings->setJreSystemLibraryPaths(storedJreSystemLibraryPaths); - AppPath::setAppPath(storedAppPath); -#endif - } - - void test_sourcegroup_java_maven_generates_expected_output() - { - std::vector mavenPaths = utility::getMavenExecutablePathDetector()->getPaths(); - - TS_ASSERT(!mavenPaths.empty()); - - if (!mavenPaths.empty()) - { - ApplicationSettings::getInstance()->setMavenPath(mavenPaths.front()); - } - - const std::wstring projectName = L"java_maven"; - - ProjectSettings projectSettings; - projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); - - std::shared_ptr sourceGroupSettings = - std::make_shared("fake_id", &projectSettings); - sourceGroupSettings->setSourceExtensions({ L".java" }); - sourceGroupSettings->setExcludeFilterStrings({ L"**/Foo.java" }); - sourceGroupSettings->setJavaStandard({ L"10" }); - sourceGroupSettings->setMavenProjectFilePath({ getInputDirectoryPath(projectName).concatenate(L"my-app/pom.xml") }); - sourceGroupSettings->setShouldIndexMavenTests(true); - - std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); - - std::vector storedJreSystemLibraryPaths = applicationSettings->getJreSystemLibraryPaths(); - - applicationSettings->setJreSystemLibraryPaths({ FilePath(L"test/jre/system/library/path.jar") }); - - generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); - - applicationSettings->setJreSystemLibraryPaths(storedJreSystemLibraryPaths); - } - - void test_sourcegroup_java_sonargraph_with_java_modules_generates_expected_output() - { - const std::wstring projectName = L"java_sonargraph"; - - ProjectSettings projectSettings; - projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); - - std::shared_ptr sourceGroupSettings = - std::make_shared("fake_id", &projectSettings); - sourceGroupSettings->setUseJreSystemLibrary(true); - sourceGroupSettings->setClasspath({ - FilePath(L"test/classpath/file.jar"), FilePath(L"test/classpath/dir") - }); - sourceGroupSettings->setSonargraphProjectPath(getInputDirectoryPath(projectName).concatenate(L"sonargraph/system.sonargraph")); - - std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); - - std::vector storedJreSystemLibraryPaths = applicationSettings->getJreSystemLibraryPaths(); - - applicationSettings->setJreSystemLibraryPaths({ FilePath(L"test/jre/system/library/path.jar") }); - - generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); - - applicationSettings->setJreSystemLibraryPaths(storedJreSystemLibraryPaths); - } - - void test_source_group_custom_command_generates_expected_output() - { - const std::wstring projectName = L"custom_command"; - - ProjectSettings projectSettings; - projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); - - std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); - sourceGroupSettings->setCustomCommand(L"echo \"Hello World\""); - sourceGroupSettings->setSourcePaths({ getInputDirectoryPath(projectName).concatenate(L"/src") }); - sourceGroupSettings->setSourceExtensions({ L".txt" }); - sourceGroupSettings->setExcludeFilterStrings({ L"**/excluded/**" }); - - generateAndCompareExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); - } - - // Special Tests - - void test_sourcegroup_java_sonargraph_with_cpp_modules_does_not_generate_output() - { - const std::wstring projectName = L"cxx_sonargraph_cpp_manual"; - const FilePath sonargraphProjectFilePath(getInputDirectoryPath(projectName).concatenate(L"sonargraph/system.sonargraph")); - - TS_ASSERT(sonargraphProjectFilePath.exists()); - - ProjectSettings projectSettings; - projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); - - std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); - sourceGroupSettings->setSonargraphProjectPath(sonargraphProjectFilePath); - - std::shared_ptr output = generateExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); - - TS_ASSERT_EQUALS(0, output->getLineCount()); - } - - void test_sourcegroup_cxx_sonargraph_with_java_modules_does_not_generate_output() - { - const std::wstring projectName = L"java_sonargraph"; - const FilePath sonargraphProjectFilePath(getInputDirectoryPath(projectName).concatenate(L"sonargraph/system.sonargraph")); - - TS_ASSERT(sonargraphProjectFilePath.exists()); - - ProjectSettings projectSettings; - projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); - - std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); - sourceGroupSettings->setSonargraphProjectPath(sonargraphProjectFilePath); - - std::shared_ptr output = generateExpectedOutput(projectName, std::make_shared(sourceGroupSettings)); - - TS_ASSERT_EQUALS(0, output->getLineCount()); - } - - void test_can_destroy_application_instance() - { - Application::destroyInstance(); - TS_ASSERT_EQUALS(0, Application::getInstance().use_count()); - } - - // Utility - -private: - static FilePath getInputDirectoryPath(const std::wstring& projectName) - { - return FilePath(L"data/SourceGroupTestSuite/" + projectName + L"/input").makeAbsolute().makeCanonical(); - } - - static FilePath getOutputDirectoryPath(const std::wstring& projectName) - { - return FilePath(L"data/SourceGroupTestSuite/" + projectName + L"/expected_output").makeAbsolute().makeCanonical(); - } - - std::string setupJavaEnvironmentFactory() - { - if (!JavaEnvironmentFactory::getInstance()) - { - std::string errorString; -#ifdef _WIN32 - const std::string separator = ";"; -#else - const std::string separator = ":"; -#endif - std::string classPath = ""; - { - const std::vector jarNames = utility::getRequiredJarNames(); - for (size_t i = 0; i < jarNames.size(); i++) - { - if (i != 0) - { - classPath += separator; - } - classPath += FilePath(L"../app/data/java/lib/").concatenate(jarNames[i]).str(); - } - } - - JavaEnvironmentFactory::createInstance( - classPath, - errorString - ); - - return errorString; - } - - return ""; - } - - void generateAndCompareExpectedOutput( - std::wstring projectName, - std::shared_ptr sourceGroup) - { - const std::shared_ptr output = generateExpectedOutput(projectName, sourceGroup); -#ifdef WIN32 - const std::wstring expectedOutputFileName = L"output_windows.txt"; -#else - const std::wstring expectedOutputFileName = L"output_unix.txt"; -#endif - const FilePath expectedOutputFilePath = getOutputDirectoryPath(projectName).concatenate(expectedOutputFileName); - if (s_updateExpectedOutput || !expectedOutputFilePath.exists()) - { - std::ofstream expectedOutputFile; - expectedOutputFile.open(expectedOutputFilePath.str()); - expectedOutputFile << output->getText(); - expectedOutputFile.close(); - } - else - { - const std::shared_ptr expectedOutput = TextAccess::createFromFile(expectedOutputFilePath); - TSM_ASSERT_EQUALS(L"Output does not match the expected line count for project \"" + projectName + L"\".", expectedOutput->getLineCount(), output->getLineCount()); - if (expectedOutput->getLineCount() == output->getLineCount()) - { - for (size_t i = 1; i <= expectedOutput->getLineCount(); i++) - { - TS_ASSERT_EQUALS(expectedOutput->getLine(i), output->getLine(i)); - } - } - } - } - - std::shared_ptr generateExpectedOutput( - std::wstring projectName, - std::shared_ptr sourceGroup) - { - const FilePath projectDataRoot = getInputDirectoryPath(projectName).makeAbsolute(); - - std::vector> indexerCommands = sourceGroup->getIndexerCommands(sourceGroup->getAllSourceFilePaths()); - - std::sort( - indexerCommands.begin(), - indexerCommands.end(), - [](std::shared_ptr a, std::shared_ptr b) - { - return a->getSourceFilePath().wstr() < b->getSourceFilePath().wstr(); - } - ); - - std::wstring outputString; - for (std::shared_ptr indexerCommand : indexerCommands) - { - outputString += indexerCommandToString(indexerCommand, projectDataRoot); - } - - return TextAccess::createFromString(utility::encodeToUtf8(outputString)); - } - - std::wstring indexerCommandToString(std::shared_ptr indexerCommand, const FilePath& baseDirectory) - { - if (indexerCommand) - { - if (std::shared_ptr indexerCommandCxx = std::dynamic_pointer_cast(indexerCommand)) - { - return indexerCommandCxxToString(indexerCommandCxx, baseDirectory); - } - if (std::shared_ptr indexerCommandJava = std::dynamic_pointer_cast(indexerCommand)) - { - return indexerCommandJavaToString(indexerCommandJava, baseDirectory); - } - if (std::shared_ptr indexerCommandCustom = std::dynamic_pointer_cast(indexerCommand)) - { - return indexerCommandCustomToString(indexerCommandCustom, baseDirectory); - } - return L"Unsupported indexer command type: " + utility::decodeFromUtf8(indexerCommandTypeToString(indexerCommand->getIndexerCommandType())); - } - return L"No IndexerCommand provided."; - } - - std::wstring indexerCommandCxxToString(std::shared_ptr indexerCommand, const FilePath& baseDirectory) - { - std::wstring result; - result += L"SourceFilePath: \"" + indexerCommand->getSourceFilePath().getRelativeTo(baseDirectory).wstr() + L"\"\n"; - for (const FilePath& indexedPath : indexerCommand->getIndexedPaths()) - { - result += L"\tIndexedPath: \"" + indexedPath.getRelativeTo(baseDirectory).wstr() + L"\"\n"; - } - for (std::wstring compilerFlag : indexerCommand->getCompilerFlags()) - { - FilePath flagAsPath(compilerFlag); - if (flagAsPath.exists()) - { - compilerFlag = flagAsPath.getRelativeTo(baseDirectory).wstr(); - } - result += L"\tCompilerFlag: \"" + compilerFlag + L"\"\n"; - } - for (const FilePathFilter& filter : indexerCommand->getExcludeFilters()) - { - result += L"\tExcludeFilter: \"" + filter.wstr() + L"\"\n"; - } - return result; - } - - std::wstring indexerCommandJavaToString(std::shared_ptr indexerCommand, const FilePath& baseDirectory) - { - std::wstring result; - result += L"SourceFilePath: \"" + indexerCommand->getSourceFilePath().getRelativeTo(baseDirectory).wstr() + L"\"\n"; - result += L"\tLanguageStandard: \"" + indexerCommand->getLanguageStandard() + L"\"\n"; - for (const FilePath& classPathItem : indexerCommand->getClassPath()) - { - result += L"\tClassPathItem: \"" + classPathItem.getRelativeTo(baseDirectory).wstr() + L"\"\n"; - } - return result; - } - - std::wstring indexerCommandCustomToString(std::shared_ptr indexerCommand, const FilePath& baseDirectory) - { - std::wstring result; - result += L"SourceFilePath: \"" + indexerCommand->getSourceFilePath().getRelativeTo(baseDirectory).wstr() + L"\"\n"; - result += L"\tCustom Command: \"" + indexerCommand->getCustomCommand() + L"\"\n"; - return result; - } -}; diff --git a/src/test/SourceLocationCollectionTestSuite.cpp b/src/test/SourceLocationCollectionTestSuite.cpp new file mode 100644 index 00000000..70570b1a --- /dev/null +++ b/src/test/SourceLocationCollectionTestSuite.cpp @@ -0,0 +1,166 @@ +#include "catch.hpp" + +#include "SourceLocation.h" +#include "SourceLocationCollection.h" +#include "SourceLocationFile.h" + +TEST_CASE("source locations get created with other end") +{ + SourceLocationCollection collection; + const SourceLocation* a = collection.addSourceLocation(LOCATION_TOKEN, 1, {1}, FilePath(L"file.c"), 2, 3, 4, 5); + + REQUIRE(a); + REQUIRE(a->isStartLocation()); + REQUIRE(!a->isEndLocation()); + + const SourceLocation* b = a->getOtherLocation(); + + REQUIRE(b); + REQUIRE(!b->isStartLocation()); + REQUIRE(b->isEndLocation()); + + REQUIRE(a == b->getOtherLocation()); + REQUIRE(a == b->getStartLocation()); + REQUIRE(a == a->getStartLocation()); + REQUIRE(b == a->getEndLocation()); + REQUIRE(b == b->getEndLocation()); +} + +TEST_CASE("source locations do not get created with wrong input") +{ + SourceLocationCollection collection; + SourceLocation* a = collection.addSourceLocation(LOCATION_TOKEN, 1, {1}, FilePath(L"file.c"), 2, 3, 2, 1); + SourceLocation* b = collection.addSourceLocation(LOCATION_TOKEN, 2, {1}, FilePath(L"file.c"), 4, 1, 1, 10); + + REQUIRE(!a); + REQUIRE(!b); +} + +TEST_CASE("source locations get unique id but both ends have the same") +{ + SourceLocationCollection collection; + SourceLocation* a = collection.addSourceLocation(LOCATION_TOKEN, 1, {1}, FilePath(L"file.c"), 1, 1, 1, 1); + SourceLocation* b = collection.addSourceLocation(LOCATION_TOKEN, 2, {2}, FilePath(L"file.c"), 1, 1, 1, 1); + SourceLocation* c = collection.addSourceLocation(LOCATION_TOKEN, 3, {3}, FilePath(L"file.c"), 1, 1, 1, 1); + + REQUIRE(1 == collection.getSourceLocationFileCount()); + REQUIRE(3 == collection.getSourceLocationCount()); + + REQUIRE(a->getLocationId() == 1); + REQUIRE(b->getLocationId() == 2); + REQUIRE(c->getLocationId() == 3); + + REQUIRE(a->getLocationId() != b->getLocationId()); + REQUIRE(b->getLocationId() != c->getLocationId()); + REQUIRE(c->getLocationId() != a->getLocationId()); + + REQUIRE(a->getLocationId() == a->getOtherLocation()->getLocationId()); + REQUIRE(b->getLocationId() == b->getOtherLocation()->getLocationId()); + REQUIRE(c->getLocationId() == c->getOtherLocation()->getLocationId()); +} + +TEST_CASE("source locations have right file path line column and token id") +{ + SourceLocationCollection collection; + SourceLocation* a = collection.addSourceLocation(LOCATION_TOKEN, 1, {1}, FilePath(L"file.c"), 2, 3, 4, 5); + + REQUIRE(1 == a->getTokenIds()[0]); + REQUIRE(2 == a->getLineNumber()); + REQUIRE(3 == a->getColumnNumber()); + REQUIRE(4 == a->getOtherLocation()->getLineNumber()); + REQUIRE(5 == a->getOtherLocation()->getColumnNumber()); + REQUIRE(L"file.c" == a->getFilePath().wstr()); +} + +TEST_CASE("finding source locations by id") +{ + SourceLocationCollection collection; + SourceLocation* a = collection.addSourceLocation(LOCATION_TOKEN, 1, {1}, FilePath(L"file.c"), 2, 3, 4, 5); + SourceLocation* b = collection.addSourceLocation(LOCATION_TOKEN, 2, {6}, FilePath(L"file.c"), 7, 8, 9, 10); + + REQUIRE(a == collection.getSourceLocationById(a->getLocationId())); + REQUIRE(b == collection.getSourceLocationById(b->getLocationId())); +} + +TEST_CASE("creating plain copy of all locations in line range") +{ + SourceLocationCollection collection; + SourceLocation* a = collection.addSourceLocation(LOCATION_TOKEN, 1, {1}, FilePath(L"file.c"), 2, 3, 4, 5); + SourceLocation* b = collection.addSourceLocation(LOCATION_TOKEN, 2, {1}, FilePath(L"file.c"), 3, 3, 4, 5); + SourceLocation* c = collection.addSourceLocation(LOCATION_TOKEN, 3, {1}, FilePath(L"file.c"), 1, 3, 5, 5); + SourceLocation* d = collection.addSourceLocation(LOCATION_TOKEN, 4, {1}, FilePath(L"file.c"), 1, 5, 4, 5); + + Id ida = a->getLocationId(); + Id idb = b->getLocationId(); + Id idc = c->getLocationId(); + Id idd = d->getLocationId(); + + unsigned int fromLine = 2; + unsigned int toLine = 4; + + SourceLocationCollection copy; + SourceLocation* x = collection.getSourceLocationById(ida); + + x->getSourceLocationFile()->forEachSourceLocation( + [©, fromLine, toLine](SourceLocation* location) + { + if (location->getLineNumber() >= fromLine && location->getLineNumber() <= toLine) + { + copy.addSourceLocationCopy(location); + } + } + ); + + REQUIRE(1 == copy.getSourceLocationFileCount()); + REQUIRE(3 == copy.getSourceLocationCount()); + + REQUIRE(copy.getSourceLocationById(ida)); + REQUIRE(copy.getSourceLocationById(idb)); + REQUIRE(!copy.getSourceLocationById(idc)); + REQUIRE(copy.getSourceLocationById(idd)); + + REQUIRE(a != copy.getSourceLocationById(ida)); + REQUIRE(d != copy.getSourceLocationById(idd)); + + REQUIRE(copy.getSourceLocationById(ida)->getStartLocation()); + REQUIRE(copy.getSourceLocationById(ida)->getEndLocation()); + + REQUIRE(!copy.getSourceLocationById(idd)->getStartLocation()); + REQUIRE(copy.getSourceLocationById(idd)->getEndLocation()); +} + +TEST_CASE("get source locations filtered by lines") +{ + SourceLocationCollection collection; + SourceLocation* a = collection.addSourceLocation(LOCATION_TOKEN, 1, {1}, FilePath(L"file.c"), 1, 3, 1, 5); + SourceLocation* b = collection.addSourceLocation(LOCATION_TOKEN, 2, {1}, FilePath(L"file.c"), 1, 3, 2, 5); + SourceLocation* c = collection.addSourceLocation(LOCATION_TOKEN, 3, {1}, FilePath(L"file.c"), 2, 3, 2, 5); + SourceLocation* d = collection.addSourceLocation(LOCATION_TOKEN, 4, {1}, FilePath(L"file.c"), 3, 3, 4, 5); + SourceLocation* e = collection.addSourceLocation(LOCATION_TOKEN, 5, {1}, FilePath(L"file.c"), 3, 5, 5, 5); + SourceLocation* f = collection.addSourceLocation(LOCATION_TOKEN, 6, {1}, FilePath(L"file.c"), 1, 5, 5, 5); + SourceLocation* g = collection.addSourceLocation(LOCATION_TOKEN, 7, {1}, FilePath(L"file.c"), 5, 5, 5, 5); + + SourceLocationCollection copy; + copy.addSourceLocationFile( + collection.getSourceLocationById(a->getLocationId())->getSourceLocationFile()->getFilteredByLines(2, 4)); + + REQUIRE(1 == copy.getSourceLocationFileCount()); + REQUIRE(4 == copy.getSourceLocationCount()); + + REQUIRE(!copy.getSourceLocationById(a->getLocationId())); + REQUIRE(copy.getSourceLocationById(b->getLocationId())); + REQUIRE(copy.getSourceLocationById(c->getLocationId())); + REQUIRE(copy.getSourceLocationById(d->getLocationId())); + REQUIRE(copy.getSourceLocationById(e->getLocationId())); + REQUIRE(!copy.getSourceLocationById(f->getLocationId())); + REQUIRE(!copy.getSourceLocationById(g->getLocationId())); + + REQUIRE(b != copy.getSourceLocationById(b->getLocationId())); + REQUIRE(c != copy.getSourceLocationById(c->getLocationId())); + + REQUIRE(!copy.getSourceLocationById(b->getLocationId())->getStartLocation()); + REQUIRE(copy.getSourceLocationById(b->getLocationId())->getEndLocation()); + + REQUIRE(copy.getSourceLocationById(e->getLocationId())->getStartLocation()); + REQUIRE(!copy.getSourceLocationById(e->getLocationId())->getEndLocation()); +} diff --git a/src/test/SourceLocationCollectionTestSuite.h b/src/test/SourceLocationCollectionTestSuite.h deleted file mode 100644 index 4698b4f0..00000000 --- a/src/test/SourceLocationCollectionTestSuite.h +++ /dev/null @@ -1,170 +0,0 @@ -#include - -#include "SourceLocation.h" -#include "SourceLocationCollection.h" -#include "SourceLocationFile.h" - -class SourceLocationCollectionTestSuite : public CxxTest::TestSuite -{ -public: - void test_source_locations_get_created_with_other_end() - { - SourceLocationCollection collection; - const SourceLocation* a = collection.addSourceLocation(LOCATION_TOKEN, 1, {1}, FilePath(L"file.c"), 2, 3, 4, 5); - - TS_ASSERT(a); - TS_ASSERT(a->isStartLocation()); - TS_ASSERT(!a->isEndLocation()); - - const SourceLocation* b = a->getOtherLocation(); - - TS_ASSERT(b); - TS_ASSERT(!b->isStartLocation()); - TS_ASSERT(b->isEndLocation()); - - TS_ASSERT_EQUALS(a, b->getOtherLocation()); - TS_ASSERT_EQUALS(a, b->getStartLocation()); - TS_ASSERT_EQUALS(a, a->getStartLocation()); - TS_ASSERT_EQUALS(b, a->getEndLocation()); - TS_ASSERT_EQUALS(b, b->getEndLocation()); - } - - void test_source_locations_do_not_get_created_with_wrong_input() - { - SourceLocationCollection collection; - SourceLocation* a = collection.addSourceLocation(LOCATION_TOKEN, 1, {1}, FilePath(L"file.c"), 2, 3, 2, 1); - SourceLocation* b = collection.addSourceLocation(LOCATION_TOKEN, 2, {1}, FilePath(L"file.c"), 4, 1, 1, 10); - - TS_ASSERT(!a); - TS_ASSERT(!b); - } - - void test_source_locations_get_unique_id_but_both_ends_have_the_same() - { - SourceLocationCollection collection; - SourceLocation* a = collection.addSourceLocation(LOCATION_TOKEN, 1, {1}, FilePath(L"file.c"), 1, 1, 1, 1); - SourceLocation* b = collection.addSourceLocation(LOCATION_TOKEN, 2, {2}, FilePath(L"file.c"), 1, 1, 1, 1); - SourceLocation* c = collection.addSourceLocation(LOCATION_TOKEN, 3, {3}, FilePath(L"file.c"), 1, 1, 1, 1); - - TS_ASSERT_EQUALS(1, collection.getSourceLocationFileCount()); - TS_ASSERT_EQUALS(3, collection.getSourceLocationCount()); - - TS_ASSERT_EQUALS(a->getLocationId(), 1); - TS_ASSERT_EQUALS(b->getLocationId(), 2); - TS_ASSERT_EQUALS(c->getLocationId(), 3); - - TS_ASSERT_DIFFERS(a->getLocationId(), b->getLocationId()); - TS_ASSERT_DIFFERS(b->getLocationId(), c->getLocationId()); - TS_ASSERT_DIFFERS(c->getLocationId(), a->getLocationId()); - - TS_ASSERT_EQUALS(a->getLocationId(), a->getOtherLocation()->getLocationId()); - TS_ASSERT_EQUALS(b->getLocationId(), b->getOtherLocation()->getLocationId()); - TS_ASSERT_EQUALS(c->getLocationId(), c->getOtherLocation()->getLocationId()); - } - - void test_source_locations_have_right_file_path_line_column_and_token_id() - { - SourceLocationCollection collection; - SourceLocation* a = collection.addSourceLocation(LOCATION_TOKEN, 1, {1}, FilePath(L"file.c"), 2, 3, 4, 5); - - TS_ASSERT_EQUALS(1, a->getTokenIds()[0]); - TS_ASSERT_EQUALS(2, a->getLineNumber()); - TS_ASSERT_EQUALS(3, a->getColumnNumber()); - TS_ASSERT_EQUALS(4, a->getOtherLocation()->getLineNumber()); - TS_ASSERT_EQUALS(5, a->getOtherLocation()->getColumnNumber()); - TS_ASSERT_EQUALS(L"file.c", a->getFilePath().wstr()); - } - - void test_finding_source_locations_by_id() - { - SourceLocationCollection collection; - SourceLocation* a = collection.addSourceLocation(LOCATION_TOKEN, 1, {1}, FilePath(L"file.c"), 2, 3, 4, 5); - SourceLocation* b = collection.addSourceLocation(LOCATION_TOKEN, 2, {6}, FilePath(L"file.c"), 7, 8, 9, 10); - - TS_ASSERT_EQUALS(a, collection.getSourceLocationById(a->getLocationId())); - TS_ASSERT_EQUALS(b, collection.getSourceLocationById(b->getLocationId())); - } - - void test_creating_plain_copy_of_all_locations_in_line_range() - { - SourceLocationCollection collection; - SourceLocation* a = collection.addSourceLocation(LOCATION_TOKEN, 1, {1}, FilePath(L"file.c"), 2, 3, 4, 5); - SourceLocation* b = collection.addSourceLocation(LOCATION_TOKEN, 2, {1}, FilePath(L"file.c"), 3, 3, 4, 5); - SourceLocation* c = collection.addSourceLocation(LOCATION_TOKEN, 3, {1}, FilePath(L"file.c"), 1, 3, 5, 5); - SourceLocation* d = collection.addSourceLocation(LOCATION_TOKEN, 4, {1}, FilePath(L"file.c"), 1, 5, 4, 5); - - Id ida = a->getLocationId(); - Id idb = b->getLocationId(); - Id idc = c->getLocationId(); - Id idd = d->getLocationId(); - - unsigned int fromLine = 2; - unsigned int toLine = 4; - - SourceLocationCollection copy; - SourceLocation* x = collection.getSourceLocationById(ida); - - x->getSourceLocationFile()->forEachSourceLocation( - [©, fromLine, toLine](SourceLocation* location) - { - if (location->getLineNumber() >= fromLine && location->getLineNumber() <= toLine) - { - copy.addSourceLocationCopy(location); - } - } - ); - - TS_ASSERT_EQUALS(1, copy.getSourceLocationFileCount()); - TS_ASSERT_EQUALS(3, copy.getSourceLocationCount()); - - TS_ASSERT(copy.getSourceLocationById(ida)); - TS_ASSERT(copy.getSourceLocationById(idb)); - TS_ASSERT(!copy.getSourceLocationById(idc)); - TS_ASSERT(copy.getSourceLocationById(idd)); - - TS_ASSERT_DIFFERS(a, copy.getSourceLocationById(ida)); - TS_ASSERT_DIFFERS(d, copy.getSourceLocationById(idd)); - - TS_ASSERT(copy.getSourceLocationById(ida)->getStartLocation()); - TS_ASSERT(copy.getSourceLocationById(ida)->getEndLocation()); - - TS_ASSERT(!copy.getSourceLocationById(idd)->getStartLocation()); - TS_ASSERT(copy.getSourceLocationById(idd)->getEndLocation()); - } - - void test_get_source_locations_filtered_by_lines() - { - SourceLocationCollection collection; - SourceLocation* a = collection.addSourceLocation(LOCATION_TOKEN, 1, {1}, FilePath(L"file.c"), 1, 3, 1, 5); - SourceLocation* b = collection.addSourceLocation(LOCATION_TOKEN, 2, {1}, FilePath(L"file.c"), 1, 3, 2, 5); - SourceLocation* c = collection.addSourceLocation(LOCATION_TOKEN, 3, {1}, FilePath(L"file.c"), 2, 3, 2, 5); - SourceLocation* d = collection.addSourceLocation(LOCATION_TOKEN, 4, {1}, FilePath(L"file.c"), 3, 3, 4, 5); - SourceLocation* e = collection.addSourceLocation(LOCATION_TOKEN, 5, {1}, FilePath(L"file.c"), 3, 5, 5, 5); - SourceLocation* f = collection.addSourceLocation(LOCATION_TOKEN, 6, {1}, FilePath(L"file.c"), 1, 5, 5, 5); - SourceLocation* g = collection.addSourceLocation(LOCATION_TOKEN, 7, {1}, FilePath(L"file.c"), 5, 5, 5, 5); - - SourceLocationCollection copy; - copy.addSourceLocationFile( - collection.getSourceLocationById(a->getLocationId())->getSourceLocationFile()->getFilteredByLines(2, 4)); - - TS_ASSERT_EQUALS(1, copy.getSourceLocationFileCount()); - TS_ASSERT_EQUALS(4, copy.getSourceLocationCount()); - - TS_ASSERT(!copy.getSourceLocationById(a->getLocationId())); - TS_ASSERT(copy.getSourceLocationById(b->getLocationId())); - TS_ASSERT(copy.getSourceLocationById(c->getLocationId())); - TS_ASSERT(copy.getSourceLocationById(d->getLocationId())); - TS_ASSERT(copy.getSourceLocationById(e->getLocationId())); - TS_ASSERT(!copy.getSourceLocationById(f->getLocationId())); - TS_ASSERT(!copy.getSourceLocationById(g->getLocationId())); - - TS_ASSERT_DIFFERS(b, copy.getSourceLocationById(b->getLocationId())); - TS_ASSERT_DIFFERS(c, copy.getSourceLocationById(c->getLocationId())); - - TS_ASSERT(!copy.getSourceLocationById(b->getLocationId())->getStartLocation()); - TS_ASSERT(copy.getSourceLocationById(b->getLocationId())->getEndLocation()); - - TS_ASSERT(copy.getSourceLocationById(e->getLocationId())->getStartLocation()); - TS_ASSERT(!copy.getSourceLocationById(e->getLocationId())->getEndLocation()); - } -}; diff --git a/src/test/SqliteBookmarkStorageTestSuite.cpp b/src/test/SqliteBookmarkStorageTestSuite.cpp new file mode 100644 index 00000000..7a0c9de2 --- /dev/null +++ b/src/test/SqliteBookmarkStorageTestSuite.cpp @@ -0,0 +1,105 @@ +#include "catch.hpp" + +#include "FileSystem.h" +#include "SqliteBookmarkStorage.h" + +TEST_CASE("add bookmarks") +{ + FilePath databasePath(L"data/SQLiteTestSuite/bookmarkTest.sqlite"); + size_t bookmarkCount = 4; + int result = -1; + { + FileSystem::remove(databasePath); + SqliteBookmarkStorage storage(databasePath); + storage.setup(); + + for (size_t i = 0; i < bookmarkCount; i++) + { + const Id categoryId = storage.addBookmarkCategory(StorageBookmarkCategoryData(L"test category")).id; + storage.addBookmark(StorageBookmarkData(L"test bookmark", L"test comment", TimeStamp::now().toString(), categoryId)); + } + + result = storage.getAllBookmarks().size(); + } + + FileSystem::remove(databasePath); + + REQUIRE(result == bookmarkCount); +} + +TEST_CASE("add bookmarked node") +{ + FilePath databasePath(L"data/SQLiteTestSuite/bookmarkTest.sqlite"); + size_t bookmarkCount = 4; + int result = -1; + { + FileSystem::remove(databasePath); + SqliteBookmarkStorage storage(databasePath); + storage.setup(); + + const Id categoryId = storage.addBookmarkCategory(StorageBookmarkCategoryData(L"test category")).id; + const Id bookmarkId = storage.addBookmark(StorageBookmarkData(L"test bookmark", L"test comment", TimeStamp::now().toString(), categoryId)).id; + + for (size_t i = 0; i < bookmarkCount; i++) + { + storage.addBookmarkedNode(StorageBookmarkedNodeData(bookmarkId, L"test name")); + } + + result = storage.getAllBookmarkedNodes().size(); + } + + FileSystem::remove(databasePath); + + REQUIRE(result == bookmarkCount); +} + +TEST_CASE("remove bookmark also removes bookmarked node") +{ + FilePath databasePath(L"data/SQLiteTestSuite/bookmarkTest.sqlite"); + int result = -1; + { + FileSystem::remove(databasePath); + SqliteBookmarkStorage storage(databasePath); + storage.setup(); + + const Id categoryId = storage.addBookmarkCategory(StorageBookmarkCategoryData(L"test category")).id; + const Id bookmarkId = storage.addBookmark(StorageBookmarkData(L"test bookmark", L"test comment", TimeStamp::now().toString(), categoryId)).id; + storage.addBookmarkedNode(StorageBookmarkedNodeData(bookmarkId, L"test name")); + + storage.removeBookmark(bookmarkId); + + result = storage.getAllBookmarkedNodes().size(); + } + + FileSystem::remove(databasePath); + + REQUIRE(result == 0); +} + +TEST_CASE("edit nodeBookmark") +{ + FilePath databasePath(L"data/SQLiteTestSuite/bookmarkTest.sqlite"); + + const std::wstring updatedName = L"updated name"; + const std::wstring updatedComment = L"updated comment"; + + StorageBookmark storageBookmark; + { + FileSystem::remove(databasePath); + SqliteBookmarkStorage storage(databasePath); + storage.setup(); + + const Id categoryId = storage.addBookmarkCategory(StorageBookmarkCategoryData(L"test category")).id; + const Id bookmarkId = storage.addBookmark(StorageBookmarkData(L"test bookmark", L"test comment", TimeStamp::now().toString(), categoryId)).id; + storage.addBookmarkedNode(StorageBookmarkedNodeData(bookmarkId, L"test name")); + + storage.updateBookmark(bookmarkId, updatedName, updatedComment, categoryId); + + storageBookmark = storage.getAllBookmarks().front(); + } + + FileSystem::remove(databasePath); + + REQUIRE(updatedName == storageBookmark.name); + REQUIRE(updatedComment == storageBookmark.comment); +} diff --git a/src/test/SqliteBookmarkStorageTestSuite.h b/src/test/SqliteBookmarkStorageTestSuite.h deleted file mode 100644 index 53ad0b02..00000000 --- a/src/test/SqliteBookmarkStorageTestSuite.h +++ /dev/null @@ -1,110 +0,0 @@ -#include - -#include - -#include "SqliteBookmarkStorage.h" - -class SqliteBookmarkStorageTestSuite: public CxxTest::TestSuite -{ -public: - void test_add_bookmarks() - { - FilePath databasePath(L"data/SQLiteTestSuite/bookmarkTest.sqlite"); - size_t bookmarkCount = 4; - int result = -1; - { - FileSystem::remove(databasePath); - SqliteBookmarkStorage storage(databasePath); - storage.setup(); - - for (size_t i = 0; i < bookmarkCount; i++) - { - const Id categoryId = storage.addBookmarkCategory(StorageBookmarkCategoryData(L"test category")).id; - storage.addBookmark(StorageBookmarkData(L"test bookmark", L"test comment", TimeStamp::now().toString(), categoryId)); - } - - result = storage.getAllBookmarks().size(); - } - - FileSystem::remove(databasePath); - - TS_ASSERT_EQUALS(result, bookmarkCount); - } - - void test_add_bookmarked_node() - { - FilePath databasePath(L"data/SQLiteTestSuite/bookmarkTest.sqlite"); - size_t bookmarkCount = 4; - int result = -1; - { - FileSystem::remove(databasePath); - SqliteBookmarkStorage storage(databasePath); - storage.setup(); - - const Id categoryId = storage.addBookmarkCategory(StorageBookmarkCategoryData(L"test category")).id; - const Id bookmarkId = storage.addBookmark(StorageBookmarkData(L"test bookmark", L"test comment", TimeStamp::now().toString(), categoryId)).id; - - for (size_t i = 0; i < bookmarkCount; i++) - { - storage.addBookmarkedNode(StorageBookmarkedNodeData(bookmarkId, L"test name")); - } - - result = storage.getAllBookmarkedNodes().size(); - } - - FileSystem::remove(databasePath); - - TS_ASSERT_EQUALS(result, bookmarkCount); - } - - void test_remove_bookmark_also_removes_bookmarked_node() - { - FilePath databasePath(L"data/SQLiteTestSuite/bookmarkTest.sqlite"); - int result = -1; - { - FileSystem::remove(databasePath); - SqliteBookmarkStorage storage(databasePath); - storage.setup(); - - const Id categoryId = storage.addBookmarkCategory(StorageBookmarkCategoryData(L"test category")).id; - const Id bookmarkId = storage.addBookmark(StorageBookmarkData(L"test bookmark", L"test comment", TimeStamp::now().toString(), categoryId)).id; - storage.addBookmarkedNode(StorageBookmarkedNodeData(bookmarkId, L"test name")); - - storage.removeBookmark(bookmarkId); - - result = storage.getAllBookmarkedNodes().size(); - } - - FileSystem::remove(databasePath); - - TS_ASSERT_EQUALS(result, 0); - } - - void test_edit_nodeBookmark() - { - FilePath databasePath(L"data/SQLiteTestSuite/bookmarkTest.sqlite"); - - const std::wstring updatedName = L"updated name"; - const std::wstring updatedComment = L"updated comment"; - - StorageBookmark storageBookmark; - { - FileSystem::remove(databasePath); - SqliteBookmarkStorage storage(databasePath); - storage.setup(); - - const Id categoryId = storage.addBookmarkCategory(StorageBookmarkCategoryData(L"test category")).id; - const Id bookmarkId = storage.addBookmark(StorageBookmarkData(L"test bookmark", L"test comment", TimeStamp::now().toString(), categoryId)).id; - storage.addBookmarkedNode(StorageBookmarkedNodeData(bookmarkId, L"test name")); - - storage.updateBookmark(bookmarkId, updatedName, updatedComment, categoryId); - - storageBookmark = storage.getAllBookmarks().front(); - } - - FileSystem::remove(databasePath); - - TS_ASSERT_EQUALS(updatedName, storageBookmark.name); - TS_ASSERT_EQUALS(updatedComment, storageBookmark.comment); - } -}; diff --git a/src/test/SqliteIndexStorageTestSuite.cpp b/src/test/SqliteIndexStorageTestSuite.cpp new file mode 100644 index 00000000..58403560 --- /dev/null +++ b/src/test/SqliteIndexStorageTestSuite.cpp @@ -0,0 +1,78 @@ +#include "catch.hpp" + +#include "FileSystem.h" +#include "SqliteIndexStorage.h" + +TEST_CASE("storage adds node successfully") +{ + FilePath databasePath(L"data/SQLiteTestSuite/test.sqlite"); + int nodeCount = -1; + { + SqliteIndexStorage storage(databasePath); + storage.setup(); + storage.beginTransaction(); + storage.addNode(StorageNodeData(0, L"a")); + storage.commitTransaction(); + nodeCount = storage.getNodeCount(); + } + FileSystem::remove(databasePath); + + REQUIRE(1 == nodeCount); +} + +TEST_CASE("storage removes node successfully") +{ + FilePath databasePath(L"data/SQLiteTestSuite/test.sqlite"); + int nodeCount = -1; + { + SqliteIndexStorage storage(databasePath); + storage.setup(); + storage.beginTransaction(); + int nodeId = storage.addNode(StorageNodeData(0, L"a")); + storage.removeElement(nodeId); + storage.commitTransaction(); + nodeCount = storage.getNodeCount(); + } + FileSystem::remove(databasePath); + + REQUIRE(0 == nodeCount); +} + +TEST_CASE("storage adds edge successfully") +{ + FilePath databasePath(L"data/SQLiteTestSuite/test.sqlite"); + int edgeCount = -1; + { + SqliteIndexStorage storage(databasePath); + storage.setup(); + storage.beginTransaction(); + int sourceNodeId = storage.addNode(StorageNodeData(0, L"a")); + int targetNodeId = storage.addNode(StorageNodeData(0, L"b")); + storage.addEdge(StorageEdgeData(0, sourceNodeId, targetNodeId)); + storage.commitTransaction(); + edgeCount = storage.getEdgeCount(); + } + FileSystem::remove(databasePath); + + REQUIRE(1 == edgeCount); +} + +TEST_CASE("storage removes edge successfully") +{ + FilePath databasePath(L"data/SQLiteTestSuite/test.sqlite"); + int edgeCount = -1; + { + SqliteIndexStorage storage(databasePath); + storage.setup(); + storage.beginTransaction(); + int sourceNodeId = storage.addNode(StorageNodeData(0, L"a")); + int targetNodeId = storage.addNode(StorageNodeData(0, L"b")); + int edgeId = storage.addEdge(StorageEdgeData(0, sourceNodeId, targetNodeId)); + storage.removeElement(edgeId); + storage.commitTransaction(); + edgeCount = storage.getEdgeCount(); + } + FileSystem::remove(databasePath); + + REQUIRE(0 == edgeCount); +} diff --git a/src/test/SqliteIndexStorageTestSuite.h b/src/test/SqliteIndexStorageTestSuite.h deleted file mode 100644 index 91e889c8..00000000 --- a/src/test/SqliteIndexStorageTestSuite.h +++ /dev/null @@ -1,83 +0,0 @@ -#include - -#include - -#include "SqliteIndexStorage.h" - -class SqliteIndexStorageTestSuite: public CxxTest::TestSuite -{ -public: - void test_storage_adds_node_successfully() - { - FilePath databasePath(L"data/SQLiteTestSuite/test.sqlite"); - int nodeCount = -1; - { - SqliteIndexStorage storage(databasePath); - storage.setup(); - storage.beginTransaction(); - storage.addNode(StorageNodeData(0, L"a")); - storage.commitTransaction(); - nodeCount = storage.getNodeCount(); - } - FileSystem::remove(databasePath); - - TS_ASSERT_EQUALS(1, nodeCount); - } - - void test_storage_removes_node_successfully() - { - FilePath databasePath(L"data/SQLiteTestSuite/test.sqlite"); - int nodeCount = -1; - { - SqliteIndexStorage storage(databasePath); - storage.setup(); - storage.beginTransaction(); - int nodeId = storage.addNode(StorageNodeData(0, L"a")); - storage.removeElement(nodeId); - storage.commitTransaction(); - nodeCount = storage.getNodeCount(); - } - FileSystem::remove(databasePath); - - TS_ASSERT_EQUALS(0, nodeCount); - } - - void test_storage_adds_edge_successfully() - { - FilePath databasePath(L"data/SQLiteTestSuite/test.sqlite"); - int edgeCount = -1; - { - SqliteIndexStorage storage(databasePath); - storage.setup(); - storage.beginTransaction(); - int sourceNodeId = storage.addNode(StorageNodeData(0, L"a")); - int targetNodeId = storage.addNode(StorageNodeData(0, L"b")); - storage.addEdge(StorageEdgeData(0, sourceNodeId, targetNodeId)); - storage.commitTransaction(); - edgeCount = storage.getEdgeCount(); - } - FileSystem::remove(databasePath); - - TS_ASSERT_EQUALS(1, edgeCount); - } - - void test_storage_removes_edge_successfully() - { - FilePath databasePath(L"data/SQLiteTestSuite/test.sqlite"); - int edgeCount = -1; - { - SqliteIndexStorage storage(databasePath); - storage.setup(); - storage.beginTransaction(); - int sourceNodeId = storage.addNode(StorageNodeData(0, L"a")); - int targetNodeId = storage.addNode(StorageNodeData(0, L"b")); - int edgeId = storage.addEdge(StorageEdgeData(0, sourceNodeId, targetNodeId)); - storage.removeElement(edgeId); - storage.commitTransaction(); - edgeCount = storage.getEdgeCount(); - } - FileSystem::remove(databasePath); - - TS_ASSERT_EQUALS(0, edgeCount); - } -}; diff --git a/src/test/StorageTestSuite.cpp b/src/test/StorageTestSuite.cpp new file mode 100644 index 00000000..3dd15666 --- /dev/null +++ b/src/test/StorageTestSuite.cpp @@ -0,0 +1,273 @@ +#include "catch.hpp" + +#include "utilityString.h" + +#include "ParseLocation.h" +#include "IntermediateStorage.h" +#include "PersistentStorage.h" + +namespace +{ + class TestStorage : public PersistentStorage + { + public: + TestStorage() : PersistentStorage(FilePath(L"data/test.sqlite"), FilePath(L"data/testBookmarks.sqlite")) + { + clear(); + } + + //const size_t getNodeCount() const + //{ + // return getGraph().getNodeCount(); + //} + + //const size_t getEdgeCount() const + //{ + // return getGraph().getEdgeCount(); + //} + }; + + ParseLocation validLocation(Id locationId = 0) + { + return ParseLocation(1, 1, locationId, 1, locationId); + } + + NameHierarchy createNameHierarchy(std::wstring s) + { + NameHierarchy nameHierarchy(NAME_DELIMITER_CXX); + for (std::wstring element : utility::splitToVector(s, nameDelimiterTypeToString(NAME_DELIMITER_CXX))) + { + nameHierarchy.push(element); + } + return nameHierarchy; + } + + NameHierarchy createFunctionNameHierarchy(std::wstring ret, std::wstring name, std::wstring parameters) + { + NameHierarchy nameHierarchy = createNameHierarchy(name); + std::wstring lastName = nameHierarchy.back().getName(); + nameHierarchy.pop(); + nameHierarchy.push(NameElement(lastName, ret, parameters)); + return nameHierarchy; + } +} + +TEST_CASE("storage saves file") +{ + TestStorage storage; + + std::wstring filePath = L"path/to/test.h"; + + std::shared_ptr intermetiateStorage = std::make_shared(); + Id id = intermetiateStorage->addNode(StorageNodeData(NodeType::typeToInt(NodeType::NODE_FILE), NameHierarchy::serialize(NameHierarchy(filePath, NAME_DELIMITER_FILE)))).first; + intermetiateStorage->addFile(StorageFile(id, filePath, L"someLanguage", "someTime", true, true)); + + storage.inject(intermetiateStorage.get()); + + REQUIRE(storage.getNameHierarchyForNodeId(id).getQualifiedName() == filePath); + REQUIRE(storage.getNodeTypeForNodeWithId(id).isFile()); +} + +TEST_CASE("storage saves node") +{ + NameHierarchy a = createNameHierarchy(L"type"); + + TestStorage storage; + + std::shared_ptr intermetiateStorage = std::make_shared(); + intermetiateStorage->addNode(StorageNodeData(NodeType::typeToInt(NodeType::NODE_TYPEDEF), NameHierarchy::serialize(a))); + + storage.inject(intermetiateStorage.get()); + + Id storedId = storage.getNodeIdForNameHierarchy(a); + + REQUIRE(storedId != 0); + REQUIRE(storage.getNodeTypeForNodeWithId(storedId).getType() == NodeType::NODE_TYPEDEF); +} + +TEST_CASE("storage saves field as member") +{ + NameHierarchy a = createNameHierarchy(L"Struct"); + NameHierarchy b = createNameHierarchy(L"Struct::m_field"); + + TestStorage storage; + + std::shared_ptr intermetiateStorage = std::make_shared(); + + Id aId = intermetiateStorage->addNode(StorageNodeData(NodeType::typeToInt(NodeType::NODE_STRUCT), NameHierarchy::serialize(a))).first; + intermetiateStorage->addSymbol(StorageSymbol(aId, DEFINITION_EXPLICIT)); + + Id bId = intermetiateStorage->addNode(StorageNodeData(NodeType::typeToInt(NodeType::NODE_FIELD), NameHierarchy::serialize(b))).first; + intermetiateStorage->addSymbol(StorageSymbol(bId, DEFINITION_EXPLICIT)); + intermetiateStorage->addEdge(StorageEdgeData(Edge::typeToInt(Edge::EDGE_MEMBER), aId, bId)); + + storage.inject(intermetiateStorage.get()); + bool foundEdge = false; + + const Id sourceId = storage.getNodeIdForNameHierarchy(a); + const Id targetId = storage.getNodeIdForNameHierarchy(b); + for (auto edge : storage.getStorageEdges()) + { + if (edge.sourceNodeId == sourceId && edge.targetNodeId == targetId && edge.type == Edge::typeToInt(Edge::EDGE_MEMBER)) + { + foundEdge = true; + } + } + REQUIRE(foundEdge); +} + +TEST_CASE("storage saves method static") +{ + //TestStorage storage; + //Id id = storage.onMethodParsed( + // validLocation(1), + // ParseFunction(typeUsage("void"), createNameHierarchy("isMethod"), parameters("bool"), true), + // ParserClient::ACCESS_NONE, + // ParserClient::ABSTRACTION_NONE, + // validLocation(4) + //); + + //Node* node = storage.getNodeWithId(id); + //TS_ASSERT(node); + //TS_ASSERT_EQUALS(node->getQualifiedNameWithSignature(), "isMethod"); + //TS_ASSERT_EQUALS(node->getType(), NodeType::NODE_METHOD); + //TS_ASSERT(node->getComponent()); +} + +TEST_CASE("storage clears single file data of single file storage") +{ + /* + m_filePath = FilePath(L"file.cpp"); + TestStorage storage; + storage.onFunctionParsed( + validLocation(), ParseFunction(typeUsage("bool"), createNameHierarchy("isTrue"), + parameters("char")), validLocation() + ); + + REQUIRE(storage.getNodeCount() == 3); + REQUIRE(storage.getEdgeCount() == 2); + REQUIRE(storage.tokenLocationCollection().getTokenLocations().size() == 4); + + std::set files; + files.insert(FilePath(m_filePath)); + storage.clearFileData(files); + + REQUIRE(storage.getNodeCount() == 0); + REQUIRE(storage.getEdgeCount() == 0); + REQUIRE(storage.tokenLocationCollection().getTokenLocations().size() == 0);;*/ +} + +TEST_CASE("storage clears unreferenced single file data of multi file storage") +{ + //m_filePath = "file.h"; + + //TestStorage storage; + + //ParseFunction isTrue = ParseFunction(typeUsage("bool"), createNameHierarchy("isTrue"), parameters("char")); + //storage.onFunctionParsed(validLocation(), isTrue, validLocation()); + + //m_filePath = "file.cpp"; + + //ParseFunction main = ParseFunction(typeUsage("int"), createNameHierarchy("main"), parameters("void")); + //storage.onFunctionParsed(validLocation(), main, validLocation()); + + //storage.onCallParsed(validLocation(), main, isTrue); + + //TS_ASSERT_EQUALS(storage.getNodeCount(), 6); + //TS_ASSERT_EQUALS(storage.getEdgeCount(), 5); + //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 9); + + //std::set files; + //files.insert(FilePath("file.cpp")); + //storage.clearFileData(files); + + //TS_ASSERT_EQUALS(storage.getNodeCount(), 3); + //TS_ASSERT_EQUALS(storage.getEdgeCount(), 2); + //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 4);*/ +} + +TEST_CASE("storage clears referenced single file data of multi file storage") +{ + //m_filePath = "file.h"; + + //TestStorage storage; + + //ParseFunction isTrue = ParseFunction(typeUsage("bool"), createNameHierarchy("isTrue"), parameters("void")); + //storage.onFunctionParsed(validLocation(), isTrue, validLocation()); + + //m_filePath = "file.cpp"; + + //ParseFunction main = ParseFunction(typeUsage("int"), createNameHierarchy("main"), parameters("void")); + //storage.onFunctionParsed(validLocation(), main, validLocation()); + + //storage.onCallParsed(validLocation(), main, isTrue); + + //TS_ASSERT_EQUALS(storage.getNodeCount(), 5); + //TS_ASSERT_EQUALS(storage.getEdgeCount(), 5); + //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 9); + + //std::set files; + //files.insert(FilePath("file.h")); + //storage.clearFileData(files); + + //TS_ASSERT_EQUALS(storage.getNodeCount(), 4); + //TS_ASSERT_EQUALS(storage.getEdgeCount(), 3); + //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 5); +} + +TEST_CASE("storage clears multi file data of multi file storage") +{ + //m_filePath = "file.h"; + + //TestStorage storage; + + //ParseFunction isTrue = ParseFunction(typeUsage("bool"), createNameHierarchy("isTrue"), parameters("void")); + //storage.onFunctionParsed(validLocation(), isTrue, validLocation()); + + //m_filePath = "file.cpp"; + + //ParseFunction main = ParseFunction(typeUsage("int"), createNameHierarchy("main"), parameters("void")); + //storage.onFunctionParsed(validLocation(), main, validLocation()); + + //storage.onCallParsed(validLocation(), main, isTrue); + + //TS_ASSERT_EQUALS(storage.getNodeCount(), 5); + //TS_ASSERT_EQUALS(storage.getEdgeCount(), 5); + //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 9); + + //std::set filePaths; + //filePaths.insert(FilePath("file.cpp")); + //filePaths.insert(FilePath("file.h")); + //storage.clearFileData(filePaths); + + //TS_ASSERT_EQUALS(storage.getNodeCount(), 0); + //TS_ASSERT_EQUALS(storage.getEdgeCount(), 0); + //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 0); +} + +TEST_CASE("storage finds and removes depending file nodes") +{ + //TestStorage storage; + + //Id id1 = storage.onFileParsed("f.h"); + //Id id2 = storage.onFileParsed("file.h"); + //Id id3 = storage.onFileParsed("file.cpp"); + //Id id4 = storage.onFileIncludeParsed(validLocation(), "file.h", "f.h"); + //Id id5 = storage.onFileIncludeParsed(validLocation(), "file.cpp", "file.h"); + + //std::string name1 = storage.getNodeWithId(id2)->getQualifiedNameWithSignature(); + //std::string name2 = storage.getNodeWithId(id3)->getQualifiedNameWithSignature(); + + //std::set filePaths; + //filePaths.insert(FilePath(name1)); + //std::set dependingFilePaths = storage.getDependingFilePathsAndRemoveFileNodes(filePaths); + + //TS_ASSERT_EQUALS(dependingFilePaths.size(), 1); + //TS_ASSERT_EQUALS(dependingFilePaths.begin()->str(), name2); + + //TS_ASSERT(storage.getNodeWithId(id1)); + //TS_ASSERT(!storage.getNodeWithId(id2)); + //TS_ASSERT(!storage.getNodeWithId(id3)); + //TS_ASSERT(!storage.getEdgeWithId(id4)); + //TS_ASSERT(!storage.getEdgeWithId(id5)); +} diff --git a/src/test/StorageTestSuite.h b/src/test/StorageTestSuite.h deleted file mode 100644 index 4bda2379..00000000 --- a/src/test/StorageTestSuite.h +++ /dev/null @@ -1,284 +0,0 @@ -#include - -#include "utilityString.h" - -#include "ParseLocation.h" -#include "IntermediateStorage.h" -#include "PersistentStorage.h" - -class StorageTestSuite: public CxxTest::TestSuite -{ -public: - void setUp() - { - m_filePath = FilePath(L"file.cpp"); - } - - void test_storage_saves_file() - { - TestStorage storage; - - std::wstring filePath = L"path/to/test.h"; - - std::shared_ptr intermetiateStorage = std::make_shared(); - Id id = intermetiateStorage->addNode(StorageNodeData(NodeType::typeToInt(NodeType::NODE_FILE), NameHierarchy::serialize(NameHierarchy(filePath, NAME_DELIMITER_FILE)))).first; - intermetiateStorage->addFile(StorageFile(id, filePath, L"someLanguage", "someTime", true, true)); - - storage.inject(intermetiateStorage.get()); - - TS_ASSERT_EQUALS(storage.getNameHierarchyForNodeId(id).getQualifiedName(), filePath); - TS_ASSERT(storage.getNodeTypeForNodeWithId(id).isFile()); - - } - void test_storage_saves_node() - { - NameHierarchy a = createNameHierarchy(L"type"); - - TestStorage storage; - - std::shared_ptr intermetiateStorage = std::make_shared(); - intermetiateStorage->addNode(StorageNodeData(NodeType::typeToInt(NodeType::NODE_TYPEDEF), NameHierarchy::serialize(a))); - - storage.inject(intermetiateStorage.get()); - - Id storedId = storage.getNodeIdForNameHierarchy(a); - - TS_ASSERT(storedId != 0); - TS_ASSERT_EQUALS(storage.getNodeTypeForNodeWithId(storedId).getType(), NodeType::NODE_TYPEDEF); - } - - - void test_storage_saves_field_as_member() - { - NameHierarchy a = createNameHierarchy(L"Struct"); - NameHierarchy b = createNameHierarchy(L"Struct::m_field"); - - TestStorage storage; - - std::shared_ptr intermetiateStorage = std::make_shared(); - - Id aId = intermetiateStorage->addNode(StorageNodeData(NodeType::typeToInt(NodeType::NODE_STRUCT), NameHierarchy::serialize(a))).first; - intermetiateStorage->addSymbol(StorageSymbol(aId, DEFINITION_EXPLICIT)); - - Id bId = intermetiateStorage->addNode(StorageNodeData(NodeType::typeToInt(NodeType::NODE_FIELD), NameHierarchy::serialize(b))).first; - intermetiateStorage->addSymbol(StorageSymbol(bId, DEFINITION_EXPLICIT)); - intermetiateStorage->addEdge(StorageEdgeData(Edge::typeToInt(Edge::EDGE_MEMBER), aId, bId)); - - storage.inject(intermetiateStorage.get()); - bool foundEdge = false; - - const Id sourceId = storage.getNodeIdForNameHierarchy(a); - const Id targetId = storage.getNodeIdForNameHierarchy(b); - for (auto edge : storage.getStorageEdges()) - { - if (edge.sourceNodeId == sourceId && edge.targetNodeId == targetId && edge.type == Edge::typeToInt(Edge::EDGE_MEMBER)) - { - foundEdge = true; - } - } - TS_ASSERT(foundEdge); - } - - - void test_storage_saves_method_static() - { - //TestStorage storage; - //Id id = storage.onMethodParsed( - // validLocation(1), - // ParseFunction(typeUsage("void"), createNameHierarchy("isMethod"), parameters("bool"), true), - // ParserClient::ACCESS_NONE, - // ParserClient::ABSTRACTION_NONE, - // validLocation(4) - //); - - //Node* node = storage.getNodeWithId(id); - //TS_ASSERT(node); - //TS_ASSERT_EQUALS(node->getQualifiedNameWithSignature(), "isMethod"); - //TS_ASSERT_EQUALS(node->getType(), NodeType::NODE_METHOD); - //TS_ASSERT(node->getComponent()); - } - - void test_storage_clears_single_file_data_of_single_file_storage() - { - /*TestStorage storage; - storage.onFunctionParsed( - validLocation(), ParseFunction(typeUsage("bool"), createNameHierarchy("isTrue"), - parameters("char")), validLocation() - ); - - TS_ASSERT_EQUALS(storage.getNodeCount(), 3); - TS_ASSERT_EQUALS(storage.getEdgeCount(), 2); - TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 4); - - std::set files; - files.insert(FilePath(m_filePath)); - storage.clearFileData(files); - - TS_ASSERT_EQUALS(storage.getNodeCount(), 0); - TS_ASSERT_EQUALS(storage.getEdgeCount(), 0); - TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 0);*/ - } - - void test_storage_clears_unreferenced_single_file_data_of_multi_file_storage() - { - //m_filePath = "file.h"; - - //TestStorage storage; - - //ParseFunction isTrue = ParseFunction(typeUsage("bool"), createNameHierarchy("isTrue"), parameters("char")); - //storage.onFunctionParsed(validLocation(), isTrue, validLocation()); - - //m_filePath = "file.cpp"; - - //ParseFunction main = ParseFunction(typeUsage("int"), createNameHierarchy("main"), parameters("void")); - //storage.onFunctionParsed(validLocation(), main, validLocation()); - - //storage.onCallParsed(validLocation(), main, isTrue); - - //TS_ASSERT_EQUALS(storage.getNodeCount(), 6); - //TS_ASSERT_EQUALS(storage.getEdgeCount(), 5); - //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 9); - - //std::set files; - //files.insert(FilePath("file.cpp")); - //storage.clearFileData(files); - - //TS_ASSERT_EQUALS(storage.getNodeCount(), 3); - //TS_ASSERT_EQUALS(storage.getEdgeCount(), 2); - //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 4);*/ - } - - void test_storage_clears_referenced_single_file_data_of_multi_file_storage() - { - //m_filePath = "file.h"; - - //TestStorage storage; - - //ParseFunction isTrue = ParseFunction(typeUsage("bool"), createNameHierarchy("isTrue"), parameters("void")); - //storage.onFunctionParsed(validLocation(), isTrue, validLocation()); - - //m_filePath = "file.cpp"; - - //ParseFunction main = ParseFunction(typeUsage("int"), createNameHierarchy("main"), parameters("void")); - //storage.onFunctionParsed(validLocation(), main, validLocation()); - - //storage.onCallParsed(validLocation(), main, isTrue); - - //TS_ASSERT_EQUALS(storage.getNodeCount(), 5); - //TS_ASSERT_EQUALS(storage.getEdgeCount(), 5); - //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 9); - - //std::set files; - //files.insert(FilePath("file.h")); - //storage.clearFileData(files); - - //TS_ASSERT_EQUALS(storage.getNodeCount(), 4); - //TS_ASSERT_EQUALS(storage.getEdgeCount(), 3); - //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 5); - } - - void test_storage_clears_multi_file_data_of_multi_file_storage() - { - //m_filePath = "file.h"; - - //TestStorage storage; - - //ParseFunction isTrue = ParseFunction(typeUsage("bool"), createNameHierarchy("isTrue"), parameters("void")); - //storage.onFunctionParsed(validLocation(), isTrue, validLocation()); - - //m_filePath = "file.cpp"; - - //ParseFunction main = ParseFunction(typeUsage("int"), createNameHierarchy("main"), parameters("void")); - //storage.onFunctionParsed(validLocation(), main, validLocation()); - - //storage.onCallParsed(validLocation(), main, isTrue); - - //TS_ASSERT_EQUALS(storage.getNodeCount(), 5); - //TS_ASSERT_EQUALS(storage.getEdgeCount(), 5); - //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 9); - - //std::set filePaths; - //filePaths.insert(FilePath("file.cpp")); - //filePaths.insert(FilePath("file.h")); - //storage.clearFileData(filePaths); - - //TS_ASSERT_EQUALS(storage.getNodeCount(), 0); - //TS_ASSERT_EQUALS(storage.getEdgeCount(), 0); - //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 0); - } - - void test_storage_finds_and_removes_depending_file_nodes() - { - //TestStorage storage; - - //Id id1 = storage.onFileParsed("f.h"); - //Id id2 = storage.onFileParsed("file.h"); - //Id id3 = storage.onFileParsed("file.cpp"); - //Id id4 = storage.onFileIncludeParsed(validLocation(), "file.h", "f.h"); - //Id id5 = storage.onFileIncludeParsed(validLocation(), "file.cpp", "file.h"); - - //std::string name1 = storage.getNodeWithId(id2)->getQualifiedNameWithSignature(); - //std::string name2 = storage.getNodeWithId(id3)->getQualifiedNameWithSignature(); - - //std::set filePaths; - //filePaths.insert(FilePath(name1)); - //std::set dependingFilePaths = storage.getDependingFilePathsAndRemoveFileNodes(filePaths); - - //TS_ASSERT_EQUALS(dependingFilePaths.size(), 1); - //TS_ASSERT_EQUALS(dependingFilePaths.begin()->str(), name2); - - //TS_ASSERT(storage.getNodeWithId(id1)); - //TS_ASSERT(!storage.getNodeWithId(id2)); - //TS_ASSERT(!storage.getNodeWithId(id3)); - //TS_ASSERT(!storage.getEdgeWithId(id4)); - //TS_ASSERT(!storage.getEdgeWithId(id5)); - } - -private: - class TestStorage - : public PersistentStorage - { - public: - TestStorage() - : PersistentStorage(FilePath(L"data/test.sqlite"), FilePath(L"data/testBookmarks.sqlite")) - { - clear(); - } - - //const size_t getNodeCount() const - //{ - // return getGraph().getNodeCount(); - //} - - //const size_t getEdgeCount() const - //{ - // return getGraph().getEdgeCount(); - //} - }; - - ParseLocation validLocation(Id locationId = 0) const - { - return ParseLocation(1, 1, locationId, 1, locationId); - } - - NameHierarchy createFunctionNameHierarchy(std::wstring ret, std::wstring name, std::wstring parameters) const - { - NameHierarchy nameHierarchy = createNameHierarchy(name); - std::wstring lastName = nameHierarchy.back().getName(); - nameHierarchy.pop(); - nameHierarchy.push(NameElement(lastName, ret, parameters)); - return nameHierarchy; - } - - NameHierarchy createNameHierarchy(std::wstring s) const - { - NameHierarchy nameHierarchy(NAME_DELIMITER_CXX); - for (std::wstring element: utility::splitToVector(s, nameDelimiterTypeToString(NAME_DELIMITER_CXX))) - { - nameHierarchy.push(element); - } - return nameHierarchy; - } - - FilePath m_filePath; -}; diff --git a/src/test/TaskSchedulerTestSuite.cpp b/src/test/TaskSchedulerTestSuite.cpp new file mode 100644 index 00000000..e96d7620 --- /dev/null +++ b/src/test/TaskSchedulerTestSuite.cpp @@ -0,0 +1,283 @@ +#include "catch.hpp" + +#include +#include + +#include "Blackboard.h" +#include "Task.h" +#include "TaskGroupSelector.h" +#include "TaskGroupSequence.h" +#include "TaskScheduler.h" + +namespace +{ + void executeTask(Task& task) + { + std::shared_ptr blakboard = std::make_shared(); + while (true) + { + if (task.update(blakboard) != Task::STATE_RUNNING) + { + return; + } + } + } + + class TestTask : public Task + { + public: + TestTask(int* orderCountPtr, int updateCount, TaskState returnState = STATE_SUCCESS) + : orderCount(*orderCountPtr) + , updateCount(updateCount) + , returnState(returnState) + , enterCallOrder(0) + , updateCallOrder(0) + , exitCallOrder(0) + , resetCallOrder(0) + { + } + + virtual void doEnter(std::shared_ptr blakboard) + { + enterCallOrder = ++orderCount; + } + + virtual TaskState doUpdate(std::shared_ptr blakboard) + { + updateCallOrder = ++orderCount; + + if (updateCount < 0) + { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + return Task::STATE_RUNNING; + } + + updateCount--; + if (updateCount) + { + return Task::STATE_RUNNING; + } + + return returnState; + } + + virtual void doExit(std::shared_ptr blakboard) + { + exitCallOrder = ++orderCount; + } + + virtual void doReset(std::shared_ptr blakboard) + { + resetCallOrder = ++orderCount; + } + + int& orderCount; + int updateCount; + TaskState returnState; + + int enterCallOrder; + int updateCallOrder; + int exitCallOrder; + int resetCallOrder; + }; + + class TestTaskDispatch : public TestTask + { + public: + TestTaskDispatch(int* orderCountPtr, int updateCount, TaskScheduler* scheduler) + : TestTask(orderCountPtr, updateCount) + , scheduler(scheduler) + { + } + + virtual TaskState doUpdate(std::shared_ptr blakboard) + { + subTask = std::make_shared(&orderCount, 1); + scheduler->pushTask(subTask); + + return TestTask::doUpdate(blakboard); + } + + TaskScheduler* scheduler; + std::shared_ptr subTask; + }; + + void waitForThread(TaskScheduler& scheduler) + { + static const int THREAD_WAIT_TIME_MS = 20; + do + { + std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_WAIT_TIME_MS)); + } while (scheduler.hasTasksQueued()); + } +} + +TEST_CASE("scheduler loop starts and stops") +{ + TaskScheduler scheduler(0); + REQUIRE(!scheduler.loopIsRunning()); + + scheduler.startSchedulerLoopThreaded(); + + waitForThread(scheduler); + + REQUIRE(scheduler.loopIsRunning()); + + scheduler.stopSchedulerLoop(); + + waitForThread(scheduler); + + REQUIRE(!scheduler.loopIsRunning()); +} + +TEST_CASE("tasks get executed without scheduling in correct order") +{ + int order = 0; + TestTask task(&order, 1); + + executeTask(task); + + REQUIRE(3 == order); + + REQUIRE(1 == task.enterCallOrder); + REQUIRE(2 == task.updateCallOrder); + REQUIRE(3 == task.exitCallOrder); +} + +TEST_CASE("scheduled tasks get processed with callbacks in correct order") +{ + TaskScheduler scheduler(0); + scheduler.startSchedulerLoopThreaded(); + + int order = 0; + std::shared_ptr task = std::make_shared(&order, 1); + + scheduler.pushTask(task); + + waitForThread(scheduler); + + scheduler.stopSchedulerLoop(); + + REQUIRE(3 == order); + + REQUIRE(1 == task->enterCallOrder); + REQUIRE(2 == task->updateCallOrder); + REQUIRE(3 == task->exitCallOrder); +} + +TEST_CASE("sequential task group to process tasks in correct order") +{ + TaskScheduler scheduler(0); + scheduler.startSchedulerLoopThreaded(); + + int order = 0; + std::shared_ptr task1 = std::make_shared(&order, 1); + std::shared_ptr task2 = std::make_shared(&order, 1); + + std::shared_ptr taskGroup = std::make_shared(); + taskGroup->addTask(task1); + taskGroup->addTask(task2); + + scheduler.pushTask(taskGroup); + + waitForThread(scheduler); + + scheduler.stopSchedulerLoop(); + + REQUIRE(6 == order); + + REQUIRE(1 == task1->enterCallOrder); + REQUIRE(2 == task1->updateCallOrder); + REQUIRE(3 == task1->exitCallOrder); + + REQUIRE(4 == task2->enterCallOrder); + REQUIRE(5 == task2->updateCallOrder); + REQUIRE(6 == task2->exitCallOrder); +} + +TEST_CASE("sequential task group does not evaluate tasks after failure") +{ + TaskScheduler scheduler(0); + scheduler.startSchedulerLoopThreaded(); + + int order = 0; + std::shared_ptr task1 = std::make_shared(&order, 1, Task::STATE_FAILURE); + std::shared_ptr task2 = std::make_shared(&order, -1); + + std::shared_ptr taskGroup = std::make_shared(); + taskGroup->addTask(task1); + taskGroup->addTask(task2); + + scheduler.pushTask(taskGroup); + + waitForThread(scheduler); + + scheduler.stopSchedulerLoop(); + + REQUIRE(1 == task1->enterCallOrder); + REQUIRE(2 == task1->updateCallOrder); + REQUIRE(3 == task1->exitCallOrder); + + REQUIRE(0 == task2->enterCallOrder); + REQUIRE(0 == task2->updateCallOrder); + REQUIRE(0 == task2->exitCallOrder); +} + +TEST_CASE("sequential task group does not evaluate tasks after success") +{ + TaskScheduler scheduler(0); + scheduler.startSchedulerLoopThreaded(); + + int order = 0; + std::shared_ptr task1 = std::make_shared(&order, 1, Task::STATE_FAILURE); + std::shared_ptr task2 = std::make_shared(&order, 1, Task::STATE_SUCCESS); + std::shared_ptr task3 = std::make_shared(&order, -1); + + std::shared_ptr taskGroup = std::make_shared(); + taskGroup->addTask(task1); + taskGroup->addTask(task2); + taskGroup->addTask(task3); + + scheduler.pushTask(taskGroup); + + waitForThread(scheduler); + + scheduler.stopSchedulerLoop(); + + REQUIRE(1 == task1->enterCallOrder); + REQUIRE(2 == task1->updateCallOrder); + REQUIRE(3 == task1->exitCallOrder); + + REQUIRE(4 == task2->enterCallOrder); + REQUIRE(5 == task2->updateCallOrder); + REQUIRE(6 == task2->exitCallOrder); + + REQUIRE(0 == task3->enterCallOrder); + REQUIRE(0 == task3->updateCallOrder); + REQUIRE(0 == task3->exitCallOrder); +} + +TEST_CASE("task scheduling within task processing") +{ + TaskScheduler scheduler(0); + scheduler.startSchedulerLoopThreaded(); + + int order = 0; + std::shared_ptr task = std::make_shared(&order, 1, &scheduler); + + scheduler.pushTask(task); + + waitForThread(scheduler); + + scheduler.stopSchedulerLoop(); + + REQUIRE(6 == order); + + REQUIRE(1 == task->enterCallOrder); + REQUIRE(2 == task->updateCallOrder); + REQUIRE(3 == task->exitCallOrder); + + REQUIRE(4 == task->subTask->enterCallOrder); + REQUIRE(5 == task->subTask->updateCallOrder); + REQUIRE(6 == task->subTask->exitCallOrder); +} diff --git a/src/test/TaskSchedulerTestSuite.h b/src/test/TaskSchedulerTestSuite.h deleted file mode 100644 index a3ecd53d..00000000 --- a/src/test/TaskSchedulerTestSuite.h +++ /dev/null @@ -1,286 +0,0 @@ -#include - -#include -#include - -#include "Blackboard.h" -#include "Task.h" -#include "TaskGroupSelector.h" -#include "TaskGroupSequence.h" -#include "TaskScheduler.h" - -class TaskSchedulerTestSuite: public CxxTest::TestSuite -{ -public: - void test_scheduler_loop_starts_and_stops(void) - { - TaskScheduler scheduler(0); - TS_ASSERT(!scheduler.loopIsRunning()); - - scheduler.startSchedulerLoopThreaded(); - - waitForThread(scheduler); - - TS_ASSERT(scheduler.loopIsRunning()); - - scheduler.stopSchedulerLoop(); - - waitForThread(scheduler); - - TS_ASSERT(!scheduler.loopIsRunning()); - } - - void test_tasks_get_executed_without_scheduling_in_correct_order(void) - { - int order = 0; - TestTask task(&order, 1); - - executeTask(task); - - TS_ASSERT_EQUALS(3, order); - - TS_ASSERT_EQUALS(1, task.enterCallOrder); - TS_ASSERT_EQUALS(2, task.updateCallOrder); - TS_ASSERT_EQUALS(3, task.exitCallOrder); - } - - void test_scheduled_tasks_get_processed_with_callbacks_in_correct_order(void) - { - TaskScheduler scheduler(0); - scheduler.startSchedulerLoopThreaded(); - - int order = 0; - std::shared_ptr task = std::make_shared(&order, 1); - - scheduler.pushTask(task); - - waitForThread(scheduler); - - scheduler.stopSchedulerLoop(); - - TS_ASSERT_EQUALS(3, order); - - TS_ASSERT_EQUALS(1, task->enterCallOrder); - TS_ASSERT_EQUALS(2, task->updateCallOrder); - TS_ASSERT_EQUALS(3, task->exitCallOrder); - } - - void test_sequential_task_group_to_process_tasks_in_correct_order(void) - { - TaskScheduler scheduler(0); - scheduler.startSchedulerLoopThreaded(); - - int order = 0; - std::shared_ptr task1 = std::make_shared(&order, 1); - std::shared_ptr task2 = std::make_shared(&order, 1); - - std::shared_ptr taskGroup = std::make_shared(); - taskGroup->addTask(task1); - taskGroup->addTask(task2); - - scheduler.pushTask(taskGroup); - - waitForThread(scheduler); - - scheduler.stopSchedulerLoop(); - - TS_ASSERT_EQUALS(6, order); - - TS_ASSERT_EQUALS(1, task1->enterCallOrder); - TS_ASSERT_EQUALS(2, task1->updateCallOrder); - TS_ASSERT_EQUALS(3, task1->exitCallOrder); - - TS_ASSERT_EQUALS(4, task2->enterCallOrder); - TS_ASSERT_EQUALS(5, task2->updateCallOrder); - TS_ASSERT_EQUALS(6, task2->exitCallOrder); - } - - void test_sequential_task_group_does_not_evaluate_tasks_after_failure(void) - { - TaskScheduler scheduler(0); - scheduler.startSchedulerLoopThreaded(); - - int order = 0; - std::shared_ptr task1 = std::make_shared(&order, 1, Task::STATE_FAILURE); - std::shared_ptr task2 = std::make_shared(&order, -1); - - std::shared_ptr taskGroup = std::make_shared(); - taskGroup->addTask(task1); - taskGroup->addTask(task2); - - scheduler.pushTask(taskGroup); - - waitForThread(scheduler); - - scheduler.stopSchedulerLoop(); - - TS_ASSERT_EQUALS(1, task1->enterCallOrder); - TS_ASSERT_EQUALS(2, task1->updateCallOrder); - TS_ASSERT_EQUALS(3, task1->exitCallOrder); - - TS_ASSERT_EQUALS(0, task2->enterCallOrder); - TS_ASSERT_EQUALS(0, task2->updateCallOrder); - TS_ASSERT_EQUALS(0, task2->exitCallOrder); - } - - void test_sequential_task_group_does_not_evaluate_tasks_after_success(void) - { - TaskScheduler scheduler(0); - scheduler.startSchedulerLoopThreaded(); - - int order = 0; - std::shared_ptr task1 = std::make_shared(&order, 1, Task::STATE_FAILURE); - std::shared_ptr task2 = std::make_shared(&order, 1, Task::STATE_SUCCESS); - std::shared_ptr task3 = std::make_shared(&order, -1); - - std::shared_ptr taskGroup = std::make_shared(); - taskGroup->addTask(task1); - taskGroup->addTask(task2); - taskGroup->addTask(task3); - - scheduler.pushTask(taskGroup); - - waitForThread(scheduler); - - scheduler.stopSchedulerLoop(); - - TS_ASSERT_EQUALS(1, task1->enterCallOrder); - TS_ASSERT_EQUALS(2, task1->updateCallOrder); - TS_ASSERT_EQUALS(3, task1->exitCallOrder); - - TS_ASSERT_EQUALS(4, task2->enterCallOrder); - TS_ASSERT_EQUALS(5, task2->updateCallOrder); - TS_ASSERT_EQUALS(6, task2->exitCallOrder); - - TS_ASSERT_EQUALS(0, task3->enterCallOrder); - TS_ASSERT_EQUALS(0, task3->updateCallOrder); - TS_ASSERT_EQUALS(0, task3->exitCallOrder); - } - - void test_task_scheduling_within_task_processing() - { - TaskScheduler scheduler(0); - scheduler.startSchedulerLoopThreaded(); - - int order = 0; - std::shared_ptr task = std::make_shared(&order, 1, &scheduler); - - scheduler.pushTask(task); - - waitForThread(scheduler); - - scheduler.stopSchedulerLoop(); - - TS_ASSERT_EQUALS(6, order); - - TS_ASSERT_EQUALS(1, task->enterCallOrder); - TS_ASSERT_EQUALS(2, task->updateCallOrder); - TS_ASSERT_EQUALS(3, task->exitCallOrder); - - TS_ASSERT_EQUALS(4, task->subTask->enterCallOrder); - TS_ASSERT_EQUALS(5, task->subTask->updateCallOrder); - TS_ASSERT_EQUALS(6, task->subTask->exitCallOrder); - } - -private: - void executeTask(Task& task) - { - std::shared_ptr blakboard = std::make_shared(); - while (true) - { - if (task.update(blakboard) != Task::STATE_RUNNING) - { - return; - } - } - } - - class TestTask: public Task - { - public: - TestTask(int* orderCountPtr, int updateCount, TaskState returnState = STATE_SUCCESS) - : orderCount(*orderCountPtr) - , updateCount(updateCount) - , returnState(returnState) - , enterCallOrder(0) - , updateCallOrder(0) - , exitCallOrder(0) - , resetCallOrder(0) - { - } - - virtual void doEnter(std::shared_ptr blakboard) - { - enterCallOrder = ++orderCount; - } - - virtual TaskState doUpdate(std::shared_ptr blakboard) - { - updateCallOrder = ++orderCount; - - if (updateCount < 0) - { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - return Task::STATE_RUNNING; - } - - updateCount--; - if (updateCount) - { - return Task::STATE_RUNNING; - } - - return returnState; - } - - virtual void doExit(std::shared_ptr blakboard) - { - exitCallOrder = ++orderCount; - } - - virtual void doReset(std::shared_ptr blakboard) - { - resetCallOrder = ++orderCount; - } - - int& orderCount; - int updateCount; - TaskState returnState; - - int enterCallOrder; - int updateCallOrder; - int exitCallOrder; - int resetCallOrder; - }; - - class TestTaskDispatch: public TestTask - { - public: - TestTaskDispatch(int* orderCountPtr, int updateCount, TaskScheduler* scheduler) - : TestTask(orderCountPtr, updateCount) - , scheduler(scheduler) - { - } - - virtual TaskState doUpdate(std::shared_ptr blakboard) - { - subTask = std::make_shared(&orderCount, 1); - scheduler->pushTask(subTask); - - return TestTask::doUpdate(blakboard); - } - - TaskScheduler* scheduler; - std::shared_ptr subTask; - }; - - void waitForThread(TaskScheduler& scheduler) const - { - static const int THREAD_WAIT_TIME_MS = 20; - do - { - std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_WAIT_TIME_MS)); - } - while (scheduler.hasTasksQueued()); - } -}; diff --git a/src/test/TestSuiteFixture.cpp b/src/test/TestSuiteFixture.cpp deleted file mode 100644 index 1b85bf40..00000000 --- a/src/test/TestSuiteFixture.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "TestSuiteFixture.h" - -#include - -#include "ApplicationSettings.h" - -TestSuiteFixture::TestSuiteFixture() -{ -} - -TestSuiteFixture::~TestSuiteFixture() -{ -} - -bool TestSuiteFixture::setUpWorld() -{ -#ifdef __linux__ - const std::string homedir = getenv("HOME"); - - if (!homedir.empty()) - { - if(!ApplicationSettings::getInstance()->load( - FilePath(homedir + "/.config/sourcetrail/ApplicationSettings.xml") - )) - { - std::cout << "no settings" << std::endl; - return false; - } - } - else - { - std::cout << "no homedir" << std::endl; - return false; - } - -#else - ApplicationSettings::getInstance()->load(FilePath(L"data/TestSettings.xml")); -#endif - - return true; -} - -bool TestSuiteFixture::tearDownWorld() -{ - return true; -} diff --git a/src/test/TestSuiteFixture.h b/src/test/TestSuiteFixture.h deleted file mode 100644 index aea73704..00000000 --- a/src/test/TestSuiteFixture.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef TEST_SUITE_FIXTURE_H -#define TEST_SUITE_FIXTURE_H - -#include - -class TestSuiteFixture : public CxxTest::GlobalFixture -{ -public: - TestSuiteFixture(); - virtual ~TestSuiteFixture(); - - virtual bool setUpWorld(); - virtual bool tearDownWorld(); -}; - -// According to the CxxTest Documentation global fixtures are actually supposed to be implemented as global static instances -// See http://cxxtest.com/guide.html for more details -static TestSuiteFixture testSuiteFixture; - -#endif // TEST_SUITE_FIXTURE_H diff --git a/src/test/TextAccessTestSuite.cpp b/src/test/TextAccessTestSuite.cpp new file mode 100644 index 00000000..3548a78a --- /dev/null +++ b/src/test/TextAccessTestSuite.cpp @@ -0,0 +1,144 @@ +#include "catch.hpp" + +#include "TextAccess.h" + +namespace +{ + std::string getTestText() + { + std::string text = + "\"But the plans were on display . . .\"\n" + "\"On display? I eventually had to go down to the cellar to find them.\"\n" + "\"That's the display department.\"\n" + "\"With a torch.\"\n" + "\"Ah, well the lights had probably gone.\"\n" + "\"So had the stairs.\"\n" + "\"But look, you found the notice, didn't you?\"\n" + "\"Yes,\" said Arthur, \"yes I did. It was on display in the bottom of a locked" + " filing cabinet stuck in a disused lavatory with a sign on the door saying" + " Beware of the Leopard.\"\n"; + + return text; + } +} + +TEST_CASE("textAccessString constructor") +{ + std::string text = getTestText(); + + std::shared_ptr textAccess = TextAccess::createFromString(text); + + REQUIRE(textAccess.get() != nullptr); +} + +TEST_CASE("textAccessString lines count") +{ + std::string text = getTestText(); + unsigned int lineCount = 8; + + std::shared_ptr textAccess = TextAccess::createFromString(text); + + REQUIRE(textAccess->getLineCount() == lineCount); +} + +TEST_CASE("textAccessString lines content") +{ + std::string text = getTestText(); + + std::shared_ptr textAccess = TextAccess::createFromString(text); + std::vector lines = textAccess->getLines(1, 4); + + REQUIRE(lines.size() == 4); + REQUIRE(lines[0] == "\"But the plans were on display . . .\"\n"); + REQUIRE(lines[1] == "\"On display? I eventually had to go down to the cellar to find them.\"\n"); + REQUIRE(lines[2] == "\"That's the display department.\"\n"); + REQUIRE(lines[3] == "\"With a torch.\"\n"); +} + +TEST_CASE("textAccessString lines content error handling") +{ + std::string text = getTestText(); + + std::shared_ptr textAccess = TextAccess::createFromString(text); + std::vector lines = textAccess->getLines(3, 2); + + REQUIRE(lines.size() == 0); + + lines = textAccess->getLines(10, 3); + + REQUIRE(lines.size() == 0); + + lines = textAccess->getLines(1, 10); + + REQUIRE(lines.size() == 0); + + std::string line = textAccess->getLine(0); + + REQUIRE(line == ""); + + lines = textAccess->getLines(0, 2); + + REQUIRE(line == ""); +} + +TEST_CASE("textAccessString single line content") +{ + std::string text = getTestText(); + + std::shared_ptr textAccess = TextAccess::createFromString(text); + std::string line = textAccess->getLine(6); + + REQUIRE(line == "\"So had the stairs.\"\n"); +} + +TEST_CASE("textAccessString all lines") +{ + std::string text = getTestText(); + unsigned int lineCount = 8; + + std::shared_ptr textAccess = TextAccess::createFromString(text); + std::vector lines = textAccess->getAllLines(); + + REQUIRE(lines.size() == lineCount); +} + +TEST_CASE("textAccessFile constructor") +{ + FilePath filePath(L"data/TextAccessTestSuite/text.txt"); + + std::shared_ptr textAccess = TextAccess::createFromFile(filePath); + + REQUIRE(textAccess.get() != nullptr); +} + +TEST_CASE("textAccessFile lines count") +{ + FilePath filePath(L"data/TextAccessTestSuite/text.txt"); + unsigned int lineCount = 7; + + std::shared_ptr textAccess = TextAccess::createFromFile(filePath); + + REQUIRE(textAccess->getLineCount() == lineCount); +} + +TEST_CASE("textAccessFile lines content") +{ + FilePath filePath(L"data/TextAccessTestSuite/text.txt"); + + std::shared_ptr textAccess = TextAccess::createFromFile(filePath); + std::vector lines = textAccess->getLines(1, 4); + + REQUIRE(lines.size() == 4); + REQUIRE(lines[0] == "\"If you're a researcher on this book thing and you were on Earth, you must have been gathering material on it.\"\n"); + REQUIRE(lines[1] == "\"Well, I was able to extend the original entry a bit, yes.\"\n"); + REQUIRE(lines[2] == "\"Let me see what it says in this edition, then. I've got to see it.\"\n"); + REQUIRE(lines[3] == "... \"What? Harmless! Is that all it's got to say? Harmless! One word! ... Well, for God's sake I hope you managed to recitify that a bit.\"\n"); +} + +TEST_CASE("textAccessFile get filePath") +{ + FilePath filePath(L"data/TextAccessTestSuite/text.txt"); + std::shared_ptr textAccess = TextAccess::createFromFile(filePath); + + REQUIRE(textAccess->getFilePath() == filePath); +} diff --git a/src/test/TextAccessTestSuite.h b/src/test/TextAccessTestSuite.h deleted file mode 100644 index a9f073a7..00000000 --- a/src/test/TextAccessTestSuite.h +++ /dev/null @@ -1,146 +0,0 @@ -#include - -#include "TextAccess.h" - -class TextAccessTestSuite : public CxxTest::TestSuite -{ -public: - void test_textAccessString_constructor() - { - std::string text = getTestText(); - - std::shared_ptr textAccess = TextAccess::createFromString(text); - - TS_ASSERT(textAccess.get() != nullptr); - } - - void test_textAccessString_lines_count() - { - std::string text = getTestText(); - unsigned int lineCount = 8; - - std::shared_ptr textAccess = TextAccess::createFromString(text); - - TS_ASSERT_EQUALS(textAccess->getLineCount(), lineCount); - } - - void test_textAccessString_lines_content() - { - std::string text = getTestText(); - - std::shared_ptr textAccess = TextAccess::createFromString(text); - std::vector lines = textAccess->getLines(1, 4); - - TS_ASSERT_EQUALS(lines.size(), 4); - TS_ASSERT_EQUALS(lines[0], "\"But the plans were on display . . .\"\n"); - TS_ASSERT_EQUALS(lines[1], "\"On display? I eventually had to go down to the cellar to find them.\"\n"); - TS_ASSERT_EQUALS(lines[2], "\"That's the display department.\"\n"); - TS_ASSERT_EQUALS(lines[3], "\"With a torch.\"\n"); - } - - void test_textAccessString_lines_content_error_handling() - { - std::string text = getTestText(); - - std::shared_ptr textAccess = TextAccess::createFromString(text); - std::vector lines = textAccess->getLines(3, 2); - - TS_ASSERT_EQUALS(lines.size(), 0); - - lines = textAccess->getLines(10, 3); - - TS_ASSERT_EQUALS(lines.size(), 0); - - lines = textAccess->getLines(1, 10); - - TS_ASSERT_EQUALS(lines.size(), 0); - - std::string line = textAccess->getLine(0); - - TS_ASSERT_EQUALS(line, ""); - - lines = textAccess->getLines(0, 2); - - TS_ASSERT_EQUALS(line, ""); - } - - void test_textAccessString_single_line_content() - { - std::string text = getTestText(); - - std::shared_ptr textAccess = TextAccess::createFromString(text); - std::string line = textAccess->getLine(6); - - TS_ASSERT_EQUALS(line, "\"So had the stairs.\"\n"); - } - - void test_textAccessString_all_lines() - { - std::string text = getTestText(); - unsigned int lineCount = 8; - - std::shared_ptr textAccess = TextAccess::createFromString(text); - std::vector lines = textAccess->getAllLines(); - - TS_ASSERT_EQUALS(lines.size(), lineCount); - } - - void test_textAccessFile_constructor() - { - FilePath filePath(L"data/TextAccessTestSuite/text.txt"); - - std::shared_ptr textAccess = TextAccess::createFromFile(filePath); - - TS_ASSERT(textAccess.get() != nullptr); - } - - void test_textAccessFile_lines_count() - { - FilePath filePath(L"data/TextAccessTestSuite/text.txt"); - unsigned int lineCount = 7; - - std::shared_ptr textAccess = TextAccess::createFromFile(filePath); - - TS_ASSERT_EQUALS(textAccess->getLineCount(), lineCount); - } - - void test_textAccessFile_lines_content() - { - FilePath filePath(L"data/TextAccessTestSuite/text.txt"); - - std::shared_ptr textAccess = TextAccess::createFromFile(filePath); - std::vector lines = textAccess->getLines(1, 4); - - TS_ASSERT_EQUALS(lines.size(), 4); - TS_ASSERT_EQUALS(lines[0], "\"If you're a researcher on this book thing and you were on Earth, you must have been gathering material on it.\"\n"); - TS_ASSERT_EQUALS(lines[1], "\"Well, I was able to extend the original entry a bit, yes.\"\n"); - TS_ASSERT_EQUALS(lines[2], "\"Let me see what it says in this edition, then. I've got to see it.\"\n"); - TS_ASSERT_EQUALS(lines[3], "... \"What? Harmless! Is that all it's got to say? Harmless! One word! ... Well, for God's sake I hope you managed to recitify that a bit.\"\n"); - } - - void test_textAccessFile_get_filePath() - { - FilePath filePath(L"data/TextAccessTestSuite/text.txt"); - std::shared_ptr textAccess = TextAccess::createFromFile(filePath); - - TS_ASSERT_EQUALS(textAccess->getFilePath(), filePath); - } - -private: - std::string getTestText() - { - std::string text = - "\"But the plans were on display . . .\"\n" - "\"On display? I eventually had to go down to the cellar to find them.\"\n" - "\"That's the display department.\"\n" - "\"With a torch.\"\n" - "\"Ah, well the lights had probably gone.\"\n" - "\"So had the stairs.\"\n" - "\"But look, you found the notice, didn't you?\"\n" - "\"Yes,\" said Arthur, \"yes I did. It was on display in the bottom of a locked" - " filing cabinet stuck in a disused lavatory with a sign on the door saying" - " Beware of the Leopard.\"\n"; - - return text; - } -}; diff --git a/src/test/UtilityMavenTestSuite.cpp b/src/test/UtilityMavenTestSuite.cpp new file mode 100644 index 00000000..320d283a --- /dev/null +++ b/src/test/UtilityMavenTestSuite.cpp @@ -0,0 +1,112 @@ +#include "catch.hpp" + +#include "FilePath.h" +#include "utility.h" +#include "utilityMaven.h" +#include "utilityPathDetection.h" + +TEST_CASE("maven path detector is working") +{ + std::shared_ptr mavenPathDetector = utility::getMavenExecutablePathDetector(); + REQUIRE(mavenPathDetector->isWorking()); +} + +TEST_CASE("maven wrapper detects source directories of simple projects") +{ + std::shared_ptr mavenPathDetector = utility::getMavenExecutablePathDetector(); + REQUIRE(!mavenPathDetector->getPaths().empty()); + + if (!mavenPathDetector->getPaths().empty()) + { + std::vector result = utility::mavenGetAllDirectoriesFromEffectivePom( + mavenPathDetector->getPaths().front(), FilePath(L"data/UtilityMavenTestSuite/simple_maven_project"), FilePath(L"data/UtilityMavenTestSuite").makeAbsolute(), false + ); + + REQUIRE(utility::containsElement( + result, FilePath(L"data/UtilityMavenTestSuite/simple_maven_project/src/main/java").makeAbsolute() + )); + + REQUIRE(!utility::containsElement( + result, FilePath(L"data/UtilityMavenTestSuite/simple_maven_project/src/test/java").makeAbsolute() + )); + } +} + +TEST_CASE("maven wrapper detects source and test directories of simple projects") +{ + std::shared_ptr mavenPathDetector = utility::getMavenExecutablePathDetector(); + REQUIRE(!mavenPathDetector->getPaths().empty()); + + if (!mavenPathDetector->getPaths().empty()) + { + std::vector result = utility::mavenGetAllDirectoriesFromEffectivePom( + mavenPathDetector->getPaths().front(), FilePath(L"data/UtilityMavenTestSuite/simple_maven_project"), FilePath(L"data/UtilityMavenTestSuite").makeAbsolute(), true + ); + + REQUIRE(utility::containsElement( + result, FilePath(L"data/UtilityMavenTestSuite/simple_maven_project/src/main/java").makeAbsolute() + )); + + REQUIRE(utility::containsElement( + result, FilePath(L"data/UtilityMavenTestSuite/simple_maven_project/src/test/java").makeAbsolute() + )); + } +} + +TEST_CASE("maven wrapper detects source directories of nested modules") +{ + std::shared_ptr mavenPathDetector = utility::getMavenExecutablePathDetector(); + REQUIRE(!mavenPathDetector->getPaths().empty()); + + if (!mavenPathDetector->getPaths().empty()) + { + std::vector result = utility::mavenGetAllDirectoriesFromEffectivePom( + mavenPathDetector->getPaths().front(), FilePath(L"data/UtilityMavenTestSuite/nested_maven_project"), FilePath(L"data/UtilityMavenTestSuite").makeAbsolute(), false + ); + + REQUIRE(utility::containsElement( + result, FilePath(L"data/UtilityMavenTestSuite/nested_maven_project/module_1/src/main/java").makeAbsolute() + )); + + REQUIRE(utility::containsElement( + result, FilePath(L"data/UtilityMavenTestSuite/nested_maven_project/module_2/src/main/java").makeAbsolute() + )); + + REQUIRE(!utility::containsElement( + result, FilePath(L"data/UtilityMavenTestSuite/nested_maven_project/module_1/src/test/java").makeAbsolute() + )); + + REQUIRE(!utility::containsElement( + result, FilePath(L"data/UtilityMavenTestSuite/nested_maven_project/module_2/src/test/java").makeAbsolute() + )); + } +} + +TEST_CASE("maven wrapper detects source and test directories of nested modules") +{ + std::shared_ptr mavenPathDetector = utility::getMavenExecutablePathDetector(); + REQUIRE(!mavenPathDetector->getPaths().empty()); + + if (!mavenPathDetector->getPaths().empty()) + { + std::vector result = utility::mavenGetAllDirectoriesFromEffectivePom( + mavenPathDetector->getPaths().front(), FilePath(L"data/UtilityMavenTestSuite/nested_maven_project"), FilePath(L"data/UtilityMavenTestSuite").makeAbsolute(), true + ); + + REQUIRE(utility::containsElement( + result, FilePath(L"data/UtilityMavenTestSuite/nested_maven_project/module_1/src/main/java").makeAbsolute() + )); + + REQUIRE(utility::containsElement( + result, FilePath(L"data/UtilityMavenTestSuite/nested_maven_project/module_2/src/main/java").makeAbsolute() + )); + + REQUIRE(utility::containsElement( + result, FilePath(L"data/UtilityMavenTestSuite/nested_maven_project/module_1/src/test/java").makeAbsolute() + )); + + REQUIRE(utility::containsElement( + result, FilePath(L"data/UtilityMavenTestSuite/nested_maven_project/module_2/src/test/java").makeAbsolute() + )); + } +} diff --git a/src/test/UtilityMavenTestSuite.h b/src/test/UtilityMavenTestSuite.h deleted file mode 100644 index 8b3557de..00000000 --- a/src/test/UtilityMavenTestSuite.h +++ /dev/null @@ -1,115 +0,0 @@ -#include - -#include "FilePath.h" -#include "utilityMaven.h" -#include "utilityPathDetection.h" - -class UtilityMavenTestSuite : public CxxTest::TestSuite -{ -public: - void test_maven_path_detector_is_working() - { - std::shared_ptr mavenPathDetector = utility::getMavenExecutablePathDetector(); - TS_ASSERT(mavenPathDetector->isWorking()); - } - - void test_maven_wrapper_detects_source_directories_of_simple_projects() - { - std::shared_ptr mavenPathDetector = utility::getMavenExecutablePathDetector(); - TS_ASSERT(!mavenPathDetector->getPaths().empty()); - - if (!mavenPathDetector->getPaths().empty()) - { - std::vector result = utility::mavenGetAllDirectoriesFromEffectivePom( - mavenPathDetector->getPaths().front(), FilePath(L"data/UtilityMavenTestSuite/simple_maven_project"), FilePath(L"data/UtilityMavenTestSuite").makeAbsolute(), false - ); - - TS_ASSERT(utility::containsElement( - result, FilePath(L"data/UtilityMavenTestSuite/simple_maven_project/src/main/java").makeAbsolute() - )); - - TS_ASSERT(!utility::containsElement( - result, FilePath(L"data/UtilityMavenTestSuite/simple_maven_project/src/test/java").makeAbsolute() - )); - } - } - - void test_maven_wrapper_detects_source_and_test_directories_of_simple_projects() - { - std::shared_ptr mavenPathDetector = utility::getMavenExecutablePathDetector(); - TS_ASSERT(!mavenPathDetector->getPaths().empty()); - - if (!mavenPathDetector->getPaths().empty()) - { - std::vector result = utility::mavenGetAllDirectoriesFromEffectivePom( - mavenPathDetector->getPaths().front(), FilePath(L"data/UtilityMavenTestSuite/simple_maven_project"), FilePath(L"data/UtilityMavenTestSuite").makeAbsolute(), true - ); - - TS_ASSERT(utility::containsElement( - result, FilePath(L"data/UtilityMavenTestSuite/simple_maven_project/src/main/java").makeAbsolute() - )); - - TS_ASSERT(utility::containsElement( - result, FilePath(L"data/UtilityMavenTestSuite/simple_maven_project/src/test/java").makeAbsolute() - )); - } - } - - void test_maven_wrapper_detects_source_directories_of_nested_modules() - { - std::shared_ptr mavenPathDetector = utility::getMavenExecutablePathDetector(); - TS_ASSERT(!mavenPathDetector->getPaths().empty()); - - if (!mavenPathDetector->getPaths().empty()) - { - std::vector result = utility::mavenGetAllDirectoriesFromEffectivePom( - mavenPathDetector->getPaths().front(), FilePath(L"data/UtilityMavenTestSuite/nested_maven_project"), FilePath(L"data/UtilityMavenTestSuite").makeAbsolute(), false - ); - - TS_ASSERT(utility::containsElement( - result, FilePath(L"data/UtilityMavenTestSuite/nested_maven_project/module_1/src/main/java").makeAbsolute() - )); - - TS_ASSERT(utility::containsElement( - result, FilePath(L"data/UtilityMavenTestSuite/nested_maven_project/module_2/src/main/java").makeAbsolute() - )); - - TS_ASSERT(!utility::containsElement( - result, FilePath(L"data/UtilityMavenTestSuite/nested_maven_project/module_1/src/test/java").makeAbsolute() - )); - - TS_ASSERT(!utility::containsElement( - result, FilePath(L"data/UtilityMavenTestSuite/nested_maven_project/module_2/src/test/java").makeAbsolute() - )); - } - } - - void test_maven_wrapper_detects_source_and_test_directories_of_nested_modules() - { - std::shared_ptr mavenPathDetector = utility::getMavenExecutablePathDetector(); - TS_ASSERT(!mavenPathDetector->getPaths().empty()); - - if (!mavenPathDetector->getPaths().empty()) - { - std::vector result = utility::mavenGetAllDirectoriesFromEffectivePom( - mavenPathDetector->getPaths().front(), FilePath(L"data/UtilityMavenTestSuite/nested_maven_project"), FilePath(L"data/UtilityMavenTestSuite").makeAbsolute(), true - ); - - TS_ASSERT(utility::containsElement( - result, FilePath(L"data/UtilityMavenTestSuite/nested_maven_project/module_1/src/main/java").makeAbsolute() - )); - - TS_ASSERT(utility::containsElement( - result, FilePath(L"data/UtilityMavenTestSuite/nested_maven_project/module_2/src/main/java").makeAbsolute() - )); - - TS_ASSERT(utility::containsElement( - result, FilePath(L"data/UtilityMavenTestSuite/nested_maven_project/module_1/src/test/java").makeAbsolute() - )); - - TS_ASSERT(utility::containsElement( - result, FilePath(L"data/UtilityMavenTestSuite/nested_maven_project/module_2/src/test/java").makeAbsolute() - )); - } - } -}; diff --git a/src/test/UtilityStringTestSuite.cpp b/src/test/UtilityStringTestSuite.cpp new file mode 100644 index 00000000..fc53c582 --- /dev/null +++ b/src/test/UtilityStringTestSuite.cpp @@ -0,0 +1,302 @@ +#include "catch.hpp" + +#include "utilityString.h" + +TEST_CASE("split with char delimiter") +{ + std::deque result = utility::split("A,B,C", ','); + + REQUIRE(result.size() == 3); + REQUIRE(result.at(0) == "A"); + REQUIRE(result.at(1) == "B"); + REQUIRE(result.at(2) == "C"); +} + +TEST_CASE("split with string delimiter") +{ + std::deque result = utility::split("A->B>C", "->"); + + REQUIRE(result.size() == 2); + REQUIRE(result.at(0) == "A"); + REQUIRE(result.at(1) == "B>C"); +} + +TEST_CASE("split on empty string") +{ + std::deque result = utility::split("", "->"); + + REQUIRE(result.size() == 1); + REQUIRE(result.at(0) == ""); +} + +TEST_CASE("split with unused delimiter") +{ + std::deque result = utility::split("A:B:C", ";"); + + REQUIRE(result.size() == 1); + REQUIRE(result.at(0) == "A:B:C"); +} + +TEST_CASE("split with delimiters next to each") +{ + std::deque result = utility::split("A::B:C", ':'); + + REQUIRE(result.size() == 4); + REQUIRE(result.at(0) == "A"); + REQUIRE(result.at(1) == ""); + REQUIRE(result.at(2) == "B"); + REQUIRE(result.at(3) == "C"); +} + +TEST_CASE("split with delimiter at start") +{ + std::deque result = utility::split(":B:C", ':'); + + REQUIRE(result.size() == 3); + REQUIRE(result.at(0) == ""); + REQUIRE(result.at(1) == "B"); + REQUIRE(result.at(2) == "C"); +} + +TEST_CASE("split with delimiter at end") +{ + std::deque result = utility::split("B:C:", ':'); + + REQUIRE(result.size() == 3); + REQUIRE(result.at(0) == "B"); + REQUIRE(result.at(1) == "C"); + REQUIRE(result.at(2) == ""); +} + +TEST_CASE("join with char delimiter") +{ + std::deque list; + list.push_back("A"); + list.push_back("B"); + list.push_back("C"); + + std::string result = utility::join(list, ','); + REQUIRE(result == "A,B,C"); +} + +TEST_CASE("join with string delimiter") +{ + std::deque list; + list.push_back("A"); + list.push_back("B"); + list.push_back("C"); + + std::string result = utility::join(list, "=="); + REQUIRE(result == "A==B==C"); +} + +TEST_CASE("join on empty list") +{ + std::deque list; + std::string result = utility::join(list, ','); + REQUIRE(result == ""); +} + +TEST_CASE("join with empty strings in list") +{ + std::deque list; + list.push_back("A"); + list.push_back(""); + list.push_back(""); + + std::string result = utility::join(list, ':'); + REQUIRE(result == "A::"); +} + +TEST_CASE("tokenize with string") +{ + std::deque result = utility::tokenize("A->B->C", "->"); + + REQUIRE(result.size() == 5); + REQUIRE(result.at(0) == "A"); + REQUIRE(result.at(1) == "->"); + REQUIRE(result.at(2) == "B"); + REQUIRE(result.at(3) == "->"); + REQUIRE(result.at(4) == "C"); +} + +TEST_CASE("tokenize with string and delimiter at start") +{ + std::deque result = utility::tokenize("->B", "->"); + + REQUIRE(result.size() == 2); + REQUIRE(result.at(0) == "->"); + REQUIRE(result.at(1) == "B"); +} + +TEST_CASE("tokenize with string and delimiter at end") +{ + std::deque result = utility::tokenize("C+", '+'); + + REQUIRE(result.size() == 2); + REQUIRE(result.at(0) == "C"); + REQUIRE(result.at(1) == "+"); +} + +TEST_CASE("tokenize with deque") +{ + std::deque result = utility::tokenize("A->B=C->D", "->"); + result = utility::tokenize(result, "="); + + REQUIRE(result.size() == 7); + REQUIRE(result.at(0) == "A"); + REQUIRE(result.at(1) == "->"); + REQUIRE(result.at(2) == "B"); + REQUIRE(result.at(3) == "="); + REQUIRE(result.at(4) == "C"); + REQUIRE(result.at(5) == "->"); + REQUIRE(result.at(6) == "D"); +} + +TEST_CASE("substr before first with single delimiter occurence") +{ + REQUIRE(utility::substrBeforeFirst("foo bar", ' ') == "foo"); +} + +TEST_CASE("substr before first with multiple delimiter occurences") +{ + REQUIRE(utility::substrBeforeFirst("foo bar foo", ' ') == "foo"); +} + +TEST_CASE("substr before first with no delimiter occurence") +{ + REQUIRE(utility::substrBeforeFirst("foobar", ' ') == "foobar"); +} + +TEST_CASE("substr before first with delimiter at start") +{ + REQUIRE(utility::substrBeforeFirst(" foobar", ' ') == ""); +} + +TEST_CASE("substr before first with delimiter at end") +{ + REQUIRE(utility::substrBeforeFirst("foobar ", ' ') == "foobar"); +} + +TEST_CASE("substr before last with single delimiter occurence") +{ + REQUIRE(utility::substrBeforeLast("foo bar", ' ') == "foo"); +} + +TEST_CASE("substr before last with multiple delimiter occurences") +{ + REQUIRE(utility::substrBeforeLast("foo bar foo", ' ') == "foo bar"); +} + +TEST_CASE("substr before last with no delimiter occurence") +{ + REQUIRE(utility::substrBeforeLast("foobar", ' ') == "foobar"); +} + +TEST_CASE("substr before last with delimiter at start") +{ + REQUIRE(utility::substrBeforeLast(" foobar", ' ') == ""); +} + +TEST_CASE("substr before last with delimiter at end") +{ + REQUIRE(utility::substrBeforeLast("foobar ", ' ') == "foobar"); +} + +TEST_CASE("substr after with single delimiter occurence") +{ + REQUIRE(utility::substrAfter("foo bar", ' ') == "bar"); +} + +TEST_CASE("substr after with multiple delimiter occurences") +{ + REQUIRE(utility::substrAfter("foo bar foo", ' ') == "bar foo"); +} + +TEST_CASE("substr after with no delimiter occurence") +{ + REQUIRE(utility::substrAfter("foobar", ' ') == "foobar"); +} + +TEST_CASE("substr after with delimiter at start") +{ + REQUIRE(utility::substrAfter(" foobar", ' ') == "foobar"); +} + +TEST_CASE("substr after with delimiter at end") +{ + REQUIRE(utility::substrAfter("foobar ", ' ') == ""); +} + +TEST_CASE("empty string is detected as prefix of any other string") +{ + const std::string foo = "foo"; + + REQUIRE(utility::isPrefix("", foo)); +} + +TEST_CASE("prefix of bigger text is detected as prefix") +{ + const std::string foobar = "foobar"; + const std::string foo = "foo"; + + REQUIRE(utility::isPrefix(foo, foobar)); +} + +TEST_CASE("prefix is detected as prefix of self") +{ + const std::string foo = "foo"; + + REQUIRE(utility::isPrefix(foo, foo)); +} + +TEST_CASE("different texts are not detected of prefixes of each other") +{ + const std::string foo = "foo"; + const std::string bar = "bar"; + + REQUIRE(!utility::isPrefix(foo, bar)); + REQUIRE(!utility::isPrefix(bar, foo)); +} + +TEST_CASE("to lower case") +{ + REQUIRE("foobar" == utility::toLowerCase("FooBar")); + REQUIRE("foobar" == utility::toLowerCase("FOOBAR")); + REQUIRE("foobar" == utility::toLowerCase("foobar")); +} + +TEST_CASE("equals case insensitive with different cases") +{ + const std::string foo = "FooBar"; + const std::string foo2 = "foobar"; + + REQUIRE(utility::equalsCaseInsensitive(foo, foo2)); +} + +TEST_CASE("equals case insensitive with same cases") +{ + const std::string foo = "foobar"; + const std::string foo2 = "foobar"; + + REQUIRE(utility::equalsCaseInsensitive(foo, foo2)); +} + +TEST_CASE("equals case insensitive with different strings") +{ + const std::string foo = "foo"; + const std::string foo2 = "foobar"; + + REQUIRE(!utility::equalsCaseInsensitive(foo, foo2)); +} + +TEST_CASE("replace") +{ + REQUIRE("fubar" == utility::replace("foobar", "oo", "u")); + REQUIRE("fuuuubar" == utility::replace("foobar", "o", "uu")); + REQUIRE("bar" == utility::replace("foobar", "foo", "")); + REQUIRE("foobar" == utility::replace("foobar", "", "i")); + REQUIRE("foobar" == utility::replace("foobar", "", "")); + REQUIRE("" == utility::replace("", "foo", "bar")); + REQUIRE("foobar" == utility::replace("foobar", "ba", "ba")); +} diff --git a/src/test/UtilityStringTestSuite.h b/src/test/UtilityStringTestSuite.h deleted file mode 100644 index 117fcfa4..00000000 --- a/src/test/UtilityStringTestSuite.h +++ /dev/null @@ -1,306 +0,0 @@ -#include - -#include "utilityString.h" - -class UtilityStringTestSuite : public CxxTest::TestSuite -{ -public: - void test_split_with_char_delimiter() - { - std::deque result = utility::split("A,B,C", ','); - - TS_ASSERT_EQUALS(result.size(), 3); - TS_ASSERT_EQUALS(result.at(0), "A"); - TS_ASSERT_EQUALS(result.at(1), "B"); - TS_ASSERT_EQUALS(result.at(2), "C"); - } - - void test_split_with_string_delimiter() - { - std::deque result = utility::split("A->B>C", "->"); - - TS_ASSERT_EQUALS(result.size(), 2); - TS_ASSERT_EQUALS(result.at(0), "A"); - TS_ASSERT_EQUALS(result.at(1), "B>C"); - } - - void test_split_on_empty_string() - { - std::deque result = utility::split("", "->"); - - TS_ASSERT_EQUALS(result.size(), 1); - TS_ASSERT_EQUALS(result.at(0), ""); - } - - void test_split_with_unused_delimiter() - { - std::deque result = utility::split("A:B:C", ";"); - - TS_ASSERT_EQUALS(result.size(), 1); - TS_ASSERT_EQUALS(result.at(0), "A:B:C"); - } - - void test_split_with_delimiters_next_to_each() - { - std::deque result = utility::split("A::B:C", ':'); - - TS_ASSERT_EQUALS(result.size(), 4); - TS_ASSERT_EQUALS(result.at(0), "A"); - TS_ASSERT_EQUALS(result.at(1), ""); - TS_ASSERT_EQUALS(result.at(2), "B"); - TS_ASSERT_EQUALS(result.at(3), "C"); - } - - void test_split_with_delimiter_at_start() - { - std::deque result = utility::split(":B:C", ':'); - - TS_ASSERT_EQUALS(result.size(), 3); - TS_ASSERT_EQUALS(result.at(0), ""); - TS_ASSERT_EQUALS(result.at(1), "B"); - TS_ASSERT_EQUALS(result.at(2), "C"); - } - - void test_split_with_delimiter_at_end() - { - std::deque result = utility::split("B:C:", ':'); - - TS_ASSERT_EQUALS(result.size(), 3); - TS_ASSERT_EQUALS(result.at(0), "B"); - TS_ASSERT_EQUALS(result.at(1), "C"); - TS_ASSERT_EQUALS(result.at(2), ""); - } - - void test_join_with_char_delimiter() - { - std::deque list; - list.push_back("A"); - list.push_back("B"); - list.push_back("C"); - - std::string result = utility::join(list, ','); - TS_ASSERT_EQUALS(result, "A,B,C"); - } - - void test_join_with_string_delimiter() - { - std::deque list; - list.push_back("A"); - list.push_back("B"); - list.push_back("C"); - - std::string result = utility::join(list, "=="); - TS_ASSERT_EQUALS(result, "A==B==C"); - } - - void test_join_on_empty_list() - { - std::deque list; - std::string result = utility::join(list, ','); - TS_ASSERT_EQUALS(result, ""); - } - - void test_join_with_empty_strings_in_list() - { - std::deque list; - list.push_back("A"); - list.push_back(""); - list.push_back(""); - - std::string result = utility::join(list, ':'); - TS_ASSERT_EQUALS(result, "A::"); - } - - void test_tokenize_with_string() - { - std::deque result = utility::tokenize("A->B->C", "->"); - - TS_ASSERT_EQUALS(result.size(), 5); - TS_ASSERT_EQUALS(result.at(0), "A"); - TS_ASSERT_EQUALS(result.at(1), "->"); - TS_ASSERT_EQUALS(result.at(2), "B"); - TS_ASSERT_EQUALS(result.at(3), "->"); - TS_ASSERT_EQUALS(result.at(4), "C"); - } - - void test_tokenize_with_string_and_delimiter_at_start() - { - std::deque result = utility::tokenize("->B", "->"); - - TS_ASSERT_EQUALS(result.size(), 2); - TS_ASSERT_EQUALS(result.at(0), "->"); - TS_ASSERT_EQUALS(result.at(1), "B"); - } - - void test_tokenize_with_string_and_delimiter_at_end() - { - std::deque result = utility::tokenize("C+", '+'); - - TS_ASSERT_EQUALS(result.size(), 2); - TS_ASSERT_EQUALS(result.at(0), "C"); - TS_ASSERT_EQUALS(result.at(1), "+"); - } - - void test_tokenize_with_deque() - { - std::deque result = utility::tokenize("A->B=C->D", "->"); - result = utility::tokenize(result, "="); - - TS_ASSERT_EQUALS(result.size(), 7); - TS_ASSERT_EQUALS(result.at(0), "A"); - TS_ASSERT_EQUALS(result.at(1), "->"); - TS_ASSERT_EQUALS(result.at(2), "B"); - TS_ASSERT_EQUALS(result.at(3), "="); - TS_ASSERT_EQUALS(result.at(4), "C"); - TS_ASSERT_EQUALS(result.at(5), "->"); - TS_ASSERT_EQUALS(result.at(6), "D"); - } - - void test_substr_before_first_with_single_delimiter_occurence() - { - TS_ASSERT_EQUALS(utility::substrBeforeFirst("foo bar", ' '), "foo"); - } - - void test_substr_before_first_with_multiple_delimiter_occurences() - { - TS_ASSERT_EQUALS(utility::substrBeforeFirst("foo bar foo", ' '), "foo"); - } - - void test_substr_before_first_with_no_delimiter_occurence() - { - TS_ASSERT_EQUALS(utility::substrBeforeFirst("foobar", ' '), "foobar"); - } - - void test_substr_before_first_with_delimiter_at_start() - { - TS_ASSERT_EQUALS(utility::substrBeforeFirst(" foobar", ' '), ""); - } - - void test_substr_before_first_with_delimiter_at_end() - { - TS_ASSERT_EQUALS(utility::substrBeforeFirst("foobar ", ' '), "foobar"); - } - - void test_substr_before_last_with_single_delimiter_occurence() - { - TS_ASSERT_EQUALS(utility::substrBeforeLast("foo bar", ' '), "foo"); - } - - void test_substr_before_last_with_multiple_delimiter_occurences() - { - TS_ASSERT_EQUALS(utility::substrBeforeLast("foo bar foo", ' '), "foo bar"); - } - - void test_substr_before_last_with_no_delimiter_occurence() - { - TS_ASSERT_EQUALS(utility::substrBeforeLast("foobar", ' '), "foobar"); - } - - void test_substr_before_last_with_delimiter_at_start() - { - TS_ASSERT_EQUALS(utility::substrBeforeLast(" foobar", ' '), ""); - } - - void test_substr_before_last_with_delimiter_at_end() - { - TS_ASSERT_EQUALS(utility::substrBeforeLast("foobar ", ' '), "foobar"); - } - - void test_substr_after_with_single_delimiter_occurence() - { - TS_ASSERT_EQUALS(utility::substrAfter("foo bar", ' '), "bar"); - } - - void test_substr_after_with_multiple_delimiter_occurences() - { - TS_ASSERT_EQUALS(utility::substrAfter("foo bar foo", ' '), "bar foo"); - } - - void test_substr_after_with_no_delimiter_occurence() - { - TS_ASSERT_EQUALS(utility::substrAfter("foobar", ' '), "foobar"); - } - - void test_substr_after_with_delimiter_at_start() - { - TS_ASSERT_EQUALS(utility::substrAfter(" foobar", ' '), "foobar"); - } - - void test_substr_after_with_delimiter_at_end() - { - TS_ASSERT_EQUALS(utility::substrAfter("foobar ", ' '), ""); - } - - void test_empty_string_is_detected_as_prefix_of_any_other_string() - { - const std::string foo = "foo"; - - TS_ASSERT(utility::isPrefix("", foo)); - } - - void test_prefix_of_bigger_text_is_detected_as_prefix() - { - const std::string foobar = "foobar"; - const std::string foo = "foo"; - - TS_ASSERT(utility::isPrefix(foo, foobar)); - } - - void test_prefix_is_detected_as_prefix_of_self() - { - const std::string foo = "foo"; - - TS_ASSERT(utility::isPrefix(foo, foo)); - } - - void test_different_texts_are_not_detected_of_prefixes_of_each_other() - { - const std::string foo = "foo"; - const std::string bar = "bar"; - - TS_ASSERT(!utility::isPrefix(foo, bar)); - TS_ASSERT(!utility::isPrefix(bar, foo)); - } - - void test_to_lower_case() - { - TS_ASSERT_EQUALS("foobar", utility::toLowerCase("FooBar")); - TS_ASSERT_EQUALS("foobar", utility::toLowerCase("FOOBAR")); - TS_ASSERT_EQUALS("foobar", utility::toLowerCase("foobar")); - } - - void test_equals_case_insensitive_with_different_cases() - { - const std::string foo = "FooBar"; - const std::string foo2 = "foobar"; - - TS_ASSERT(utility::equalsCaseInsensitive(foo, foo2)); - } - - void test_equals_case_insensitive_with_same_cases() - { - const std::string foo = "foobar"; - const std::string foo2 = "foobar"; - - TS_ASSERT(utility::equalsCaseInsensitive(foo, foo2)); - } - - void test_equals_case_insensitive_with_different_strings() - { - const std::string foo = "foo"; - const std::string foo2 = "foobar"; - - TS_ASSERT(!utility::equalsCaseInsensitive(foo, foo2)); - } - - void test_replace() - { - TS_ASSERT_EQUALS("fubar", utility::replace("foobar", "oo", "u")); - TS_ASSERT_EQUALS("fuuuubar", utility::replace("foobar", "o", "uu")); - TS_ASSERT_EQUALS("bar", utility::replace("foobar", "foo", "")); - TS_ASSERT_EQUALS("foobar", utility::replace("foobar", "", "i")); - TS_ASSERT_EQUALS("foobar", utility::replace("foobar", "", "")); - TS_ASSERT_EQUALS("", utility::replace("", "foo", "bar")); - TS_ASSERT_EQUALS("foobar", utility::replace("foobar", "ba", "ba")); - } -}; diff --git a/src/test/UtilityTestSuite.cpp b/src/test/UtilityTestSuite.cpp new file mode 100644 index 00000000..80b5230b --- /dev/null +++ b/src/test/UtilityTestSuite.cpp @@ -0,0 +1,13 @@ +#include "catch.hpp" + +#include "utility.h" + +TEST_CASE("trim blank spaces of string") +{ + REQUIRE(utility::trim(" foo ") == "foo"); +} + +TEST_CASE("trim blank spaces of wstring") +{ + REQUIRE(utility::trim(L" foo ") == L"foo"); +} diff --git a/src/test/UtilityTestSuite.h b/src/test/UtilityTestSuite.h deleted file mode 100644 index 875dd080..00000000 --- a/src/test/UtilityTestSuite.h +++ /dev/null @@ -1,17 +0,0 @@ -#include - -#include "utility.h" - -class UtilityTestSuite : public CxxTest::TestSuite -{ -public: - void test_trim_blank_spaces_of_string() - { - TS_ASSERT_EQUALS(utility::trim(" foo "), "foo"); - } - - void test_trim_blank_spaces_of_wstring() - { - TS_ASSERT_EQUALS(utility::trim(L" foo "), L"foo"); - } -}; diff --git a/src/test/Vector2TestSuite.cpp b/src/test/Vector2TestSuite.cpp new file mode 100644 index 00000000..620bb8dc --- /dev/null +++ b/src/test/Vector2TestSuite.cpp @@ -0,0 +1,264 @@ +#include "catch.hpp" + +#include "Vector2.h" + +TEST_CASE("vec2i constructors") +{ + Vec2i vec0; + Vec2i vec1(1, 1); + Vec2i vec2(vec1); + + REQUIRE(0 == vec0.x); + REQUIRE(0 == vec0.y); + + REQUIRE(1 == vec1.x); + REQUIRE(1 == vec1.y); + + REQUIRE(1 == vec2.x); + REQUIRE(1 == vec2.y); +} + +TEST_CASE("vec2f constructors") +{ + Vec2f vec0; + Vec2f vec1(1.0f, 1.0f); + Vec2f vec2(vec1); + + REQUIRE(0.0f == vec0.x); + REQUIRE(0.0f == vec0.y); + + REQUIRE(1.0f == vec1.x); + REQUIRE(1.0f == vec1.y); + + REQUIRE(1.0f == vec2.x); + REQUIRE(1.0f == vec2.y); +} + +TEST_CASE("vector2 get length") +{ + Vec2f vec0(3.0f, 3.0f); + REQUIRE(18.0f == vec0.getLengthSquared()); + vec0.x = -3.0f; + REQUIRE(18.0f == vec0.getLengthSquared()); + REQUIRE(std::sqrt(18.0f) == vec0.getLength()); + + Vec2i vec1(4, 2); + REQUIRE(20.0f == vec1.getLengthSquared()); + REQUIRE(std::sqrt(20.0f) == vec1.getLength()); +} + + +TEST_CASE("vector2 normalization") +{ + Vec2f vec0(1.0f, 1.0f); + + float targetXY = 1.0f / std::sqrt(2.0f); + + REQUIRE(targetXY == Approx(vec0.normalized().x)); + REQUIRE(targetXY == Approx(vec0.normalized().y)); + + vec0.normalize(); + REQUIRE(targetXY == Approx(vec0.x)); + REQUIRE(targetXY == Approx(vec0.y)); + + float x = 17.53f; + float y = 42.42f; + vec0.x = x; + vec0.y = y; + float targetX = x / std::sqrt(x * x + y * y); + float targetY = y / std::sqrt(x * x + y * y); + vec0.normalize(); + REQUIRE(targetX == Approx(vec0.x)); + REQUIRE(targetY == Approx(vec0.y)); +} + +TEST_CASE("vector2 comparison") +{ + Vec2i vec0(42, 24); + Vec2i vec1(42, 24); + Vec2i vec2(69, 96); + + REQUIRE(true == vec0.isEqual(vec1)); + REQUIRE(false == vec0.isEqual(vec2)); + REQUIRE(true == vec0.isSame(vec0)); + REQUIRE(false == vec0.isSame(vec1)); + + Vec2f vec3(42.0f, 24.0f); + Vec2f vec4(42.0f, 24.0f); + Vec2f vec5(69.0f, 96.0f); + + REQUIRE(true == vec3.isEqual(vec4)); + REQUIRE(false == vec3.isEqual(vec5)); + REQUIRE(true == vec3.isSame(vec3)); + REQUIRE(false == vec3.isSame(vec4)); +} + +TEST_CASE("vector2 comparison operators") +{ + Vec2i vec0(42, 24); + Vec2i vec1(42, 24); + Vec2i vec2(69, 96); + + REQUIRE(true == (vec0 == vec0)); + REQUIRE(true == (vec0 == vec1)); + REQUIRE(false == (vec0 == vec2)); + REQUIRE(false == (vec0 != vec0)); + REQUIRE(false == (vec0 != vec1)); + REQUIRE(true == (vec0 != vec2)); + + Vec2f vec3(42.0f, 24.0f); + Vec2f vec4(42.0f, 24.0f); + Vec2f vec5(69.0f, 96.0f); + + REQUIRE(true == (vec3 == vec3)); + REQUIRE(true == (vec3 == vec4)); + REQUIRE(false == (vec3 == vec5)); + REQUIRE(false == (vec3 != vec3)); + REQUIRE(false == (vec3 != vec4)); + REQUIRE(true == (vec3 != vec5)); +} + +TEST_CASE("assignment operator") +{ + Vec2i vec0(42, 24); + Vec2i vec1(69, 96); + + vec1 = vec0; + REQUIRE(42 == vec1.x); + REQUIRE(24 == vec1.y); + REQUIRE(true == vec0.isEqual(vec1)); + REQUIRE(false == vec0.isSame(vec1)); + + Vec2i vec2(42, 24); + Vec2i vec3(69, 96); + + vec2 = vec3; + REQUIRE(69 == vec2.x); + REQUIRE(96 == vec2.y); + REQUIRE(true == vec3.isEqual(vec2)); + REQUIRE(false == vec3.isSame(vec2)); +} + +TEST_CASE("addition operators") +{ + Vec2i vec0(-2, 2); + Vec2i vec1(3, -3); + Vec2i vec2 = vec0 + vec1; + + REQUIRE(1 == vec2.x); + REQUIRE(-1 == vec2.y); + + REQUIRE(-2 == vec0.x); + REQUIRE(2 == vec0.y); + REQUIRE(3 == vec1.x); + REQUIRE(-3 == vec1.y); + + vec0 += vec1; + REQUIRE(1 == vec0.x); + REQUIRE(-1 == vec0.y); +} + +TEST_CASE("subtraction operators") +{ + Vec2f vec0(-2.0f, 2.0f); + Vec2f vec1(3.0f, -3.0f); + Vec2f vec2 = vec0 - vec1; + + REQUIRE(-5.0f == vec2.x); + REQUIRE(5.0f == vec2.y); + + REQUIRE(-2.0f == vec0.x); + REQUIRE(2.0f == vec0.y); + REQUIRE(3.0f == vec1.x); + REQUIRE(-3.0f == vec1.y); + + vec0 -= vec1; + REQUIRE(-5.0f == vec0.x); + REQUIRE(5.0f == vec0.y); +} + +TEST_CASE("scalar multiplication operators") +{ + Vec2f vec0(-2.0f, 2.0f); + Vec2f vec1 = vec0 * 42.0f; + + REQUIRE(-84.0f == vec1.x); + REQUIRE(84.0f == vec1.y); + + REQUIRE(-2.0f == vec0.x); + REQUIRE(2.0f == vec0.y); + + vec0 *= 42.0f; + + REQUIRE(-84.0f == vec0.x); + REQUIRE(84.0f == vec0.y); + + Vec2i vec2(-2, 2); + Vec2i vec3 = vec2 * 42.4f; + + REQUIRE(-84 == (int)vec3.x); + REQUIRE(84 == (int)vec3.y); + + Vec2i vec3b = vec2 * 42.5f; + + REQUIRE(-85 == (int)vec3b.x); + REQUIRE(85 == (int)vec3b.y); + + REQUIRE(-2 == vec2.x); + REQUIRE(2 == vec2.y); + + vec2 *= 42; + REQUIRE(-84 == vec2.x); + REQUIRE(84 == vec2.y); + + vec2 *= 0.5f; + REQUIRE(-42 == vec2.x); + REQUIRE(42 == vec2.y); +} + +TEST_CASE("dot product operator") +{ + Vec2f vec0(2.0f, 4.0f); + Vec2f vec1(3.0f, 6.0f); + Vec2f vec2(-2.0f, -1.0f); + + REQUIRE(30.0f == vec0.dotProduct(vec1)); + REQUIRE(-8.0f == vec0.dotProduct(vec2)); + + Vec2f vec3(2, 4); + Vec2f vec4(3, 6); + Vec2f vec5(-2, -1); + + REQUIRE(30 == vec3.dotProduct(vec4)); + REQUIRE(-8 == vec3.dotProduct(vec5)); +} + +TEST_CASE("scalar division operators") +{ + Vec2f vec0(42.0f, 24.0f); + Vec2f vec1 = vec0 / 2.0f; + Vec2f vec2 = vec0 / 0.5f; + + REQUIRE(42.0f == vec0.x); + REQUIRE(24.0f == vec0.y); + REQUIRE(21.0f == vec1.x); + REQUIRE(12.0f == vec1.y); + REQUIRE(84.0f == vec2.x); + REQUIRE(48.0f == vec2.y); + + vec0 /= 2.0f; + REQUIRE(21.0f == vec0.x); + REQUIRE(12.0f == vec0.y); + + Vec2i vec3(42, 24); + Vec2i vec4 = vec3 / 2; + + REQUIRE(42 == vec3.x); + REQUIRE(24 == vec3.y); + REQUIRE(21 == vec4.x); + REQUIRE(12 == vec4.y); + + vec3 /= 2; + REQUIRE(21 == vec3.x); + REQUIRE(12 == vec3.y); +} diff --git a/src/test/Vector2TestSuite.h b/src/test/Vector2TestSuite.h deleted file mode 100644 index f95025a7..00000000 --- a/src/test/Vector2TestSuite.h +++ /dev/null @@ -1,268 +0,0 @@ -#include - -#include "Vector2.h" - -class Vector2TestSuite : public CxxTest::TestSuite -{ -public: - void test_vec2i_constructors() - { - Vec2i vec0; - Vec2i vec1(1, 1); - Vec2i vec2(vec1); - - TS_ASSERT_EQUALS(0, vec0.x); - TS_ASSERT_EQUALS(0, vec0.y); - - TS_ASSERT_EQUALS(1, vec1.x); - TS_ASSERT_EQUALS(1, vec1.y); - - TS_ASSERT_EQUALS(1, vec2.x); - TS_ASSERT_EQUALS(1, vec2.y); - } - - void test_vec2f_constructors() - { - Vec2f vec0; - Vec2f vec1(1.0f, 1.0f); - Vec2f vec2(vec1); - - TS_ASSERT_EQUALS(0.0f, vec0.x); - TS_ASSERT_EQUALS(0.0f, vec0.y); - - TS_ASSERT_EQUALS(1.0f, vec1.x); - TS_ASSERT_EQUALS(1.0f, vec1.y); - - TS_ASSERT_EQUALS(1.0f, vec2.x); - TS_ASSERT_EQUALS(1.0f, vec2.y); - } - - void test_vector2_get_length() - { - Vec2f vec0(3.0f, 3.0f); - TS_ASSERT_EQUALS(18.0f, vec0.getLengthSquared()); - vec0.x = -3.0f; - TS_ASSERT_EQUALS(18.0f, vec0.getLengthSquared()); - TS_ASSERT_EQUALS(std::sqrt(18.0f), vec0.getLength()); - - Vec2i vec1(4, 2); - TS_ASSERT_EQUALS(20.0f, vec1.getLengthSquared()); - TS_ASSERT_EQUALS(std::sqrt(20.0f), vec1.getLength()); - } - - - void test_vector2_normalization() - { - Vec2f vec0(1.0f, 1.0f); - - float targetXY = 1.0f / std::sqrt(2.0f); - - TS_ASSERT_DELTA(targetXY, vec0.normalized().x, 1e-7); - TS_ASSERT_DELTA(targetXY, vec0.normalized().y, 1e-7); - - vec0.normalize(); - TS_ASSERT_DELTA(targetXY, vec0.x, 1e-7); - TS_ASSERT_DELTA(targetXY, vec0.y, 1e-7); - - float x = 17.53f; - float y = 42.42f; - vec0.x = x; - vec0.y = y; - float targetX = x / std::sqrt(x * x + y * y); - float targetY = y / std::sqrt(x * x + y * y); - vec0.normalize(); - TS_ASSERT_DELTA(targetX, vec0.x, 1e-7); - TS_ASSERT_DELTA(targetY, vec0.y, 1e-7); - } - - void test_vector2_comparison() - { - Vec2i vec0(42, 24); - Vec2i vec1(42, 24); - Vec2i vec2(69, 96); - - TS_ASSERT_EQUALS(true, vec0.isEqual(vec1)); - TS_ASSERT_EQUALS(false, vec0.isEqual(vec2)); - TS_ASSERT_EQUALS(true, vec0.isSame(vec0)); - TS_ASSERT_EQUALS(false, vec0.isSame(vec1)); - - Vec2f vec3(42.0f, 24.0f); - Vec2f vec4(42.0f, 24.0f); - Vec2f vec5(69.0f, 96.0f); - - TS_ASSERT_EQUALS(true, vec3.isEqual(vec4)); - TS_ASSERT_EQUALS(false, vec3.isEqual(vec5)); - TS_ASSERT_EQUALS(true, vec3.isSame(vec3)); - TS_ASSERT_EQUALS(false, vec3.isSame(vec4)); - } - - void test_vector2_comparison_operators() - { - Vec2i vec0(42, 24); - Vec2i vec1(42, 24); - Vec2i vec2(69, 96); - - TS_ASSERT_EQUALS(true, vec0 == vec0); - TS_ASSERT_EQUALS(true, vec0 == vec1); - TS_ASSERT_EQUALS(false, vec0 == vec2); - TS_ASSERT_EQUALS(false, vec0 != vec0); - TS_ASSERT_EQUALS(false, vec0 != vec1); - TS_ASSERT_EQUALS(true, vec0 != vec2); - - Vec2f vec3(42.0f, 24.0f); - Vec2f vec4(42.0f, 24.0f); - Vec2f vec5(69.0f, 96.0f); - - TS_ASSERT_EQUALS(true, vec3 == vec3); - TS_ASSERT_EQUALS(true, vec3 == vec4); - TS_ASSERT_EQUALS(false, vec3 == vec5); - TS_ASSERT_EQUALS(false, vec3 != vec3); - TS_ASSERT_EQUALS(false, vec3 != vec4); - TS_ASSERT_EQUALS(true, vec3 != vec5); - } - - void test_assignment_operator() - { - Vec2i vec0(42, 24); - Vec2i vec1(69, 96); - - vec1 = vec0; - TS_ASSERT_EQUALS(42, vec1.x); - TS_ASSERT_EQUALS(24, vec1.y); - TS_ASSERT_EQUALS(true, vec0.isEqual(vec1)); - TS_ASSERT_EQUALS(false, vec0.isSame(vec1)); - - Vec2i vec2(42, 24); - Vec2i vec3(69, 96); - - vec2 = vec3; - TS_ASSERT_EQUALS(69, vec2.x); - TS_ASSERT_EQUALS(96, vec2.y); - TS_ASSERT_EQUALS(true, vec3.isEqual(vec2)); - TS_ASSERT_EQUALS(false, vec3.isSame(vec2)); - } - - void test_addition_operators() - { - Vec2i vec0(-2, 2); - Vec2i vec1(3, -3); - Vec2i vec2 = vec0 + vec1; - - TS_ASSERT_EQUALS(1, vec2.x); - TS_ASSERT_EQUALS(-1, vec2.y); - - TS_ASSERT_EQUALS(-2, vec0.x); - TS_ASSERT_EQUALS(2, vec0.y); - TS_ASSERT_EQUALS(3, vec1.x); - TS_ASSERT_EQUALS(-3, vec1.y); - - vec0 += vec1; - TS_ASSERT_EQUALS(1, vec0.x); - TS_ASSERT_EQUALS(-1, vec0.y); - } - - void test_subtraction_operators() - { - Vec2f vec0(-2.0f, 2.0f); - Vec2f vec1(3.0f, -3.0f); - Vec2f vec2 = vec0 - vec1; - - TS_ASSERT_EQUALS(-5.0f, vec2.x); - TS_ASSERT_EQUALS(5.0f, vec2.y); - - TS_ASSERT_EQUALS(-2.0f, vec0.x); - TS_ASSERT_EQUALS(2.0f, vec0.y); - TS_ASSERT_EQUALS(3.0f, vec1.x); - TS_ASSERT_EQUALS(-3.0f, vec1.y); - - vec0 -= vec1; - TS_ASSERT_EQUALS(-5.0f, vec0.x); - TS_ASSERT_EQUALS(5.0f, vec0.y); - } - - void test_scalar_multiplication_operators() - { - Vec2f vec0(-2.0f, 2.0f); - Vec2f vec1 = vec0 * 42.0f; - - TS_ASSERT_EQUALS(-84.0f, vec1.x); - TS_ASSERT_EQUALS(84.0f, vec1.y); - - TS_ASSERT_EQUALS(-2.0f, vec0.x); - TS_ASSERT_EQUALS(2.0f, vec0.y); - - vec0 *= 42.0f; - - TS_ASSERT_EQUALS(-84.0f, vec0.x); - TS_ASSERT_EQUALS(84.0f, vec0.y); - - Vec2i vec2(-2, 2); - Vec2i vec3 = vec2 * 42.4f; - - TS_ASSERT_EQUALS(-84, (int)vec3.x); - TS_ASSERT_EQUALS(84, (int)vec3.y); - - Vec2i vec3b = vec2 * 42.5f; - - TS_ASSERT_EQUALS(-85, (int)vec3b.x); - TS_ASSERT_EQUALS(85, (int)vec3b.y); - - TS_ASSERT_EQUALS(-2, vec2.x); - TS_ASSERT_EQUALS(2, vec2.y); - - vec2 *= 42; - TS_ASSERT_EQUALS(-84, vec2.x); - TS_ASSERT_EQUALS(84, vec2.y); - - vec2 *= 0.5f; - TS_ASSERT_EQUALS(-42, vec2.x); - TS_ASSERT_EQUALS(42, vec2.y); - } - - void test_dot_product_operator() - { - Vec2f vec0(2.0f, 4.0f); - Vec2f vec1(3.0f, 6.0f); - Vec2f vec2(-2.0f, -1.0f); - - TS_ASSERT_EQUALS(30.0f, vec0.dotProduct(vec1)); - TS_ASSERT_EQUALS(-8.0f, vec0.dotProduct(vec2)); - - Vec2f vec3(2, 4); - Vec2f vec4(3, 6); - Vec2f vec5(-2, -1); - - TS_ASSERT_EQUALS(30, vec3.dotProduct(vec4)); - TS_ASSERT_EQUALS(-8, vec3.dotProduct(vec5)); - } - - void test_scalar_division_operators() - { - Vec2f vec0(42.0f, 24.0f); - Vec2f vec1 = vec0 / 2.0f; - Vec2f vec2 = vec0 / 0.5f; - - TS_ASSERT_EQUALS(42.0f, vec0.x); - TS_ASSERT_EQUALS(24.0f, vec0.y); - TS_ASSERT_EQUALS(21.0f, vec1.x); - TS_ASSERT_EQUALS(12.0f, vec1.y); - TS_ASSERT_EQUALS(84.0f, vec2.x); - TS_ASSERT_EQUALS(48.0f, vec2.y); - - vec0 /= 2.0f; - TS_ASSERT_EQUALS(21.0f, vec0.x); - TS_ASSERT_EQUALS(12.0f, vec0.y); - - Vec2i vec3(42, 24); - Vec2i vec4 = vec3 / 2; - - TS_ASSERT_EQUALS(42, vec3.x); - TS_ASSERT_EQUALS(24, vec3.y); - TS_ASSERT_EQUALS(21, vec4.x); - TS_ASSERT_EQUALS(12, vec4.y); - - vec3 /= 2; - TS_ASSERT_EQUALS(21, vec3.x); - TS_ASSERT_EQUALS(12, vec3.y); - } -}; diff --git a/src/test/readme.txt b/src/test/readme.txt deleted file mode 100644 index 54dced1c..00000000 --- a/src/test/readme.txt +++ /dev/null @@ -1,18 +0,0 @@ ---------- -Debugging ---------- -To debug unit tests remove the post-build event, set the execution directory for 'Coati_test' to '.../Coati/bin/test' and run 'Coati_test' as startup project - - -Post build event (if you manage to delete it without saving it somewhere): - -setlocal -cd $(ProjectDir)../../bin/test/ -$(OutDir)$(TargetName)$(TargetExt) -if %errorlevel% neq 0 goto :cmEnd -:cmEnd -endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone -:cmErrorLevel -exit /b %1 -:cmDone -if %errorlevel% neq 0 goto :VCEnd \ No newline at end of file diff --git a/src/test/test_main.cpp b/src/test/test_main.cpp new file mode 100644 index 00000000..e2b12c4c --- /dev/null +++ b/src/test/test_main.cpp @@ -0,0 +1,39 @@ +#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() function + +#include "catch.hpp" +// IMPORTANT NOTE: removed signal listener for "EXCEPTION_ACCESS_VIOLATION" from catch source code because it interferes with the jni interface that emits such a signal on purpose + +#include "ApplicationSettings.h" + +struct EventListener : Catch::TestEventListenerBase +{ + using TestEventListenerBase::TestEventListenerBase; // inherit constructor + + void testRunStarting(const Catch::TestRunInfo& testRunInfo) override + { +#ifdef __linux__ + const std::string homedir = getenv("HOME"); + + if (!homedir.empty()) + { + if(!ApplicationSettings::getInstance()->load( + FilePath(homedir + "/.config/sourcetrail/ApplicationSettings.xml") + )) + { + std::cout << "no settings" << std::endl; + return false; + } + } + else + { + std::cout << "no homedir" << std::endl; + return false; + } + +#else + ApplicationSettings::getInstance()->load(FilePath(L"data/TestSettings.xml")); +#endif + } +}; + +CATCH_REGISTER_LISTENER(EventListener)