diff --git a/tests/src/test_bitarchivereader.cpp b/tests/src/test_bitarchivereader.cpp index 094f4f49..497a1d4c 100644 --- a/tests/src/test_bitarchivereader.cpp +++ b/tests/src/test_bitarchivereader.cpp @@ -18,6 +18,7 @@ #include "utils/filesystem.hpp" #include "utils/format.hpp" #include "utils/shared_lib.hpp" +#include "utils/source_location.hpp" #include #include @@ -70,100 +71,96 @@ static_assert( std::is_move_constructible< BitArchiveItemInfo >::value, static_assert( std::is_move_assignable< BitArchiveItemInfo >::value, "BitArchiveItemInfo is not move-assignable." ); +void require_archive_tests( const BitArchiveReader& info, const source_location& location ) { #ifdef BIT7Z_BUILD_FOR_P7ZIP -#define REQUIRE_ARCHIVE_TESTS( info ) \ - do { \ - const auto& detectedFormat = (info).detectedFormat(); \ - if ( detectedFormat == BitFormat::Rar || detectedFormat == BitFormat::Rar5 ) { \ - break; \ - } \ - REQUIRE_NOTHROW( (info).test() ); \ - for ( uint32_t index = 0; index < (info).itemsCount(); ++index ) { \ - REQUIRE_NOTHROW( (info).testItem( index ) ); \ - } \ - REQUIRE_THROWS_AS( (info).testItem( (info).itemsCount() ), BitException ); \ - } while( false ) -#else -#define REQUIRE_ARCHIVE_TESTS( info ) \ - do { \ - REQUIRE_NOTHROW( (info).test() ); \ - for ( uint32_t index = 0; index < (info).itemsCount(); ++index ) { \ - REQUIRE_NOTHROW( (info).testItem( index ) ); \ - } \ - REQUIRE_THROWS_AS( (info).testItem( (info).itemsCount() ), BitException ); \ - } while( false ) + const auto& detectedFormat = (info).detectedFormat(); + if ( detectedFormat == BitFormat::Rar || detectedFormat == BitFormat::Rar5 ) { + return; + } #endif + REQUIRE_NOTHROW( ( info ).test() ); + for ( uint32_t index = 0; index < ( info ).itemsCount(); ++index ) { + REQUIRE_NOTHROW( ( info ).testItem( index ) ); + } + REQUIRE_THROWS_AS( ( info ).testItem( ( info ).itemsCount() ), BitException ); +} -#define REQUIRE_ARCHIVE_ITEM( format, item, expectedItem ) \ - do { \ - INFO( "Failed while checking file " << Catch::StringMaker< tstring >::convert( (item).name() ) );\ - REQUIRE( (item).isDir() == (expectedItem).fileInfo.isDir ); \ - \ - if ( !(item).isDir() ) { \ - REQUIRE( (item).isEncrypted() == (expectedItem).isEncrypted ); \ - } \ - \ - if ( format_has_path_metadata( format ) ) { \ - REQUIRE( (item).extension() == (expectedItem).fileInfo.ext ); \ - REQUIRE( (item).name() == (expectedItem).fileInfo.name ); \ - REQUIRE( (item).path() == (expectedItem).inArchivePath ); \ - } \ - \ - if ( format_has_size_metadata( format ) ) { \ - /* Note: some archive formats (e.g. BZip2) do not provide the size metadata! */ \ - REQUIRE( (item).size() == (expectedItem).fileInfo.size ); \ - } \ - \ - if ( ( format_has_crc( format ) && !(item).itemProperty( BitProperty::CRC ).isEmpty() ) && \ - ( ( (format) != BitFormat::Rar5 ) || !(item).isEncrypted() ) ) { \ - /* For some reason, encrypted Rar5 archives messes up the values of CRCs*/ \ - REQUIRE( (item).crc() == (expectedItem).fileInfo.crc32 ); \ - } \ - } while( false ) - -#define REQUIRE_ARCHIVE_CONTENT( info, input ) \ - do { \ - REQUIRE_FALSE( (info).archiveProperties().empty() ); \ - \ - const auto& archive_content = (input).content(); \ - REQUIRE( (info).itemsCount() == archive_content.items.size() ); \ - REQUIRE( (info).filesCount() == archive_content.fileCount ); \ - REQUIRE( (info).foldersCount() == archive_content.items.size() - archive_content.fileCount ); \ - \ - const auto& format = (info).format(); \ - if ( format_has_size_metadata( format ) ) { \ - REQUIRE( (info).size() == archive_content.size ); \ - REQUIRE( (info).packSize() == (input).packedSize() ); \ - } \ - \ - REQUIRE_FALSE( (info).isMultiVolume() ); \ - REQUIRE( (info).volumesCount() == 1 ); \ - \ - std::vector< BitArchiveItemInfo > items; \ - REQUIRE_NOTHROW( items = (info).items() ); \ - REQUIRE( items.size() == (info).itemsCount() ); \ - \ - const bool archive_stores_paths = format_has_path_metadata( format ); \ - const bool from_filesystem = !(info).archivePath().empty(); \ - size_t found_items = 0; \ - for ( const auto& archivedItem : archive_content.items ) { \ - for ( const auto& item : items ) { \ - if ( archive_stores_paths || (from_filesystem) ) { \ - if ( item.name() != archivedItem.fileInfo.name ) { \ - continue; \ - } \ - REQUIRE( (info).find( item.path() ) != (info).cend() ); \ - REQUIRE( (info).contains( item.path() ) ); \ - } \ - REQUIRE( (info).isItemEncrypted( item.index() ) == archivedItem.isEncrypted ); \ - REQUIRE( (info).isItemFolder( item.index() ) == archivedItem.fileInfo.isDir ); \ - REQUIRE_ARCHIVE_ITEM( format, item, archivedItem ); \ - found_items++; \ - break; \ - } \ - } \ - REQUIRE( items.size() == found_items ); \ - } while ( false ) +void require_archive_item( const BitInFormat& format, + const BitArchiveItem& item, + const ArchivedItem& expectedItem, + const source_location& location ) { + INFO( "Failed while checking archive item " << Catch::StringMaker< tstring >::convert( item.name() ) ); + INFO( " from " << location.file_name() << ":" << location.line() ); + REQUIRE( item.isDir() == expectedItem.fileInfo.isDir ); + + if ( !item.isDir() ) { + REQUIRE( item.isEncrypted() == expectedItem.isEncrypted ); + } + + if ( format_has_path_metadata( format ) ) { + REQUIRE( item.extension() == expectedItem.fileInfo.ext ); + REQUIRE( item.name() == expectedItem.fileInfo.name ); + REQUIRE( item.path() == expectedItem.inArchivePath ); + } + + if ( format_has_size_metadata( format ) ) { + /* Note: some archive formats (e.g. BZip2) do not provide the size metadata! */ + REQUIRE( item.size() == expectedItem.fileInfo.size ); + } + + if ( ( format_has_crc( format ) && !item.itemProperty( BitProperty::CRC ).isEmpty() ) && + ( ( format != BitFormat::Rar5 ) || !item.isEncrypted() ) ) { + /* For some reason, encrypted Rar5 archives messes up the values of CRCs*/ + REQUIRE( item.crc() == expectedItem.fileInfo.crc32 ); + } +} + +inline void require_archive_content( const BitArchiveReader& info, + const TestInputArchive& input, + const source_location& location ) { + INFO( "Failed while checking content of " << Catch::StringMaker< tstring >::convert( info.archivePath() ) ); + INFO( " from " << location.file_name() << ":" << location.line() ); + REQUIRE_FALSE( info.archiveProperties().empty() ); + + const auto& archive_content = input.content(); + REQUIRE( info.itemsCount() == archive_content.items.size() ); + REQUIRE( info.filesCount() == archive_content.fileCount ); + REQUIRE( info.foldersCount() == ( archive_content.items.size() - archive_content.fileCount ) ); + + const auto& format = info.format(); + if ( format_has_size_metadata( format ) ) { + REQUIRE( info.size() == archive_content.size ); + REQUIRE( info.packSize() == input.packedSize() ); + } + + REQUIRE_FALSE( info.isMultiVolume() ); + REQUIRE( info.volumesCount() == 1 ); + + std::vector< BitArchiveItemInfo > items; + REQUIRE_NOTHROW( items = info.items() ); + REQUIRE( items.size() == info.itemsCount() ); + + const bool archive_stores_paths = format_has_path_metadata( format ); + const bool from_filesystem = !info.archivePath().empty(); + size_t found_items = 0; + for ( const auto& archivedItem : archive_content.items ) { + for ( const auto& item : items ) { + if ( archive_stores_paths || (from_filesystem) ) { + if ( item.name() != archivedItem.fileInfo.name ) { + continue; + } + REQUIRE( info.find( item.path() ) != info.cend() ); + REQUIRE( info.contains( item.path() ) ); + } + REQUIRE( info.isItemEncrypted( item.index() ) == archivedItem.isEncrypted ); + REQUIRE( info.isItemFolder( item.index() ) == archivedItem.fileInfo.isDir ); + require_archive_item( format, item, archivedItem, location ); + found_items++; + break; + } + } + REQUIRE( items.size() == found_items ); +} struct SingleFileArchive : public TestInputArchive { SingleFileArchive( std::string extension, const BitInFormat& format, std::size_t packedSize ) @@ -219,8 +216,8 @@ TEMPLATE_TEST_CASE( "BitArchiveReader: Reading archives containing only a single } REQUIRE_FALSE( info.hasEncryptedItems() ); REQUIRE_FALSE( info.isEncrypted() ); - REQUIRE_ARCHIVE_CONTENT( info, testArchive ); - REQUIRE_ARCHIVE_TESTS( info ); + require_archive_content( info, testArchive, BIT7Z_CURRENT_LOCATION ); + require_archive_tests( info, BIT7Z_CURRENT_LOCATION ); } } @@ -254,8 +251,8 @@ TEMPLATE_TEST_CASE( "BitArchiveReader: Reading archives containing multiple file } REQUIRE_FALSE( info.hasEncryptedItems() ); REQUIRE_FALSE( info.isEncrypted() ); - REQUIRE_ARCHIVE_CONTENT( info, testArchive ); - REQUIRE_ARCHIVE_TESTS( info ); + require_archive_content( info, testArchive, BIT7Z_CURRENT_LOCATION ); + require_archive_tests( info, BIT7Z_CURRENT_LOCATION ); } } @@ -290,8 +287,8 @@ TEMPLATE_TEST_CASE( "BitArchiveReader: Reading archives containing multiple item } REQUIRE_FALSE( info.hasEncryptedItems() ); REQUIRE_FALSE( info.isEncrypted() ); - REQUIRE_ARCHIVE_CONTENT( info, testArchive ); - REQUIRE_ARCHIVE_TESTS( info ); + require_archive_content( info, testArchive, BIT7Z_CURRENT_LOCATION ); + require_archive_tests( info, BIT7Z_CURRENT_LOCATION ); } } @@ -333,7 +330,7 @@ TEMPLATE_TEST_CASE( "BitArchiveReader: Reading archives containing encrypted ite const BitArchiveReader info( test::sevenzip_lib(), inputArchive, testArchive.format() ); REQUIRE( info.hasEncryptedItems() ); REQUIRE( info.isEncrypted() ); - REQUIRE_ARCHIVE_CONTENT( info, testArchive ); + require_archive_content( info, testArchive, BIT7Z_CURRENT_LOCATION ); REQUIRE_THROWS( info.test() ); } @@ -341,8 +338,8 @@ TEMPLATE_TEST_CASE( "BitArchiveReader: Reading archives containing encrypted ite const BitArchiveReader info( test::sevenzip_lib(), inputArchive, testArchive.format(), password ); REQUIRE( info.hasEncryptedItems() ); REQUIRE( info.isEncrypted() ); - REQUIRE_ARCHIVE_CONTENT( info, testArchive ); - REQUIRE_ARCHIVE_TESTS( info ); + require_archive_content( info, testArchive, BIT7Z_CURRENT_LOCATION ); + require_archive_tests( info, BIT7Z_CURRENT_LOCATION ); } } } @@ -388,8 +385,8 @@ TEMPLATE_TEST_CASE( "BitArchiveReader: Reading header-encrypted archives", const BitArchiveReader info( test::sevenzip_lib(), inputArchive, testArchive.format(), password ); REQUIRE( info.hasEncryptedItems() ); REQUIRE( info.isEncrypted() ); - REQUIRE_ARCHIVE_CONTENT( info, testArchive ); - REQUIRE_ARCHIVE_TESTS( info ); + require_archive_content( info, testArchive, BIT7Z_CURRENT_LOCATION ); + require_archive_tests( info, BIT7Z_CURRENT_LOCATION ); } } } @@ -418,7 +415,7 @@ TEST_CASE( "BitArchiveReader: Reading metadata of multi-volume archives", "[bita REQUIRE( info.volumesCount() == 3 ); REQUIRE( info.itemsCount() == 1 ); REQUIRE( info.items()[ 0 ].name() == arcFileName.stem().string< tchar >() ); - REQUIRE_ARCHIVE_TESTS( info ); + require_archive_tests( info, BIT7Z_CURRENT_LOCATION ); } SECTION( "Opening as a whole archive" ) { @@ -427,8 +424,11 @@ TEST_CASE( "BitArchiveReader: Reading metadata of multi-volume archives", "[bita testArchive.format() ); // REQUIRE( info.isMultiVolume() ); // REQUIRE( info.volumesCount() == 3 ); - REQUIRE_ARCHIVE_ITEM( testArchive.format(), info.items()[ 0 ], testArchive.content().items[ 0 ] ); - REQUIRE_ARCHIVE_TESTS( info ); + require_archive_item( testArchive.format(), + info.items()[ 0 ], + testArchive.content().items[ 0 ], + BIT7Z_CURRENT_LOCATION ); + require_archive_tests( info, BIT7Z_CURRENT_LOCATION ); } } } @@ -441,10 +441,10 @@ TEST_CASE( "BitArchiveReader: Reading metadata of multi-volume archives", "[bita REQUIRE( info.itemsCount() == 1 ); const ArchivedItem expectedItem{ clouds, clouds.name }; - REQUIRE_ARCHIVE_ITEM( BitFormat::Rar5, info.items()[ 0 ], expectedItem ); + require_archive_item( BitFormat::Rar5, info.items()[ 0 ], expectedItem, BIT7Z_CURRENT_LOCATION ); #ifndef BIT7Z_BUILD_FOR_P7ZIP - REQUIRE_ARCHIVE_TESTS( info ); + require_archive_tests( info, BIT7Z_CURRENT_LOCATION ); #endif } @@ -456,10 +456,10 @@ TEST_CASE( "BitArchiveReader: Reading metadata of multi-volume archives", "[bita REQUIRE( info.itemsCount() == 1 ); const ArchivedItem expectedItem{ clouds, clouds.name }; - REQUIRE_ARCHIVE_ITEM( BitFormat::Rar, info.items()[ 0 ], expectedItem ); + require_archive_item( BitFormat::Rar, info.items()[ 0 ], expectedItem, BIT7Z_CURRENT_LOCATION ); #ifndef BIT7Z_BUILD_FOR_P7ZIP - REQUIRE_ARCHIVE_TESTS( info ); + require_archive_tests( info, BIT7Z_CURRENT_LOCATION ); #endif } } @@ -491,8 +491,8 @@ TEMPLATE_TEST_CASE( "BitArchiveReader: Reading an empty archive", REQUIRE( info.archivePath().empty() ); // No archive path for buffer/streamed archives } REQUIRE_FALSE( info.isEncrypted() ); - REQUIRE_ARCHIVE_CONTENT( info, testArchive ); - REQUIRE_ARCHIVE_TESTS( info ); + require_archive_content( info, testArchive, BIT7Z_CURRENT_LOCATION ); + require_archive_tests( info, BIT7Z_CURRENT_LOCATION ); } } @@ -502,7 +502,7 @@ TEST_CASE( "BitArchiveReader: Solid archive detection", "[bitarchivereader]" ) { SECTION( "Solid 7z" ) { const BitArchiveReader info( test::sevenzip_lib(), BIT7Z_STRING( "solid.7z" ), BitFormat::SevenZip ); REQUIRE( info.isSolid() ); - REQUIRE_ARCHIVE_TESTS( info ); + require_archive_tests( info, BIT7Z_CURRENT_LOCATION ); } SECTION( "Solid RAR" ) { @@ -510,14 +510,14 @@ TEST_CASE( "BitArchiveReader: Solid archive detection", "[bitarchivereader]" ) { REQUIRE( info.isSolid() ); #ifndef BIT7Z_BUILD_FOR_P7ZIP - REQUIRE_ARCHIVE_TESTS( info ); + require_archive_tests( info, BIT7Z_CURRENT_LOCATION ); #endif } SECTION( "Non solid 7z" ) { const BitArchiveReader info( test::sevenzip_lib(), BIT7Z_STRING( "non_solid.7z" ), BitFormat::SevenZip ); REQUIRE( !info.isSolid() ); - REQUIRE_ARCHIVE_TESTS( info ); + require_archive_tests( info, BIT7Z_CURRENT_LOCATION ); } SECTION( "Non-solid RAR" ) { @@ -525,7 +525,7 @@ TEST_CASE( "BitArchiveReader: Solid archive detection", "[bitarchivereader]" ) { REQUIRE( !info.isSolid() ); #ifndef BIT7Z_BUILD_FOR_P7ZIP - REQUIRE_ARCHIVE_TESTS( info ); + require_archive_tests( info, BIT7Z_CURRENT_LOCATION ); #endif } } diff --git a/tests/src/utils/source_location.hpp b/tests/src/utils/source_location.hpp new file mode 100644 index 00000000..cf9d186a --- /dev/null +++ b/tests/src/utils/source_location.hpp @@ -0,0 +1,88 @@ +/* + * bit7z - A C++ static library to interface with the 7-zip shared libraries. + * Copyright (c) 2014-2023 Riccardo Ostani - All Rights Reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef SOURCE_LOCATION_HPP +#define SOURCE_LOCATION_HPP + +#include + +#include + +namespace bit7z { // NOLINT(modernize-concat-nested-namespaces) +namespace test { + +/** + * A naive version of C++20's std::source_location. + */ +struct source_location final { + public: + using number_type = std::uint_least32_t; + + +#ifdef _MSC_VER + BIT7Z_NODISCARD + static constexpr auto current( const char* fileName, + const char* functionName, + number_type lineNumber ) noexcept -> source_location { + return { fileName, functionName, lineNumber }; + } +#else + BIT7Z_NODISCARD + static constexpr auto current( const char* fileName = __builtin_FILE(), + const char* functionName = __builtin_FUNCTION(), + number_type lineNumber = __builtin_LINE() ) noexcept -> source_location { + return { fileName, functionName, lineNumber }; + } +#endif + + source_location( const source_location& ) = default; + + auto operator=( const source_location& ) -> source_location& = default; + + source_location( source_location&& ) = default; + + auto operator=( source_location&& ) -> source_location& = default; + + ~source_location() = default; + + BIT7Z_NODISCARD + constexpr auto file_name() const noexcept -> const char* { + return mFileName; + } + + BIT7Z_NODISCARD + BIT7Z_MAYBE_UNUSED + constexpr auto function_name() const noexcept -> const char* { + return mFunctionName; + } + + BIT7Z_NODISCARD + constexpr auto line() const noexcept -> source_location::number_type { + return mLineNumber; + } + + private: + constexpr source_location( const char* fileName, const char* functionName, number_type lineNumber ) noexcept + : mFileName( fileName ), mFunctionName( functionName ), mLineNumber( lineNumber ) {} + + const char* mFileName; + const char* mFunctionName; + number_type mLineNumber; +}; + +#ifdef _MSC_VER +#define BIT7Z_CURRENT_LOCATION source_location::current( __FILE__, __FUNCTION__, __LINE__ ) +#else +#define BIT7Z_CURRENT_LOCATION source_location::current() +#endif + +} // namespace test +} // namespace bit7z + +#endif //SOURCE_LOCATION_HPP \ No newline at end of file