From 411fdd7a9d8469b2efbe66873f330a4fceb963b8 Mon Sep 17 00:00:00 2001 From: Zhang Sheng Date: Thu, 22 May 2025 08:28:21 +0800 Subject: [PATCH 1/6] test: add file encoding detection for text files add file encoding detection for text files Log: add file encoding detection for text files --- tests/main.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/main.cpp b/tests/main.cpp index 631c1bb..4d05fec 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -3,12 +3,15 @@ // SPDX-License-Identifier: LGPL-3.0-or-later #include "docparser.h" + #include #include #include #include #include #include +#include + #include int main(int argc, char **argv) @@ -49,7 +52,12 @@ int main(int argc, char **argv) return 1; } - auto fromEncoding = Dtk::Core::DTextEncoding::detectFileEncoding(filePath); + QMimeDatabase mimeDatabase; + QMimeType mimeType = mimeDatabase.mimeTypeForFile(filePath); + QString mimeTypeName = mimeType.name(); + QByteArray fromEncoding = "utf-8"; + if (mimeTypeName.startsWith("text/")) + fromEncoding = Dtk::Core::DTextEncoding::detectFileEncoding(filePath); // Print file information qInfo() << "Parsing file:" << filePath << "From encoding: " << fromEncoding; From 3fde237d4a3ae917b22a04a9c18fb8f5133e216f Mon Sep 17 00:00:00 2001 From: Zhang Sheng Date: Thu, 22 May 2025 08:28:58 +0800 Subject: [PATCH 2/6] chore: bump version to 1.0.18 1.0.18 Log: --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index ce0643a..b496e4e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +docparser (1.0.18) unstable; urgency=medium + + * refactor + + -- Zhang Sheng Thu, 22 May 2025 08:28:35 +0800 + docparser (1.0.17) unstable; urgency=medium * add tests From 2310dde3f80cd9294bd79a25a8c5aecfff758baa Mon Sep 17 00:00:00 2001 From: Zhang Sheng Date: Mon, 26 May 2025 13:52:53 +0800 Subject: [PATCH 3/6] feat: integrate libmagic for MIME type detection in docparser - Added support for file type detection using libmagic to determine if a file is a text file based on its MIME type. - Introduced a new function `isTextFileByMimeType` that utilizes libmagic to check the MIME type of files. - Updated the file conversion logic to handle unknown extensions by checking the file content, enhancing the robustness of the document parsing process. - Included necessary logging for better traceability of MIME type detection results. Log: This enhancement improves the accuracy of file type detection in the document parser. --- CMakeLists.txt | 1 + src/docparser.cpp | 72 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 13be557..a56cffb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ pkg_check_modules(DEPS REQUIRED libxml-2.0 uuid tinyxml2 + libmagic ) # 添加子目录 diff --git a/src/docparser.cpp b/src/docparser.cpp index 60e18c5..795d464 100644 --- a/src/docparser.cpp +++ b/src/docparser.cpp @@ -23,6 +23,7 @@ #include #include #include +#include static bool isTextSuffix(std::string_view suffix) { @@ -39,6 +40,65 @@ static bool isTextSuffix(std::string_view suffix) return validSuffixes.count(lowercaseSuffix) > 0; } +/** + * @brief Check if a file is a text file using libmagic MIME type detection + * @param filename The path to the file to check + * @return true if the file is detected as text, false otherwise + */ +static bool isTextFileByMimeType(const std::string &filename) +{ + magic_t magic_cookie = magic_open(MAGIC_MIME_TYPE); + if (magic_cookie == nullptr) { + std::cerr << "ERROR: [isTextFileByMimeType] Failed to initialize libmagic" << std::endl; + return false; + } + + if (magic_load(magic_cookie, nullptr) != 0) { + std::cerr << "ERROR: [isTextFileByMimeType] Failed to load magic database: " + << magic_error(magic_cookie) << std::endl; + magic_close(magic_cookie); + return false; + } + + const char *mime_type = magic_file(magic_cookie, filename.c_str()); + if (mime_type == nullptr) { + std::cerr << "ERROR: [isTextFileByMimeType] Failed to detect MIME type for " + << filename << ": " << magic_error(magic_cookie) << std::endl; + magic_close(magic_cookie); + return false; + } + + std::string mimeStr(mime_type); + magic_close(magic_cookie); + + std::cout << "INFO: [isTextFileByMimeType] Detected MIME type: " << mimeStr + << " for file: " << filename << std::endl; + + // Check if MIME type starts with "text/" + bool isText = mimeStr.substr(0, 5) == "text/"; + + // Also consider some application types that are actually text + if (!isText) { + static const std::unordered_set textApplicationTypes = { + "application/json", + "application/xml", + "application/javascript", + "application/x-sh", + "application/x-shellscript", + "application/x-perl", + "application/x-python", + "application/x-ruby", + "application/x-awk", + "application/x-desktop", + "application/x-yaml" + }; + + isText = textApplicationTypes.count(mimeStr) > 0; + } + + return isText; +} + // 预处理后缀映射,避免多次strcasecmp比较 using FileCreator = std::unique_ptr (*)(const std::string &, const std::string &); @@ -157,7 +217,17 @@ static std::string doConvertFile(const std::string &filename, std::string suffix if (it != extensionMap.end()) { document = it->second(filename, suffix); } else { - throw std::logic_error("Unsupported file extension: " + filename); + // Extension not found in map, check if it's a text file by content + std::cout << "INFO: [doConvertFile] Unknown file extension '" << suffix + << "', checking file content for text type: " << filename << std::endl; + + if (isTextFileByMimeType(filename)) { + std::cout << "INFO: [doConvertFile] File detected as text by MIME type analysis: " + << filename << std::endl; + document = createTxt(filename, suffix); + } else { + throw std::logic_error("Unsupported file extension: " + filename); + } } } From f372612afc97e779cabb563a0e8851f9c6abb0f6 Mon Sep 17 00:00:00 2001 From: Zhang Sheng Date: Mon, 26 May 2025 14:21:00 +0800 Subject: [PATCH 4/6] chore: update debian control file to include libmagic-dev - Added libmagic-dev to the Build-Depends section to support MIME type detection in the document parser. - This change complements the recent integration of libmagic for improved file type detection. Log: Ensures all necessary dependencies are included for the new functionality. --- debian/control | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/debian/control b/debian/control index b9ccdc1..dbc8418 100644 --- a/debian/control +++ b/debian/control @@ -12,7 +12,8 @@ Build-Depends: libfreetype6-dev|libfreetype-dev, libxml2-dev, uuid-dev, - libtinyxml2-dev + libtinyxml2-dev, + libmagic-dev Standards-Version: 4.3.0 Package: libdocparser From beb3e8976d49a2fad33281b6da57a326b34d8795 Mon Sep 17 00:00:00 2001 From: Zhang Sheng Date: Mon, 26 May 2025 14:23:09 +0800 Subject: [PATCH 5/6] chore: bump version to 1.0.19 integrate libmagic for MIME type detection in docparser Log: --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index b496e4e..559230a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +docparser (1.0.19) unstable; urgency=medium + + * integrate libmagic for MIME type detection in docparser + + -- Zhang Sheng Mon, 26 May 2025 14:21:27 +0800 + docparser (1.0.18) unstable; urgency=medium * refactor From e0a1f90e832ffdc1ea1dc19173f650c9b3e7b755 Mon Sep 17 00:00:00 2001 From: Zhang Sheng Date: Mon, 26 May 2025 14:50:47 +0800 Subject: [PATCH 6/6] test: enhance CMake configuration for automated testing - Added support for automated testing by creating a new executable `docparser_autotest`. - Configured CMake to enable testing and link necessary libraries for the new test executable. - Set up a test named `DocParserAutoTest` with a timeout property to ensure robust test execution. Log: This update improves the testing framework integration for the document parser. --- tests/CMakeLists.txt | 40 +++- tests/autotest.cpp | 474 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 512 insertions(+), 2 deletions(-) create mode 100644 tests/autotest.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5546901..00f1795 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,14 +1,26 @@ -find_package(Qt6 REQUIRED COMPONENTS Core) +set(CMAKE_AUTOMOC ON) +find_package(Qt6 REQUIRED COMPONENTS Core Test) find_package(Dtk6 REQUIRED COMPONENTS Core) +# Enable testing +enable_testing() + # 设置测试程序的源文件 set(TEST_SOURCES main.cpp ) +# 设置自动化测试的源文件 +set(AUTOTEST_SOURCES + autotest.cpp +) + # 创建测试可执行文件 add_executable(docparser_test ${TEST_SOURCES}) +# 创建自动化测试可执行文件 +add_executable(docparser_autotest ${AUTOTEST_SOURCES}) + # 链接docparser库和Qt6Core target_link_libraries(docparser_test PRIVATE @@ -17,13 +29,37 @@ target_link_libraries(docparser_test Dtk6::Core ) +# 链接自动化测试所需的库 +target_link_libraries(docparser_autotest + PRIVATE + docparser + Qt6::Core + Qt6::Test +) + # 设置包含目录 target_include_directories(docparser_test PRIVATE ${CMAKE_SOURCE_DIR}/src ) +target_include_directories(docparser_autotest + PRIVATE + ${CMAKE_SOURCE_DIR}/src +) + +# 添加测试到CTest +add_test( + NAME DocParserAutoTest + COMMAND docparser_autotest +) + +# 设置测试属性 +set_tests_properties(DocParserAutoTest PROPERTIES + TIMEOUT 300 # 5 minutes timeout for all tests +) + # 安装测试程序(可选) -install(TARGETS docparser_test +install(TARGETS docparser_test docparser_autotest RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) diff --git a/tests/autotest.cpp b/tests/autotest.cpp new file mode 100644 index 0000000..d9cb7e5 --- /dev/null +++ b/tests/autotest.cpp @@ -0,0 +1,474 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "docparser.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief Unit test class for DocParser library + * + * This test suite validates the functionality of the DocParser library, + * including file parsing, format detection, error handling, and edge cases. + */ +class DocParserAutoTest : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + // Basic functionality tests + void testTextFileConversion_data(); + void testTextFileConversion(); + void testNonExistentFile(); + void testEmptyFile(); + + // File extension handling tests + void testCaseInsensitiveExtensions_data(); + void testCaseInsensitiveExtensions(); + void testFileWithoutExtension(); + void testFileWithMultipleDots(); + + // Error handling tests + void testInvalidFilePath(); + void testPermissionDeniedFile(); + void testCorruptedFile(); + + // Performance and edge case tests + void testLargeFile(); + void testSpecialCharactersInPath(); + void testUnicodeContent(); + + // MIME type detection tests + void testMimeTypeDetection(); + +private: + QString createTestFile(const QString &content, const QString &suffix = "txt"); + QString createBinaryTestFile(const QByteArray &data, const QString &suffix); + void verifyConversionResult(const std::string &result, const QString &expectedContent); + +private: + QTemporaryDir *m_tempDir = nullptr; + QStringList m_createdFiles; +}; + +void DocParserAutoTest::initTestCase() +{ + qInfo() << "INFO: [DocParserAutoTest::initTestCase] Starting DocParser unit tests"; + + // Create temporary directory for test files + m_tempDir = new QTemporaryDir(); + QVERIFY2(m_tempDir->isValid(), "Failed to create temporary directory for tests"); + + qInfo() << "INFO: [DocParserAutoTest::initTestCase] Test directory created:" << m_tempDir->path(); +} + +void DocParserAutoTest::cleanupTestCase() +{ + // Clean up created files + for (const QString &file : m_createdFiles) { + QFile::remove(file); + } + m_createdFiles.clear(); + + delete m_tempDir; + m_tempDir = nullptr; + + qInfo() << "INFO: [DocParserAutoTest::cleanupTestCase] Test cleanup completed"; +} + +void DocParserAutoTest::init() +{ + // Per-test initialization if needed +} + +void DocParserAutoTest::cleanup() +{ + // Per-test cleanup if needed +} + +void DocParserAutoTest::testTextFileConversion_data() +{ + QTest::addColumn("content"); + QTest::addColumn("extension"); + QTest::addColumn("description"); + + QTest::newRow("simple_txt") << "Hello, World!" + << "txt" + << "Simple text file"; + QTest::newRow("markdown") << "# Title\n\nThis is **markdown** content." + << "md" + << "Markdown file"; + QTest::newRow("json") << R"({"key": "value", "number": 42})" + << "json" + << "JSON file"; + QTest::newRow("xml") << "\nvalue" + << "xml" + << "XML file"; + QTest::newRow("html") << "

