diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index 487cf64bea8..7ebe0900982 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -8,33 +8,70 @@ jobs: env: TZ: America/Los_Angeles steps: - - name: Checkout - uses: actions/checkout@v3 - - run: sudo apt-get update - - name: Install additional dependencies - run: sudo apt-get install -y gettext cmake libxslt-dev xsltproc ninja-build libboost-all-dev libgtk-3-dev guile-2.2-dev libgwengui-gtk3-dev libaqbanking-dev libofx-dev libdbi-dev libdbd-sqlite3 libwebkit2gtk-4.0-dev googletest - - name: Install language packs. - run: sudo apt-get --reinstall install -y language-pack-en language-pack-fr - - run: | - echo "ROOT_DIR=$GITHUB_WORKSPACE/.." >> $GITHUB_ENV - - name: Create Directories - run: | - pwd - mkdir $ROOT_DIR/inst - mkdir build - - name: Configure GnuCash - run: | - cd build - cmake -G Ninja -DWITH_PYTHON=ON -DCMAKE_INSTALL_PREFIX=$ROOT_DIR/inst $GITHUB_WORKSPACE - - name: Build and Test GnuCash - run: | - cd build - ninja - ninja distcheck - env: - CTEST_OUTPUT_ON_FAILURE: On - - uses: actions/upload-artifact@v3 - if: failure() - with: - name: TestLog - path: ${{ github.workspace }}/build/Testing/Temporary/LastTest.log + - name: Checkout + uses: actions/checkout@v3 + - run: sudo apt-get update + - name: Install additional dependencies + run: sudo apt-get install -y gettext cmake libxslt-dev xsltproc ninja-build libboost-all-dev libgtk-3-dev guile-2.2-dev libgwengui-gtk3-dev libaqbanking-dev libofx-dev libdbi-dev libdbd-sqlite3 libwebkit2gtk-4.0-dev googletest + - name: Install language packs. + run: sudo apt-get --reinstall install -y language-pack-en language-pack-fr + - run: | + echo "ROOT_DIR=$GITHUB_WORKSPACE/.." >> $GITHUB_ENV + - name: Create Directories + run: | + pwd + mkdir $ROOT_DIR/inst + mkdir build + - name: Configure GnuCash + run: | + cd build + cmake -G Ninja -DWITH_PYTHON=ON -DCMAKE_INSTALL_PREFIX=$ROOT_DIR/inst $GITHUB_WORKSPACE + - name: Build and Test GnuCash + run: | + cd build + ninja + ninja distcheck + env: + CTEST_OUTPUT_ON_FAILURE: On + - uses: actions/upload-artifact@v3 + if: failure() + with: + name: TestLog + path: ${{ github.workspace }}/build/Testing/Temporary/LastTest.log + ci_tests_ASAN: + runs-on: ubuntu-latest + name: Address Sanitizer CI Tests + continue-on-error: true + env: + TZ: America/Los_Angeles + steps: + - name: Checkout + uses: actions/checkout@v3 + - run: sudo apt-get update + - name: Install additional dependencies + run: sudo apt-get install -y gettext cmake libxslt-dev xsltproc ninja-build libboost-all-dev libgtk-3-dev guile-2.2-dev libgwengui-gtk3-dev libaqbanking-dev libofx-dev libdbi-dev libdbd-sqlite3 libwebkit2gtk-4.0-dev googletest + - name: Install language packs. + run: sudo apt-get --reinstall install -y language-pack-en language-pack-fr + - run: | + echo "ROOT_DIR=$GITHUB_WORKSPACE/.." >> $GITHUB_ENV + - name: Create Directories + run: | + pwd + mkdir $ROOT_DIR/inst + mkdir build + - name: Configure GnuCash + run: | + cd build + cmake -G Ninja -DWITH_PYTHON=ON -DCMAKE_INSTALL_PREFIX=$ROOT_DIR/inst $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=Asan + - name: Build and Test GnuCash + run: | + cd build + ninja + ninja check + env: + CTEST_OUTPUT_ON_FAILURE: On + - uses: actions/upload-artifact@v3 + if: failure() + with: + name: TestLog + path: ${{ github.workspace }}/build/Testing/Temporary/LastTest.log diff --git a/CMakeLists.txt b/CMakeLists.txt index dd9148a5237..c67221a98ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -610,6 +610,23 @@ if (MINGW) set( CMAKE_CXX_FLAGS "-DWINVER=0x0500 -D_EMULATE_GLIBC=0 ${CMAKE_CXX_FLAGS}") # Workaround for bug in gtest on mingw, see https://github.com/google/googletest/issues/893 and https://github.com/google/googletest/issues/920 endif() +if (APPLE) + execute_process(COMMAND clang --print-file-name=libclang_rt.asan_osx_dynamic.dylib + OUTPUT_VARIABLE ASAN_DYNAMIC_LIB + OUTPUT_STRIP_TRAILING_WHITESPACE) + set(ASAN_DYNAMIC_LIB_ENV "DYLD_INSERT_LIBRARIES=${ASAN_DYNAMIC_LIB}") +elseif(UNIX) + execute_process(COMMAND gcc -print-file-name=libasan.so OUTPUT_VARIABLE LIBASAN_PATH OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND gcc -print-file-name=libstdc++.so OUTPUT_VARIABLE LIBSTDCXX_PATH OUTPUT_STRIP_TRAILING_WHITESPACE) + set(PRELOADS "${LIBASAN_PATH} ${LIBSTDCXX_PATH}") + set(ASAN_OPTIONS "detect_leaks=0:fast_unwind_on_malloc=0") + set(ASAN_DYNAMIC_LIB_ENV "LD_PRELOAD=${PRELOADS};ASAN_OPTIONS=${ASAN_OPTIONS}") +endif () +set(ASAN_BUILD_OPTIONS -fsanitize=address -fsanitize=undefined) +set(ASAN_COMPILE_OPTIONS -g ${ASAN_BUILD_OPTIONS}) +add_compile_options("$<$:${ASAN_COMPILE_OPTIONS}>") +add_link_options("$<$:${ASAN_BUILD_OPTIONS}>") + if (APPLE AND WITH_GNUCASH) set(CMAKE_MACOSX_RPATH ON) set(CMAKE_INSTALL_NAME_DIR "${CMAKE_INSTALL_FULL_LIBDIR}") diff --git a/bindings/guile/gnc-optiondb.i b/bindings/guile/gnc-optiondb.i index 3340e246370..c2ac2e432a5 100644 --- a/bindings/guile/gnc-optiondb.i +++ b/bindings/guile/gnc-optiondb.i @@ -1725,13 +1725,13 @@ gnc_register_multichoice_callback_option(GncOptionDBPtr& db, static void test_book_set_data(QofBook* book, const char* key, void* data) { - qof_book_set_data(book, key, data); + qof_book_set_data(book, key, data); } static void test_book_clear_data(QofBook* book, const char* key) { - qof_book_set_data(book, key, nullptr); + qof_book_set_data(book, key, nullptr); } static void diff --git a/bindings/python/tests/CMakeLists.txt b/bindings/python/tests/CMakeLists.txt index 9ec9ca29a9c..77733f88049 100644 --- a/bindings/python/tests/CMakeLists.txt +++ b/bindings/python/tests/CMakeLists.txt @@ -7,11 +7,11 @@ if (WITH_PYTHON) endif() add_custom_target(test-python-bindings ALL DEPENDS unittest_support gnucash-core-c-build gnucash-core-c-py sw-core-utils-build sw-core-utils-py sw-app-utils-build sw-app-utils-py) add_dependencies(check test-python-bindings) - add_test(python-bindings ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/runTests.py.in) - set_property(TEST python-bindings PROPERTY ENVIRONMENT - GNC_BUILDDIR=${CMAKE_BINARY_DIR} - PYTHONPATH=${PYTHON_SYSCONFIG_BUILD}:${LIBDIR_BUILD}/gnucash:${test_core_dir} - ) + add_test(NAME python-bindings COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/runTests.py.in) + set(PYTHON_ENV "GNC_UNINSTALLED=1;GNC_BUILDDIR=${CMAKE_BINARY_DIR};PYTHONPATH=${PYTHON_SYSCONFIG_BUILD}:${LIBDIR_BUILD}/gnucash:${test_core_dir}") + set(ASAN_ENV "${ASAN_DYNAMIC_LIB_ENV};ASAN_OPTIONS=fast_unwind_on_malloc=0") + set_tests_properties(python-bindings PROPERTIES ENVIRONMENT "$,${PYTHON_ENV};${ASAN_ENV},${PYTHON_ENV}>") + endif() set(test_python_bindings_DATA diff --git a/common/cmake_modules/GncAddSchemeTargets.cmake b/common/cmake_modules/GncAddSchemeTargets.cmake index 63c1b3d2055..bf86629b048 100644 --- a/common/cmake_modules/GncAddSchemeTargets.cmake +++ b/common/cmake_modules/GncAddSchemeTargets.cmake @@ -266,18 +266,23 @@ function(gnc_add_scheme_targets _TARGET) message(" GNC_MODULE_PATH: ${_GNC_MODULE_PATH}") endif() #We quote the arguments to stop CMake stripping the path separators. + set (GUILE_ENV + "${LIBRARY_PATH}" + "GNC_UNINSTALLED=YES" + "GNC_BUILDDIR=${CMAKE_BINARY_DIR}" + "GUILE_LOAD_PATH=${_GUILE_LOAD_PATH}" + "GUILE_LOAD_COMPILED_PATH=${_GUILE_LOAD_COMPILED_PATH}" + "GNC_MODULE_PATH=${_GNC_MODULE_PATH}" + ) + add_custom_command( OUTPUT ${output_file} COMMAND ${CMAKE_COMMAND} -E env - "${LIBRARY_PATH}" - "GNC_UNINSTALLED=YES" - "GNC_BUILDDIR=${CMAKE_BINARY_DIR}" - "GUILE_LOAD_PATH=${_GUILE_LOAD_PATH}" - "GUILE_LOAD_COMPILED_PATH=${_GUILE_LOAD_COMPILED_PATH}" - "GNC_MODULE_PATH=${_GNC_MODULE_PATH}" + "${GUILE_ENV}$<$:;${ASAN_DYNAMIC_LIB_ENV}>" ${GUILE_EXECUTABLE} -e "\(@@ \(guild\) main\)" -s ${GUILD_EXECUTABLE} compile -o ${output_file} ${source_file_abs_path} DEPENDS ${guile_depends} MAIN_DEPENDENCY ${source_file_abs_path} + COMMAND_EXPAND_LISTS VERBATIM ) endforeach(source_file) diff --git a/common/cmake_modules/GncAddTest.cmake b/common/cmake_modules/GncAddTest.cmake index 6d095afc4b1..bf40e3e7583 100644 --- a/common/cmake_modules/GncAddTest.cmake +++ b/common/cmake_modules/GncAddTest.cmake @@ -77,36 +77,35 @@ function(gnc_add_test _TARGET _SOURCE_FILES TEST_INCLUDE_VAR_NAME TEST_LIBS_VAR_ # Extra arguments are treated as environment variables set(HAVE_ENV_VARS TRUE) endif() + set(ENVVARS "GNC_UNINSTALLED=YES;GNC_BUILDDIR=${CMAKE_BINARY_DIR}") + if (HAVE_ENV_VARS) + list(APPEND ENVVARS ${ARGN}) + endif() set(TEST_INCLUDE_DIRS ${${TEST_INCLUDE_VAR_NAME}}) set(TEST_LIBS ${${TEST_LIBS_VAR_NAME}}) set_source_files_properties (${_SOURCE_FILES} PROPERTIES OBJECT_DEPENDS ${CONFIG_H}) - add_executable(${_TARGET} EXCLUDE_FROM_ALL ${_SOURCE_FILES}) - target_link_libraries(${_TARGET} ${TEST_LIBS}) - target_include_directories(${_TARGET} PRIVATE ${TEST_INCLUDE_DIRS}) - if (${HAVE_ENV_VARS}) - add_test(${_TARGET} ${CMAKE_BINARY_DIR}/bin/${_TARGET}) - set_tests_properties(${_TARGET} PROPERTIES ENVIRONMENT "GNC_UNINSTALLED=YES;GNC_BUILDDIR=${CMAKE_BINARY_DIR};${ARGN}") + if (CMAKE_GENERATOR STREQUAL Xcode) + add_test(NAME ${_TARGET} COMMAND ${_TARGET} CONFIGURATIONS Debug;Release) else() - if (CMAKE_GENERATOR STREQUAL Xcode) - add_test(NAME ${_TARGET} COMMAND ${_TARGET} CONFIGURATIONS Debug;Release) - else() - add_test(NAME ${_TARGET} COMMAND ${_TARGET}) - endif() - set_tests_properties(${_TARGET} PROPERTIES ENVIRONMENT "GNC_UNINSTALLED=YES;GNC_BUILDDIR=${CMAKE_BINARY_DIR}") + add_test(NAME ${_TARGET} COMMAND ${_TARGET}) endif() + add_executable(${_TARGET} EXCLUDE_FROM_ALL ${_SOURCE_FILES}) + target_link_libraries(${_TARGET} PRIVATE ${TEST_LIBS}) + target_include_directories(${_TARGET} PRIVATE ${TEST_INCLUDE_DIRS}) + set_tests_properties(${_TARGET} PROPERTIES ENVIRONMENT "$,${ENVVARS};ASAN_OPTIONS=fast_unwind_on_malloc=0,${ENVVARS}>") add_dependencies(check ${_TARGET}) endfunction() function(gnc_add_test_with_guile _TARGET _SOURCE_FILES TEST_INCLUDE_VAR_NAME TEST_LIBS_VAR_NAME) get_guile_env() gnc_add_test(${_TARGET} "${_SOURCE_FILES}" "${TEST_INCLUDE_VAR_NAME}" "${TEST_LIBS_VAR_NAME}" - "${GUILE_ENV};${ARGN}" + "${GUILE_ENV}$<$:;${ASAN_DYNAMIC_LIB_ENV}>;${ARGN}" ) endfunction() function(gnc_add_scheme_test _TARGET _SOURCE_FILE) - add_test(${_TARGET} ${GUILE_EXECUTABLE} --debug -c " + add_test(NAME ${_TARGET} COMMAND ${GUILE_EXECUTABLE} --debug -c " (set! %load-hook (lambda (filename) (when (and filename @@ -119,7 +118,7 @@ function(gnc_add_scheme_test _TARGET _SOURCE_FILE) (exit (run-test))" ) get_guile_env() - set_tests_properties(${_TARGET} PROPERTIES ENVIRONMENT "${GUILE_ENV};${ARGN}") + set_tests_properties(${_TARGET} PROPERTIES ENVIRONMENT "$,${GUILE_ENV};${ASAN_DYNAMIC_LIB_ENV};ASAN_OPTIONS=fast_unwind_on_malloc=0;${ARGN},${GUILE_ENV};${ARGN}>") endfunction() function(gnc_add_scheme_tests _SOURCE_FILES) diff --git a/libgnucash/backend/dbi/gnc-backend-dbi.cpp b/libgnucash/backend/dbi/gnc-backend-dbi.cpp index 1235ad1b9f9..f4b83b41170 100644 --- a/libgnucash/backend/dbi/gnc-backend-dbi.cpp +++ b/libgnucash/backend/dbi/gnc-backend-dbi.cpp @@ -1030,7 +1030,7 @@ template<> bool QofDbiBackendProvider::type_check(const char *uri) { FILE* f; - gchar buf[50]; + gchar buf[51]{}; G_GNUC_UNUSED size_t chars_read; gint status; gchar* filename; @@ -1050,7 +1050,7 @@ QofDbiBackendProvider::type_check(const char *uri) } // OK if file has the correct header - chars_read = fread (buf, sizeof (buf), 1, f); + chars_read = fread (buf, sizeof (buf) - 1, 1, f); status = fclose (f); if (status < 0) { diff --git a/libgnucash/backend/xml/gnc-xml-backend.cpp b/libgnucash/backend/xml/gnc-xml-backend.cpp index 7894d02f57c..6f48e574f10 100644 --- a/libgnucash/backend/xml/gnc-xml-backend.cpp +++ b/libgnucash/backend/xml/gnc-xml-backend.cpp @@ -234,7 +234,9 @@ GncXmlBackend::load(QofBook* book, QofBackendLoadType loadType) if (loadType != LOAD_TYPE_INITIAL_LOAD) return; error = ERR_BACKEND_NO_ERR; - m_book = book; + if (m_book) + g_object_unref(m_book); + m_book = QOF_BOOK(g_object_ref(book)); int rc; switch (determine_file_type (m_fullpath)) @@ -306,7 +308,8 @@ GncXmlBackend::sync(QofBook* book) * for multiple books have been removed in the meantime and there is just one * book, no more. */ - if (m_book == nullptr) m_book = book; + if (m_book == nullptr) + m_book = QOF_BOOK(g_object_ref(book)); if (book != m_book) return; if (qof_book_is_readonly (m_book)) diff --git a/libgnucash/backend/xml/test/CMakeLists.txt b/libgnucash/backend/xml/test/CMakeLists.txt index 58b537e7387..81b5e72ea56 100644 --- a/libgnucash/backend/xml/test/CMakeLists.txt +++ b/libgnucash/backend/xml/test/CMakeLists.txt @@ -13,7 +13,7 @@ set(XML_TEST_INCLUDE_DIRS ) -set(XML_TEST_LIBS gnc-engine gnc-test-engine test-core ${LIBXML2_LDFLAGS} -lz) +set(XML_TEST_LIBS gnc-backend-xml-utils gnc-engine gnc-test-engine test-core ${LIBXML2_LDFLAGS} -lz) set(XML_GTEST_LIBS ${XML_TEST_LIBS} gtest) function(add_xml_test _TARGET _SOURCE_FILES) @@ -26,40 +26,6 @@ function(add_xml_gtest _TARGET _SOURCE_FILES) target_compile_options(${_TARGET} PRIVATE -DU_SHOW_CPLUSPLUS_API=0 -DG_LOG_DOMAIN=\"gnc.backend.xml\") endfunction() -################################ - -set(test_backend_xml_base_SOURCES - ${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/sixtp-dom-parsers.cpp - ${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/sixtp-dom-generators.cpp - ${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/sixtp-utils.cpp - ${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/sixtp.cpp - ${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/sixtp-stack.cpp - ${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/sixtp-to-dom-parser.cpp - ${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/gnc-xml-helper.cpp -) - -## the xml backend is now a GModule - this test does -## not load it as a module and cannot link to it -## and remain portable. - -set(test_backend_xml_module_SOURCES - ${test_backend_xml_base_SOURCES} - ${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/io-example-account.cpp - ${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/io-gncxml-gen.cpp - ${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/io-gncxml-v2.cpp - ${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/io-utils.cpp - ${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/gnc-account-xml-v2.cpp - ${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/gnc-budget-xml-v2.cpp - ${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/gnc-lot-xml-v2.cpp - ${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/gnc-recurrence-xml-v2.cpp - ${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/gnc-schedxaction-xml-v2.cpp - ${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/gnc-freqspec-xml-v2.cpp - ${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/gnc-transaction-xml-v2.cpp - ${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/gnc-commodity-xml-v2.cpp - ${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/gnc-book-xml-v2.cpp - ${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/gnc-pricedb-xml-v2.cpp -) - set_local_dist(test_backend_xml_DIST_local CMakeLists.txt grab-types.pl @@ -84,8 +50,8 @@ set_local_dist(test_backend_xml_DIST_local ) set(test_backend_xml_DIST ${test_backend_xml_DIST_local} ${test_backend_xml_test_files_DIST} PARENT_SCOPE) -add_xml_test(test-dom-converters1 "${test_backend_xml_base_SOURCES};test-dom-converters1.cpp") -add_xml_test(test-kvp-frames "${test_backend_xml_base_SOURCES};test-kvp-frames.cpp") +add_xml_test(test-dom-converters1 "test-dom-converters1.cpp") +add_xml_test(test-kvp-frames "test-kvp-frames.cpp") add_xml_test(test-load-backend test-load-backend.cpp) add_xml_test(test-load-xml2 test-load-xml2.cpp GNC_TEST_FILES=${CMAKE_CURRENT_SOURCE_DIR}/test-files/xml2 @@ -96,19 +62,19 @@ add_xml_test(test-load-xml2 test-load-xml2.cpp #) add_xml_test(test-load-example-account - "${test_backend_xml_module_SOURCES};test-load-example-account.cpp" + "test-load-example-account.cpp" GNC_ACCOUNT_PATH=${CMAKE_SOURCE_DIR}/data/accounts/C ) target_compile_options(test-load-example-account PRIVATE -DU_SHOW_CPLUSPLUS_API=0) add_xml_gtest(test-load-save-files gtest-load-save-files.cpp GNC_TEST_FILES=${CMAKE_CURRENT_SOURCE_DIR}/test-files/load-save ) -add_xml_test(test-string-converters "${test_backend_xml_base_SOURCES};test-string-converters.cpp") -add_xml_test(test-xml-account "${test_backend_xml_module_SOURCES};test-xml-account.cpp;test-file-stuff.cpp") -add_xml_test(test-xml-commodity "${test_backend_xml_module_SOURCES};test-xml-commodity.cpp;test-file-stuff.cpp") -add_xml_test(test-xml-pricedb "${test_backend_xml_module_SOURCES};test-xml-pricedb.cpp;test-file-stuff.cpp") -add_xml_test(test-xml-transaction "${test_backend_xml_module_SOURCES};test-xml-transaction.cpp;test-file-stuff.cpp") -add_xml_test(test-xml2-is-file "${test_backend_xml_module_SOURCES};test-xml2-is-file.cpp" +add_xml_test(test-string-converters "test-string-converters.cpp") +add_xml_test(test-xml-account "test-xml-account.cpp;test-file-stuff.cpp") +add_xml_test(test-xml-commodity "test-xml-commodity.cpp;test-file-stuff.cpp") +add_xml_test(test-xml-pricedb "test-xml-pricedb.cpp;test-file-stuff.cpp") +add_xml_test(test-xml-transaction "test-xml-transaction.cpp;test-file-stuff.cpp") +add_xml_test(test-xml2-is-file "test-xml2-is-file.cpp" GNC_TEST_FILES=${CMAKE_CURRENT_SOURCE_DIR}/test-files/xml2) set(test-real-data-env diff --git a/libgnucash/engine/Split.c b/libgnucash/engine/Split.c index 252babe507b..0272b020a57 100644 --- a/libgnucash/engine/Split.c +++ b/libgnucash/engine/Split.c @@ -720,9 +720,15 @@ xaccFreeSplit (Split *split) split->date_reconciled = 0; G_OBJECT_CLASS (QOF_INSTANCE_GET_CLASS (&split->inst))->dispose(G_OBJECT (split)); - // Is this right? - if (split->gains_split) split->gains_split->gains_split = NULL; - /* qof_instance_release(&split->inst); */ + + if (split->gains_split) + { + Split *other = xaccSplitGetOtherSplit(split->gains_split); + split->gains_split->gains_split = NULL; + if (other) + other->gains_split = NULL; + } + g_object_unref(split); } diff --git a/libgnucash/engine/gnc-date.cpp b/libgnucash/engine/gnc-date.cpp index 3dc541313dc..e7f0d49aeae 100644 --- a/libgnucash/engine/gnc-date.cpp +++ b/libgnucash/engine/gnc-date.cpp @@ -341,7 +341,8 @@ gnc_date_string_to_dateformat(const char* fmt_str, QofDateFormat *format) const char* gnc_date_monthformat_to_string(GNCDateMonthFormat format) { - switch (format) + //avoid UB if format is out of range + switch (static_cast(format)) { case GNCDATE_MONTH_NUMBER: return "number"; @@ -438,7 +439,9 @@ QofDateFormat qof_date_format_get (void) void qof_date_format_set(QofDateFormat df) { - if (df >= DATE_FORMAT_FIRST && df <= DATE_FORMAT_LAST) +//avoid UB if df is out of range + auto dfi{static_cast(df)}; + if (dfi >= DATE_FORMAT_FIRST && dfi <= DATE_FORMAT_LAST) { prevQofDateFormat = dateFormat; dateFormat = df; diff --git a/libgnucash/engine/gnc-timezone.cpp b/libgnucash/engine/gnc-timezone.cpp index b1db4769b4b..5584e46e51a 100644 --- a/libgnucash/engine/gnc-timezone.cpp +++ b/libgnucash/engine/gnc-timezone.cpp @@ -477,8 +477,8 @@ namespace IANAParser endian_swap(&info.gmtoff); tzinfo.push_back( {info, &fileblock[abbrev + info.abbrind], - fileblock[std_dist + index] != '\0', - fileblock[gmt_dist + index] != '\0'}); + (index < isstd_count ? fileblock[std_dist + index] != '\0' : true), + (index < isgmt_count ? fileblock[gmt_dist + index] != '\0' : false)}); } } diff --git a/libgnucash/engine/qofbook.cpp b/libgnucash/engine/qofbook.cpp index 13bdb71f5dc..7575f7a72fc 100644 --- a/libgnucash/engine/qofbook.cpp +++ b/libgnucash/engine/qofbook.cpp @@ -32,6 +32,7 @@ * Copyright (c) 2000 Dave Peticolas * Copyright (c) 2007 David Hampton */ +#include "qof-string-cache.h" #include #include @@ -53,6 +54,7 @@ #include "qofobject-p.h" #include "qofbookslots.h" #include "kvp-frame.hpp" +#include "gnc-lot.h" // For GNC_ID_ROOT_ACCOUNT: #include "AccountP.h" @@ -111,7 +113,8 @@ qof_book_init (QofBook *book) qof_instance_init_data (&book->inst, QOF_ID_BOOK, book); - book->data_tables = g_hash_table_new (g_str_hash, g_str_equal); + book->data_tables = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify)qof_string_cache_remove, NULL); book->data_table_finalizers = g_hash_table_new (g_str_hash, g_str_equal); book->book_open = 'y'; @@ -317,6 +320,13 @@ qof_book_finalize_real (G_GNUC_UNUSED GObject *bookp) { } +static void +destroy_lot(QofInstance *inst, [[maybe_unused]]void* data) +{ + auto lot{GNC_LOT(inst)}; + gnc_lot_destroy(lot); +} + void qof_book_destroy (QofBook *book) { @@ -333,6 +343,11 @@ qof_book_destroy (QofBook *book) */ g_hash_table_foreach (book->data_table_finalizers, book_final, book); + /* Lots hold a variety of pointers that need to still exist while + * cleaning them up so run its book_end before the rest. + */ + auto lots{qof_book_get_collection(book, GNC_ID_LOT)}; + qof_collection_foreach(lots, destroy_lot, nullptr); qof_object_book_end (book); g_hash_table_destroy (book->data_table_finalizers); @@ -350,7 +365,6 @@ qof_book_destroy (QofBook *book) cols = book->hash_of_collections; g_object_unref (book); g_hash_table_destroy (cols); - /*book->hash_of_collections = NULL;*/ LEAVE ("book=%p", book); } @@ -450,13 +464,14 @@ qof_book_set_backend (QofBook *book, QofBackend *be) /* ====================================================================== */ /* Store arbitrary pointers in the QofBook for data storage extensibility */ -/* XXX if data is NULL, we should remove the key from the hash table! - */ void qof_book_set_data (QofBook *book, const char *key, gpointer data) { if (!book || !key) return; - g_hash_table_insert (book->data_tables, (gpointer)key, data); + if (data) + g_hash_table_insert (book->data_tables, (gpointer)CACHE_INSERT(key), data); + else + g_hash_table_remove(book->data_tables, key); } void diff --git a/libgnucash/engine/test/test-guid.cpp b/libgnucash/engine/test/test-guid.cpp index 2f14d2bc66c..78a4e5c4f6c 100644 --- a/libgnucash/engine/test/test-guid.cpp +++ b/libgnucash/engine/test/test-guid.cpp @@ -82,7 +82,7 @@ run_test (void) auto ent = QOF_INSTANCE(g_object_new(QOF_TYPE_INSTANCE, "guid", &guid, NULL)); do_test ((NULL == qof_collection_lookup_entity (col, &guid)), "duplicate guid"); - ent->e_type = type; + ent->e_type = CACHE_INSERT(type); qof_collection_insert_entity (col, ent); do_test ((NULL != qof_collection_lookup_entity (col, &guid)), "guid not found"); diff --git a/libgnucash/engine/test/test-qofbook.c b/libgnucash/engine/test/test-qofbook.c index f131bcbb4c6..e74d37a7e7f 100644 --- a/libgnucash/engine/test/test-qofbook.c +++ b/libgnucash/engine/test/test-qofbook.c @@ -962,10 +962,7 @@ test_book_new_destroy( void ) qof_book_set_data_fin( book, key, (gpointer) data, mock_final_cb ); test_struct.called = FALSE; - g_test_message( "Testing book destroy" ); qof_book_destroy( book ); - g_assert_true( qof_book_shutting_down( book ) ); - g_assert_true( test_struct.called ); } void diff --git a/libgnucash/engine/test/test-qofobject.c b/libgnucash/engine/test/test-qofobject.c index fae13795876..75d41c452a8 100644 --- a/libgnucash/engine/test/test-qofobject.c +++ b/libgnucash/engine/test/test-qofobject.c @@ -303,8 +303,8 @@ test_qof_object_book_begin( Fixture *fixture, gconstpointer pData ) g_assert_cmpint( g_list_index( get_book_list(), (gconstpointer) book2 ), != , -1 ); g_assert_cmpint( object_book_begin_struct.call_count, == , list_length ); - qof_object_foreach_type ((QofForeachTypeCB)g_free, NULL); qof_book_destroy( book2 ); + qof_object_foreach_type ((QofForeachTypeCB)g_free, NULL); } static void @@ -389,8 +389,8 @@ test_qof_object_is_dirty( Fixture *fixture, gconstpointer pData ) g_assert_true( qof_object_is_dirty( book ) == TRUE ); g_assert_cmpint( object_dirty_struct.call_count, == , 1 ); /* should break on first */ - qof_object_foreach_type ((QofForeachTypeCB)g_free, NULL); qof_book_destroy( book ); + qof_object_foreach_type ((QofForeachTypeCB)g_free, NULL); } static struct @@ -433,8 +433,8 @@ test_qof_object_mark_clean( Fixture *fixture, gconstpointer pData ) qof_object_mark_clean( book ); g_assert_cmpint( object_mark_clean_struct.call_count, == , list_length ); - qof_object_foreach_type ((QofForeachTypeCB)g_free, NULL); qof_book_destroy( book ); + qof_object_foreach_type ((QofForeachTypeCB)g_free, NULL); } static struct