sc/qa/uitest/calc_tests8/dataProvider.py | 40 ++++++++++++++++++++++++- sc/source/ui/dataprovider/csvdataprovider.cxx | 12 +++++-- sc/source/ui/dataprovider/htmldataprovider.cxx | 8 +++-- sc/source/ui/dataprovider/sqldataprovider.cxx | 7 +++- sc/source/ui/dataprovider/xmldataprovider.cxx | 7 +++- 5 files changed, 62 insertions(+), 12 deletions(-)
New commits: commit 655ba73e506d75ca9988a428620f543b213774b7 Author: Neil Roberts <[email protected]> AuthorDate: Sat Nov 29 12:42:11 2025 +0100 Commit: Noel Grandin <[email protected]> CommitDate: Wed Dec 3 19:23:03 2025 +0100 tdf#169077: Add a UITest Co-authored-by: Regina Henschel <[email protected]> Change-Id: I7facde3d9aeea7ac3662da060ff821dfa2af7716 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/194828 Tested-by: Jenkins Reviewed-by: Noel Grandin <[email protected]> diff --git a/sc/qa/uitest/calc_tests8/dataProvider.py b/sc/qa/uitest/calc_tests8/dataProvider.py index d9052e1bc813..f4ae2917f81b 100644 --- a/sc/qa/uitest/calc_tests8/dataProvider.py +++ b/sc/qa/uitest/calc_tests8/dataProvider.py @@ -8,8 +8,11 @@ # from uitest.framework import UITestCase -from uitest.uihelper.common import get_state_as_dict, get_url_for_data_file +from uitest.uihelper.common import get_state_as_dict, get_url_for_data_file, select_by_text from libreoffice.uno.propertyvalue import mkPropertyValues +from libreoffice.calc.document import get_cell_by_position + +import os class DataProvider(UITestCase): @@ -39,4 +42,39 @@ class DataProvider(UITestCase): xApply.executeAction, args=('CLICK', ()), close_button="close") as dialog: pass + def do_import(self, data_format, test_file, identifier, expected_value): + with self.ui_test.create_doc_in_start_center("calc") as xDoc: + + # Make a database range on A1:K11 called "TestDB" + with self.ui_test.execute_dialog_through_command(".uno:DefineDBName") as xDialog: + xName = xDialog.getChild("entry") + xName.executeAction("SET", mkPropertyValues({"TEXT": "TestDB"})) + + xRange = xDialog.getChild("assign") + xRange.executeAction("SET", mkPropertyValues({"TEXT": "$Sheet1.$A$1:$K$11"})) + + # Set the provider for the range to be the data from the file + with self.ui_test.execute_dialog_through_command(".uno:DataProvider") as xDialog: + xRange = xDialog.getChild("select_db_range") + select_by_text(xRange, "TestDB") + + xFormat = xDialog.getChild("provider_lst") + select_by_text(xFormat, data_format) + + xURL = xDialog.getChild("ed_url") + xURL.executeAction("SET", mkPropertyValues({"TEXT": test_file})) + + xId = xDialog.getChild("ed_id") + xId.executeAction("SET", mkPropertyValues({"TEXT": identifier})) + + # Check that the import updated the A1 cell. + self.assertEqual(get_cell_by_position(xDoc, 0, 0, 0).getString(), expected_value) + + def test_html_import(self): + # tdf#169077: Without the fix the none of the data gets imported + test_file = os.path.join(os.getenv("SRCDIR"), + "sc", "qa", "unit", "data", "dataprovider", "html", + "test1.html") + self.do_import("HTML", test_file, "//table", "Col1") + # vim: set shiftwidth=4 softtabstop=4 expandtab: commit 79b78ab1751f699ac5923cc49cb04eab2ead108c Author: Neil Roberts <[email protected]> AuthorDate: Tue Dec 2 11:18:50 2025 +0100 Commit: Noel Grandin <[email protected]> CommitDate: Wed Dec 3 19:22:52 2025 +0100 dataprovider: Run the import directly on the main thread When Import is called with mbDeterministic set to true, all of the data providers would previously launch the import thread and then immediately join the thread thus tying the main thread to the import thread. This is equivalent to just doing the work directly on the main thread because in either case the main thread is blocked until the work is complete. However, normally when the main thread tries to acquire the solar mutex it will additionally process any pending yield requests from other threads. In this case this yield processing doesn’t happen because the import thread returns false for IsMainThread even though in practice it’s working as if it’s the main thread because the real main thread is waiting on it. This patch instead makes it just run the import code directly in the current thread when mbDeterministic is true instead of launching the other thread. This is probably slightly more efficient, but the main reason to do this is to fix UI tests that invoke this code. In that case UIObjectUnoObj::executeAction will call Scheduler::ProcessEventsToIdle after sending the click event but without this patch that function’s attempt to cause the main thread to yield will never return when using the SVP backend. That happens because that backend doesn’t release the solar mutex when waiting for the main thread to yield and the import thread doesn’t yield when trying to acquire the mutex because it isn’t the main thread. Instead deadlock occurs. Change-Id: I0c0cf4542f899118e5b6da8bb68b4d645e3fe40e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/194916 Tested-by: Jenkins Reviewed-by: Noel Grandin <[email protected]> diff --git a/sc/source/ui/dataprovider/csvdataprovider.cxx b/sc/source/ui/dataprovider/csvdataprovider.cxx index 30874102d446..9cac06a48f2a 100644 --- a/sc/source/ui/dataprovider/csvdataprovider.cxx +++ b/sc/source/ui/dataprovider/csvdataprovider.cxx @@ -154,21 +154,25 @@ void CSVDataProvider::Import() mpDoc->ResetClip(mpDocument, SCTAB(0)); mxCSVFetchThread = new CSVFetchThread(*mpDoc, mrDataSource.getURL(), [this]() { this->ImportFinished(); }, std::vector(mrDataSource.getDataTransformation())); - mxCSVFetchThread->launch(); if (mbDeterministic) { SolarMutexReleaser aReleaser; - mxCSVFetchThread->join(); + mxCSVFetchThread->execute(); // tdf#165658 An exception may have happened during the parsing of the file. - // Since parsing happens in a separate thread, here we need to check if - // something wrong happened and then rethrow the exception + // CSVFetchThread::execute catches the exception and stores it in case it is running in a + // separate thread. Here we need to check if something wrong happened and then rethrow the + // exception if (mxCSVFetchThread->IsParseError()) { std::rethrow_exception(mxCSVFetchThread->GetLastException()); } } + else + { + mxCSVFetchThread->launch(); + } } void CSVDataProvider::ImportFinished() diff --git a/sc/source/ui/dataprovider/htmldataprovider.cxx b/sc/source/ui/dataprovider/htmldataprovider.cxx index 29810a54384f..d4195d41b9f7 100644 --- a/sc/source/ui/dataprovider/htmldataprovider.cxx +++ b/sc/source/ui/dataprovider/htmldataprovider.cxx @@ -256,12 +256,14 @@ void HTMLDataProvider::Import() mpDoc->ResetClip(mpDocument, SCTAB(0)); mxHTMLFetchThread = new HTMLFetchThread(*mpDoc, mrDataSource.getURL(), mrDataSource.getID(), [this]() { this->ImportFinished(); }, std::vector(mrDataSource.getDataTransformation())); - mxHTMLFetchThread->launch(); - if (mbDeterministic) { SolarMutexReleaser aReleaser; - mxHTMLFetchThread->join(); + mxHTMLFetchThread->execute(); + } + else + { + mxHTMLFetchThread->launch(); } } diff --git a/sc/source/ui/dataprovider/sqldataprovider.cxx b/sc/source/ui/dataprovider/sqldataprovider.cxx index 05401075bf86..d015db233d33 100644 --- a/sc/source/ui/dataprovider/sqldataprovider.cxx +++ b/sc/source/ui/dataprovider/sqldataprovider.cxx @@ -147,12 +147,15 @@ void SQLDataProvider::Import() mxSQLFetchThread = new SQLFetchThread(*mpDoc, mrDataSource.getID(), std::bind(&SQLDataProvider::ImportFinished, this), std::vector(mrDataSource.getDataTransformation())); - mxSQLFetchThread->launch(); if (mbDeterministic) { SolarMutexReleaser aReleaser; - mxSQLFetchThread->join(); + mxSQLFetchThread->execute(); + } + else + { + mxSQLFetchThread->launch(); } } diff --git a/sc/source/ui/dataprovider/xmldataprovider.cxx b/sc/source/ui/dataprovider/xmldataprovider.cxx index 7572c3f358ec..3fe8ca016546 100644 --- a/sc/source/ui/dataprovider/xmldataprovider.cxx +++ b/sc/source/ui/dataprovider/xmldataprovider.cxx @@ -106,12 +106,15 @@ void XMLDataProvider::Import() mxXMLFetchThread = new XMLFetchThread( *mpDoc, mrDataSource.getURL(), mrDataSource.getXMLImportParam(), mrDataSource.getID(), [this]() { this->ImportFinished(); }, std::vector(mrDataSource.getDataTransformation())); - mxXMLFetchThread->launch(); if (mbDeterministic) { SolarMutexReleaser aReleaser; - mxXMLFetchThread->join(); + mxXMLFetchThread->execute(); + } + else + { + mxXMLFetchThread->launch(); } }