Test

" + << "html" + << "HTML file"; + QTest::newRow("css") << "body { color: red; font-size: 14px; }" + << "css" + << "CSS file"; + QTest::newRow("javascript") << "function test() { return 'hello'; }" + << "js" + << "JavaScript file"; + QTest::newRow("empty_text") << "" + << "txt" + << "Empty text file"; + QTest::newRow("multiline") << "Line 1\nLine 2\nLine 3\n" + << "txt" + << "Multi-line text"; +} + +void DocParserAutoTest::testTextFileConversion() +{ + QFETCH(QString, content); + QFETCH(QString, extension); + QFETCH(QString, description); + + qInfo() << "INFO: [DocParserAutoTest::testTextFileConversion] Testing" << description; + + QString testFile = createTestFile(content, extension); + QVERIFY(!testFile.isEmpty()); + + std::string result = DocParser::convertFile(testFile.toStdString()); + + if (content.isEmpty()) { + QVERIFY2(result.empty(), qPrintable(QString("Expected empty result for: %1").arg(description))); + } else { + verifyConversionResult(result, content); + } +} + +void DocParserAutoTest::testNonExistentFile() +{ + qInfo() << "INFO: [DocParserAutoTest::testNonExistentFile] Testing non-existent file"; + + QString nonExistentFile = m_tempDir->path() + "/non_existent_file.txt"; + std::string result = DocParser::convertFile(nonExistentFile.toStdString()); + + QVERIFY2(result.empty(), "Non-existent file should return empty content"); +} + +void DocParserAutoTest::testEmptyFile() +{ + qInfo() << "INFO: [DocParserAutoTest::testEmptyFile] Testing empty file"; + + QString emptyFile = createTestFile("", "txt"); + QVERIFY(!emptyFile.isEmpty()); + + std::string result = DocParser::convertFile(emptyFile.toStdString()); + QVERIFY2(result.empty(), "Empty file should return empty content"); +} + +void DocParserAutoTest::testCaseInsensitiveExtensions_data() +{ + QTest::addColumn("extension"); + QTest::addColumn("content"); + + QTest::newRow("uppercase_TXT") << "TXT" + << "Uppercase extension test"; + QTest::newRow("mixed_case_Txt") << "Txt" + << "Mixed case extension test"; + QTest::newRow("uppercase_MD") << "MD" + << "# Uppercase Markdown"; + QTest::newRow("mixed_case_Html") << "Html" + << "Mixed case HTML"; +} + +void DocParserAutoTest::testCaseInsensitiveExtensions() +{ + QFETCH(QString, extension); + QFETCH(QString, content); + + qInfo() << "INFO: [DocParserAutoTest::testCaseInsensitiveExtensions] Testing extension:" << extension; + + QString testFile = createTestFile(content, extension); + QVERIFY(!testFile.isEmpty()); + + std::string result = DocParser::convertFile(testFile.toStdString()); + verifyConversionResult(result, content); +} + +void DocParserAutoTest::testFileWithoutExtension() +{ + qInfo() << "INFO: [DocParserAutoTest::testFileWithoutExtension] Testing file without extension"; + + QString content = "File without extension"; + QTemporaryFile tempFile(m_tempDir->path() + "/no_extension_XXXXXX"); + QVERIFY(tempFile.open()); + + QTextStream stream(&tempFile); + stream << content; + tempFile.close(); + + // Keep the file around for testing + tempFile.setAutoRemove(false); + m_createdFiles << tempFile.fileName(); + + std::string result = DocParser::convertFile(tempFile.fileName().toStdString()); + QVERIFY2(result.empty(), "File without extension should return empty content"); +} + +void DocParserAutoTest::testFileWithMultipleDots() +{ + qInfo() << "INFO: [DocParserAutoTest::testFileWithMultipleDots] Testing file with multiple dots"; + + QString content = "File with multiple dots in name"; + QString fileName = m_tempDir->path() + "/test.file.with.dots.txt"; + + QFile file(fileName); + QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text)); + + QTextStream stream(&file); + stream << content; + file.close(); + + m_createdFiles << fileName; + + std::string result = DocParser::convertFile(fileName.toStdString()); + verifyConversionResult(result, content); +} + +void DocParserAutoTest::testInvalidFilePath() +{ + qInfo() << "INFO: [DocParserAutoTest::testInvalidFilePath] Testing invalid file paths"; + + QStringList invalidPaths = { + "", // Empty string + "/dev/null/invalid/path.txt", // Invalid path + "/proc/invalid_file.txt", // Permission issues + QString(1000, 'a') + ".txt" // Extremely long path + }; + + for (const QString &path : invalidPaths) { + std::string result = DocParser::convertFile(path.toStdString()); + QVERIFY2(result.empty(), qPrintable(QString("Invalid path should return empty: %1").arg(path))); + } +} + +void DocParserAutoTest::testPermissionDeniedFile() +{ + qInfo() << "INFO: [DocParserAutoTest::testPermissionDeniedFile] Testing permission denied scenario"; + + // Create a file and remove read permissions + QString content = "Permission test content"; + QString testFile = createTestFile(content, "txt"); + QVERIFY(!testFile.isEmpty()); + + // Remove read permissions + QFile::setPermissions(testFile, QFileDevice::WriteOwner); + + std::string result = DocParser::convertFile(testFile.toStdString()); + + // Restore permissions for cleanup + QFile::setPermissions(testFile, QFileDevice::ReadOwner | QFileDevice::WriteOwner); + + // File without read permission should return empty content + QVERIFY2(result.empty(), "File without read permission should return empty content"); +} + +void DocParserAutoTest::testCorruptedFile() +{ + qInfo() << "INFO: [DocParserAutoTest::testCorruptedFile] Testing corrupted file handling"; + + // Create a file with invalid content that looks like a document but isn't + QByteArray corruptedData; + corruptedData.append("PK"); // ZIP signature start (like DOCX) + corruptedData.append('\x03'); + corruptedData.append('\x04'); + corruptedData.append("corrupted_content_not_valid_zip"); + + QString corruptedFile = createBinaryTestFile(corruptedData, "docx"); + QVERIFY(!corruptedFile.isEmpty()); + + std::string result = DocParser::convertFile(corruptedFile.toStdString()); + // Corrupted files should be handled gracefully and return empty content + QVERIFY2(result.empty(), "Corrupted file should return empty content"); +} + +void DocParserAutoTest::testLargeFile() +{ + qInfo() << "INFO: [DocParserAutoTest::testLargeFile] Testing large file handling"; + + // Create a large text file (1MB) + const int largeSize = 1024 * 1024; // 1MB + QString largeContent; + largeContent.reserve(largeSize); + + for (int i = 0; i < largeSize / 50; ++i) { + largeContent += QString("This is line %1 with some content.\n").arg(i); + } + + QString largeFile = createTestFile(largeContent, "txt"); + QVERIFY(!largeFile.isEmpty()); + + // Measure parsing time + QElapsedTimer timer; + timer.start(); + + std::string result = DocParser::convertFile(largeFile.toStdString()); + + qint64 elapsed = timer.elapsed(); + qInfo() << "INFO: [DocParserAutoTest::testLargeFile] Large file parsing took" << elapsed << "ms"; + + QVERIFY2(!result.empty(), "Large file should be parsed successfully"); + QVERIFY2(elapsed < 10000, "Large file parsing should complete within 10 seconds"); // Performance check +} + +void DocParserAutoTest::testSpecialCharactersInPath() +{ + qInfo() << "INFO: [DocParserAutoTest::testSpecialCharactersInPath] Testing special characters in file path"; + + QString content = "Content with special path characters"; + + // Test various special characters in filename + QStringList specialNames = { + "test file with spaces.txt", + "test-file-with-dashes.txt", + "test_file_with_underscores.txt", + "测试中文文件名.txt", // Chinese characters + "файл_тест.txt" // Cyrillic characters + }; + + for (const QString &specialName : specialNames) { + QString specialPath = m_tempDir->path() + "/" + specialName; + + QFile file(specialPath); + if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream stream(&file); + stream << content; + file.close(); + + m_createdFiles << specialPath; + + std::string result = DocParser::convertFile(specialPath.toStdString()); + verifyConversionResult(result, content); + } + } +} + +void DocParserAutoTest::testUnicodeContent() +{ + qInfo() << "INFO: [DocParserAutoTest::testUnicodeContent] Testing Unicode content handling"; + + QStringList unicodeContents = { + "Hello 世界! 🌍", + "Здравствуй мир!", + "مرحبا بالعالم", + "こんにちは世界", + "Emoji test: 😀🎉📝✨" + }; + + for (const QString &unicodeContent : unicodeContents) { + QString testFile = createTestFile(unicodeContent, "txt"); + QVERIFY(!testFile.isEmpty()); + + std::string result = DocParser::convertFile(testFile.toStdString()); + QVERIFY2(!result.empty(), "Unicode content should be parsed successfully"); + + // Verify Unicode content is preserved + QString resultStr = QString::fromStdString(result); + QVERIFY2(resultStr.contains(unicodeContent.split(' ').first()), + "Unicode characters should be preserved in parsing result"); + } +} + +void DocParserAutoTest::testMimeTypeDetection() +{ + qInfo() << "INFO: [DocParserAutoTest::testMimeTypeDetection] Testing MIME type detection"; + + // Create a file without extension but with text content + QString content = "This is a text file without proper extension"; + QString noExtFile = m_tempDir->path() + "/no_extension_text_file"; + + QFile file(noExtFile); + QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text)); + + QTextStream stream(&file); + stream << content; + file.close(); + + m_createdFiles << noExtFile; + + // The library should detect this as a text file via MIME type + std::string result = DocParser::convertFile(noExtFile.toStdString()); + // Note: This test depends on the MIME type detection implementation + // It may return empty if MIME detection is strict about extensions + qInfo() << "INFO: [DocParserAutoTest::testMimeTypeDetection] MIME detection result length:" << result.length(); +} + +QString DocParserAutoTest::createTestFile(const QString &content, const QString &suffix) +{ + QString fileName = m_tempDir->path() + QString("/test_file_%1.%2").arg(QRandomGenerator::global()->generate()).arg(suffix); + + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qCritical() << "ERROR: [DocParserAutoTest::createTestFile] Failed to create test file:" << fileName; + return QString(); + } + + QTextStream stream(&file); + stream << content; + file.close(); + + m_createdFiles << fileName; + return fileName; +} + +QString DocParserAutoTest::createBinaryTestFile(const QByteArray &data, const QString &suffix) +{ + QString fileName = m_tempDir->path() + QString("/binary_test_file_%1.%2").arg(QRandomGenerator::global()->generate()).arg(suffix); + + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly)) { + qCritical() << "ERROR: [DocParserAutoTest::createBinaryTestFile] Failed to create binary test file:" << fileName; + return QString(); + } + + file.write(data); + file.close(); + + m_createdFiles << fileName; + return fileName; +} + +void DocParserAutoTest::verifyConversionResult(const std::string &result, const QString &expectedContent) +{ + QVERIFY2(!result.empty(), "Conversion result should not be empty"); + + QString resultStr = QString::fromStdString(result); + + // Verify that the result contains the expected content or is at least reasonable + QVERIFY2(resultStr.length() > 0, "Result should have content"); + + // For text files, the content should be similar (allowing for encoding differences) + if (expectedContent.length() > 0) { + // Basic sanity check - result shouldn't be drastically different in length + double lengthRatio = static_cast(resultStr.length()) / expectedContent.length(); + QVERIFY2(lengthRatio >= 0.5 && lengthRatio <= 2.0, + qPrintable(QString("Result length ratio %1 seems unreasonable").arg(lengthRatio))); + } +} + +// Include the moc file for Qt's meta-object system +#include "autotest.moc" + +// Test main function +QTEST_MAIN(DocParserAutoTest)