This is an automated email from the ASF dual-hosted git repository.

swebb2066 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git


The following commit(s) were added to refs/heads/master by this push:
     new b1ced8a3 Simplify using the Qt event loop to watch for configuration 
file change (#574)
b1ced8a3 is described below

commit b1ced8a36baa3df96b7ff3858244852063c527de
Author: Stephen Webb <[email protected]>
AuthorDate: Sat Dec 27 11:43:11 2025 +1100

    Simplify using the Qt event loop to watch for configuration file change 
(#574)
    
    * Add a unit test for qt::Configuration::configureFromFileAndWatch
---
 src/main/CMakeLists.txt                            |  15 ++-
 src/main/cpp-qt/CMakeLists.txt                     |  51 ++++---
 src/main/cpp-qt/configuration.cpp                  | 146 ++++++++++-----------
 src/main/include/log4cxx-qt/configuration.h        |  45 ++++---
 src/site/markdown/qt-support.md                    |   4 +-
 src/test/cpp/CMakeLists.txt                        |   9 ++
 src/test/cpp/qtconfigurationtest.cpp               | 132 +++++++++++++++++++
 .../resources/input/qtConfigurationTest.properties |  25 ++++
 8 files changed, 296 insertions(+), 131 deletions(-)

diff --git a/src/main/CMakeLists.txt b/src/main/CMakeLists.txt
index 5134fe18..42fabda7 100644
--- a/src/main/CMakeLists.txt
+++ b/src/main/CMakeLists.txt
@@ -16,9 +16,16 @@
 #
 add_subdirectory(include)
 add_subdirectory(cpp)
-add_subdirectory(cpp-qt)
 add_subdirectory(resources)
 
+# Log4cxx Qt support, if enabled
+if(LOG4CXX_QT_SUPPORT)
+  add_subdirectory(cpp-qt)
+  target_include_directories(log4cxx-qt PUBLIC
+      $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+  )
+endif(LOG4CXX_QT_SUPPORT)
+
 # setup include directories 
 include(GNUInstallDirs)
 target_include_directories(log4cxx PUBLIC 
@@ -34,12 +41,6 @@ if(LOG4CXX_CFSTRING)
   endif()
 endif()
 
-if(LOG4CXX_QT_SUPPORT)
-    target_include_directories(log4cxx-qt PUBLIC
-      $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
-    )
-endif(LOG4CXX_QT_SUPPORT)
-
 option(LOG4CXX_ABI_CHECK "Check for ABI changes" OFF)
 if(LOG4CXX_ABI_CHECK)
   if(NOT "log4cxx" STREQUAL "${LOG4CXX_NS}")
diff --git a/src/main/cpp-qt/CMakeLists.txt b/src/main/cpp-qt/CMakeLists.txt
index 50372d30..a0999533 100644
--- a/src/main/cpp-qt/CMakeLists.txt
+++ b/src/main/cpp-qt/CMakeLists.txt
@@ -15,31 +15,28 @@
 # limitations under the License.
 #
 
-# Log4cxx Qt support, if enabled
-if(LOG4CXX_QT_SUPPORT)
-    find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
-    find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core)
+find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
+find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core)
 
-    add_library(log4cxx-qt)
-    if(BUILD_SHARED_LIBS)
-        target_compile_definitions(log4cxx-qt PRIVATE LOG4CXX)
-    else()
-        target_compile_definitions(log4cxx-qt PUBLIC LOG4CXX_STATIC)
-    endif()
-    add_dependencies(log4cxx-qt log4cxx-qt-include)
-    target_compile_definitions(log4cxx-qt PRIVATE "QT_NO_KEYWORDS")
-    target_link_libraries(log4cxx-qt Qt${QT_VERSION_MAJOR}::Core log4cxx)
-    target_sources(log4cxx-qt
-        PRIVATE
-        messagehandler.cpp
-        configuration.cpp
-    )
-    set_target_properties(log4cxx-qt PROPERTIES
-      VERSION ${LIBLOG4CXX_LIB_VERSION}
-      SOVERSION ${LIBLOG4CXX_LIB_SOVERSION}
-      OUTPUT_NAME ${LOG4CXX_LIB_NAME}-qt
-      PDB_NAME ${LOG4CXX_LIB_NAME}-qt
-      COMPILE_PDB_NAME ${LOG4CXX_LIB_NAME}-qt
-      EXPORT_NAME ${LOG4CXX_LIB_NAME}-qt
-    )
-endif( LOG4CXX_QT_SUPPORT)
+add_library(log4cxx-qt)
+if(BUILD_SHARED_LIBS)
+    target_compile_definitions(log4cxx-qt PRIVATE LOG4CXX)
+else()
+    target_compile_definitions(log4cxx-qt PUBLIC LOG4CXX_STATIC)
+endif()
+add_dependencies(log4cxx-qt log4cxx-qt-include)
+target_compile_definitions(log4cxx-qt PRIVATE "QT_NO_KEYWORDS")
+target_link_libraries(log4cxx-qt Qt${QT_VERSION_MAJOR}::Core log4cxx)
+target_sources(log4cxx-qt
+    PRIVATE
+    messagehandler.cpp
+    configuration.cpp
+)
+set_target_properties(log4cxx-qt PROPERTIES
+  VERSION ${LIBLOG4CXX_LIB_VERSION}
+  SOVERSION ${LIBLOG4CXX_LIB_SOVERSION}
+  OUTPUT_NAME ${LOG4CXX_LIB_NAME}-qt
+  PDB_NAME ${LOG4CXX_LIB_NAME}-qt
+  COMPILE_PDB_NAME ${LOG4CXX_LIB_NAME}-qt
+  EXPORT_NAME ${LOG4CXX_LIB_NAME}-qt
+)
diff --git a/src/main/cpp-qt/configuration.cpp 
b/src/main/cpp-qt/configuration.cpp
index 29ab7fe9..1654efd5 100644
--- a/src/main/cpp-qt/configuration.cpp
+++ b/src/main/cpp-qt/configuration.cpp
@@ -17,115 +17,108 @@
 #include <log4cxx-qt/configuration.h>
 #include <log4cxx-qt/transcoder.h>
 #include <log4cxx/helpers/loglog.h>
+#include <log4cxx/helpers/stringhelper.h>
 #include <log4cxx/xml/domconfigurator.h>
 #include <log4cxx/propertyconfigurator.h>
-
 #include <QFileSystemWatcher>
 #include <QDir>
 #include <QFile>
 #include <memory>
 #include <QDebug>
 
-namespace LOG4CXX_NS
-{
-namespace qt
+namespace
 {
-using helpers::LogLog;
-
-static std::unique_ptr<QFileSystemWatcher> watcher;
-static QString configFilename;
-
-static void loadXMLFile(const QString& path){
-       QFileInfo fi(configFilename);
-       if(!fi.exists()){
-               return;
-       }
-       LOG4CXX_DECODE_QSTRING(lsPath, path);
-       xml::DOMConfigurator::configure(lsPath);
-}
+using namespace LOG4CXX_NS;
 
-static void loadPropertiesFile(const QString& path){
-       QFileInfo fi(configFilename);
-       if(!fi.exists()){
-               return;
-       }
-       LOG4CXX_DECODE_QSTRING(lsPath, path);
-       PropertyConfigurator::configure(lsPath);
+spi::ConfigurationStatus tryLoadFile(const LogString& lsFilename)
+{
+       return helpers::StringHelper::endsWith(lsFilename, LOG4CXX_STR(".xml"))
+               ? xml::DOMConfigurator::configure(lsFilename)
+               : PropertyConfigurator::configure(lsFilename);
 }
 
-static void dirChanged(const QString&){
-       QFileInfo fi(configFilename);
-       if(fi.exists()){
-               // From the Qt docs:
-               // Note that QFileSystemWatcher stops monitoring files once 
they have been renamed
-               // or removed from disk, and directories once they have been 
removed from disk.
-               //
-               // Some text editing programs will replace the file with a new 
one, which deletes
-               // the old file(thus causing Qt to remove the watch), so we 
need to add in the
-               // file whenever the directory changes.
-               // See also: 
https://stackoverflow.com/questions/18300376/qt-qfilesystemwatcher-signal-filechanged-gets-emited-only-once
-               watcher->addPath(configFilename);
-       }
+spi::ConfigurationStatus tryLoadFile(const QString& filename)
+{
+       LOG4CXX_DECODE_QSTRING(lsFilename, filename);
+       return tryLoadFile(lsFilename);
 }
 
-Configuration::Configuration(){}
+} // namespace
 
-spi::ConfigurationStatus Configuration::tryLoadFile(const QString& filename){
-       auto stat = spi:: ConfigurationStatus::NotConfigured;
-       auto isXML = false;
+namespace LOG4CXX_NS::qt
+{
 
-       LOG4CXX_DECODE_QSTRING(lsFilename, filename);
-       if(filename.endsWith(".xml")){
-               stat = xml::DOMConfigurator::configure(lsFilename);
-               isXML = true;
-       }else if(filename.endsWith(".properties")){
-               stat = PropertyConfigurator::configure(lsFilename);
+void Configuration::reconfigureWhenModified(const QString& filename)
+{
+       static auto watcher = std::make_unique<QFileSystemWatcher>();
+       QFileInfo fi(filename);
+       // From the Qt docs:
+       // Note that QFileSystemWatcher stops monitoring files once they have 
been renamed
+       // or removed from disk, and directories once they have been removed 
from disk.
+       //
+       // Some text editing programs will replace the file with a new one, 
which deletes
+       // the old file(thus causing Qt to remove the watch), so we need to add 
in the
+       // file whenever the directory changes.
+       // See also: 
https://stackoverflow.com/questions/18300376/qt-qfilesystemwatcher-signal-filechanged-gets-emited-only-once
+       watcher->addPath(fi.absolutePath());
+       if (helpers::LogLog::isDebugEnabled())
+       {
+               LOG4CXX_DECODE_QSTRING(lsDir, fi.absolutePath());
+               helpers::LogLog::debug(LOG4CXX_STR("Watching directory ") + 
lsDir);
        }
-
-       if( stat == spi::ConfigurationStatus::Configured ){
-               watcher = std::make_unique<QFileSystemWatcher>();
-               configFilename = filename;
-               QFileInfo fi(filename);
-               watcher->addPath(fi.dir().absolutePath());
-               watcher->addPath(filename);
-
-               QObject::connect(watcher.get(), 
&QFileSystemWatcher::directoryChanged,
-                                                &dirChanged);
-               if(isXML){
-                       QObject::connect(watcher.get(), 
&QFileSystemWatcher::fileChanged,
-                                                        &loadXMLFile);
-               }else{
-                       QObject::connect(watcher.get(), 
&QFileSystemWatcher::fileChanged,
-                                                        &loadPropertiesFile);
-               }
+       watcher->addPath(fi.absoluteFilePath());
+       if (helpers::LogLog::isDebugEnabled())
+       {
+               LOG4CXX_DECODE_QSTRING(lsFile, fi.absoluteFilePath());
+               helpers::LogLog::debug(LOG4CXX_STR("Watching file ") + lsFile);
        }
+       QObject::connect(watcher.get(), &QFileSystemWatcher::directoryChanged
+               , [fi](const QString&){
+                       if (fi.exists())
+                               watcher->addPath(fi.absoluteFilePath());
+               });
+       QObject::connect(watcher.get(), &QFileSystemWatcher::fileChanged
+               , [](const QString& fullPath){
+                       tryLoadFile(fullPath);
+               });
+}
 
-       return stat;
+void Configuration::reconfigureWhenModified(const LogString& lsFilename)
+{
+       LOG4CXX_ENCODE_QSTRING(filename, lsFilename);
+       reconfigureWhenModified(filename);
 }
 
-std::tuple<spi::ConfigurationStatus,QString>
-Configuration::configureFromFileAndWatch(const QVector<QString>& directories,
-                                                                               
 const QVector<QString>& filenames){
-       for( QString dir : directories ){
-               for( QString fname : filenames ){
+       std::tuple<spi::ConfigurationStatus,QString>
+Configuration::configureFromFileAndWatch
+       ( const QVector<QString>& directories
+       , const QVector<QString>& filenames
+       )
+{
+       for( QString dir : directories )
+       {
+               for( QString fname : filenames )
+               {
                        QString candidate_str = dir + "/" + fname;
                        QFile candidate(candidate_str);
 
-                       if (LogLog::isDebugEnabled())
+                       if (helpers::LogLog::isDebugEnabled())
                        {
                                LOG4CXX_DECODE_QSTRING(msg, "Checking file " + 
candidate_str);
-                               LogLog::debug(msg);
+                               helpers::LogLog::debug(msg);
                        }
                        if (candidate.exists())
                        {
                                auto configStatus = tryLoadFile(candidate_str);
-                               if( configStatus == 
spi::ConfigurationStatus::Configured ){
+                               if( configStatus == 
spi::ConfigurationStatus::Configured )
+                               {
+                                       reconfigureWhenModified(candidate_str);
                                        return {configStatus, candidate_str};
                                }
-                               if (LogLog::isDebugEnabled())
+                               if (helpers::LogLog::isDebugEnabled())
                                {
                                        LOG4CXX_DECODE_QSTRING(failmsg, "Unable 
to load  " + candidate_str + ": trying next");
-                                       LogLog::debug(failmsg);
+                                       helpers::LogLog::debug(failmsg);
                                }
                        }
                }
@@ -134,5 +127,4 @@ Configuration::configureFromFileAndWatch(const 
QVector<QString>& directories,
        return {spi::ConfigurationStatus::NotConfigured, QString()};
 }
 
-} //namespace helpers
-} //namespace log4cxx
+} // namespace LOG4CXX_NS::qt
diff --git a/src/main/include/log4cxx-qt/configuration.h 
b/src/main/include/log4cxx-qt/configuration.h
index ea8c393a..131dba4d 100644
--- a/src/main/include/log4cxx-qt/configuration.h
+++ b/src/main/include/log4cxx-qt/configuration.h
@@ -17,33 +17,42 @@
 #ifndef LOG4CXX_QT_CONFIGURATION_H
 #define LOG4CXX_QT_CONFIGURATION_H
 
+#include <log4cxx/spi/configurator.h>
 #include <QString>
 #include <QVector>
-#include <log4cxx/log4cxx.h>
-#include <log4cxx/defaultconfigurator.h>
+#include <tuple>
 
-namespace LOG4CXX_NS {
-namespace qt {
-
-class LOG4CXX_EXPORT Configuration {
-private:
-       Configuration();
-
-       static LOG4CXX_NS::spi::ConfigurationStatus tryLoadFile(const QString& 
filename);
+namespace LOG4CXX_NS::qt
+{
 
+/// Configuration support methods that use the Qt event loop
+/// to reload the configuration file when it is modified.
+class LOG4CXX_EXPORT Configuration
+{
 public:
        /**
-        * Configure Log4cxx and watch the file for changes.  See also 
DefaultConfigurator::configureFromFile.
+        * Select the file to configure Log4cxx and watch the file for changes. 
 See also DefaultConfigurator::configureFromFile.
         *
-        * @param directories
-        * @param filenames
-        * @return
+        * @param directories Each directory is checked for each entry in \c 
filenames
+        * @param filenames Each file name is checked in each entry in \c 
directories
+        * @return the selected file path if Log4cxx was successfully configured
+        */
+       static std::tuple<spi::ConfigurationStatus, QString> 
configureFromFileAndWatch
+               ( const QVector<QString>& directories
+               , const QVector<QString>& filenames
+               );
+
+       /**
+        * Set up a QFileSystemWatcher that will reconfigure Log4cxx when \c 
fullPath is modified.
+        */
+       static void reconfigureWhenModified(const QString& fullPath);
+
+       /**
+        * Set up a QFileSystemWatcher that will reconfigure Log4cxx when \c 
fullPath is modified.
         */
-       static std::tuple<LOG4CXX_NS::spi::ConfigurationStatus,QString> 
configureFromFileAndWatch(const QVector<QString>& directories,
-                                                                               
                                                                                
                   const QVector<QString>& filenames);
+       static void reconfigureWhenModified(const LogString& fullPath);
 };
 
-} /* namespace qt */
-} /* namespace log4cxx */
+} // namespace LOG4CXX_NS::qt
 
 #endif /* LOG4CXX_QT_CONFIGURATION_H */
diff --git a/src/site/markdown/qt-support.md b/src/site/markdown/qt-support.md
index 7e0bb13e..06d7b944 100644
--- a/src/site/markdown/qt-support.md
+++ b/src/site/markdown/qt-support.md
@@ -28,8 +28,8 @@ routed to Log4cxx, a message handler for Qt must be installed.
 
 Log4cxx provides a cmake build option `LOG4CXX_QT_SUPPORT=ON`
 which adds the log4cxx::qt namespace methods
-for directing Qt messages to Log4cxx and
-using the Qt event loop to process a configuration file change.
+for [directing Qt messages to Log4cxx](@ref log4cxx::qt::messageHandler) and
+[using the Qt event loop](@ref log4cxx::qt::Configuration) to process a 
configuration file change.
 Use the target `log4cxx-qt` instead of `log4cxx`
 in your `target_link_libraries` cmake directive.
 Also, including `log4cxx-qt/logger.h` allows you to use QString values
diff --git a/src/test/cpp/CMakeLists.txt b/src/test/cpp/CMakeLists.txt
index 4eb99f7d..a3126e53 100644
--- a/src/test/cpp/CMakeLists.txt
+++ b/src/test/cpp/CMakeLists.txt
@@ -80,6 +80,9 @@ endif()
 if(${LOG4CXX_EVENTS_AT_EXIT})
     set(ALL_LOG4CXX_TESTS ${ALL_LOG4CXX_TESTS} eventsatexittest)
 endif()
+if(LOG4CXX_QT_SUPPORT)
+  list(APPEND ALL_LOG4CXX_TESTS qtconfigurationtest)
+endif(LOG4CXX_QT_SUPPORT)
 foreach(fileName IN LISTS ALL_LOG4CXX_TESTS)
     add_executable(${fileName} "${fileName}.cpp")
 endforeach()
@@ -123,6 +126,11 @@ if(LOG4CXX_CFSTRING)
   set(CFSTR_TESTS filetestcase messagebuffertest leveltestcase streamtestcase 
transcodertestcase)
 endif()
 foreach(testName IN LISTS ALL_LOG4CXX_TESTS)
+    if (${testName} STREQUAL "qtconfigurationtest")
+        target_include_directories(${testName} PRIVATE 
$<TARGET_PROPERTY:log4cxx-qt,INCLUDE_DIRECTORIES>)
+        target_compile_definitions(${testName} PRIVATE "QT_NO_KEYWORDS")
+        target_link_libraries(${testName} PRIVATE log4cxx-qt)
+    endif()
     if (${testName} IN_LIST CFSTR_TESTS)
       if(APPLE)
         target_compile_options(${testName} PRIVATE "-fconstant-cfstrings")
@@ -137,6 +145,7 @@ foreach(testName IN LISTS ALL_LOG4CXX_TESTS)
     if(HAS_LIBESMTP)
       target_link_libraries(${testName} PRIVATE ${ESMTP_LIBRARIES})
     endif()
+
     add_test(NAME ${testName}
         COMMAND ${testName} -v
         WORKING_DIRECTORY ${UNIT_TEST_WORKING_DIR}
diff --git a/src/test/cpp/qtconfigurationtest.cpp 
b/src/test/cpp/qtconfigurationtest.cpp
new file mode 100644
index 00000000..dd025831
--- /dev/null
+++ b/src/test/cpp/qtconfigurationtest.cpp
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "logunit.h"
+#include <log4cxx/logger.h>
+#include <log4cxx/logmanager.h>
+#include <log4cxx/helpers/bytebuffer.h>
+#include <log4cxx/helpers/exception.h>
+#include <log4cxx/helpers/fileoutputstream.h>
+#include <log4cxx/helpers/loglog.h>
+#include <log4cxx/helpers/optionconverter.h>
+#include <log4cxx/helpers/pool.h>
+#include <log4cxx/helpers/transcoder.h>
+#include <log4cxx-qt/configuration.h>
+#include <log4cxx-qt/transcoder.h>
+#include <apr_file_io.h>
+#include "apr_time.h"
+#include <QCoreApplication>
+#include <QFileInfo>
+
+using namespace log4cxx;
+
+LOGUNIT_CLASS(QtConfigurationTestCase)
+{
+       LOGUNIT_TEST_SUITE(QtConfigurationTestCase);
+       LOGUNIT_TEST(test1);
+       LOGUNIT_TEST_SUITE_END();
+       helpers::Pool m_pool;
+       char m_buf[2048];
+       QString m_configFile;
+       spi::ConfigurationStatus m_status;
+public:
+
+       void copyPropertyFile(const LogString& lsDestDir)
+       {
+               LOG4CXX_ENCODE_CHAR(destDir, lsDestDir);
+               auto status = apr_file_copy
+                  ( "input/qtConfigurationTest.properties"
+                  , (destDir + "/qtConfigurationTest.properties").c_str()
+                  , APR_FPROT_UREAD | APR_FPROT_UWRITE
+                  , m_pool.getAPRPool()
+                  );
+               if (APR_SUCCESS != status)
+                  
helpers::LogLog::warn(helpers::Exception::makeMessage(lsDestDir + 
LOG4CXX_STR("/qtConfigurationTest.properties"), status));
+       }
+
+       void setUp()
+       {
+               int argc{ 0 };
+               char* argv[] = {nullptr};
+               static QCoreApplication init_once{argc, argv};
+               auto lsTempDir = 
helpers::OptionConverter::getSystemProperty(LOG4CXX_STR("TEMP"), 
LOG4CXX_STR("/tmp"));
+               copyPropertyFile(lsTempDir);
+               LOG4CXX_ENCODE_QSTRING(qTempDir, lsTempDir);
+               QVector<QString> paths
+                       { qTempDir
+                       };
+               QVector<QString> names
+                       { QString("qtConfigurationTest.properties")
+                       };
+               std::tie(m_status, m_configFile) = 
qt::Configuration::configureFromFileAndWatch(paths, names);
+       }
+
+       void tearDown()
+       {
+               LogManager::shutdown();
+               apr_file_remove(m_configFile.toUtf8().constData(), 
m_pool.getAPRPool());
+               // wait 0.2 sec to ensure the file is really gone on Windows
+               apr_sleep(200000);
+       }
+
+       void test1()
+       {
+               LOGUNIT_ASSERT_EQUAL(m_status, 
spi::ConfigurationStatus::Configured);
+               auto debugLogger1 = LogManager::getLogger(LOG4CXX_STR("test1"));
+               LOGUNIT_ASSERT(debugLogger1);
+               LOGUNIT_ASSERT(!debugLogger1->isDebugEnabled());
+               auto debugLogger2 = LogManager::getLogger(LOG4CXX_STR("test2"));
+               LOGUNIT_ASSERT(debugLogger2);
+               LOGUNIT_ASSERT(debugLogger2->isDebugEnabled());
+               LOGUNIT_ASSERT(QFileInfo(m_configFile).exists());
+               // wait 1 sec to ensure the modification time is different to 
creation date
+               for (auto i : {1, 2})
+               {
+                       QCoreApplication::processEvents();
+                       apr_sleep(500000); // 500 ms
+               }
+               auto debugLogger = LogManager::getLogger(LOG4CXX_STR("test3"));
+               LOGUNIT_ASSERT(debugLogger);
+               LOGUNIT_ASSERT(!debugLogger->isDebugEnabled());
+
+               // Append a configuration for test3 logger
+               helpers::ByteBuffer bbuf(m_buf, sizeof(m_buf));
+               int sz = 0;
+               for (const char* p = "\nlog4j.logger.test3=DEBUG\n"; *p; ++p)
+               {
+                       bbuf.put(*p);
+                       ++sz;
+               }
+               bbuf.position(0);
+               bbuf.limit(sz);
+               LOG4CXX_DECODE_QSTRING(lsConfigFile, m_configFile);
+               helpers::FileOutputStream of(lsConfigFile, true);
+               of.write(bbuf, m_pool);
+               of.flush(m_pool);
+               of.close(m_pool);
+               helpers::LogLog::debug(LOG4CXX_STR("Updated ") + lsConfigFile);
+
+               // wait 0.1 sec for the change to be noticed
+               for (auto i : {1, 2})
+               {
+                       QCoreApplication::processEvents();
+                       apr_sleep(50000); // 50 ms
+               }
+               LOGUNIT_ASSERT(debugLogger->isDebugEnabled());
+       }
+};
+
+LOGUNIT_TEST_SUITE_REGISTRATION(QtConfigurationTestCase);
diff --git a/src/test/resources/input/qtConfigurationTest.properties 
b/src/test/resources/input/qtConfigurationTest.properties
new file mode 100644
index 00000000..11a41434
--- /dev/null
+++ b/src/test/resources/input/qtConfigurationTest.properties
@@ -0,0 +1,25 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+log4j.rootCategory=INFO, A1
+
+log4j.appender.A1=org.apache.log4j.FileAppender
+log4j.appender.A1.File=${PROGRAM_FILE_PATH.PARENT_PATH}/${PROGRAM_FILE_PATH.STEM}.log
+log4j.appender.A1.Append=false
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%m%n
+
+#log4j.logger.test1=DEBUG
+log4j.logger.test2=DEBUG

Reply via email to